diff options
| author | Tshepang Mbambo <tshepang@gmail.com> | 2025-04-28 06:25:10 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-04-28 06:25:10 +0200 |
| commit | 1b53c12753a9ecead0e9728e01dcc2a7c07c18f9 (patch) | |
| tree | 87e76fd223f1937ee26537e1028d44e7f6b65544 | |
| parent | d91ffb6da5c306150e53f73a361f69837bc39851 (diff) | |
| parent | aa15830ee2defbac8e784db5510bb914e6e4c820 (diff) | |
| download | rust-1b53c12753a9ecead0e9728e01dcc2a7c07c18f9.tar.gz rust-1b53c12753a9ecead0e9728e01dcc2a7c07c18f9.zip | |
Merge pull request #2358 from rust-lang/rustc-pull
Rustc pull update
416 files changed, 5188 insertions, 3041 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index efe4157aae2..93316b9cff7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -91,6 +91,17 @@ jobs: # Check the `calculate_matrix` job to see how is the matrix defined. include: ${{ fromJSON(needs.calculate_matrix.outputs.jobs) }} steps: + - name: Install cargo in AWS CodeBuild + if: matrix.codebuild + run: | + # Check if cargo is installed + if ! command -v cargo &> /dev/null; then + echo "Cargo not found, installing Rust..." + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile=minimal + # Make cargo available in PATH + echo "$HOME/.cargo/bin" >> $GITHUB_PATH + fi + - name: disable git crlf conversion run: git config --global core.autocrlf false @@ -165,6 +176,8 @@ jobs: run: src/ci/scripts/install-ninja.sh - name: enable ipv6 on Docker + # Don't run on codebuild because systemctl is not available + if: ${{ !matrix.codebuild }} run: src/ci/scripts/enable-docker-ipv6.sh # Disable automatic line ending conversion (again). On Windows, when we're diff --git a/Cargo.lock b/Cargo.lock index cfdd873e80f..be704e84a50 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -737,6 +737,7 @@ dependencies = [ "libc", "miow", "miropt-test-tools", + "rayon", "regex", "rustfix", "semver", @@ -3699,6 +3700,9 @@ dependencies = [ [[package]] name = "rustc_fs_util" version = "0.0.0" +dependencies = [ + "tempfile", +] [[package]] name = "rustc_graphviz" @@ -4011,7 +4015,6 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", - "tempfile", "tracing", ] @@ -4158,7 +4161,6 @@ dependencies = [ "rustc_data_structures", "rustc_index", "rustc_macros", - "rustc_serialize", "rustc_type_ir", "rustc_type_ir_macros", "tracing", @@ -4917,15 +4919,6 @@ dependencies = [ ] [[package]] -name = "spdx" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58b69356da67e2fc1f542c71ea7e654a361a79c938e4424392ecf4fa065d2193" -dependencies = [ - "smallvec", -] - -[[package]] name = "spdx-expression" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5744,9 +5737,9 @@ dependencies = [ [[package]] name = "wasi-preview1-component-adapter-provider" -version = "29.0.1" +version = "31.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcd9f21bbde82ba59e415a8725e6ad0d0d7e9e460b1a3ccbca5bdee952c1a324" +checksum = "86fabda09a0d89ffd1615b297b4a5d4b4d99df9598aeb24685837e63019e927b" [[package]] name = "wasm-bindgen" @@ -5808,9 +5801,9 @@ dependencies = [ [[package]] name = "wasm-component-ld" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "580305a8e3f1b7a79859a8db897de643533b2851c5eb080fe5800233f16dec88" +checksum = "a60a07a994a3538b57d8c5f8caba19f4793fb4c7156276e5e90e90acbb829e20" dependencies = [ "anyhow", "clap", @@ -5818,7 +5811,7 @@ dependencies = [ "libc", "tempfile", "wasi-preview1-component-adapter-provider", - "wasmparser 0.223.1", + "wasmparser 0.229.0", "wat", "windows-sys 0.59.0", "winsplit", @@ -5845,39 +5838,24 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.223.1" +version = "0.229.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a0a96fdeaee8fbeb4bd917fb8157d48c0d61c3b1f4ee4c363c8e8d68b2f4fe8" -dependencies = [ - "leb128", - "wasmparser 0.223.1", -] - -[[package]] -name = "wasm-encoder" -version = "0.228.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d30290541f2d4242a162bbda76b8f2d8b1ac59eab3568ed6f2327d52c9b2c4" +checksum = "38ba1d491ecacb085a2552025c10a675a6fddcbd03b1fc9b36c536010ce265d2" dependencies = [ "leb128fmt", - "wasmparser 0.228.0", + "wasmparser 0.229.0", ] [[package]] name = "wasm-metadata" -version = "0.223.1" +version = "0.229.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7e37883181704d96b89dbd8f1291be13694c71cd0663aebb94b46d235a377" +checksum = "78fdb7d29a79191ab363dc90c1ddd3a1e880ffd5348d92d48482393a9e6c5f4d" dependencies = [ "anyhow", "indexmap", - "serde", - "serde_derive", - "serde_json", - "spdx", - "url", - "wasm-encoder 0.223.1", - "wasmparser 0.223.1", + "wasm-encoder 0.229.0", + "wasmparser 0.229.0", ] [[package]] @@ -5901,9 +5879,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.223.1" +version = "0.229.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "664b980991ed9a8c834eb528a8979ab1109edcf52dc05dd5751e2cc3fb31035d" +checksum = "0cc3b1f053f5d41aa55640a1fa9b6d1b8a9e4418d118ce308d20e24ff3575a8c" dependencies = [ "bitflags", "hashbrown", @@ -5913,34 +5891,23 @@ dependencies = [ ] [[package]] -name = "wasmparser" -version = "0.228.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4abf1132c1fdf747d56bbc1bb52152400c70f336870f968b85e89ea422198ae3" -dependencies = [ - "bitflags", - "indexmap", - "semver", -] - -[[package]] name = "wast" -version = "228.0.0" +version = "229.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5aae124478cb51439f6587f074a3a5e835afd22751c59a87b2e2a882727c97" +checksum = "63fcaff613c12225696bb163f79ca38ffb40e9300eff0ff4b8aa8b2f7eadf0d9" dependencies = [ "bumpalo", "leb128fmt", "memchr", "unicode-width 0.2.0", - "wasm-encoder 0.228.0", + "wasm-encoder 0.229.0", ] [[package]] name = "wat" -version = "1.228.0" +version = "1.229.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ec29c89a8d055df988de7236483bf569988ac3d6905899f6af5ef920f9385ad" +checksum = "4189bad08b70455a9e9e67dc126d2dcf91fac143a80f1046747a5dde6d4c33e0" dependencies = [ "wast", ] @@ -6399,9 +6366,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.223.1" +version = "0.229.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fc2fcc52a79f6f010a89c867e53e06d4227f86c58984add3e37f32b8e7af5f0" +checksum = "7f550067740e223bfe6c4878998e81cdbe2529dd9a793dc49248dd6613394e8b" dependencies = [ "anyhow", "bitflags", @@ -6410,17 +6377,17 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.223.1", + "wasm-encoder 0.229.0", "wasm-metadata", - "wasmparser 0.223.1", + "wasmparser 0.229.0", "wit-parser", ] [[package]] name = "wit-parser" -version = "0.223.1" +version = "0.229.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "263fde17f1fbe55a413f16eb59094bf751795c6da469a05c3d45ea6c77df6b40" +checksum = "459c6ba62bf511d6b5f2a845a2a736822e38059c1cfa0b644b467bbbfae4efa6" dependencies = [ "anyhow", "id-arena", @@ -6431,7 +6398,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.223.1", + "wasmparser 0.229.0", ] [[package]] diff --git a/INSTALL.md b/INSTALL.md index 30e08201d6d..98eb825cd10 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -75,8 +75,31 @@ See [the rustc-dev-guide for more info][sysllvm]. 2. Configure the build settings: + If you're unsure which build configurations to use and need a good default, you + can run the interactive `x.py setup` command. This will guide you through selecting + a config profile, setting up the LSP, configuring a Git hook, etc. + + With `configure` script, you can handle multiple configurations in a single + command which is useful to create complex/advanced config files. For example: + ```sh - ./configure + ./configure --build=aarch64-unknown-linux-gnu \ + --enable-full-tools \ + --enable-profiler \ + --enable-sanitizers \ + --enable-compiler-docs \ + --set target.aarch64-unknown-linux-gnu.linker=clang \ + --set target.aarch64-unknown-linux-gnu.ar=/rustroot/bin/llvm-ar \ + --set target.aarch64-unknown-linux-gnu.ranlib=/rustroot/bin/llvm-ranlib \ + --set llvm.link-shared=true \ + --set llvm.thin-lto=true \ + --set llvm.libzstd=true \ + --set llvm.ninja=false \ + --set rust.debug-assertions=false \ + --set rust.jemalloc \ + --set rust.use-lld=true \ + --set rust.lto=thin \ + --set rust.codegen-units=1 ``` If you plan to use `x.py install` to create an installation, you can either diff --git a/bootstrap.example.toml b/bootstrap.example.toml index 72c4492d465..72fd56fb5bd 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -500,7 +500,7 @@ # building without optimizations takes much longer than optimizing. Further, some platforms # fail to build without this optimization (c.f. #65352). # The valid options are: -# true - Enable optimizations. +# true - Enable optimizations (same as 3). # false - Disable optimizations. # 0 - Disable optimizations. # 1 - Basic optimizations. diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 1532ca77f71..8986430141b 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1927,7 +1927,7 @@ impl AttrArgs { } /// Delimited arguments, as used in `#[attr()/[]/{}]` or `mac!()/[]/{}`. -#[derive(Clone, Encodable, Decodable, Debug)] +#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] pub struct DelimArgs { pub dspan: DelimSpan, pub delim: Delimiter, // Note: `Delimiter::Invisible` never occurs @@ -1942,18 +1942,6 @@ impl DelimArgs { } } -impl<CTX> HashStable<CTX> for DelimArgs -where - CTX: crate::HashStableContext, -{ - fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { - let DelimArgs { dspan, delim, tokens } = self; - dspan.hash_stable(ctx, hasher); - delim.hash_stable(ctx, hasher); - tokens.hash_stable(ctx, hasher); - } -} - /// Represents a macro definition. #[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] pub struct MacroDef { diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index 294c6c9ba7a..1471262d2d6 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -6,6 +6,7 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc( html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", test(attr(deny(warnings))) @@ -14,7 +15,6 @@ #![feature(associated_type_defaults)] #![feature(box_patterns)] #![feature(if_let_guard)] -#![feature(let_chains)] #![feature(negative_impls)] #![feature(never_type)] #![feature(rustdoc_internals)] diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 534e85e8bcb..1e14b4d6723 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -32,12 +32,12 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(rust_logo)] #![feature(assert_matches)] #![feature(box_patterns)] #![feature(exact_size_is_empty)] #![feature(if_let_guard)] -#![feature(let_chains)] #![feature(rustdoc_internals)] // tidy-alphabetical-end @@ -916,7 +916,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } fn lower_delim_args(&self, args: &DelimArgs) -> DelimArgs { - DelimArgs { dspan: args.dspan, delim: args.delim, tokens: args.tokens.clone() } + args.clone() } /// Lower an associated item constraint. diff --git a/compiler/rustc_ast_passes/src/lib.rs b/compiler/rustc_ast_passes/src/lib.rs index 093199cf342..7956057f88e 100644 --- a/compiler/rustc_ast_passes/src/lib.rs +++ b/compiler/rustc_ast_passes/src/lib.rs @@ -4,11 +4,11 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(rust_logo)] #![feature(box_patterns)] #![feature(if_let_guard)] #![feature(iter_is_partitioned)] -#![feature(let_chains)] #![feature(rustdoc_internals)] // tidy-alphabetical-end diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 9546ffafb19..48da9fc63b8 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -878,6 +878,7 @@ impl<'a> State<'a> { } } } else { + self.end(); // Close the ibox for the pattern. self.word(","); } self.end(); // Close enclosing cbox. diff --git a/compiler/rustc_attr_data_structures/src/lib.rs b/compiler/rustc_attr_data_structures/src/lib.rs index c61b44b273d..679fe935484 100644 --- a/compiler/rustc_attr_data_structures/src/lib.rs +++ b/compiler/rustc_attr_data_structures/src/lib.rs @@ -1,7 +1,7 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(rust_logo)] -#![feature(let_chains)] #![feature(rustdoc_internals)] // tidy-alphabetical-end diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 63597b37cb5..55c3df003fe 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -3,7 +3,7 @@ use std::collections::BTreeMap; use std::ops::Deref; use std::sync::LazyLock; -use rustc_ast::{self as ast, DelimArgs}; +use rustc_ast as ast; use rustc_attr_data_structures::AttributeKind; use rustc_errors::{DiagCtxtHandle, Diagnostic}; use rustc_feature::Features; @@ -315,11 +315,7 @@ impl<'sess> AttributeParser<'sess> { fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs { match args { ast::AttrArgs::Empty => AttrArgs::Empty, - ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(DelimArgs { - dspan: args.dspan, - delim: args.delim, - tokens: args.tokens.clone(), - }), + ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(args.clone()), // This is an inert key-value attribute - it will never be visible to macros // after it gets lowered to HIR. Therefore, we can extract literals to handle // nonterminals in `#[doc]` (e.g. `#[doc = $e]`). diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index 249e71ef70d..b9692c01e2c 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -77,8 +77,8 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(rust_logo)] -#![feature(let_chains)] #![feature(rustdoc_internals)] // tidy-alphabetical-end diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index 134f30ed6f5..0de4bd67f0c 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -49,7 +49,7 @@ impl<'tcx> UniverseInfo<'tcx> { UniverseInfo::RelateTys { expected, found } } - pub(crate) fn report_error( + pub(crate) fn report_erroneous_element( &self, mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>, placeholder: ty::PlaceholderRegion, @@ -68,7 +68,7 @@ impl<'tcx> UniverseInfo<'tcx> { mbcx.buffer_error(err); } UniverseInfo::TypeOp(ref type_op_info) => { - type_op_info.report_error(mbcx, placeholder, error_element, cause); + type_op_info.report_erroneous_element(mbcx, placeholder, error_element, cause); } UniverseInfo::Other => { // FIXME: This error message isn't great, but it doesn't show @@ -145,8 +145,11 @@ pub(crate) trait TypeOpInfo<'tcx> { error_region: Option<ty::Region<'tcx>>, ) -> Option<Diag<'infcx>>; + /// Constraints require that `error_element` appear in the + /// values of `placeholder`, but this cannot be proven to + /// hold. Report an error. #[instrument(level = "debug", skip(self, mbcx))] - fn report_error( + fn report_erroneous_element( &self, mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>, placeholder: ty::PlaceholderRegion, @@ -190,12 +193,7 @@ pub(crate) trait TypeOpInfo<'tcx> { let nice_error = self.nice_error(mbcx, cause, placeholder_region, error_region); debug!(?nice_error); - - if let Some(nice_error) = nice_error { - mbcx.buffer_error(nice_error); - } else { - mbcx.buffer_error(self.fallback_error(tcx, span)); - } + mbcx.buffer_error(nice_error.unwrap_or_else(|| self.fallback_error(tcx, span))); } } @@ -450,7 +448,8 @@ fn try_extract_error_from_region_constraints<'a, 'tcx>( ty::ReVar(vid) => universe_of_region(vid), _ => ty::UniverseIndex::ROOT, }; - let matches = + // Are the two regions the same? + let regions_the_same = |a_region: Region<'tcx>, b_region: Region<'tcx>| match (a_region.kind(), b_region.kind()) { (RePlaceholder(a_p), RePlaceholder(b_p)) => a_p.bound == b_p.bound, _ => a_region == b_region, @@ -459,7 +458,7 @@ fn try_extract_error_from_region_constraints<'a, 'tcx>( |constraint: &Constraint<'tcx>, cause: &SubregionOrigin<'tcx>, exact| match *constraint { Constraint::RegSubReg(sub, sup) if ((exact && sup == placeholder_region) - || (!exact && matches(sup, placeholder_region))) + || (!exact && regions_the_same(sup, placeholder_region))) && sup != sub => { Some((sub, cause.clone())) @@ -468,23 +467,21 @@ fn try_extract_error_from_region_constraints<'a, 'tcx>( if (exact && sup == placeholder_region && !universe_of_region(vid).can_name(placeholder_universe)) - || (!exact && matches(sup, placeholder_region)) => + || (!exact && regions_the_same(sup, placeholder_region)) => { Some((ty::Region::new_var(infcx.tcx, vid), cause.clone())) } _ => None, }; - let mut info = region_constraints - .constraints - .iter() - .find_map(|(constraint, cause)| check(constraint, cause, true)); - if info.is_none() { - info = region_constraints + + let mut find_culprit = |exact_match: bool| { + region_constraints .constraints .iter() - .find_map(|(constraint, cause)| check(constraint, cause, false)); - } - let (sub_region, cause) = info?; + .find_map(|(constraint, cause)| check(constraint, cause, exact_match)) + }; + + let (sub_region, cause) = find_culprit(true).or_else(|| find_culprit(false))?; debug!(?sub_region, "cause = {:#?}", cause); let error = match (error_region, sub_region.kind()) { diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 4423edb0605..3bec07afa0f 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -405,7 +405,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let universe = placeholder.universe; let universe_info = self.regioncx.universe_info(universe); - universe_info.report_error(self, placeholder, error_element, cause); + universe_info.report_erroneous_element(self, placeholder, error_element, cause); } RegionErrorKind::RegionError { fr_origin, longer_fr, shorter_fr, is_reported } => { diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 83a9827e8f8..51d37353520 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -2,12 +2,12 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(rust_logo)] #![feature(assert_matches)] #![feature(box_patterns)] #![feature(file_buffered)] #![feature(if_let_guard)] -#![feature(let_chains)] #![feature(negative_impls)] #![feature(never_type)] #![feature(rustc_attrs)] diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index f8af9e59f63..c256051c122 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1628,30 +1628,23 @@ impl<'tcx> RegionInferenceContext<'tcx> { let longer_fr_scc = self.constraint_sccs.scc(longer_fr); debug!("check_bound_universal_region: longer_fr_scc={:?}", longer_fr_scc,); - for error_element in self.scc_values.elements_contained_in(longer_fr_scc) { - match error_element { - RegionElement::Location(_) | RegionElement::RootUniversalRegion(_) => {} - // If we have some bound universal region `'a`, then the only - // elements it can contain is itself -- we don't know anything - // else about it! - RegionElement::PlaceholderRegion(placeholder1) => { - if placeholder == placeholder1 { - continue; - } - } - } - + // If we have some bound universal region `'a`, then the only + // elements it can contain is itself -- we don't know anything + // else about it! + if let Some(error_element) = self + .scc_values + .elements_contained_in(longer_fr_scc) + .find(|e| *e != RegionElement::PlaceholderRegion(placeholder)) + { + // Stop after the first error, it gets too noisy otherwise, and does not provide more information. errors_buffer.push(RegionErrorKind::BoundUniversalRegionError { longer_fr, error_element, placeholder, }); - - // Stop after the first error, it gets too noisy otherwise, and does not provide more - // information. - break; + } else { + debug!("check_bound_universal_region: all bounds satisfied"); } - debug!("check_bound_universal_region: all bounds satisfied"); } #[instrument(level = "debug", skip(self, infcx, errors_buffer))] @@ -2071,7 +2064,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { constraint.category }; - match category { + let interest = match category { // Returns usually provide a type to blame and have specially written diagnostics, // so prioritize them. ConstraintCategory::Return(_) => 0, @@ -2123,9 +2116,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { // specific, and are not used for relations that would make sense to blame. ConstraintCategory::BoringNoLocation => 6, // Do not blame internal constraints. - ConstraintCategory::Internal => 7, - ConstraintCategory::IllegalUniverse => 8, - } + ConstraintCategory::IllegalUniverse => 7, + ConstraintCategory::Internal => 8, + }; + + debug!("constraint {constraint:?} category: {category:?}, interest: {interest:?}"); + + interest }; let best_choice = if blame_source { diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index d9ac5b5cb13..f1427218cdb 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -21,7 +21,7 @@ rustc_index::newtype_index! { /// An individual element in a region value -- the value of a /// particular region variable consists of a set of these elements. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub(crate) enum RegionElement { /// A point in the control-flow graph. Location(Location), diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index bcd40f980e6..70e817db2a6 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -5,6 +5,7 @@ #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(assert_matches)] @@ -12,7 +13,6 @@ #![feature(box_patterns)] #![feature(decl_macro)] #![feature(if_let_guard)] -#![feature(let_chains)] #![feature(proc_macro_internals)] #![feature(proc_macro_quote)] #![feature(rustdoc_internals)] diff --git a/compiler/rustc_codegen_cranelift/example/example.rs b/compiler/rustc_codegen_cranelift/example/example.rs index 1ef2aa5dd8e..aeb38331edb 100644 --- a/compiler/rustc_codegen_cranelift/example/example.rs +++ b/compiler/rustc_codegen_cranelift/example/example.rs @@ -1,6 +1,6 @@ #![feature(no_core, unboxed_closures)] #![no_core] -#![allow(dead_code)] +#![allow(dead_code, unnecessary_transmutes)] extern crate mini_core; diff --git a/compiler/rustc_codegen_gcc/example/example.rs b/compiler/rustc_codegen_gcc/example/example.rs index 03470b74d0a..888fa89201e 100644 --- a/compiler/rustc_codegen_gcc/example/example.rs +++ b/compiler/rustc_codegen_gcc/example/example.rs @@ -1,6 +1,6 @@ #![feature(no_core, unboxed_closures)] #![no_core] -#![allow(dead_code)] +#![allow(dead_code, unnecessary_transmutes)] extern crate mini_core; @@ -11,11 +11,7 @@ fn abc(a: u8) -> u8 { } fn bcd(b: bool, a: u8) -> u8 { - if b { - a * 2 - } else { - a * 3 - } + if b { a * 2 } else { a * 3 } } fn call() { diff --git a/compiler/rustc_codegen_gcc/rust-toolchain b/compiler/rustc_codegen_gcc/rust-toolchain index fd898c59707..452d3f22dc5 100644 --- a/compiler/rustc_codegen_gcc/rust-toolchain +++ b/compiler/rustc_codegen_gcc/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2025-04-17" +channel = "nightly-2025-04-25" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index dbdf37ee6c9..396c6d57950 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -165,10 +165,10 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { let mut input_registers = vec![]; for op in rust_operands { - if let InlineAsmOperandRef::In { reg, .. } = *op { - if let ConstraintOrRegister::Register(reg_name) = reg_to_gcc(reg) { - input_registers.push(reg_name); - } + if let InlineAsmOperandRef::In { reg, .. } = *op + && let ConstraintOrRegister::Register(reg_name) = reg_to_gcc(reg) + { + input_registers.push(reg_name); } } diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index a63da6b6e27..918195364ff 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -33,12 +33,11 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { } pub fn const_bitcast(&self, value: RValue<'gcc>, typ: Type<'gcc>) -> RValue<'gcc> { - if value.get_type() == self.bool_type.make_pointer() { - if let Some(pointee) = typ.get_pointee() { - if pointee.dyncast_vector().is_some() { - panic!() - } - } + if value.get_type() == self.bool_type.make_pointer() + && let Some(pointee) = typ.get_pointee() + && pointee.dyncast_vector().is_some() + { + panic!() } // NOTE: since bitcast makes a value non-constant, don't bitcast if not necessary as some // SIMD builtins require a constant value. diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs index acb39374628..0a67bd7bc71 100644 --- a/compiler/rustc_codegen_gcc/src/consts.rs +++ b/compiler/rustc_codegen_gcc/src/consts.rs @@ -242,10 +242,10 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { let fn_attrs = self.tcx.codegen_fn_attrs(def_id); let global = if def_id.is_local() && !self.tcx.is_foreign_item(def_id) { - if let Some(global) = self.get_declared_value(sym) { - if self.val_ty(global) != self.type_ptr_to(gcc_type) { - span_bug!(self.tcx.def_span(def_id), "Conflicting types for static"); - } + if let Some(global) = self.get_declared_value(sym) + && self.val_ty(global) != self.type_ptr_to(gcc_type) + { + span_bug!(self.tcx.def_span(def_id), "Conflicting types for static"); } let is_tls = fn_attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL); diff --git a/compiler/rustc_codegen_gcc/src/debuginfo.rs b/compiler/rustc_codegen_gcc/src/debuginfo.rs index 55e01687400..f3ced864395 100644 --- a/compiler/rustc_codegen_gcc/src/debuginfo.rs +++ b/compiler/rustc_codegen_gcc/src/debuginfo.rs @@ -126,14 +126,15 @@ fn make_mir_scope<'gcc, 'tcx>( return; }; - if let Some(ref vars) = *variables { - if !vars.contains(scope) && scope_data.inlined.is_none() { - // Do not create a DIScope if there are no variables defined in this - // MIR `SourceScope`, and it's not `inlined`, to avoid debuginfo bloat. - debug_context.scopes[scope] = parent_scope; - instantiated.insert(scope); - return; - } + if let Some(ref vars) = *variables + && !vars.contains(scope) + && scope_data.inlined.is_none() + { + // Do not create a DIScope if there are no variables defined in this + // MIR `SourceScope`, and it's not `inlined`, to avoid debuginfo bloat. + debug_context.scopes[scope] = parent_scope; + instantiated.insert(scope); + return; } let loc = cx.lookup_debug_loc(scope_data.span.lo()); diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index 202764d5649..955f9020235 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -136,14 +136,12 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri }); features.extend(feats); - if diagnostics { - if let Some(f) = check_tied_features(sess, &featsmap) { - sess.dcx().emit_err(TargetFeatureDisableOrEnable { - features: f, - span: None, - missing_features: None, - }); - } + if diagnostics && let Some(f) = check_tied_features(sess, &featsmap) { + sess.dcx().emit_err(TargetFeatureDisableOrEnable { + features: f, + span: None, + missing_features: None, + }); } features diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 624fdb4043c..555f164e53f 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -22,7 +22,7 @@ #![warn(rust_2018_idioms)] #![warn(unused_lifetimes)] #![deny(clippy::pattern_type_mismatch)] -#![allow(clippy::needless_lifetimes)] +#![allow(clippy::needless_lifetimes, clippy::uninlined_format_args)] // Some "regular" crates we want to share with rustc extern crate object; diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs index ae98b3d0b56..5745acce6fe 100644 --- a/compiler/rustc_codegen_gcc/src/type_of.rs +++ b/compiler/rustc_codegen_gcc/src/type_of.rs @@ -102,10 +102,10 @@ fn uncached_gcc_type<'gcc, 'tcx>( let mut name = with_no_trimmed_paths!(layout.ty.to_string()); if let (&ty::Adt(def, _), &Variants::Single { index }) = (layout.ty.kind(), &layout.variants) + && def.is_enum() + && !def.variants().is_empty() { - if def.is_enum() && !def.variants().is_empty() { - write!(&mut name, "::{}", def.variant(index).name).unwrap(); - } + write!(&mut name, "::{}", def.variant(index).name).unwrap(); } if let (&ty::Coroutine(_, _), &Variants::Single { index }) = (layout.ty.kind(), &layout.variants) @@ -264,10 +264,10 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> { } fn immediate_gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc> { - if let BackendRepr::Scalar(ref scalar) = self.backend_repr { - if scalar.is_bool() { - return cx.type_i1(); - } + if let BackendRepr::Scalar(ref scalar) = self.backend_repr + && scalar.is_bool() + { + return cx.type_i1(); } self.gcc_type(cx) } diff --git a/compiler/rustc_codegen_gcc/tests/lang_tests_common.rs b/compiler/rustc_codegen_gcc/tests/lang_tests_common.rs index 64c932a2658..d5a0d71c4b2 100644 --- a/compiler/rustc_codegen_gcc/tests/lang_tests_common.rs +++ b/compiler/rustc_codegen_gcc/tests/lang_tests_common.rs @@ -1,5 +1,7 @@ //! The common code for `tests/lang_tests_*.rs` +#![allow(clippy::uninlined_format_args)] + use std::env::{self, current_dir}; use std::path::{Path, PathBuf}; use std::process::Command; diff --git a/compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs b/compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs index c1254c51ce9..e627886a9d5 100644 --- a/compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs +++ b/compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs @@ -5,6 +5,7 @@ // stdout: 10 // 10 // 42 +// 1 #![feature(no_core)] #![no_std] @@ -21,6 +22,8 @@ fn int_cast(a: u16, b: i16) -> (u8, u16, u32, usize, i8, i16, i32, isize, u8, u3 ) } +static mut ONE: usize = 1; + #[no_mangle] extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { let (a, b, c, d, e, f, g, h, i, j) = int_cast(10, 42); @@ -28,6 +31,10 @@ extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { libc::printf(b"%d\n\0" as *const u8 as *const i8, c); libc::printf(b"%ld\n\0" as *const u8 as *const i8, d); libc::printf(b"%ld\n\0" as *const u8 as *const i8, j); + + let ptr = ONE as *mut usize; + let value = ptr as usize; + libc::printf(b"%ld\n\0" as *const u8 as *const i8, value); } 0 } diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index a8b49e9552c..925898d8173 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -584,12 +584,10 @@ fn thin_lto( } } -fn enable_autodiff_settings(ad: &[config::AutoDiff], module: &mut ModuleCodegen<ModuleLlvm>) { +fn enable_autodiff_settings(ad: &[config::AutoDiff]) { for &val in ad { + // We intentionally don't use a wildcard, to not forget handling anything new. match val { - config::AutoDiff::PrintModBefore => { - unsafe { llvm::LLVMDumpModule(module.module_llvm.llmod()) }; - } config::AutoDiff::PrintPerf => { llvm::set_print_perf(true); } @@ -603,17 +601,23 @@ fn enable_autodiff_settings(ad: &[config::AutoDiff], module: &mut ModuleCodegen< llvm::set_inline(true); } config::AutoDiff::LooseTypes => { - llvm::set_loose_types(false); + llvm::set_loose_types(true); } config::AutoDiff::PrintSteps => { llvm::set_print(true); } - // We handle this below + // We handle this in the PassWrapper.cpp + config::AutoDiff::PrintPasses => {} + // We handle this in the PassWrapper.cpp + config::AutoDiff::PrintModBefore => {} + // We handle this in the PassWrapper.cpp config::AutoDiff::PrintModAfter => {} - // We handle this below + // We handle this in the PassWrapper.cpp config::AutoDiff::PrintModFinal => {} // This is required and already checked config::AutoDiff::Enable => {} + // We handle this below + config::AutoDiff::NoPostopt => {} } } // This helps with handling enums for now. @@ -647,27 +651,27 @@ pub(crate) fn run_pass_manager( // We then run the llvm_optimize function a second time, to optimize the code which we generated // in the enzyme differentiation pass. let enable_ad = config.autodiff.contains(&config::AutoDiff::Enable); - let stage = - if enable_ad { write::AutodiffStage::DuringAD } else { write::AutodiffStage::PostAD }; + let stage = if thin { + write::AutodiffStage::PreAD + } else { + if enable_ad { write::AutodiffStage::DuringAD } else { write::AutodiffStage::PostAD } + }; if enable_ad { - enable_autodiff_settings(&config.autodiff, module); + enable_autodiff_settings(&config.autodiff); } unsafe { write::llvm_optimize(cgcx, dcx, module, None, config, opt_level, opt_stage, stage)?; } - if cfg!(llvm_enzyme) && enable_ad { - // This is the post-autodiff IR, mainly used for testing and educational purposes. - if config.autodiff.contains(&config::AutoDiff::PrintModAfter) { - unsafe { llvm::LLVMDumpModule(module.module_llvm.llmod()) }; - } - + if cfg!(llvm_enzyme) && enable_ad && !thin { let opt_stage = llvm::OptStage::FatLTO; let stage = write::AutodiffStage::PostAD; - unsafe { - write::llvm_optimize(cgcx, dcx, module, None, config, opt_level, opt_stage, stage)?; + if !config.autodiff.contains(&config::AutoDiff::NoPostopt) { + unsafe { + write::llvm_optimize(cgcx, dcx, module, None, config, opt_level, opt_stage, stage)?; + } } // This is the final IR, so people should be able to inspect the optimized autodiff output, diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 18d221d232e..4ac77c8f7f1 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -572,6 +572,10 @@ pub(crate) unsafe fn llvm_optimize( let consider_ad = cfg!(llvm_enzyme) && config.autodiff.contains(&config::AutoDiff::Enable); let run_enzyme = autodiff_stage == AutodiffStage::DuringAD; + let print_before_enzyme = config.autodiff.contains(&config::AutoDiff::PrintModBefore); + let print_after_enzyme = config.autodiff.contains(&config::AutoDiff::PrintModAfter); + let print_passes = config.autodiff.contains(&config::AutoDiff::PrintPasses); + let merge_functions; let unroll_loops; let vectorize_slp; let vectorize_loop; @@ -579,13 +583,20 @@ pub(crate) unsafe fn llvm_optimize( // When we build rustc with enzyme/autodiff support, we want to postpone size-increasing // optimizations until after differentiation. Our pipeline is thus: (opt + enzyme), (full opt). // We therefore have two calls to llvm_optimize, if autodiff is used. + // + // We also must disable merge_functions, since autodiff placeholder/dummy bodies tend to be + // identical. We run opts before AD, so there is a chance that LLVM will merge our dummies. + // In that case, we lack some dummy bodies and can't replace them with the real AD code anymore. + // We then would need to abort compilation. This was especially common in test cases. if consider_ad && autodiff_stage != AutodiffStage::PostAD { + merge_functions = false; unroll_loops = false; vectorize_slp = false; vectorize_loop = false; } else { unroll_loops = opt_level != config::OptLevel::Size && opt_level != config::OptLevel::SizeMin; + merge_functions = config.merge_functions; vectorize_slp = config.vectorize_slp; vectorize_loop = config.vectorize_loop; } @@ -663,13 +674,16 @@ pub(crate) unsafe fn llvm_optimize( thin_lto_buffer, config.emit_thin_lto, config.emit_thin_lto_summary, - config.merge_functions, + merge_functions, unroll_loops, vectorize_slp, vectorize_loop, config.no_builtins, config.emit_lifetime_markers, run_enzyme, + print_before_enzyme, + print_after_enzyme, + print_passes, sanitizer_options.as_ref(), pgo_gen_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()), pgo_use_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()), diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 27f7f95f100..04c8118b616 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -594,6 +594,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { fn load(&mut self, ty: &'ll Type, ptr: &'ll Value, align: Align) -> &'ll Value { unsafe { let load = llvm::LLVMBuildLoad2(self.llbuilder, ty, ptr, UNNAMED); + let align = align.min(self.cx().tcx.sess.target.max_reliable_alignment()); llvm::LLVMSetAlignment(load, align.bytes() as c_uint); load } @@ -807,6 +808,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { assert_eq!(self.cx.type_kind(self.cx.val_ty(ptr)), TypeKind::Pointer); unsafe { let store = llvm::LLVMBuildStore(self.llbuilder, val, ptr); + let align = align.min(self.cx().tcx.sess.target.max_reliable_alignment()); let align = if flags.contains(MemFlags::UNALIGNED) { 1 } else { align.bytes() as c_uint }; llvm::LLVMSetAlignment(store, align); diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index e7c071d05aa..0147bd5a665 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -473,7 +473,7 @@ pub(crate) fn differentiate<'ll>( return Err(diag_handler.handle().emit_almost_fatal(AutoDiffWithoutEnable)); } - // Before dumping the module, we want all the TypeTrees to become part of the module. + // Here we replace the placeholder code with the actual autodiff code, which calls Enzyme. for item in diff_items.iter() { let name = item.source.clone(); let fn_def: Option<&llvm::Value> = cx.get_function(&name); diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 425381b0ffa..b2feeacdb46 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -6,6 +6,7 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(assert_matches)] @@ -15,7 +16,6 @@ #![feature(if_let_guard)] #![feature(impl_trait_in_assoc_type)] #![feature(iter_intersperse)] -#![feature(let_chains)] #![feature(rustdoc_internals)] #![feature(slice_as_array)] #![feature(try_blocks)] diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 9ff04f72903..ffb490dcdc2 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2454,6 +2454,9 @@ unsafe extern "C" { DisableSimplifyLibCalls: bool, EmitLifetimeMarkers: bool, RunEnzyme: bool, + PrintBeforeEnzyme: bool, + PrintAfterEnzyme: bool, + PrintPasses: bool, SanitizerOptions: Option<&SanitizerOptions>, PGOGenPath: *const c_char, PGOUsePath: *const c_char, diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index 34c84c64070..1e1bdfb5977 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -13,9 +13,9 @@ use object::read::archive::ArchiveFile; use object::read::macho::FatArch; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::memmap::Mmap; +use rustc_fs_util::TempDirBuilder; use rustc_session::Session; use rustc_span::Symbol; -use tempfile::Builder as TempFileBuilder; use tracing::trace; use super::metadata::search_for_section; @@ -501,7 +501,7 @@ impl<'a> ArArchiveBuilder<'a> { // it creates. We need it to be the default mode for back compat reasons however. (See // #107495) To handle this we are telling tempfile to create a temporary directory instead // and then inside this directory create a file using File::create. - let archive_tmpdir = TempFileBuilder::new() + let archive_tmpdir = TempDirBuilder::new() .suffix(".temp-archive") .tempdir_in(output.parent().unwrap_or_else(|| Path::new(""))) .map_err(|err| { diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 8de68925cab..323538969d7 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -18,7 +18,7 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_errors::{DiagCtxtHandle, LintDiagnostic}; -use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize}; +use rustc_fs_util::{TempDirBuilder, fix_windows_verbatim_for_gcc, try_canonicalize}; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_macros::LintDiagnostic; use rustc_metadata::fs::{METADATA_FILENAME, copy_to_stdout, emit_wrapper_file}; @@ -48,7 +48,6 @@ use rustc_target::spec::{ LinkerFeatures, LinkerFlavor, LinkerFlavorCli, Lld, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, }; -use tempfile::Builder as TempFileBuilder; use tracing::{debug, info, warn}; use super::archive::{ArchiveBuilder, ArchiveBuilderBuilder}; @@ -100,7 +99,7 @@ pub fn link_binary( }); if outputs.outputs.should_link() { - let tmpdir = TempFileBuilder::new() + let tmpdir = TempDirBuilder::new() .prefix("rustc") .tempdir() .unwrap_or_else(|error| sess.dcx().emit_fatal(errors::CreateTempDir { error })); @@ -2015,6 +2014,12 @@ fn add_linked_symbol_object( file.set_mangling(object::write::Mangling::None); } + if file.format() == object::BinaryFormat::MachO { + // Divide up the sections into sub-sections via symbols for dead code stripping. + // Without this flag, unused `#[no_mangle]` or `#[used]` cannot be discard on MachO targets. + file.set_subsections_via_symbols(); + } + // ld64 requires a relocation to load undefined symbols, see below. // Not strictly needed if linking with lld, but might as well do it there too. let ld64_section_helper = if file.format() == object::BinaryFormat::MachO { diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 5b2ed69535b..c927aae2c4c 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -2,13 +2,13 @@ #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(assert_matches)] #![feature(box_patterns)] #![feature(file_buffered)] #![feature(if_let_guard)] -#![feature(let_chains)] #![feature(negative_impls)] #![feature(rustdoc_internals)] #![feature(string_from_utf8_lossy_owned)] diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 4ca317e3a1e..40c63f2b250 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -158,6 +158,31 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.copy_op(&val, dest)?; } + sym::fadd_algebraic + | sym::fsub_algebraic + | sym::fmul_algebraic + | sym::fdiv_algebraic + | sym::frem_algebraic => { + let a = self.read_immediate(&args[0])?; + let b = self.read_immediate(&args[1])?; + + let op = match intrinsic_name { + sym::fadd_algebraic => BinOp::Add, + sym::fsub_algebraic => BinOp::Sub, + sym::fmul_algebraic => BinOp::Mul, + sym::fdiv_algebraic => BinOp::Div, + sym::frem_algebraic => BinOp::Rem, + + _ => bug!(), + }; + + let res = self.binary_op(op, &a, &b)?; + // `binary_op` already called `generate_nan` if needed. + + // FIXME: Miri should add some non-determinism to the result here to catch any dependences on exact computations. This has previously been done, but the behaviour was removed as part of constification. + self.write_immediate(*res, dest)?; + } + sym::ctpop | sym::cttz | sym::cttz_nonzero diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index da52d60ae59..7a0c2543c30 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -1,12 +1,12 @@ // tidy-alphabetical-start #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(rust_logo)] #![feature(assert_matches)] #![feature(box_patterns)] #![feature(decl_macro)] #![feature(if_let_guard)] -#![feature(let_chains)] #![feature(never_type)] #![feature(rustdoc_internals)] #![feature(slice_ptr_get)] diff --git a/compiler/rustc_data_structures/src/marker.rs b/compiler/rustc_data_structures/src/marker.rs index 5f07cfef133..dfd9bd32076 100644 --- a/compiler/rustc_data_structures/src/marker.rs +++ b/compiler/rustc_data_structures/src/marker.rs @@ -39,8 +39,15 @@ impls_dyn_send_neg!( [std::io::StderrLock<'_>] ); -#[cfg(any(unix, target_os = "hermit", target_os = "wasi", target_os = "solid_asp3"))] -// Consistent with `std`, `os_imp::Env` is `!Sync` in these platforms +#[cfg(any( + unix, + target_os = "hermit", + all(target_vendor = "fortanix", target_env = "sgx"), + target_os = "solid_asp3", + target_os = "wasi", + target_os = "xous" +))] +// Consistent with `std`, `env_imp::Env` is `!Sync` in these platforms impl !DynSend for std::env::VarsOs {} macro_rules! already_send { @@ -106,8 +113,15 @@ impls_dyn_sync_neg!( [std::sync::mpsc::Sender<T> where T] ); -#[cfg(any(unix, target_os = "hermit", target_os = "wasi", target_os = "solid_asp3"))] -// Consistent with `std`, `os_imp::Env` is `!Sync` in these platforms +#[cfg(any( + unix, + target_os = "hermit", + all(target_vendor = "fortanix", target_env = "sgx"), + target_os = "solid_asp3", + target_os = "wasi", + target_os = "xous" +))] +// Consistent with `std`, `env_imp::Env` is `!Sync` in these platforms impl !DynSync for std::env::VarsOs {} macro_rules! already_sync { diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 40cc82727a5..d18fa892814 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -7,10 +7,10 @@ // tidy-alphabetical-start #![allow(internal_features)] #![allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(decl_macro)] -#![feature(let_chains)] #![feature(panic_backtrace_config)] #![feature(panic_update_hook)] #![feature(result_flattening)] diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index c0c5dba4677..6f37bad9bb4 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -7,6 +7,7 @@ #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(array_windows)] @@ -17,7 +18,6 @@ #![feature(default_field_values)] #![feature(error_reporter)] #![feature(if_let_guard)] -#![feature(let_chains)] #![feature(negative_impls)] #![feature(never_type)] #![feature(rustc_attrs)] diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index 4222c9fe906..79f838e2e33 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -1,11 +1,11 @@ // tidy-alphabetical-start #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(rust_logo)] #![feature(array_windows)] #![feature(associated_type_defaults)] #![feature(if_let_guard)] -#![feature(let_chains)] #![feature(macro_metavar_expr)] #![feature(map_try_insert)] #![feature(proc_macro_diagnostic)] diff --git a/compiler/rustc_fs_util/Cargo.toml b/compiler/rustc_fs_util/Cargo.toml index baca3bc7d49..90a6acade8b 100644 --- a/compiler/rustc_fs_util/Cargo.toml +++ b/compiler/rustc_fs_util/Cargo.toml @@ -5,4 +5,5 @@ edition = "2024" [dependencies] # tidy-alphabetical-start +tempfile = "3.7.1" # tidy-alphabetical-end diff --git a/compiler/rustc_fs_util/src/lib.rs b/compiler/rustc_fs_util/src/lib.rs index 0df1b243d69..7a883a13b72 100644 --- a/compiler/rustc_fs_util/src/lib.rs +++ b/compiler/rustc_fs_util/src/lib.rs @@ -1,6 +1,8 @@ -use std::ffi::CString; +use std::ffi::{CString, OsStr}; use std::path::{Path, PathBuf, absolute}; -use std::{fs, io}; +use std::{env, fs, io}; + +use tempfile::TempDir; // Unfortunately, on windows, it looks like msvcrt.dll is silently translating // verbatim paths under the hood to non-verbatim paths! This manifests itself as @@ -102,3 +104,43 @@ pub fn path_to_c_string(p: &Path) -> CString { pub fn try_canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> { fs::canonicalize(&path).or_else(|_| absolute(&path)) } + +pub struct TempDirBuilder<'a, 'b> { + builder: tempfile::Builder<'a, 'b>, +} + +impl<'a, 'b> TempDirBuilder<'a, 'b> { + pub fn new() -> Self { + Self { builder: tempfile::Builder::new() } + } + + pub fn prefix<S: AsRef<OsStr> + ?Sized>(&mut self, prefix: &'a S) -> &mut Self { + self.builder.prefix(prefix); + self + } + + pub fn suffix<S: AsRef<OsStr> + ?Sized>(&mut self, suffix: &'b S) -> &mut Self { + self.builder.suffix(suffix); + self + } + + pub fn tempdir_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<TempDir> { + let dir = dir.as_ref(); + // On Windows in CI, we had been getting fairly frequent "Access is denied" + // errors when creating temporary directories. + // So this implements a simple retry with backoff loop. + #[cfg(windows)] + for wait in 1..11 { + match self.builder.tempdir_in(dir) { + Err(e) if e.kind() == io::ErrorKind::PermissionDenied => {} + t => return t, + } + std::thread::sleep(std::time::Duration::from_millis(1 << wait)); + } + self.builder.tempdir_in(dir) + } + + pub fn tempdir(&self) -> io::Result<TempDir> { + self.tempdir_in(env::temp_dir()) + } +} diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index a84857e3597..32064f96dd6 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -4,12 +4,12 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(associated_type_defaults)] #![feature(box_patterns)] #![feature(closure_track_caller)] #![feature(debug_closure_helpers)] #![feature(exhaustive_patterns)] -#![feature(let_chains)] #![feature(negative_impls)] #![feature(never_type)] #![feature(rustc_attrs)] diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 4f338c6a19e..f92b2aea160 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -730,7 +730,6 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) { .is_ok() { check_impl_items_against_trait(tcx, def_id, impl_trait_header); - check_on_unimplemented(tcx, def_id); } } } 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 06a7b1f6c29..bbf36fef1dd 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -379,13 +379,13 @@ fn compare_method_predicate_entailment<'tcx>( // Annoyingly, asking for the WF predicates of an array (with an unevaluated const (only?)) // will give back the well-formed predicate of the same array. let mut wf_args_seen: FxHashSet<_> = wf_args.iter().copied().collect(); - while let Some(arg) = wf_args.pop() { + while let Some(term) = wf_args.pop() { let Some(obligations) = rustc_trait_selection::traits::wf::obligations( infcx, param_env, impl_m_def_id, 0, - arg, + term, impl_m_span, ) else { continue; @@ -402,9 +402,9 @@ fn compare_method_predicate_entailment<'tcx>( | ty::ClauseKind::TypeOutlives(..) | ty::ClauseKind::Projection(..), ) => ocx.register_obligation(obligation), - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { - if wf_args_seen.insert(arg) { - wf_args.push(arg) + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => { + if wf_args_seen.insert(term) { + wf_args.push(term) } } _ => {} diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 33d5a86beb3..d13fafae4e8 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -76,12 +76,7 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { ) } - fn register_wf_obligation( - &self, - span: Span, - loc: Option<WellFormedLoc>, - arg: ty::GenericArg<'tcx>, - ) { + fn register_wf_obligation(&self, span: Span, loc: Option<WellFormedLoc>, term: ty::Term<'tcx>) { let cause = traits::ObligationCause::new( span, self.body_def_id, @@ -91,7 +86,7 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { self.tcx(), cause, self.param_env, - ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg))), + ty::ClauseKind::WellFormed(term), )); } } @@ -1486,8 +1481,41 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id tcx.def_span(param.def_id), matches!(param.kind, GenericParamDefKind::Type { .. }) .then(|| WellFormedLoc::Ty(param.def_id.expect_local())), - default, + default.as_term().unwrap(), ); + } else { + // If we've got a generic const parameter we still want to check its + // type is correct in case both it and the param type are fully concrete. + let GenericArgKind::Const(ct) = default.unpack() else { + continue; + }; + + let ct_ty = match ct.kind() { + ty::ConstKind::Infer(_) + | ty::ConstKind::Placeholder(_) + | ty::ConstKind::Bound(_, _) => unreachable!(), + ty::ConstKind::Error(_) | ty::ConstKind::Expr(_) => continue, + ty::ConstKind::Value(cv) => cv.ty, + ty::ConstKind::Unevaluated(uv) => { + infcx.tcx.type_of(uv.def).instantiate(infcx.tcx, uv.args) + } + ty::ConstKind::Param(param_ct) => param_ct.find_ty_from_env(wfcx.param_env), + }; + + let param_ty = tcx.type_of(param.def_id).instantiate_identity(); + if !ct_ty.has_param() && !param_ty.has_param() { + let cause = traits::ObligationCause::new( + tcx.def_span(param.def_id), + wfcx.body_def_id, + ObligationCauseCode::WellFormed(None), + ); + wfcx.register_obligation(Obligation::new( + tcx, + cause, + wfcx.param_env, + ty::ClauseKind::ConstArgHasType(ct, param_ty), + )); + } } } } diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index af1107b499f..309221f9a12 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -374,9 +374,12 @@ fn check_predicates<'tcx>( // Include the well-formed predicates of the type parameters of the impl. for arg in tcx.impl_trait_ref(impl1_def_id).unwrap().instantiate_identity().args { + let Some(term) = arg.as_term() else { + continue; + }; let infcx = &tcx.infer_ctxt().build(TypingMode::non_body_analysis()); let obligations = - wf::obligations(infcx, tcx.param_env(impl1_def_id), impl1_def_id, 0, arg, span) + wf::obligations(infcx, tcx.param_env(impl1_def_id), impl1_def_id, 0, term, span) .unwrap(); assert!(!obligations.has_infer()); diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index e1ad8124aea..309b8f2c761 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -59,6 +59,7 @@ This API is completely unstable and subject to change. #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(assert_matches)] @@ -67,7 +68,6 @@ This API is completely unstable and subject to change. #![feature(if_let_guard)] #![feature(iter_from_coroutine)] #![feature(iter_intersperse)] -#![feature(let_chains)] #![feature(never_type)] #![feature(rustdoc_internals)] #![feature(slice_partition_dedup)] diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 4608531f1d9..c95d6a277c7 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -2,7 +2,7 @@ //! the definitions in this file have equivalents in `rustc_ast_pretty`. // tidy-alphabetical-start -#![feature(let_chains)] +#![cfg_attr(bootstrap, feature(let_chains))] #![recursion_limit = "256"] // tidy-alphabetical-end diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index b19d9efe2c6..caf36ba47bd 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -501,12 +501,25 @@ impl<'a, 'tcx> CastCheck<'tcx> { .must_apply_modulo_regions() { label = false; - err.span_suggestion( - self.span, - "consider using the `From` trait instead", - format!("{}::from({})", self.cast_ty, snippet), - Applicability::MaybeIncorrect, - ); + if let ty::Adt(def, args) = self.cast_ty.kind() { + err.span_suggestion_verbose( + self.span, + "consider using the `From` trait instead", + format!( + "{}::from({})", + fcx.tcx.value_path_str_with_args(def.did(), args), + snippet + ), + Applicability::MaybeIncorrect, + ); + } else { + err.span_suggestion( + self.span, + "consider using the `From` trait instead", + format!("{}::from({})", self.cast_ty, snippet), + Applicability::MaybeIncorrect, + ); + }; } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index cac8237f8f6..362c7d8efac 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -22,8 +22,8 @@ use rustc_infer::infer::{DefineOpaqueTypes, InferResult}; use rustc_lint::builtin::SELF_CONSTRUCTOR_FROM_OUTER_ITEM; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::{ - self, AdtKind, CanonicalUserType, GenericArgKind, GenericArgsRef, GenericParamDefKind, - IsIdentity, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitableExt, UserArgs, UserSelfTy, + self, AdtKind, CanonicalUserType, GenericArgsRef, GenericParamDefKind, IsIdentity, Ty, TyCtxt, + TypeFoldable, TypeVisitable, TypeVisitableExt, UserArgs, UserSelfTy, }; use rustc_middle::{bug, span_bug}; use rustc_session::lint; @@ -573,7 +573,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Registers an obligation for checking later, during regionck, that `arg` is well-formed. pub(crate) fn register_wf_obligation( &self, - arg: ty::GenericArg<'tcx>, + term: ty::Term<'tcx>, span: Span, code: traits::ObligationCauseCode<'tcx>, ) { @@ -583,16 +583,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx, cause, self.param_env, - ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg))), + ty::ClauseKind::WellFormed(term), )); } /// Registers obligations that all `args` are well-formed. pub(crate) fn add_wf_bounds(&self, args: GenericArgsRef<'tcx>, span: Span) { - for arg in args.iter().filter(|arg| { - matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..)) - }) { - self.register_wf_obligation(arg, span, ObligationCauseCode::WellFormed(None)); + for term in args.iter().filter_map(ty::GenericArg::as_term) { + self.register_wf_obligation(term, span, ObligationCauseCode::WellFormed(None)); } } @@ -1320,27 +1318,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { infer_args: bool, ) -> ty::GenericArg<'tcx> { let tcx = self.fcx.tcx(); - match param.kind { - GenericParamDefKind::Lifetime => self - .fcx - .re_infer( - self.span, - rustc_hir_analysis::hir_ty_lowering::RegionInferReason::Param(param), - ) - .into(), - GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { - if !infer_args && let Some(default) = param.default_value(tcx) { - // If we have a default, then it doesn't matter that we're not inferring - // the type/const arguments: We provide the default where any is missing. - return default.instantiate(tcx, preceding_args); - } - // If no type/const arguments were provided, we have to infer them. - // This case also occurs as a result of some malformed input, e.g., - // a lifetime argument being given instead of a type/const parameter. - // Using inference instead of `Error` gives better error messages. - self.fcx.var_for_def(self.span, param) - } + if !infer_args && let Some(default) = param.default_value(tcx) { + // If we have a default, then it doesn't matter that we're not inferring + // the type/const arguments: We provide the default where any is missing. + return default.instantiate(tcx, preceding_args); } + // If no type/const arguments were provided, we have to infer them. + // This case also occurs as a result of some malformed input, e.g., + // a lifetime argument being given instead of a type/const parameter. + // Using inference instead of `Error` gives better error messages. + self.fcx.var_for_def(self.span, param) } } diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index af8ec373934..c3717b4efa4 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -1,11 +1,11 @@ // tidy-alphabetical-start #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(array_windows)] #![feature(box_patterns)] #![feature(if_let_guard)] #![feature(iter_intersperse)] -#![feature(let_chains)] #![feature(never_type)] #![feature(try_blocks)] // tidy-alphabetical-end diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 3caaa7f9a9d..c5000171ad7 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -863,7 +863,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { Resolver { fcx, span, body, nested_goals, should_normalize } } - fn report_error(&self, p: impl Into<ty::GenericArg<'tcx>>) -> ErrorGuaranteed { + fn report_error(&self, p: impl Into<ty::Term<'tcx>>) -> ErrorGuaranteed { if let Some(guar) = self.fcx.tainted_by_errors() { guar } else { @@ -887,7 +887,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { new_err: impl Fn(TyCtxt<'tcx>, ErrorGuaranteed) -> T, ) -> T where - T: Into<ty::GenericArg<'tcx>> + TypeSuperFoldable<TyCtxt<'tcx>> + Copy, + T: Into<ty::Term<'tcx>> + TypeSuperFoldable<TyCtxt<'tcx>> + Copy, { let tcx = self.fcx.tcx; // We must deeply normalize in the new solver, since later lints expect diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index c4698e5cbb4..d25542dadd5 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -31,9 +31,9 @@ use rustc_middle::traits::solve::Goal; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{ self, BoundVarReplacerDelegate, ConstVid, FloatVid, GenericArg, GenericArgKind, GenericArgs, - GenericArgsRef, GenericParamDefKind, InferConst, IntVid, PseudoCanonicalInput, Ty, TyCtxt, - TyVid, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, TypeVisitableExt, TypingEnv, - TypingMode, fold_regions, + GenericArgsRef, GenericParamDefKind, InferConst, IntVid, PseudoCanonicalInput, Term, TermKind, + Ty, TyCtxt, TyVid, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, + TypeVisitableExt, TypingEnv, TypingMode, fold_regions, }; use rustc_span::{Span, Symbol}; use snapshot::undo_log::InferCtxtUndoLogs; @@ -1401,6 +1401,16 @@ impl<'tcx> TyOrConstInferVar { } } + /// Tries to extract an inference variable from a type or a constant, returns `None` + /// for types other than `ty::Infer(_)` (or `InferTy::Fresh*`) and + /// for constants other than `ty::ConstKind::Infer(_)` (or `InferConst::Fresh`). + pub fn maybe_from_term(term: Term<'tcx>) -> Option<Self> { + match term.unpack() { + TermKind::Ty(ty) => Self::maybe_from_ty(ty), + TermKind::Const(ct) => Self::maybe_from_const(ct), + } + } + /// Tries to extract an inference variable from a type, returns `None` /// for types other than `ty::Infer(_)` (or `InferTy::Fresh*`). fn maybe_from_ty(ty: Ty<'tcx>) -> Option<Self> { diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index ece18f4ea64..8b2aab42042 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -16,12 +16,12 @@ #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(assert_matches)] #![feature(extend_one)] #![feature(iterator_try_collect)] -#![feature(let_chains)] #![feature(rustdoc_internals)] #![recursion_limit = "512"] // For rustdoc // tidy-alphabetical-end diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs index 67e0be93523..41280707183 100644 --- a/compiler/rustc_interface/src/lib.rs +++ b/compiler/rustc_interface/src/lib.rs @@ -1,8 +1,8 @@ // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(decl_macro)] #![feature(file_buffered)] #![feature(iter_intersperse)] -#![feature(let_chains)] #![feature(try_blocks)] // tidy-alphabetical-end diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 5c8c51c8bbc..0ceda220134 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -1,7 +1,7 @@ #![allow(rustc::bad_opt_access)] -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::BTreeMap; use std::num::NonZero; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::sync::atomic::AtomicBool; use rustc_abi::Align; @@ -89,8 +89,8 @@ where S: Into<String>, I: IntoIterator<Item = S>, { - let locations: BTreeSet<CanonicalizedPath> = - locations.into_iter().map(|s| CanonicalizedPath::new(Path::new(&s.into()))).collect(); + let locations = + locations.into_iter().map(|s| CanonicalizedPath::new(PathBuf::from(s.into()))).collect(); ExternEntry { location: ExternLocation::ExactPaths(locations), diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index e65f4beab24..41b43f64798 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -948,7 +948,7 @@ declare_lint! { /// /// ### Example /// - /// ```rust,compile_fail + /// ```rust,compile_fail,edition2021 /// #[no_mangle] /// const FOO: i32 = 5; /// ``` diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index f1dc420aa3c..7f4789ad0d9 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -41,7 +41,7 @@ declare_lint! { /// /// ### Example /// - /// ```rust,compile_fail + /// ```rust,compile_fail,edition2021 /// # #![deny(impl_trait_overcaptures)] /// # use std::fmt::Display; /// let mut x = vec![]; diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index daddd45d597..96705e79e0a 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -21,6 +21,7 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(array_windows)] @@ -28,7 +29,6 @@ #![feature(box_patterns)] #![feature(if_let_guard)] #![feature(iter_order_by)] -#![feature(let_chains)] #![feature(rustc_attrs)] #![feature(rustdoc_internals)] #![feature(try_blocks)] diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index a084dacfb5b..f9dce5a5198 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1,7 +1,9 @@ use std::iter; use std::ops::ControlFlow; -use rustc_abi::{BackendRepr, TagEncoding, VariantIdx, Variants, WrappingRange}; +use rustc_abi::{ + BackendRepr, Integer, IntegerType, TagEncoding, VariantIdx, Variants, WrappingRange, +}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::DiagMessage; use rustc_hir::intravisit::VisitorExt; @@ -1243,6 +1245,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { }; } + if let Some(IntegerType::Fixed(Integer::I128, _)) = def.repr().int { + return FfiUnsafe { + ty, + reason: fluent::lint_improper_ctypes_128bit, + help: None, + }; + } + use improper_ctypes::check_non_exhaustive_variant; let non_exhaustive = def.variant_list_has_applicable_non_exhaustive(); diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index a49eb76734f..03b8112938c 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -117,6 +117,7 @@ declare_lint_pass! { UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, UNNAMEABLE_TEST_ITEMS, UNNAMEABLE_TYPES, + UNNECESSARY_TRANSMUTES, UNREACHABLE_CODE, UNREACHABLE_PATTERNS, UNSAFE_ATTR_OUTSIDE_UNSAFE, @@ -1423,7 +1424,7 @@ declare_lint! { /// /// ### Example /// - /// ```rust,compile_fail + /// ```rust,compile_fail,edition2021 /// macro_rules! foo { /// () => {}; /// ($name) => { }; @@ -4127,7 +4128,7 @@ declare_lint! { /// /// ### Example /// - /// ```rust,compile_fail + /// ```rust,compile_fail,edition2021 /// #![deny(dependency_on_unit_never_type_fallback)] /// fn main() { /// if true { @@ -4910,6 +4911,30 @@ declare_lint! { } declare_lint! { + /// The `unnecessary_transmutes` lint detects transmutations that have safer alternatives. + /// + /// ### Example + /// + /// ```rust + /// fn bytes_at_home(x: [u8; 4]) -> u32 { + /// unsafe { std::mem::transmute(x) } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Using an explicit method is preferable over calls to + /// [`transmute`](https://doc.rust-lang.org/std/mem/fn.transmute.html) as + /// they more clearly communicate the intent, are easier to review, and + /// are less likely to accidentally result in unsoundness. + pub UNNECESSARY_TRANSMUTES, + Warn, + "detects transmutes that are shadowed by std methods" +} + +declare_lint! { /// The `tail_expr_drop_order` lint looks for those values generated at the tail expression location, /// that runs a custom `Drop` destructor. /// Some of them may be dropped earlier in Edition 2024 that they used to in Edition 2021 and prior. diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index e02c80c50b1..8bee051dd4c 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -14,6 +14,7 @@ #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/PassManager.h" #include "llvm/IR/Verifier.h" +#include "llvm/IRPrinter/IRPrintingPasses.h" #include "llvm/LTO/LTO.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/TargetRegistry.h" @@ -703,7 +704,8 @@ extern "C" LLVMRustResult LLVMRustOptimize( bool LintIR, LLVMRustThinLTOBuffer **ThinLTOBufferRef, bool EmitThinLTO, bool EmitThinLTOSummary, bool MergeFunctions, bool UnrollLoops, bool SLPVectorize, bool LoopVectorize, bool DisableSimplifyLibCalls, - bool EmitLifetimeMarkers, bool RunEnzyme, + bool EmitLifetimeMarkers, bool RunEnzyme, bool PrintBeforeEnzyme, + bool PrintAfterEnzyme, bool PrintPasses, LLVMRustSanitizerOptions *SanitizerOptions, const char *PGOGenPath, const char *PGOUsePath, bool InstrumentCoverage, const char *InstrProfileOutput, const char *PGOSampleUsePath, @@ -1048,14 +1050,38 @@ extern "C" LLVMRustResult LLVMRustOptimize( // now load "-enzyme" pass: #ifdef ENZYME if (RunEnzyme) { - registerEnzymeAndPassPipeline(PB, true); + + if (PrintBeforeEnzyme) { + // Handle the Rust flag `-Zautodiff=PrintModBefore`. + std::string Banner = "Module before EnzymeNewPM"; + MPM.addPass(PrintModulePass(outs(), Banner, true, false)); + } + + registerEnzymeAndPassPipeline(PB, false); if (auto Err = PB.parsePassPipeline(MPM, "enzyme")) { std::string ErrMsg = toString(std::move(Err)); LLVMRustSetLastError(ErrMsg.c_str()); return LLVMRustResult::Failure; } + + if (PrintAfterEnzyme) { + // Handle the Rust flag `-Zautodiff=PrintModAfter`. + std::string Banner = "Module after EnzymeNewPM"; + MPM.addPass(PrintModulePass(outs(), Banner, true, false)); + } } #endif + if (PrintPasses) { + // Print all passes from the PM: + std::string Pipeline; + raw_string_ostream SOS(Pipeline); + MPM.printPipeline(SOS, [&PIC](StringRef ClassName) { + auto PassName = PIC.getPassNameForClassName(ClassName); + return PassName.empty() ? ClassName : PassName; + }); + outs() << Pipeline; + outs() << "\n"; + } // Upgrade all calls to old intrinsics first. for (Module::iterator I = TheModule->begin(), E = TheModule->end(); I != E;) diff --git a/compiler/rustc_llvm/src/lib.rs b/compiler/rustc_llvm/src/lib.rs index 68058250a26..ed5edeef161 100644 --- a/compiler/rustc_llvm/src/lib.rs +++ b/compiler/rustc_llvm/src/lib.rs @@ -229,6 +229,7 @@ pub fn initialize_available_targets() { LLVMInitializeXtensaTargetInfo, LLVMInitializeXtensaTarget, LLVMInitializeXtensaTargetMC, + LLVMInitializeXtensaAsmPrinter, LLVMInitializeXtensaAsmParser ); init_target!( diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index edb25e79904..62ca7ce3ca9 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -1,7 +1,7 @@ // tidy-alphabetical-start #![allow(rustc::default_hash_types)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(if_let_guard)] -#![feature(let_chains)] #![feature(never_type)] #![feature(proc_macro_diagnostic)] #![feature(proc_macro_span)] diff --git a/compiler/rustc_metadata/Cargo.toml b/compiler/rustc_metadata/Cargo.toml index 08dcc3d519a..b11f9260be7 100644 --- a/compiler/rustc_metadata/Cargo.toml +++ b/compiler/rustc_metadata/Cargo.toml @@ -26,7 +26,6 @@ rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } -tempfile = "3.2" tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_metadata/src/fs.rs b/compiler/rustc_metadata/src/fs.rs index c4e1e0f1d1a..e57534b847e 100644 --- a/compiler/rustc_metadata/src/fs.rs +++ b/compiler/rustc_metadata/src/fs.rs @@ -2,11 +2,11 @@ use std::path::{Path, PathBuf}; use std::{fs, io}; use rustc_data_structures::temp_dir::MaybeTempDir; +use rustc_fs_util::TempDirBuilder; use rustc_middle::ty::TyCtxt; use rustc_session::config::{CrateType, OutFileName, OutputType}; use rustc_session::output::filename_for_metadata; use rustc_session::{MetadataKind, Session}; -use tempfile::Builder as TempFileBuilder; use crate::errors::{ BinaryOutputToTty, FailedCopyToStdout, FailedCreateEncodedMetadata, FailedCreateFile, @@ -45,7 +45,7 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) { // final destination, with an `fs::rename` call. In order for the rename to // always succeed, the temporary file needs to be on the same filesystem, // which is why we create it inside the output directory specifically. - let metadata_tmpdir = TempFileBuilder::new() + let metadata_tmpdir = TempDirBuilder::new() .prefix("rmeta") .tempdir_in(out_filename.parent().unwrap_or_else(|| Path::new(""))) .unwrap_or_else(|err| tcx.dcx().emit_fatal(FailedCreateTempdir { err })); diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index 3b44c44fcb9..3931be1654a 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -1,5 +1,6 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(coroutines)] @@ -8,7 +9,6 @@ #![feature(file_buffered)] #![feature(if_let_guard)] #![feature(iter_from_coroutine)] -#![feature(let_chains)] #![feature(macro_metavar_expr)] #![feature(min_specialization)] #![feature(never_type)] diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 8fe2cc7101b..df025aeebf0 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -29,6 +29,7 @@ #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::potential_query_instability)] #![allow(rustc::untranslatable_diagnostic)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(allocator_api)] @@ -48,7 +49,6 @@ #![feature(if_let_guard)] #![feature(intra_doc_pointers)] #![feature(iter_from_coroutine)] -#![feature(let_chains)] #![feature(min_specialization)] #![feature(negative_impls)] #![feature(never_type)] diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 98057a25f04..e8dad1e056c 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -590,6 +590,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.defaultness(def_id).has_value() } + fn impl_specializes(self, impl_def_id: Self::DefId, victim_def_id: Self::DefId) -> bool { + self.specializes((impl_def_id, victim_def_id)) + } + fn impl_is_default(self, impl_def_id: DefId) -> bool { self.defaultness(impl_def_id).is_default() } diff --git a/compiler/rustc_middle/src/ty/generic_args.rs b/compiler/rustc_middle/src/ty/generic_args.rs index 8de64b3bfac..542c0b3e6eb 100644 --- a/compiler/rustc_middle/src/ty/generic_args.rs +++ b/compiler/rustc_middle/src/ty/generic_args.rs @@ -250,17 +250,17 @@ impl<'tcx> GenericArg<'tcx> { } #[inline] - pub fn as_type(self) -> Option<Ty<'tcx>> { + pub fn as_region(self) -> Option<ty::Region<'tcx>> { match self.unpack() { - GenericArgKind::Type(ty) => Some(ty), + GenericArgKind::Lifetime(re) => Some(re), _ => None, } } #[inline] - pub fn as_region(self) -> Option<ty::Region<'tcx>> { + pub fn as_type(self) -> Option<Ty<'tcx>> { match self.unpack() { - GenericArgKind::Lifetime(re) => Some(re), + GenericArgKind::Type(ty) => Some(ty), _ => None, } } @@ -273,6 +273,15 @@ impl<'tcx> GenericArg<'tcx> { } } + #[inline] + pub fn as_term(self) -> Option<ty::Term<'tcx>> { + match self.unpack() { + GenericArgKind::Lifetime(_) => None, + GenericArgKind::Type(ty) => Some(ty.into()), + GenericArgKind::Const(ct) => Some(ct.into()), + } + } + /// Unpack the `GenericArg` as a region when it is known certainly to be a region. pub fn expect_region(self) -> ty::Region<'tcx> { self.as_region().unwrap_or_else(|| bug!("expected a region, but found another kind")) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 3698cfb9870..be00c0e116d 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -110,6 +110,7 @@ use crate::ty::codec::{TyDecoder, TyEncoder}; pub use crate::ty::diagnostics::*; use crate::ty::fast_reject::SimplifiedType; use crate::ty::util::Discr; +use crate::ty::walk::TypeWalker; pub mod abstract_const; pub mod adjustment; @@ -631,6 +632,20 @@ impl<'tcx> Term<'tcx> { TermKind::Const(ct) => ct.is_ct_infer(), } } + + /// Iterator that walks `self` and any types reachable from + /// `self`, in depth-first order. Note that just walks the types + /// that appear in `self`, it does not descend into the fields of + /// structs or variants. For example: + /// + /// ```text + /// isize => { isize } + /// Foo<Bar<isize>> => { Foo<Bar<isize>>, Bar<isize>, isize } + /// [isize] => { [isize], isize } + /// ``` + pub fn walk(self) -> TypeWalker<TyCtxt<'tcx>> { + TypeWalker::new(self.into()) + } } const TAG_MASK: usize = 0b11; diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index 02e316dfc3d..551d816941b 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -121,11 +121,10 @@ impl<'tcx> Predicate<'tcx> { /// unsoundly accept some programs. See #91068. #[inline] pub fn allow_normalization(self) -> bool { - // Keep this in sync with the one in `rustc_type_ir::inherent`! match self.kind().skip_binder() { - PredicateKind::Clause(ClauseKind::WellFormed(_)) - | PredicateKind::AliasRelate(..) - | PredicateKind::NormalizesTo(..) => false, + PredicateKind::Clause(ClauseKind::WellFormed(_)) | PredicateKind::AliasRelate(..) => { + false + } PredicateKind::Clause(ClauseKind::Trait(_)) | PredicateKind::Clause(ClauseKind::HostEffect(..)) | PredicateKind::Clause(ClauseKind::RegionOutlives(_)) @@ -137,6 +136,7 @@ impl<'tcx> Predicate<'tcx> { | PredicateKind::Coerce(_) | PredicateKind::Clause(ClauseKind::ConstEvaluatable(_)) | PredicateKind::ConstEquate(_, _) + | PredicateKind::NormalizesTo(..) | PredicateKind::Ambiguous => true, } } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index d739218af5e..1df3bff5244 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -3247,7 +3247,7 @@ define_print! { ty::ClauseKind::ConstArgHasType(ct, ty) => { p!("the constant `", print(ct), "` has type `", print(ty), "`") }, - ty::ClauseKind::WellFormed(arg) => p!(print(arg), " well-formed"), + ty::ClauseKind::WellFormed(term) => p!(print(term), " well-formed"), ty::ClauseKind::ConstEvaluatable(ct) => { p!("the constant `", print(ct), "` can be evaluated") } diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index 39fcc686c55..4d70a708732 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -138,18 +138,26 @@ impl<'tcx> Value<TyCtxt<'tcx>> for &[ty::Variance] { cycle_error: &CycleError, _guar: ErrorGuaranteed, ) -> Self { - if let Some(frame) = cycle_error.cycle.get(0) - && frame.query.dep_kind == dep_kinds::variances_of - && let Some(def_id) = frame.query.def_id - { - let n = tcx.generics_of(def_id).own_params.len(); - vec![ty::Bivariant; n].leak() - } else { - span_bug!( - cycle_error.usage.as_ref().unwrap().0, - "only `variances_of` returns `&[ty::Variance]`" - ); - } + search_for_cycle_permutation( + &cycle_error.cycle, + |cycle| { + if let Some(frame) = cycle.get(0) + && frame.query.dep_kind == dep_kinds::variances_of + && let Some(def_id) = frame.query.def_id + { + let n = tcx.generics_of(def_id).own_params.len(); + ControlFlow::Break(vec![ty::Bivariant; n].leak()) + } else { + ControlFlow::Continue(()) + } + }, + || { + span_bug!( + cycle_error.usage.as_ref().unwrap().0, + "only `variances_of` returns `&[ty::Variance]`" + ) + }, + ) } } diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs index 8e96d46dac2..a051cf570b7 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs @@ -3,10 +3,10 @@ // tidy-alphabetical-start #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(assert_matches)] #![feature(box_patterns)] #![feature(if_let_guard)] -#![feature(let_chains)] #![feature(try_blocks)] // tidy-alphabetical-end diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index a0efc623b8e..38f82b12746 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -1,10 +1,10 @@ // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(assert_matches)] #![feature(associated_type_defaults)] #![feature(box_patterns)] #![feature(exact_size_is_empty)] #![feature(file_buffered)] -#![feature(let_chains)] #![feature(never_type)] #![feature(try_blocks)] // tidy-alphabetical-end diff --git a/compiler/rustc_mir_transform/messages.ftl b/compiler/rustc_mir_transform/messages.ftl index 5628f4c9381..a1264471a2d 100644 --- a/compiler/rustc_mir_transform/messages.ftl +++ b/compiler/rustc_mir_transform/messages.ftl @@ -84,3 +84,4 @@ mir_transform_undefined_transmute = pointers cannot be transmuted to integers du .help = for more information, see https://doc.rust-lang.org/std/mem/fn.transmute.html mir_transform_unknown_pass_name = MIR pass `{$name}` is unknown and will be ignored +mir_transform_unnecessary_transmute = unnecessary transmute diff --git a/compiler/rustc_mir_transform/src/check_alignment.rs b/compiler/rustc_mir_transform/src/check_alignment.rs index b70cca14840..5115583f37c 100644 --- a/compiler/rustc_mir_transform/src/check_alignment.rs +++ b/compiler/rustc_mir_transform/src/check_alignment.rs @@ -1,3 +1,4 @@ +use rustc_abi::Align; use rustc_index::IndexVec; use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::visit::PlaceContext; @@ -11,10 +12,6 @@ pub(super) struct CheckAlignment; impl<'tcx> crate::MirPass<'tcx> for CheckAlignment { fn is_enabled(&self, sess: &Session) -> bool { - // FIXME(#112480) MSVC and rustc disagree on minimum stack alignment on x86 Windows - if sess.target.llvm_target == "i686-pc-windows-msvc" { - return false; - } sess.ub_checks() } @@ -87,6 +84,33 @@ fn insert_alignment_check<'tcx>( ))), }); + // If this target does not have reliable alignment, further limit the mask by anding it with + // the mask for the highest reliable alignment. + #[allow(irrefutable_let_patterns)] + if let max_align = tcx.sess.target.max_reliable_alignment() + && max_align < Align::MAX + { + let max_mask = max_align.bytes() - 1; + let max_mask = Operand::Constant(Box::new(ConstOperand { + span: source_info.span, + user_ty: None, + const_: Const::Val( + ConstValue::Scalar(Scalar::from_target_usize(max_mask, &tcx)), + tcx.types.usize, + ), + })); + stmts.push(Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + alignment_mask, + Rvalue::BinaryOp( + BinOp::BitAnd, + Box::new((Operand::Copy(alignment_mask), max_mask)), + ), + ))), + }); + } + // BitAnd the alignment mask with the pointer let alignment_bits = local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into(); diff --git a/compiler/rustc_mir_transform/src/check_unnecessary_transmutes.rs b/compiler/rustc_mir_transform/src/check_unnecessary_transmutes.rs new file mode 100644 index 00000000000..4aff127908e --- /dev/null +++ b/compiler/rustc_mir_transform/src/check_unnecessary_transmutes.rs @@ -0,0 +1,97 @@ +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::{Body, Location, Operand, Terminator, TerminatorKind}; +use rustc_middle::ty::*; +use rustc_session::lint::builtin::UNNECESSARY_TRANSMUTES; +use rustc_span::source_map::Spanned; +use rustc_span::{Span, sym}; + +use crate::errors::UnnecessaryTransmute as Error; + +/// Check for transmutes that overlap with stdlib methods. +/// For example, transmuting `[u8; 4]` to `u32`. +pub(super) struct CheckUnnecessaryTransmutes; + +impl<'tcx> crate::MirLint<'tcx> for CheckUnnecessaryTransmutes { + fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { + let mut checker = UnnecessaryTransmuteChecker { body, tcx }; + checker.visit_body(body); + } +} + +struct UnnecessaryTransmuteChecker<'a, 'tcx> { + body: &'a Body<'tcx>, + tcx: TyCtxt<'tcx>, +} + +impl<'a, 'tcx> UnnecessaryTransmuteChecker<'a, 'tcx> { + fn is_unnecessary_transmute( + &self, + function: &Operand<'tcx>, + arg: String, + span: Span, + ) -> Option<Error> { + let fn_sig = function.ty(self.body, self.tcx).fn_sig(self.tcx).skip_binder(); + let [input] = fn_sig.inputs() else { return None }; + + let err = |sugg| Error { span, sugg, help: None }; + + Some(match (input.kind(), fn_sig.output().kind()) { + // dont check the length; transmute does that for us. + // [u8; _] => primitive + (Array(t, _), Uint(_) | Float(_) | Int(_)) if *t.kind() == Uint(UintTy::U8) => Error { + sugg: format!("{}::from_ne_bytes({arg})", fn_sig.output()), + help: Some( + "there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order", + ), + span, + }, + // primitive => [u8; _] + (Uint(_) | Float(_) | Int(_), Array(t, _)) if *t.kind() == Uint(UintTy::U8) => Error { + sugg: format!("{input}::to_ne_bytes({arg})"), + help: Some( + "there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order", + ), + span, + }, + // char → u32 + (Char, Uint(UintTy::U32)) => err(format!("u32::from({arg})")), + // u32 → char + (Uint(UintTy::U32), Char) => Error { + sugg: format!("char::from_u32_unchecked({arg})"), + help: Some("consider `char::from_u32(…).unwrap()`"), + span, + }, + // uNN → iNN + (Uint(ty), Int(_)) => err(format!("{}::cast_signed({arg})", ty.name_str())), + // iNN → uNN + (Int(ty), Uint(_)) => err(format!("{}::cast_unsigned({arg})", ty.name_str())), + // fNN → uNN + (Float(ty), Uint(..)) => err(format!("{}::to_bits({arg})", ty.name_str())), + // uNN → fNN + (Uint(_), Float(ty)) => err(format!("{}::from_bits({arg})", ty.name_str())), + // bool → { x8 } + (Bool, Int(..) | Uint(..)) => err(format!("({arg}) as {}", fn_sig.output())), + // u8 → bool + (Uint(_), Bool) => err(format!("({arg} == 1)")), + _ => return None, + }) + } +} + +impl<'tcx> Visitor<'tcx> for UnnecessaryTransmuteChecker<'_, 'tcx> { + // Check each block's terminator for calls to pointer to integer transmutes + // in const functions or associated constants and emit a lint. + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { + if let TerminatorKind::Call { func, args, .. } = &terminator.kind + && let [Spanned { span: arg, .. }] = **args + && let Some((func_def_id, _)) = func.const_fn_def() + && self.tcx.is_intrinsic(func_def_id, sym::transmute) + && let span = self.body.source_info(location).span + && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(arg) + && let Some(lint) = self.is_unnecessary_transmute(func, snippet, span) + && let Some(hir_id) = terminator.source_info.scope.lint_root(&self.body.source_scopes) + { + self.tcx.emit_node_span_lint(UNNECESSARY_TRANSMUTES, hir_id, span, lint); + } + } +} diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index aa4c0ef1e1f..f2e8f9e1bcd 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -1,8 +1,7 @@ -pub(super) mod query; - mod counters; mod graph; mod mappings; +pub(super) mod query; mod spans; #[cfg(test)] mod tests; @@ -90,7 +89,7 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir: // Use the coverage graph to prepare intermediate data that will eventually // be used to assign physical counters and counter expressions to points in - // the control-flow graph + // the control-flow graph. let BcbCountersData { node_flow_data, priority_list } = counters::prepare_bcb_counters_data(&graph); diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index 29698b0c2e4..5b03a4987ed 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -158,6 +158,26 @@ pub(crate) struct MustNotSuspendReason { pub reason: String, } +pub(crate) struct UnnecessaryTransmute { + pub span: Span, + pub sugg: String, + pub help: Option<&'static str>, +} + +// Needed for def_path_str +impl<'a> LintDiagnostic<'a, ()> for UnnecessaryTransmute { + fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::Diag<'a, ()>) { + diag.primary_message(fluent::mir_transform_unnecessary_transmute); + diag.span_suggestion( + self.span, + "replace this with", + self.sugg, + lint::Applicability::MachineApplicable, + ); + self.help.map(|help| diag.help(help)); + } +} + #[derive(LintDiagnostic)] #[diag(mir_transform_undefined_transmute)] #[note] diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 4d74ecddfb0..5db62b7e902 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -1,4 +1,5 @@ // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(array_windows)] #![feature(assert_matches)] #![feature(box_patterns)] @@ -7,7 +8,6 @@ #![feature(file_buffered)] #![feature(if_let_guard)] #![feature(impl_trait_in_assoc_type)] -#![feature(let_chains)] #![feature(map_try_insert)] #![feature(never_type)] #![feature(try_blocks)] @@ -125,6 +125,7 @@ declare_passes! { mod check_null : CheckNull; mod check_packed_ref : CheckPackedRef; mod check_undefined_transmutes : CheckUndefinedTransmutes; + mod check_unnecessary_transmutes: CheckUnnecessaryTransmutes; // This pass is public to allow external drivers to perform MIR cleanup pub mod cleanup_post_borrowck : CleanupPostBorrowck; @@ -391,6 +392,7 @@ fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal<Body<'_>> { &Lint(check_const_item_mutation::CheckConstItemMutation), &Lint(function_item_references::FunctionItemReferences), &Lint(check_undefined_transmutes::CheckUndefinedTransmutes), + &Lint(check_unnecessary_transmutes::CheckUnnecessaryTransmutes), // What we need to do constant evaluation. &simplify::SimplifyCfg::Initial, &Lint(sanity_check::SanityCheck), diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index 8f6914f3d72..8469e0f17a6 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -1,9 +1,9 @@ // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(array_windows)] #![feature(file_buffered)] #![feature(if_let_guard)] #![feature(impl_trait_in_assoc_type)] -#![feature(let_chains)] // tidy-alphabetical-end use rustc_hir::lang_items::LangItem; diff --git a/compiler/rustc_next_trait_solver/Cargo.toml b/compiler/rustc_next_trait_solver/Cargo.toml index 63aa60f2f26..36d53901d9e 100644 --- a/compiler/rustc_next_trait_solver/Cargo.toml +++ b/compiler/rustc_next_trait_solver/Cargo.toml @@ -9,7 +9,6 @@ derive-where = "1.2.7" rustc_data_structures = { path = "../rustc_data_structures", optional = true } rustc_index = { path = "../rustc_index", default-features = false } rustc_macros = { path = "../rustc_macros", optional = true } -rustc_serialize = { path = "../rustc_serialize", optional = true } rustc_type_ir = { path = "../rustc_type_ir", default-features = false } rustc_type_ir_macros = { path = "../rustc_type_ir_macros" } tracing = "0.1" @@ -20,7 +19,6 @@ default = ["nightly"] nightly = [ "dep:rustc_data_structures", "dep:rustc_macros", - "dep:rustc_serialize", "rustc_index/nightly", "rustc_type_ir/nightly", ] diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index c4b6b18c45d..25493970a0c 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -36,7 +36,7 @@ pub trait SolverDelegate: Deref<Target = Self::Infcx> + Sized { fn well_formed_goals( &self, param_env: <Self::Interner as Interner>::ParamEnv, - arg: <Self::Interner as Interner>::GenericArg, + term: <Self::Interner as Interner>::Term, ) -> Option<Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>>; fn clone_opaque_types_for_query_response( 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 0fc313e33b3..f7bd4600943 100644 --- a/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs +++ b/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs @@ -16,6 +16,7 @@ //! relate them structurally. use rustc_type_ir::inherent::*; +use rustc_type_ir::solve::GoalSource; use rustc_type_ir::{self as ty, Interner}; use tracing::{instrument, trace}; @@ -49,7 +50,10 @@ where // Structurally normalize the lhs. let lhs = if let Some(alias) = lhs.to_alias_term() { let term = self.next_term_infer_of_kind(lhs); - self.add_normalizes_to_goal(goal.with(cx, ty::NormalizesTo { alias, term })); + self.add_goal( + GoalSource::TypeRelating, + goal.with(cx, ty::NormalizesTo { alias, term }), + ); term } else { lhs @@ -58,7 +62,10 @@ where // Structurally normalize the rhs. let rhs = if let Some(alias) = rhs.to_alias_term() { let term = self.next_term_infer_of_kind(rhs); - self.add_normalizes_to_goal(goal.with(cx, ty::NormalizesTo { alias, term })); + self.add_goal( + GoalSource::TypeRelating, + goal.with(cx, ty::NormalizesTo { alias, term }), + ); term } else { rhs 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 ecb57cc0ad7..381a732b8de 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -10,6 +10,7 @@ use rustc_type_ir::{ }; use tracing::{debug, instrument}; +use super::has_only_region_constraints; use super::trait_goals::TraitGoalProvenVia; use crate::delegate::SolverDelegate; use crate::solve::inspect::ProbeKind; @@ -771,6 +772,69 @@ where } }) } +} + +pub(super) enum AllowInferenceConstraints { + Yes, + No, +} + +impl<D, I> EvalCtxt<'_, D> +where + D: SolverDelegate<Interner = I>, + I: Interner, +{ + /// Check whether we can ignore impl candidates due to specialization. + /// + /// This is only necessary for `feature(specialization)` and seems quite ugly. + pub(super) fn filter_specialized_impls( + &mut self, + allow_inference_constraints: AllowInferenceConstraints, + candidates: &mut Vec<Candidate<I>>, + ) { + match self.typing_mode() { + TypingMode::Coherence => return, + TypingMode::Analysis { .. } + | TypingMode::Borrowck { .. } + | TypingMode::PostBorrowckAnalysis { .. } + | TypingMode::PostAnalysis => {} + } + + let mut i = 0; + 'outer: while i < candidates.len() { + let CandidateSource::Impl(victim_def_id) = candidates[i].source else { + i += 1; + continue; + }; + + for (j, c) in candidates.iter().enumerate() { + if i == j { + continue; + } + + let CandidateSource::Impl(other_def_id) = c.source else { + continue; + }; + + // See if we can toss out `victim` based on specialization. + // + // While this requires us to know *for sure* that the `lhs` impl applies + // we still use modulo regions here. This is fine as specialization currently + // assumes that specializing impls have to be always applicable, meaning that + // the only allowed region constraints may be constraints also present on the default impl. + if matches!(allow_inference_constraints, AllowInferenceConstraints::Yes) + || has_only_region_constraints(c.result) + { + if self.cx().impl_specializes(other_def_id, victim_def_id) { + candidates.remove(i); + continue 'outer; + } + } + } + + i += 1; + } + } /// Assemble and merge candidates for goals which are related to an underlying trait /// goal. Right now, this is normalizes-to and host effect goals. @@ -857,7 +921,7 @@ where } } TraitGoalProvenVia::Misc => { - let candidates = + let mut candidates = self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All); // Prefer "orphaned" param-env normalization predicates, which are used @@ -871,6 +935,13 @@ where return Ok(response); } + // We drop specialized impls to allow normalization via a final impl here. In case + // the specializing impl has different inference constraints from the specialized + // impl, proving the trait goal is already ambiguous, so we never get here. This + // means we can just ignore inference constraints and don't have to special-case + // constraining the normalized-to `term`. + self.filter_specialized_impls(AllowInferenceConstraints::Yes, &mut candidates); + let responses: Vec<_> = candidates.iter().map(|c| c.result).collect(); if let Some(response) = self.try_merge_responses(&responses) { Ok(response) diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs index d56b0e5847e..04f80a056f9 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs @@ -22,7 +22,7 @@ use tracing::{debug, instrument, trace}; use crate::canonicalizer::Canonicalizer; use crate::delegate::SolverDelegate; use crate::resolve::EagerResolver; -use crate::solve::eval_ctxt::{CurrentGoalKind, NestedGoals}; +use crate::solve::eval_ctxt::CurrentGoalKind; use crate::solve::{ CanonicalInput, CanonicalResponse, Certainty, EvalCtxt, ExternalConstraintsData, Goal, MaybeCause, NestedNormalizationGoals, NoSolution, PredefinedOpaquesData, QueryInput, @@ -112,13 +112,9 @@ where // by `try_evaluate_added_goals()`. let (certainty, normalization_nested_goals) = match self.current_goal_kind { CurrentGoalKind::NormalizesTo => { - let NestedGoals { normalizes_to_goals, goals } = - std::mem::take(&mut self.nested_goals); - if cfg!(debug_assertions) { - assert!(normalizes_to_goals.is_empty()); - if goals.is_empty() { - assert!(matches!(goals_certainty, Certainty::Yes)); - } + let goals = std::mem::take(&mut self.nested_goals); + if goals.is_empty() { + assert!(matches!(goals_certainty, Certainty::Yes)); } (certainty, NestedNormalizationGoals(goals)) } 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 9994c85d0d0..6dd554299a6 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 @@ -1,8 +1,8 @@ +use std::mem; use std::ops::ControlFlow; -use derive_where::derive_where; #[cfg(feature = "nightly")] -use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; +use rustc_macros::HashStable_NoContext; use rustc_type_ir::data_structures::{HashMap, HashSet, ensure_sufficient_stack}; use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; @@ -14,9 +14,9 @@ use rustc_type_ir::{ TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, }; -use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; use tracing::{instrument, trace}; +use super::has_only_region_constraints; use crate::coherence; use crate::delegate::SolverDelegate; use crate::solve::inspect::{self, ProofTreeBuilder}; @@ -114,7 +114,7 @@ where pub(super) search_graph: &'a mut SearchGraph<D>, - nested_goals: NestedGoals<I>, + nested_goals: Vec<(GoalSource, Goal<I, I::Predicate>)>, pub(super) origin_span: I::Span, @@ -129,38 +129,6 @@ where pub(super) inspect: ProofTreeBuilder<D>, } -#[derive_where(Clone, Debug, Default; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] -#[cfg_attr( - feature = "nightly", - derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) -)] -struct NestedGoals<I: Interner> { - /// These normalizes-to goals are treated specially during the evaluation - /// loop. In each iteration we take the RHS of the projection, replace it with - /// a fresh inference variable, and only after evaluating that goal do we - /// equate the fresh inference variable with the actual RHS of the predicate. - /// - /// This is both to improve caching, and to avoid using the RHS of the - /// projection predicate to influence the normalizes-to candidate we select. - /// - /// Forgetting to replace the RHS with a fresh inference variable when we evaluate - /// this goal results in an ICE.. - pub normalizes_to_goals: Vec<Goal<I, ty::NormalizesTo<I>>>, - /// The rest of the goals which have not yet processed or remain ambiguous. - pub goals: Vec<(GoalSource, Goal<I, I::Predicate>)>, -} - -impl<I: Interner> NestedGoals<I> { - fn new() -> Self { - Self { normalizes_to_goals: Vec::new(), goals: Vec::new() } - } - - fn is_empty(&self) -> bool { - self.normalizes_to_goals.is_empty() && self.goals.is_empty() - } -} - #[derive(PartialEq, Eq, Debug, Hash, Clone, Copy)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub enum GenerateProofTree { @@ -332,7 +300,7 @@ where let mut ecx = EvalCtxt { delegate, search_graph: &mut search_graph, - nested_goals: NestedGoals::new(), + nested_goals: Default::default(), inspect: ProofTreeBuilder::new_maybe_root(generate_proof_tree), // Only relevant when canonicalizing the response, @@ -385,7 +353,7 @@ where predefined_opaques_in_body: input.predefined_opaques_in_body, max_input_universe: canonical_input.canonical.max_universe, search_graph, - nested_goals: NestedGoals::new(), + nested_goals: Default::default(), origin_span: I::Span::dummy(), tainted: Ok(()), inspect: canonical_goal_evaluation.new_goal_evaluation_step(var_values), @@ -509,13 +477,8 @@ where Ok(response) => response, }; - let has_changed = if !response.value.var_values.is_identity_modulo_regions() - || !response.value.external_constraints.opaque_types.is_empty() - { - HasChanged::Yes - } else { - HasChanged::No - }; + let has_changed = + if !has_only_region_constraints(response) { HasChanged::Yes } else { HasChanged::No }; let (normalization_nested_goals, certainty) = self.instantiate_and_apply_query_response(goal.param_env, orig_values, response); @@ -566,8 +529,8 @@ where ty::PredicateKind::DynCompatible(trait_def_id) => { self.compute_dyn_compatible_goal(trait_def_id) } - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { - self.compute_well_formed_goal(Goal { param_env, predicate: arg }) + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => { + self.compute_well_formed_goal(Goal { param_env, predicate: term }) } ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => { self.compute_const_evaluatable_goal(Goal { param_env, predicate: ct }) @@ -629,78 +592,83 @@ where /// Goals for the next step get directly added to the nested goals of the `EvalCtxt`. fn evaluate_added_goals_step(&mut self) -> Result<Option<Certainty>, NoSolution> { let cx = self.cx(); - let mut goals = core::mem::take(&mut self.nested_goals); - // If this loop did not result in any progress, what's our final certainty. let mut unchanged_certainty = Some(Certainty::Yes); - for goal in goals.normalizes_to_goals { - // Replace the goal with an unconstrained infer var, so the - // RHS does not affect projection candidate assembly. - let unconstrained_rhs = self.next_term_infer_of_kind(goal.predicate.term); - let unconstrained_goal = goal.with( - cx, - ty::NormalizesTo { alias: goal.predicate.alias, term: unconstrained_rhs }, - ); - - let (NestedNormalizationGoals(nested_goals), _, certainty) = self.evaluate_goal_raw( - GoalEvaluationKind::Nested, - GoalSource::TypeRelating, - unconstrained_goal, - )?; - // Add the nested goals from normalization to our own nested goals. - trace!(?nested_goals); - goals.goals.extend(nested_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. + for (source, goal) in mem::take(&mut self.nested_goals) { + // We treat normalizes-to goals specially here. In each iteration we take the + // RHS of the projection, replace it with a fresh inference variable, and only + // after evaluating that goal do we equate the fresh inference variable with the + // actual RHS of the predicate. // - // 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. + // This is both to improve caching, and to avoid using the RHS of the + // projection predicate to influence the normalizes-to candidate we select. // - // 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, - goal.predicate.term, - unconstrained_rhs, - )?; - - // We only look at the `projection_ty` part here rather than - // looking at the "has changed" return from evaluate_goal, - // because we expect the `unconstrained_rhs` part of the predicate - // to have changed -- that means we actually normalized successfully! - let with_resolved_vars = self.resolve_vars_if_possible(goal); - if goal.predicate.alias != with_resolved_vars.predicate.alias { - unchanged_certainty = None; - } - - match certainty { - Certainty::Yes => {} - Certainty::Maybe(_) => { - self.nested_goals.normalizes_to_goals.push(with_resolved_vars); - unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty)); + // Forgetting to replace the RHS with a fresh inference variable when we evaluate + // this goal results in an ICE. + if let Some(pred) = goal.predicate.as_normalizes_to() { + // We should never encounter higher-ranked normalizes-to goals. + let pred = pred.no_bound_vars().unwrap(); + // Replace the goal with an unconstrained infer var, so the + // RHS does not affect projection candidate assembly. + let unconstrained_rhs = self.next_term_infer_of_kind(pred.term); + let unconstrained_goal = + goal.with(cx, ty::NormalizesTo { alias: pred.alias, term: unconstrained_rhs }); + + let (NestedNormalizationGoals(nested_goals), _, certainty) = + self.evaluate_goal_raw(GoalEvaluationKind::Nested, source, unconstrained_goal)?; + // Add the nested goals from normalization to our own nested goals. + trace!(?nested_goals); + self.nested_goals.extend(nested_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, + pred.term, + unconstrained_rhs, + )?; + + // We only look at the `projection_ty` part here rather than + // looking at the "has changed" return from evaluate_goal, + // because we expect the `unconstrained_rhs` part of the predicate + // to have changed -- that means we actually normalized successfully! + let with_resolved_vars = self.resolve_vars_if_possible(goal); + if pred.alias != goal.predicate.as_normalizes_to().unwrap().skip_binder().alias { + unchanged_certainty = None; } - } - } - for (source, goal) in goals.goals { - let (has_changed, certainty) = - self.evaluate_goal(GoalEvaluationKind::Nested, source, goal)?; - if has_changed == HasChanged::Yes { - unchanged_certainty = None; - } + match certainty { + Certainty::Yes => {} + Certainty::Maybe(_) => { + self.nested_goals.push((source, with_resolved_vars)); + unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty)); + } + } + } else { + let (has_changed, certainty) = + self.evaluate_goal(GoalEvaluationKind::Nested, source, goal)?; + if has_changed == HasChanged::Yes { + unchanged_certainty = None; + } - match certainty { - Certainty::Yes => {} - Certainty::Maybe(_) => { - self.nested_goals.goals.push((source, goal)); - unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty)); + match certainty { + Certainty::Yes => {} + Certainty::Maybe(_) => { + self.nested_goals.push((source, goal)); + unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty)); + } } } } @@ -717,23 +685,12 @@ where self.delegate.cx() } - #[instrument(level = "trace", skip(self))] - pub(super) fn add_normalizes_to_goal(&mut self, mut goal: Goal<I, ty::NormalizesTo<I>>) { - goal.predicate = goal.predicate.fold_with(&mut ReplaceAliasWithInfer::new( - self, - GoalSource::TypeRelating, - goal.param_env, - )); - self.inspect.add_normalizes_to_goal(self.delegate, self.max_input_universe, goal); - self.nested_goals.normalizes_to_goals.push(goal); - } - #[instrument(level = "debug", skip(self))] pub(super) fn add_goal(&mut self, source: GoalSource, mut goal: Goal<I, I::Predicate>) { goal.predicate = goal.predicate.fold_with(&mut ReplaceAliasWithInfer::new(self, source, goal.param_env)); self.inspect.add_goal(self.delegate, self.max_input_universe, source, goal); - self.nested_goals.goals.push((source, goal)); + self.nested_goals.push((source, goal)); } #[instrument(level = "trace", skip(self, goals))] @@ -1051,9 +1008,9 @@ where pub(super) fn well_formed_goals( &self, param_env: I::ParamEnv, - arg: I::GenericArg, + term: I::Term, ) -> Option<Vec<Goal<I, I::Predicate>>> { - self.delegate.well_formed_goals(param_env, arg) + self.delegate.well_formed_goals(param_env, term) } pub(super) fn trait_ref_is_knowable( diff --git a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs index 6a8e0790f7c..f22b275bc44 100644 --- a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs +++ b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs @@ -412,20 +412,6 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<D> { } } - pub(crate) fn add_normalizes_to_goal( - &mut self, - delegate: &D, - max_input_universe: ty::UniverseIndex, - goal: Goal<I, ty::NormalizesTo<I>>, - ) { - self.add_goal( - delegate, - max_input_universe, - GoalSource::TypeRelating, - goal.with(delegate.cx(), goal.predicate), - ); - } - pub(crate) fn add_goal( &mut self, delegate: &D, diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 0695c5acdca..c9f4fc649b5 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -70,6 +70,17 @@ fn has_no_inference_or_external_constraints<I: Interner>( && normalization_nested_goals.is_empty() } +fn has_only_region_constraints<I: Interner>(response: ty::Canonical<I, Response<I>>) -> bool { + let ExternalConstraintsData { + region_constraints: _, + ref opaque_types, + ref normalization_nested_goals, + } = *response.value.external_constraints; + response.value.var_values.is_identity_modulo_regions() + && opaque_types.is_empty() + && normalization_nested_goals.is_empty() +} + impl<'a, D, I> EvalCtxt<'a, D> where D: SolverDelegate<Interner = I>, @@ -126,7 +137,7 @@ where } #[instrument(level = "trace", skip(self))] - fn compute_well_formed_goal(&mut self, goal: Goal<I, I::GenericArg>) -> QueryResult<I> { + fn compute_well_formed_goal(&mut self, goal: Goal<I, I::Term>) -> QueryResult<I> { match self.well_formed_goals(goal.param_env, goal.predicate) { Some(goals) => { self.add_goals(GoalSource::Misc, goals); diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 9466901683e..119d197de13 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -213,9 +213,6 @@ where ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }; - // In case the associated item is hidden due to specialization, we have to - // return ambiguity this would otherwise be incomplete, resulting in - // unsoundness during coherence (#105782). let target_item_def_id = match ecx.fetch_eligible_assoc_item( goal_trait_ref, goal.predicate.def_id(), @@ -223,8 +220,28 @@ where ) { Ok(Some(target_item_def_id)) => target_item_def_id, Ok(None) => { - return ecx - .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); + match ecx.typing_mode() { + // In case the associated item is hidden due to specialization, we have to + // return ambiguity this would otherwise be incomplete, resulting in + // unsoundness during coherence (#105782). + ty::TypingMode::Coherence => { + return ecx.evaluate_added_goals_and_make_canonical_response( + Certainty::AMBIGUOUS, + ); + } + // Outside of coherence, we treat the associated item as rigid instead. + ty::TypingMode::Analysis { .. } + | ty::TypingMode::Borrowck { .. } + | ty::TypingMode::PostBorrowckAnalysis { .. } + | ty::TypingMode::PostAnalysis => { + ecx.structurally_instantiate_normalizes_to_term( + goal, + goal.predicate.alias, + ); + return ecx + .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); + } + }; } Err(guar) => return error_response(ecx, guar), }; 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 827853be280..24657496df6 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -13,7 +13,7 @@ use tracing::{instrument, trace}; use crate::delegate::SolverDelegate; use crate::solve::assembly::structural_traits::{self, AsyncCallableRelevantTypes}; -use crate::solve::assembly::{self, AssembleCandidatesFrom, Candidate}; +use crate::solve::assembly::{self, AllowInferenceConstraints, AssembleCandidatesFrom, Candidate}; use crate::solve::inspect::ProbeKind; use crate::solve::{ BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause, @@ -1308,18 +1308,6 @@ where return true; } - // We don't consider a trait-bound global if it has a projection bound. - // - // See ui/traits/next-solver/normalization-shadowing/global-trait-with-project.rs - // for an example where this is necessary. - for p in goal.param_env.caller_bounds().iter() { - if let ty::ClauseKind::Projection(proj) = p.kind().skip_binder() { - if proj.projection_term.trait_ref(self.cx()) == trait_pred.trait_ref { - return true; - } - } - } - false } _ => false, @@ -1350,6 +1338,8 @@ where }; } + self.filter_specialized_impls(AllowInferenceConstraints::No, &mut candidates); + // If there are *only* global where bounds, then make sure to return that this // is still reported as being proven-via the param-env so that rigid projections // operate correctly. Otherwise, drop all global where-bounds before merging the diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index ae9143486ee..ac4f7ed64e2 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -675,6 +675,8 @@ parse_note_pattern_alternatives_use_single_vert = alternatives in or-patterns ar parse_nul_in_c_str = null characters in C string literals are not supported +parse_or_in_let_chain = `||` operators are not supported in let chain conditions + parse_or_pattern_not_allowed_in_fn_parameters = top-level or-patterns are not allowed in function parameters parse_or_pattern_not_allowed_in_let_binding = top-level or-patterns are not allowed in `let` bindings parse_out_of_range_hex_escape = out of range hex escape diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 35cf4c1b00d..6a6fb0eb9b5 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -478,6 +478,13 @@ pub(crate) struct ExpectedExpressionFoundLet { pub comparison: Option<MaybeComparison>, } +#[derive(Diagnostic)] +#[diag(parse_or_in_let_chain)] +pub(crate) struct OrInLetChain { + #[primary_span] + pub span: Span, +} + #[derive(Subdiagnostic, Clone, Copy)] #[multipart_suggestion( parse_maybe_missing_let, diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 896e348a12d..e73d68e2037 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -4,13 +4,13 @@ #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(array_windows)] #![feature(assert_matches)] #![feature(box_patterns)] #![feature(debug_closure_helpers)] #![feature(if_let_guard)] #![feature(iter_intersperse)] -#![feature(let_chains)] #![feature(string_from_utf8_lossy_owned)] // tidy-alphabetical-end diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 370eb3f402d..f3b53971b29 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -4073,14 +4073,18 @@ impl MutVisitor for CondChecker<'_> { match e.kind { ExprKind::Let(_, _, _, ref mut recovered @ Recovered::No) => { if let Some(reason) = self.forbid_let_reason { - *recovered = Recovered::Yes(self.parser.dcx().emit_err( - errors::ExpectedExpressionFoundLet { + let error = match reason { + NotSupportedOr(or_span) => { + self.parser.dcx().emit_err(errors::OrInLetChain { span: or_span }) + } + _ => self.parser.dcx().emit_err(errors::ExpectedExpressionFoundLet { span, reason, missing_let: self.missing_let, comparison: self.comparison, - }, - )); + }), + }; + *recovered = Recovered::Yes(error); } else if self.depth > 1 { // Top level `let` is always allowed; only gate chains match self.let_chains_policy { diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 39251f1ce27..4be8a90368d 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -2058,6 +2058,17 @@ impl<'a> Parser<'a> { } self.expect_field_ty_separator()?; let ty = self.parse_ty()?; + if self.token == token::Colon && self.look_ahead(1, |&t| t != token::Colon) { + self.dcx() + .struct_span_err(self.token.span, "found single colon in a struct field type path") + .with_span_suggestion_verbose( + self.token.span, + "write a path separator here", + "::", + Applicability::MaybeIncorrect, + ) + .emit(); + } let default = if self.token == token::Eq { self.bump(); let const_expr = self.parse_expr_anon_const()?; diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 1a02d45f0e3..1093e4f4af0 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -248,19 +248,13 @@ impl<'a> Parser<'a> { segments.push(segment); if self.is_import_coupler() || !self.eat_path_sep() { - let ok_for_recovery = self.may_recover() - && match style { - PathStyle::Expr => true, - PathStyle::Type if let Some((ident, _)) = self.prev_token.ident() => { - self.token == token::Colon - && ident.as_str().chars().all(|c| c.is_lowercase()) - && self.token.span.lo() == self.prev_token.span.hi() - && self - .look_ahead(1, |token| self.token.span.hi() == token.span.lo()) - } - _ => false, - }; - if ok_for_recovery + // IMPORTANT: We can *only ever* treat single colons as typo'ed double colons in + // expression contexts (!) since only there paths cannot possibly be followed by + // a colon and still form a syntactically valid construct. In pattern contexts, + // a path may be followed by a type annotation. E.g., `let pat:ty`. In type + // contexts, a path may be followed by a list of bounds. E.g., `where ty:bound`. + if self.may_recover() + && style == PathStyle::Expr // (!) && self.token == token::Colon && self.look_ahead(1, |token| token.is_ident() && !token.is_reserved_ident()) { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index cfc71a412be..7a27bc5c387 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -680,10 +680,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } if !other_attr.has_any_name(ALLOW_LIST) { + let path = other_attr.path(); + let path: Vec<_> = path.iter().map(|s| s.as_str()).collect(); + let other_attr_name = path.join("::"); + self.dcx().emit_err(errors::NakedFunctionIncompatibleAttribute { span: other_attr.span(), naked_span: attr.span(), - attr: other_attr.name().unwrap(), + attr: other_attr_name, }); return; diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 4052264b051..b1b4b9ee927 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1249,7 +1249,7 @@ pub(crate) struct NakedFunctionIncompatibleAttribute { pub span: Span, #[label(passes_naked_attribute)] pub naked_span: Span, - pub attr: Symbol, + pub attr: String, } #[derive(Diagnostic)] diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index 93ff0f66d69..c7bb00df796 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -6,9 +6,9 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] -#![feature(let_chains)] #![feature(map_try_insert)] #![feature(rustdoc_internals)] #![feature(try_blocks)] diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 176dcbf6da4..f63d8b2d79f 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -6,7 +6,7 @@ #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] #![allow(unused_crate_dependencies)] -#![cfg_attr(feature = "rustc", feature(let_chains))] +#![cfg_attr(all(feature = "rustc", bootstrap), feature(let_chains))] // tidy-alphabetical-end pub mod constructor; diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index c7bab828659..b56f3e1971c 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -1,9 +1,9 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(associated_type_defaults)] -#![feature(let_chains)] #![feature(rustdoc_internals)] #![feature(try_blocks)] // tidy-alphabetical-end @@ -157,7 +157,7 @@ where ty.visit_with(self) } ty::ClauseKind::ConstEvaluatable(ct) => ct.visit_with(self), - ty::ClauseKind::WellFormed(arg) => arg.visit_with(self), + ty::ClauseKind::WellFormed(term) => term.visit_with(self), } } diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs index ac24628447d..d2bcde14383 100644 --- a/compiler/rustc_query_system/src/dep_graph/serialized.rs +++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs @@ -46,6 +46,7 @@ use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::sync::Lock; use rustc_data_structures::unhash::UnhashMap; use rustc_index::{Idx, IndexVec}; +use rustc_serialize::opaque::mem_encoder::MemEncoder; use rustc_serialize::opaque::{FileEncodeResult, FileEncoder, IntEncodedWithFixedSize, MemDecoder}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use tracing::{debug, instrument}; @@ -105,22 +106,12 @@ impl SerializedDepGraph { ) -> impl Iterator<Item = SerializedDepNodeIndex> + Clone { let header = self.edge_list_indices[source]; let mut raw = &self.edge_list_data[header.start()..]; - // Figure out where the edge list for `source` ends by getting the start index of the next - // edge list, or the end of the array if this is the last edge. - let end = self - .edge_list_indices - .get(source + 1) - .map(|h| h.start()) - .unwrap_or_else(|| self.edge_list_data.len() - DEP_NODE_PAD); - - // The number of edges for this node is implicitly stored in the combination of the byte - // width and the length. + let bytes_per_index = header.bytes_per_index(); - let len = (end - header.start()) / bytes_per_index; // LLVM doesn't hoist EdgeHeader::mask so we do it ourselves. let mask = header.mask(); - (0..len).map(move |_| { + (0..header.num_edges).map(move |_| { // Doing this slicing in this order ensures that the first bounds check suffices for // all the others. let index = &raw[..DEP_NODE_SIZE]; @@ -163,6 +154,7 @@ impl SerializedDepGraph { #[derive(Debug, Clone, Copy)] struct EdgeHeader { repr: usize, + num_edges: u32, } impl EdgeHeader { @@ -205,9 +197,14 @@ impl SerializedDepGraph { let graph_bytes = d.len() - (2 * IntEncodedWithFixedSize::ENCODED_SIZE) - d.position(); - let mut nodes = IndexVec::with_capacity(node_count); - let mut fingerprints = IndexVec::with_capacity(node_count); - let mut edge_list_indices = IndexVec::with_capacity(node_count); + let mut nodes = IndexVec::from_elem_n( + DepNode { kind: D::DEP_KIND_NULL, hash: PackedFingerprint::from(Fingerprint::ZERO) }, + node_count, + ); + let mut fingerprints = IndexVec::from_elem_n(Fingerprint::ZERO, node_count); + let mut edge_list_indices = + IndexVec::from_elem_n(EdgeHeader { repr: 0, num_edges: 0 }, node_count); + // This estimation assumes that all of the encoded bytes are for the edge lists or for the // fixed-size node headers. But that's not necessarily true; if any edge list has a length // that spills out of the size we can bit-pack into SerializedNodeHeader then some of the @@ -226,11 +223,14 @@ impl SerializedDepGraph { let node_header = SerializedNodeHeader::<D> { bytes: d.read_array(), _marker: PhantomData }; - let _i: SerializedDepNodeIndex = nodes.push(node_header.node()); - debug_assert_eq!(_i.index(), _index); + let index = node_header.index(); + + let node = &mut nodes[index]; + // Make sure there's no duplicate indices in the dep graph. + assert!(node_header.node().kind != D::DEP_KIND_NULL && node.kind == D::DEP_KIND_NULL); + *node = node_header.node(); - let _i: SerializedDepNodeIndex = fingerprints.push(node_header.fingerprint()); - debug_assert_eq!(_i.index(), _index); + fingerprints[index] = node_header.fingerprint(); // If the length of this node's edge list is small, the length is stored in the header. // If it is not, we fall back to another decoder call. @@ -242,12 +242,11 @@ impl SerializedDepGraph { let edges_len_bytes = node_header.bytes_per_index() * (num_edges as usize); // The in-memory structure for the edges list stores the byte width of the edges on // this node with the offset into the global edge data array. - let edges_header = node_header.edges_header(&edge_list_data); + let edges_header = node_header.edges_header(&edge_list_data, num_edges); edge_list_data.extend(d.read_raw_bytes(edges_len_bytes)); - let _i: SerializedDepNodeIndex = edge_list_indices.push(edges_header); - debug_assert_eq!(_i.index(), _index); + edge_list_indices[index] = edges_header; } // When we access the edge list data, we do a fixed-size read from the edge list data then @@ -298,9 +297,10 @@ impl SerializedDepGraph { /// * In whatever bits remain, the length of the edge list for this node, if it fits struct SerializedNodeHeader<D> { // 2 bytes for the DepNode + // 4 bytes for the index // 16 for Fingerprint in DepNode // 16 for Fingerprint in NodeInfo - bytes: [u8; 34], + bytes: [u8; 38], _marker: PhantomData<D>, } @@ -310,6 +310,7 @@ struct Unpacked { len: Option<u32>, bytes_per_index: usize, kind: DepKind, + index: SerializedDepNodeIndex, hash: PackedFingerprint, fingerprint: Fingerprint, } @@ -331,6 +332,7 @@ impl<D: Deps> SerializedNodeHeader<D> { #[inline] fn new( node: DepNode, + index: DepNodeIndex, fingerprint: Fingerprint, edge_max_index: u32, edge_count: usize, @@ -352,10 +354,11 @@ impl<D: Deps> SerializedNodeHeader<D> { let hash: Fingerprint = node.hash.into(); // Using half-open ranges ensures an unconditional panic if we get the magic numbers wrong. - let mut bytes = [0u8; 34]; + let mut bytes = [0u8; 38]; bytes[..2].copy_from_slice(&head.to_le_bytes()); - bytes[2..18].copy_from_slice(&hash.to_le_bytes()); - bytes[18..].copy_from_slice(&fingerprint.to_le_bytes()); + bytes[2..6].copy_from_slice(&index.as_u32().to_le_bytes()); + bytes[6..22].copy_from_slice(&hash.to_le_bytes()); + bytes[22..].copy_from_slice(&fingerprint.to_le_bytes()); #[cfg(debug_assertions)] { @@ -372,8 +375,9 @@ impl<D: Deps> SerializedNodeHeader<D> { #[inline] fn unpack(&self) -> Unpacked { let head = u16::from_le_bytes(self.bytes[..2].try_into().unwrap()); - let hash = self.bytes[2..18].try_into().unwrap(); - let fingerprint = self.bytes[18..].try_into().unwrap(); + let index = u32::from_le_bytes(self.bytes[2..6].try_into().unwrap()); + let hash = self.bytes[6..22].try_into().unwrap(); + let fingerprint = self.bytes[22..].try_into().unwrap(); let kind = head & mask(Self::KIND_BITS) as u16; let bytes_per_index = (head >> Self::KIND_BITS) & mask(Self::WIDTH_BITS) as u16; @@ -383,6 +387,7 @@ impl<D: Deps> SerializedNodeHeader<D> { len: len.checked_sub(1), bytes_per_index: bytes_per_index as usize + 1, kind: DepKind::new(kind), + index: SerializedDepNodeIndex::from_u32(index), hash: Fingerprint::from_le_bytes(hash).into(), fingerprint: Fingerprint::from_le_bytes(fingerprint), } @@ -399,6 +404,11 @@ impl<D: Deps> SerializedNodeHeader<D> { } #[inline] + fn index(&self) -> SerializedDepNodeIndex { + self.unpack().index + } + + #[inline] fn fingerprint(&self) -> Fingerprint { self.unpack().fingerprint } @@ -410,9 +420,10 @@ impl<D: Deps> SerializedNodeHeader<D> { } #[inline] - fn edges_header(&self, edge_list_data: &[u8]) -> EdgeHeader { + fn edges_header(&self, edge_list_data: &[u8], num_edges: u32) -> EdgeHeader { EdgeHeader { repr: (edge_list_data.len() << DEP_NODE_WIDTH_BITS) | (self.bytes_per_index() - 1), + num_edges, } } } @@ -425,10 +436,15 @@ struct NodeInfo { } impl NodeInfo { - fn encode<D: Deps>(&self, e: &mut FileEncoder) { + fn encode<D: Deps>(&self, e: &mut MemEncoder, index: DepNodeIndex) { let NodeInfo { node, fingerprint, ref edges } = *self; - let header = - SerializedNodeHeader::<D>::new(node, fingerprint, edges.max_index(), edges.len()); + let header = SerializedNodeHeader::<D>::new( + node, + index, + fingerprint, + edges.max_index(), + edges.len(), + ); e.write_array(header.bytes); if header.len().is_none() { @@ -450,8 +466,9 @@ impl NodeInfo { /// This avoids the overhead of constructing `EdgesVec`, which would be needed to call `encode`. #[inline] fn encode_promoted<D: Deps>( - e: &mut FileEncoder, + e: &mut MemEncoder, node: DepNode, + index: DepNodeIndex, fingerprint: Fingerprint, prev_index: SerializedDepNodeIndex, colors: &DepNodeColorMap, @@ -464,7 +481,7 @@ impl NodeInfo { let edge_max = edges.clone().map(|i| colors.current(i).unwrap().as_u32()).max().unwrap_or(0); - let header = SerializedNodeHeader::<D>::new(node, fingerprint, edge_max, edge_count); + let header = SerializedNodeHeader::<D>::new(node, index, fingerprint, edge_max, edge_count); e.write_array(header.bytes); if header.len().is_none() { @@ -498,6 +515,8 @@ struct EncoderState<D: Deps> { total_edge_count: usize, stats: Option<FxHashMap<DepKind, Stat>>, + mem_encoder: MemEncoder, + /// Stores the number of times we've encoded each dep kind. kind_stats: Vec<u32>, marker: PhantomData<D>, @@ -511,22 +530,28 @@ impl<D: Deps> EncoderState<D> { total_edge_count: 0, total_node_count: 0, stats: record_stats.then(FxHashMap::default), + mem_encoder: MemEncoder::new(), kind_stats: iter::repeat(0).take(D::DEP_KIND_MAX as usize + 1).collect(), marker: PhantomData, } } #[inline] + fn alloc_index(&mut self) -> DepNodeIndex { + let index = DepNodeIndex::new(self.total_node_count); + self.total_node_count += 1; + index + } + + #[inline] fn record( &mut self, node: DepNode, + index: DepNodeIndex, edge_count: usize, edges: impl FnOnce(&mut Self) -> Vec<DepNodeIndex>, record_graph: &Option<Lock<DepGraphQuery>>, ) -> DepNodeIndex { - let index = DepNodeIndex::new(self.total_node_count); - - self.total_node_count += 1; self.kind_stats[node.kind.as_usize()] += 1; self.total_edge_count += edge_count; @@ -558,14 +583,25 @@ impl<D: Deps> EncoderState<D> { index } + #[inline] + fn flush_mem_encoder(&mut self) { + let data = &mut self.mem_encoder.data; + if data.len() > 64 * 1024 { + self.encoder.emit_raw_bytes(&data[..]); + data.clear(); + } + } + /// Encodes a node to the current graph. fn encode_node( &mut self, node: &NodeInfo, record_graph: &Option<Lock<DepGraphQuery>>, ) -> DepNodeIndex { - node.encode::<D>(&mut self.encoder); - self.record(node.node, node.edges.len(), |_| node.edges[..].to_vec(), record_graph) + let index = self.alloc_index(); + node.encode::<D>(&mut self.mem_encoder, index); + self.flush_mem_encoder(); + self.record(node.node, index, node.edges.len(), |_| node.edges[..].to_vec(), record_graph) } /// Encodes a node that was promoted from the previous graph. It reads the information directly from @@ -581,20 +617,22 @@ impl<D: Deps> EncoderState<D> { record_graph: &Option<Lock<DepGraphQuery>>, colors: &DepNodeColorMap, ) -> DepNodeIndex { + let index = self.alloc_index(); let node = self.previous.index_to_node(prev_index); - let fingerprint = self.previous.fingerprint_by_index(prev_index); let edge_count = NodeInfo::encode_promoted::<D>( - &mut self.encoder, + &mut self.mem_encoder, node, + index, fingerprint, prev_index, colors, &self.previous, ); - + self.flush_mem_encoder(); self.record( node, + index, edge_count, |this| { this.previous @@ -603,12 +641,14 @@ impl<D: Deps> EncoderState<D> { .collect() }, record_graph, - ) + ); + index } fn finish(self, profiler: &SelfProfilerRef) -> FileEncodeResult { let Self { mut encoder, + mem_encoder, total_node_count, total_edge_count, stats: _, @@ -617,6 +657,8 @@ impl<D: Deps> EncoderState<D> { previous, } = self; + encoder.emit_raw_bytes(&mem_encoder.data); + let node_count = total_node_count.try_into().unwrap(); let edge_count = total_edge_count.try_into().unwrap(); diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs index 2aedd365adc..b159b876c7e 100644 --- a/compiler/rustc_query_system/src/lib.rs +++ b/compiler/rustc_query_system/src/lib.rs @@ -1,9 +1,9 @@ // tidy-alphabetical-start #![allow(rustc::potential_query_instability, internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(assert_matches)] #![feature(core_intrinsics)] #![feature(dropck_eyepatch)] -#![feature(let_chains)] #![feature(min_specialization)] // tidy-alphabetical-end diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index b121755acd9..4a252a7b528 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -10,13 +10,13 @@ #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(assert_matches)] #![feature(box_patterns)] #![feature(if_let_guard)] #![feature(iter_intersperse)] -#![feature(let_chains)] #![feature(rustc_attrs)] #![feature(rustdoc_internals)] // tidy-alphabetical-end diff --git a/compiler/rustc_sanitizers/src/lib.rs b/compiler/rustc_sanitizers/src/lib.rs index e4792563e71..729c921450e 100644 --- a/compiler/rustc_sanitizers/src/lib.rs +++ b/compiler/rustc_sanitizers/src/lib.rs @@ -4,7 +4,7 @@ //! compiler. // tidy-alphabetical-start -#![feature(let_chains)] +#![cfg_attr(bootstrap, feature(let_chains))] // tidy-alphabetical-end pub mod cfi; diff --git a/compiler/rustc_serialize/src/opaque.rs b/compiler/rustc_serialize/src/opaque.rs index 0a55504a556..00bad8e70cf 100644 --- a/compiler/rustc_serialize/src/opaque.rs +++ b/compiler/rustc_serialize/src/opaque.rs @@ -10,6 +10,8 @@ use crate::int_overflow::DebugStrictAdd; use crate::leb128; use crate::serialize::{Decodable, Decoder, Encodable, Encoder}; +pub mod mem_encoder; + // ----------------------------------------------------------------------------- // Encoder // ----------------------------------------------------------------------------- diff --git a/compiler/rustc_serialize/src/opaque/mem_encoder.rs b/compiler/rustc_serialize/src/opaque/mem_encoder.rs new file mode 100644 index 00000000000..56beebb49af --- /dev/null +++ b/compiler/rustc_serialize/src/opaque/mem_encoder.rs @@ -0,0 +1,128 @@ +use super::IntEncodedWithFixedSize; +use crate::{Encodable, Encoder, leb128}; + +pub struct MemEncoder { + pub data: Vec<u8>, +} + +impl MemEncoder { + pub fn new() -> MemEncoder { + MemEncoder { data: vec![] } + } + + #[inline] + pub fn position(&self) -> usize { + self.data.len() + } + + pub fn finish(self) -> Vec<u8> { + self.data + } + + /// Write up to `N` bytes to this encoder. + /// + /// This function can be used to avoid the overhead of calling memcpy for writes that + /// have runtime-variable length, but are small and have a small fixed upper bound. + /// + /// This can be used to do in-place encoding as is done for leb128 (without this function + /// we would need to write to a temporary buffer then memcpy into the encoder), and it can + /// also be used to implement the varint scheme we use for rmeta and dep graph encoding, + /// where we only want to encode the first few bytes of an integer. Note that common + /// architectures support fixed-size writes up to 8 bytes with one instruction, so while this + /// does in some sense do wasted work, we come out ahead. + #[inline] + pub fn write_with<const N: usize>(&mut self, visitor: impl FnOnce(&mut [u8; N]) -> usize) { + self.data.reserve(N); + + let old_len = self.data.len(); + + // SAFETY: The above `reserve` ensures that there is enough + // room to write the encoded value to the vector's internal buffer. + // The memory is also initialized as 0. + let buf = unsafe { + let buf = self.data.as_mut_ptr().add(old_len) as *mut [u8; N]; + *buf = [0; N]; + &mut *buf + }; + let written = visitor(buf); + if written > N { + Self::panic_invalid_write::<N>(written); + } + unsafe { self.data.set_len(old_len + written) }; + } + + #[cold] + #[inline(never)] + fn panic_invalid_write<const N: usize>(written: usize) { + panic!("MemEncoder::write_with::<{N}> cannot be used to write {written} bytes"); + } + + /// Helper for calls where [`MemEncoder::write_with`] always writes the whole array. + #[inline] + pub fn write_array<const N: usize>(&mut self, buf: [u8; N]) { + self.write_with(|dest| { + *dest = buf; + N + }) + } +} + +macro_rules! write_leb128 { + ($this_fn:ident, $int_ty:ty, $write_leb_fn:ident) => { + #[inline] + fn $this_fn(&mut self, v: $int_ty) { + self.write_with(|buf| leb128::$write_leb_fn(buf, v)) + } + }; +} + +impl Encoder for MemEncoder { + write_leb128!(emit_usize, usize, write_usize_leb128); + write_leb128!(emit_u128, u128, write_u128_leb128); + write_leb128!(emit_u64, u64, write_u64_leb128); + write_leb128!(emit_u32, u32, write_u32_leb128); + + #[inline] + fn emit_u16(&mut self, v: u16) { + self.write_array(v.to_le_bytes()); + } + + #[inline] + fn emit_u8(&mut self, v: u8) { + self.write_array([v]); + } + + write_leb128!(emit_isize, isize, write_isize_leb128); + write_leb128!(emit_i128, i128, write_i128_leb128); + write_leb128!(emit_i64, i64, write_i64_leb128); + write_leb128!(emit_i32, i32, write_i32_leb128); + + #[inline] + fn emit_i16(&mut self, v: i16) { + self.write_array(v.to_le_bytes()); + } + + #[inline] + fn emit_raw_bytes(&mut self, s: &[u8]) { + self.data.extend_from_slice(s); + } +} + +// Specialize encoding byte slices. This specialization also applies to encoding `Vec<u8>`s, etc., +// since the default implementations call `encode` on their slices internally. +impl Encodable<MemEncoder> for [u8] { + fn encode(&self, e: &mut MemEncoder) { + Encoder::emit_usize(e, self.len()); + e.emit_raw_bytes(self); + } +} + +impl Encodable<MemEncoder> for IntEncodedWithFixedSize { + #[inline] + fn encode(&self, e: &mut MemEncoder) { + let start_pos = e.position(); + e.write_array(self.0.to_le_bytes()); + let end_pos = e.position(); + debug_assert_eq!((end_pos - start_pos), IntEncodedWithFixedSize::ENCODED_SIZE); + } +} diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index bc92b95ce71..e2d36f6a4e2 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -246,6 +246,10 @@ pub enum AutoDiff { /// Print the module after running autodiff and optimizations. PrintModFinal, + /// Print all passes scheduled by LLVM + PrintPasses, + /// Disable extra opt run after running autodiff + NoPostopt, /// Enzyme's loose type debug helper (can cause incorrect gradients!!) /// Usable in cases where Enzyme errors with `can not deduce type of X`. LooseTypes, @@ -1646,7 +1650,7 @@ The default is {DEFAULT_EDITION} and the latest stable edition is {LATEST_STABLE static PRINT_HELP: LazyLock<String> = LazyLock::new(|| { format!( "Compiler information to print on stdout (or to a file)\n\ - INFO may be one of ({}).", + INFO may be one of <{}>.", PRINT_KINDS.iter().map(|(name, _)| format!("{name}")).collect::<Vec<_>>().join("|") ) }); @@ -1665,6 +1669,13 @@ static EMIT_HELP: LazyLock<String> = LazyLock::new(|| { /// Returns all rustc command line options, including metadata for /// each option, such as whether the option is stable. +/// +/// # Option style guidelines +/// +/// - `<param>`: Indicates a required parameter +/// - `[param]`: Indicates an optional parameter +/// - `|`: Indicates a mutually exclusive option +/// - `*`: a list element with description pub fn rustc_optgroups() -> Vec<RustcOptGroup> { use OptionKind::{Flag, FlagMulti, Multi, Opt}; use OptionStability::{Stable, Unstable}; @@ -1679,18 +1690,18 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> { "", "cfg", "Configure the compilation environment.\n\ - SPEC supports the syntax `NAME[=\"VALUE\"]`.", - "SPEC", + SPEC supports the syntax `<NAME>[=\"<VALUE>\"]`.", + "<SPEC>", ), - opt(Stable, Multi, "", "check-cfg", "Provide list of expected cfgs for checking", "SPEC"), + opt(Stable, Multi, "", "check-cfg", "Provide list of expected cfgs for checking", "<SPEC>"), opt( Stable, Multi, "L", "", "Add a directory to the library search path. \ - The optional KIND can be one of dependency, crate, native, framework, or all (the default).", - "[KIND=]PATH", + The optional KIND can be one of <dependency|crate|native|framework|all> (default: all).", + "[<KIND>=]<PATH>", ), opt( Stable, @@ -1699,46 +1710,46 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> { "", "Link the generated crate(s) to the specified native\n\ library NAME. The optional KIND can be one of\n\ - static, framework, or dylib (the default).\n\ + <static|framework|dylib> (default: dylib).\n\ Optional comma separated MODIFIERS\n\ - (bundle|verbatim|whole-archive|as-needed)\n\ + <bundle|verbatim|whole-archive|as-needed>\n\ may be specified each with a prefix of either '+' to\n\ enable or '-' to disable.", - "[KIND[:MODIFIERS]=]NAME[:RENAME]", + "[<KIND>[:<MODIFIERS>]=]<NAME>[:<RENAME>]", ), make_crate_type_option(), - opt(Stable, Opt, "", "crate-name", "Specify the name of the crate being built", "NAME"), + opt(Stable, Opt, "", "crate-name", "Specify the name of the crate being built", "<NAME>"), opt(Stable, Opt, "", "edition", &EDITION_STRING, EDITION_NAME_LIST), - opt(Stable, Multi, "", "emit", &EMIT_HELP, "TYPE[=FILE]"), - opt(Stable, Multi, "", "print", &PRINT_HELP, "INFO[=FILE]"), + opt(Stable, Multi, "", "emit", &EMIT_HELP, "<TYPE>[=<FILE>]"), + opt(Stable, Multi, "", "print", &PRINT_HELP, "<INFO>[=<FILE>]"), opt(Stable, FlagMulti, "g", "", "Equivalent to -C debuginfo=2", ""), opt(Stable, FlagMulti, "O", "", "Equivalent to -C opt-level=3", ""), - opt(Stable, Opt, "o", "", "Write output to <filename>", "FILENAME"), - opt(Stable, Opt, "", "out-dir", "Write output to compiler-chosen filename in <dir>", "DIR"), + opt(Stable, Opt, "o", "", "Write output to FILENAME", "<FILENAME>"), + opt(Stable, Opt, "", "out-dir", "Write output to compiler-chosen filename in DIR", "<DIR>"), opt( Stable, Opt, "", "explain", "Provide a detailed explanation of an error message", - "OPT", + "<OPT>", ), opt(Stable, Flag, "", "test", "Build a test harness", ""), - opt(Stable, Opt, "", "target", "Target triple for which the code is compiled", "TARGET"), - opt(Stable, Multi, "A", "allow", "Set lint allowed", "LINT"), - opt(Stable, Multi, "W", "warn", "Set lint warnings", "LINT"), - opt(Stable, Multi, "", "force-warn", "Set lint force-warn", "LINT"), - opt(Stable, Multi, "D", "deny", "Set lint denied", "LINT"), - opt(Stable, Multi, "F", "forbid", "Set lint forbidden", "LINT"), + opt(Stable, Opt, "", "target", "Target triple for which the code is compiled", "<TARGET>"), + opt(Stable, Multi, "A", "allow", "Set lint allowed", "<LINT>"), + opt(Stable, Multi, "W", "warn", "Set lint warnings", "<LINT>"), + opt(Stable, Multi, "", "force-warn", "Set lint force-warn", "<LINT>"), + opt(Stable, Multi, "D", "deny", "Set lint denied", "<LINT>"), + opt(Stable, Multi, "F", "forbid", "Set lint forbidden", "<LINT>"), opt( Stable, Multi, "", "cap-lints", "Set the most restrictive lint level. More restrictive lints are capped at this level", - "LEVEL", + "<LEVEL>", ), - opt(Stable, Multi, "C", "codegen", "Set a codegen option", "OPT[=VALUE]"), + opt(Stable, Multi, "C", "codegen", "Set a codegen option", "<OPT>[=<VALUE>]"), opt(Stable, Flag, "V", "version", "Print version info and exit", ""), opt(Stable, Flag, "v", "verbose", "Use verbose output", ""), ]; @@ -1752,29 +1763,29 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> { "", "extern", "Specify where an external rust library is located", - "NAME[=PATH]", + "<NAME>[=<PATH>]", ), - opt(Stable, Opt, "", "sysroot", "Override the system root", "PATH"), - opt(Unstable, Multi, "Z", "", "Set unstable / perma-unstable options", "FLAG"), + opt(Stable, Opt, "", "sysroot", "Override the system root", "<PATH>"), + opt(Unstable, Multi, "Z", "", "Set unstable / perma-unstable options", "<FLAG>"), opt( Stable, Opt, "", "error-format", "How errors and other messages are produced", - "human|json|short", + "<human|json|short>", ), - opt(Stable, Multi, "", "json", "Configure the JSON output of the compiler", "CONFIG"), + opt(Stable, Multi, "", "json", "Configure the JSON output of the compiler", "<CONFIG>"), opt( Stable, Opt, "", "color", "Configure coloring of output: - auto = colorize, if output goes to a tty (default); - always = always colorize output; - never = never colorize output", - "auto|always|never", + * auto = colorize, if output goes to a tty (default); + * always = always colorize output; + * never = never colorize output", + "<auto|always|never>", ), opt( Stable, @@ -1782,7 +1793,7 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> { "", "diagnostic-width", "Inform rustc of the width of the output so that diagnostics can be truncated to fit", - "WIDTH", + "<WIDTH>", ), opt( Stable, @@ -1790,9 +1801,9 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> { "", "remap-path-prefix", "Remap source names in all output (compiler messages and output files)", - "FROM=TO", + "<FROM>=<TO>", ), - opt(Unstable, Multi, "", "env-set", "Inject an environment variable", "VAR=VALUE"), + opt(Unstable, Multi, "", "env-set", "Inject an environment variable", "<VAR>=<VALUE>"), ]; options.extend(verbose_only.into_iter().map(|mut opt| { opt.is_verbose_help_only = true; @@ -2312,14 +2323,13 @@ pub fn parse_externs( let ExternOpt { crate_name: name, path, options } = split_extern_opt(early_dcx, unstable_opts, &arg).unwrap_or_else(|e| e.emit()); - let path = path.map(|p| CanonicalizedPath::new(p.as_path())); - let entry = externs.entry(name.to_owned()); use std::collections::btree_map::Entry; let entry = if let Some(path) = path { // --extern prelude_name=some_file.rlib + let path = CanonicalizedPath::new(path); match entry { Entry::Vacant(vacant) => { let files = BTreeSet::from_iter(iter::once(path)); @@ -2792,7 +2802,7 @@ pub fn make_crate_type_option() -> RustcOptGroup { "crate-type", "Comma separated list of types of crates for the compiler to emit", - "[bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]", + "<bin|lib|rlib|dylib|cdylib|staticlib|proc-macro>", ) } diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index 0e19b982a13..ec8e9898dc7 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -1,8 +1,8 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![feature(default_field_values)] #![feature(iter_intersperse)] -#![feature(let_chains)] #![feature(rustc_attrs)] // To generate CodegenOptionsTargetModifiers and UnstableOptionsTargetModifiers enums // with macro_rules, it is necessary to use recursive mechanic ("Incremental TT Munchers"). diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 36eee5f3086..5f4695fb184 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -711,7 +711,7 @@ mod desc { pub(crate) const parse_list: &str = "a space-separated list of strings"; pub(crate) const parse_list_with_polarity: &str = "a comma-separated list of strings, with elements beginning with + or -"; - pub(crate) const parse_autodiff: &str = "a comma separated list of settings: `Enable`, `PrintSteps`, `PrintTA`, `PrintAA`, `PrintPerf`, `PrintModBefore`, `PrintModAfter`, `PrintModFinal`, `LooseTypes`, `Inline`"; + pub(crate) const parse_autodiff: &str = "a comma separated list of settings: `Enable`, `PrintSteps`, `PrintTA`, `PrintAA`, `PrintPerf`, `PrintModBefore`, `PrintModAfter`, `PrintModFinal`, `PrintPasses`, `NoPostopt`, `LooseTypes`, `Inline`"; pub(crate) const parse_comma_list: &str = "a comma-separated list of strings"; pub(crate) const parse_opt_comma_list: &str = parse_comma_list; pub(crate) const parse_number: &str = "a number"; @@ -1360,6 +1360,8 @@ pub mod parse { "PrintModBefore" => AutoDiff::PrintModBefore, "PrintModAfter" => AutoDiff::PrintModAfter, "PrintModFinal" => AutoDiff::PrintModFinal, + "NoPostopt" => AutoDiff::NoPostopt, + "PrintPasses" => AutoDiff::PrintPasses, "LooseTypes" => AutoDiff::LooseTypes, "Inline" => AutoDiff::Inline, _ => { @@ -2098,6 +2100,8 @@ options! { `=PrintModBefore` `=PrintModAfter` `=PrintModFinal` + `=PrintPasses`, + `=NoPostopt` `=LooseTypes` `=Inline` Multiple options can be combined with commas."), diff --git a/compiler/rustc_session/src/utils.rs b/compiler/rustc_session/src/utils.rs index 2243e831b66..e9ddd66b5e8 100644 --- a/compiler/rustc_session/src/utils.rs +++ b/compiler/rustc_session/src/utils.rs @@ -1,4 +1,4 @@ -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::sync::OnceLock; use rustc_data_structures::profiling::VerboseTimingGuard; @@ -104,8 +104,8 @@ pub struct CanonicalizedPath { } impl CanonicalizedPath { - pub fn new(path: &Path) -> Self { - Self { original: path.to_owned(), canonicalized: try_canonicalize(path).ok() } + pub fn new(path: PathBuf) -> Self { + Self { canonicalized: try_canonicalize(&path).ok(), original: path } } pub fn canonicalized(&self) -> &PathBuf { diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs index a757329bcf2..c0ed3b90eb4 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs @@ -638,8 +638,8 @@ impl<'tcx> Stable<'tcx> for ty::ClauseKind<'tcx> { const_.stable(tables), ty.stable(tables), ), - ClauseKind::WellFormed(generic_arg) => { - stable_mir::ty::ClauseKind::WellFormed(generic_arg.unpack().stable(tables)) + ClauseKind::WellFormed(term) => { + stable_mir::ty::ClauseKind::WellFormed(term.unpack().stable(tables)) } ClauseKind::ConstEvaluatable(const_) => { stable_mir::ty::ClauseKind::ConstEvaluatable(const_.stable(tables)) diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs index 4b153007bd8..0ac9a0fb647 100644 --- a/compiler/rustc_smir/src/stable_mir/ty.rs +++ b/compiler/rustc_smir/src/stable_mir/ty.rs @@ -1462,7 +1462,7 @@ pub enum ClauseKind { TypeOutlives(TypeOutlivesPredicate), Projection(ProjectionPredicate), ConstArgHasType(TyConst, Ty), - WellFormed(GenericArgKind), + WellFormed(TermKind), ConstEvaluatable(TyConst), } diff --git a/compiler/rustc_span/src/edition.rs b/compiler/rustc_span/src/edition.rs index da298080ed2..28335734f4d 100644 --- a/compiler/rustc_span/src/edition.rs +++ b/compiler/rustc_span/src/edition.rs @@ -45,7 +45,7 @@ pub const ALL_EDITIONS: &[Edition] = &[ Edition::EditionFuture, ]; -pub const EDITION_NAME_LIST: &str = "2015|2018|2021|2024"; +pub const EDITION_NAME_LIST: &str = "<2015|2018|2021|2024|future>"; pub const DEFAULT_EDITION: Edition = Edition::Edition2015; diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 53908914965..ab0a802dc7f 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -72,21 +72,6 @@ struct SyntaxContextData { } impl SyntaxContextData { - fn new( - (parent, outer_expn, outer_transparency): SyntaxContextKey, - opaque: SyntaxContext, - opaque_and_semiopaque: SyntaxContext, - ) -> SyntaxContextData { - SyntaxContextData { - outer_expn, - outer_transparency, - parent, - opaque, - opaque_and_semiopaque, - dollar_crate_name: kw::DollarCrate, - } - } - fn root() -> SyntaxContextData { SyntaxContextData { outer_expn: ExpnId::root(), @@ -98,14 +83,6 @@ impl SyntaxContextData { } } - fn decode_placeholder() -> SyntaxContextData { - SyntaxContextData { dollar_crate_name: kw::Empty, ..SyntaxContextData::root() } - } - - fn is_decode_placeholder(&self) -> bool { - self.dollar_crate_name == kw::Empty - } - fn key(&self) -> SyntaxContextKey { (self.parent, self.outer_expn, self.outer_transparency) } @@ -148,7 +125,7 @@ impl !PartialOrd for LocalExpnId {} /// with a non-default mode. With this check in place, we can avoid the need /// to maintain separate versions of `ExpnData` hashes for each permutation /// of `HashingControls` settings. -fn assert_default_hashing_controls<CTX: HashStableContext>(ctx: &CTX, msg: &str) { +fn assert_default_hashing_controls(ctx: &impl HashStableContext, msg: &str) { match ctx.hashing_controls() { // Note that we require that `hash_spans` be set according to the global // `-Z incremental-ignore-spans` option. Normally, this option is disabled, @@ -416,7 +393,7 @@ impl HygieneData { } } - fn with<T, F: FnOnce(&mut HygieneData) -> T>(f: F) -> T { + fn with<R>(f: impl FnOnce(&mut HygieneData) -> R) -> R { with_session_globals(|session_globals| f(&mut session_globals.hygiene_data.borrow_mut())) } @@ -460,28 +437,23 @@ impl HygieneData { } fn normalize_to_macros_2_0(&self, ctxt: SyntaxContext) -> SyntaxContext { - debug_assert!(!self.syntax_context_data[ctxt.0 as usize].is_decode_placeholder()); self.syntax_context_data[ctxt.0 as usize].opaque } fn normalize_to_macro_rules(&self, ctxt: SyntaxContext) -> SyntaxContext { - debug_assert!(!self.syntax_context_data[ctxt.0 as usize].is_decode_placeholder()); self.syntax_context_data[ctxt.0 as usize].opaque_and_semiopaque } fn outer_expn(&self, ctxt: SyntaxContext) -> ExpnId { - debug_assert!(!self.syntax_context_data[ctxt.0 as usize].is_decode_placeholder()); self.syntax_context_data[ctxt.0 as usize].outer_expn } fn outer_mark(&self, ctxt: SyntaxContext) -> (ExpnId, Transparency) { - debug_assert!(!self.syntax_context_data[ctxt.0 as usize].is_decode_placeholder()); let data = &self.syntax_context_data[ctxt.0 as usize]; (data.outer_expn, data.outer_transparency) } fn parent_ctxt(&self, ctxt: SyntaxContext) -> SyntaxContext { - debug_assert!(!self.syntax_context_data[ctxt.0 as usize].is_decode_placeholder()); self.syntax_context_data[ctxt.0 as usize].parent } @@ -592,8 +564,6 @@ impl HygieneData { expn_id: ExpnId, transparency: Transparency, ) -> SyntaxContext { - debug_assert!(!self.syntax_context_data[parent.0 as usize].is_decode_placeholder()); - // Look into the cache first. let key = (parent, expn_id, transparency); if let Some(ctxt) = self.syntax_context_map.get(&key) { @@ -601,16 +571,20 @@ impl HygieneData { } // Reserve a new syntax context. + // The inserted dummy data can only be potentially accessed by nested `alloc_ctxt` calls, + // the assert below ensures that it doesn't happen. let ctxt = SyntaxContext::from_usize(self.syntax_context_data.len()); - self.syntax_context_data.push(SyntaxContextData::decode_placeholder()); + self.syntax_context_data + .push(SyntaxContextData { dollar_crate_name: sym::dummy, ..SyntaxContextData::root() }); self.syntax_context_map.insert(key, ctxt); // Opaque and semi-opaque versions of the parent. Note that they may be equal to the // parent itself. E.g. `parent_opaque` == `parent` if the expn chain contains only opaques, // and `parent_opaque_and_semiopaque` == `parent` if the expn contains only (semi-)opaques. - let parent_opaque = self.syntax_context_data[parent.0 as usize].opaque; - let parent_opaque_and_semiopaque = - self.syntax_context_data[parent.0 as usize].opaque_and_semiopaque; + let parent_data = &self.syntax_context_data[parent.0 as usize]; + assert_ne!(parent_data.dollar_crate_name, sym::dummy); + let parent_opaque = parent_data.opaque; + let parent_opaque_and_semiopaque = parent_data.opaque_and_semiopaque; // Evaluate opaque and semi-opaque versions of the new syntax context. let (opaque, opaque_and_semiopaque) = match transparency { @@ -629,8 +603,14 @@ impl HygieneData { }; // Fill the full data, now that we have it. - self.syntax_context_data[ctxt.as_u32() as usize] = - SyntaxContextData::new(key, opaque, opaque_and_semiopaque); + self.syntax_context_data[ctxt.as_u32() as usize] = SyntaxContextData { + outer_expn: expn_id, + outer_transparency: transparency, + parent, + opaque, + opaque_and_semiopaque, + dollar_crate_name: kw::DollarCrate, + }; ctxt } } @@ -650,13 +630,12 @@ pub fn walk_chain_collapsed(span: Span, to: Span) -> Span { pub fn update_dollar_crate_names(mut get_name: impl FnMut(SyntaxContext) -> Symbol) { // The new contexts that need updating are at the end of the list and have `$crate` as a name. - // Also decoding placeholders can be encountered among both old and new contexts. let mut to_update = vec![]; HygieneData::with(|data| { for (idx, scdata) in data.syntax_context_data.iter().enumerate().rev() { if scdata.dollar_crate_name == kw::DollarCrate { to_update.push((idx, kw::DollarCrate)); - } else if !scdata.is_decode_placeholder() { + } else { break; } } @@ -922,10 +901,7 @@ impl SyntaxContext { } pub(crate) fn dollar_crate_name(self) -> Symbol { - HygieneData::with(|data| { - debug_assert!(!data.syntax_context_data[self.0 as usize].is_decode_placeholder()); - data.syntax_context_data[self.0 as usize].dollar_crate_name - }) + HygieneData::with(|data| data.syntax_context_data[self.0 as usize].dollar_crate_name) } pub fn edition(self) -> Edition { @@ -1293,49 +1269,47 @@ impl HygieneEncodeContext { self.latest_ctxts ); - // Consume the current round of SyntaxContexts. - // Drop the lock() temporary early - let latest_ctxts = { mem::take(&mut *self.latest_ctxts.lock()) }; - - // It's fine to iterate over a HashMap, because the serialization - // of the table that we insert data into doesn't depend on insertion - // order + // Consume the current round of syntax contexts. + // Drop the lock() temporary early. + // It's fine to iterate over a HashMap, because the serialization of the table + // that we insert data into doesn't depend on insertion order. #[allow(rustc::potential_query_instability)] - for_all_ctxts_in(latest_ctxts.into_iter(), |index, ctxt, data| { + let latest_ctxts = { mem::take(&mut *self.latest_ctxts.lock()) }.into_iter(); + let all_ctxt_data: Vec<_> = HygieneData::with(|data| { + latest_ctxts + .map(|ctxt| (ctxt, data.syntax_context_data[ctxt.0 as usize].key())) + .collect() + }); + for (ctxt, ctxt_key) in all_ctxt_data { if self.serialized_ctxts.lock().insert(ctxt) { - encode_ctxt(encoder, index, data); + encode_ctxt(encoder, ctxt.0, &ctxt_key); } - }); - - let latest_expns = { mem::take(&mut *self.latest_expns.lock()) }; + } - // Same as above, this is fine as we are inserting into a order-independent hashset + // Same as above, but for expansions instead of syntax contexts. #[allow(rustc::potential_query_instability)] - for_all_expns_in(latest_expns.into_iter(), |expn, data, hash| { + let latest_expns = { mem::take(&mut *self.latest_expns.lock()) }.into_iter(); + let all_expn_data: Vec<_> = HygieneData::with(|data| { + latest_expns + .map(|expn| (expn, data.expn_data(expn).clone(), data.expn_hash(expn))) + .collect() + }); + for (expn, expn_data, expn_hash) in all_expn_data { if self.serialized_expns.lock().insert(expn) { - encode_expn(encoder, expn, data, hash); + encode_expn(encoder, expn, &expn_data, expn_hash); } - }); + } } debug!("encode_hygiene: Done serializing SyntaxContextData"); } } -#[derive(Default)] /// Additional information used to assist in decoding hygiene data -struct HygieneDecodeContextInner { - // Maps serialized `SyntaxContext` ids to a `SyntaxContext` in the current - // global `HygieneData`. When we deserialize a `SyntaxContext`, we need to create - // a new id in the global `HygieneData`. This map tracks the ID we end up picking, - // so that multiple occurrences of the same serialized id are decoded to the same - // `SyntaxContext`. This only stores `SyntaxContext`s which are completely decoded. - remapped_ctxts: Vec<Option<SyntaxContext>>, -} - #[derive(Default)] -/// Additional information used to assist in decoding hygiene data pub struct HygieneDecodeContext { - inner: Lock<HygieneDecodeContextInner>, + // A cache mapping raw serialized per-crate syntax context ids to corresponding decoded + // `SyntaxContext`s in the current global `HygieneData`. + remapped_ctxts: Lock<IndexVec<u32, Option<SyntaxContext>>>, } /// Register an expansion which has been decoded from the on-disk-cache for the local crate. @@ -1406,10 +1380,10 @@ pub fn decode_expn_id( // to track which `SyntaxContext`s we have already decoded. // The provided closure will be invoked to deserialize a `SyntaxContextData` // if we haven't already seen the id of the `SyntaxContext` we are deserializing. -pub fn decode_syntax_context<D: Decoder, F: FnOnce(&mut D, u32) -> SyntaxContextKey>( +pub fn decode_syntax_context<D: Decoder>( d: &mut D, context: &HygieneDecodeContext, - decode_data: F, + decode_data: impl FnOnce(&mut D, u32) -> SyntaxContextKey, ) -> SyntaxContext { let raw_id: u32 = Decodable::decode(d); if raw_id == 0 { @@ -1418,12 +1392,11 @@ pub fn decode_syntax_context<D: Decoder, F: FnOnce(&mut D, u32) -> SyntaxContext return SyntaxContext::root(); } + // Look into the cache first. // Reminder: `HygieneDecodeContext` is per-crate, so there are no collisions between // raw ids from different crate metadatas. - if let Some(ctxt) = context.inner.lock().remapped_ctxts.get(raw_id as usize).copied().flatten() - { - // This has already been decoded. - return ctxt; + if let Some(Some(ctxt)) = context.remapped_ctxts.lock().get(raw_id) { + return *ctxt; } // Don't try to decode data while holding the lock, since we need to @@ -1432,48 +1405,11 @@ pub fn decode_syntax_context<D: Decoder, F: FnOnce(&mut D, u32) -> SyntaxContext let ctxt = HygieneData::with(|hygiene_data| hygiene_data.alloc_ctxt(parent, expn_id, transparency)); - let mut inner = context.inner.lock(); - let new_len = raw_id as usize + 1; - if inner.remapped_ctxts.len() < new_len { - inner.remapped_ctxts.resize(new_len, None); - } - inner.remapped_ctxts[raw_id as usize] = Some(ctxt); + context.remapped_ctxts.lock().insert(raw_id, ctxt); ctxt } -fn for_all_ctxts_in<F: FnMut(u32, SyntaxContext, &SyntaxContextKey)>( - ctxts: impl Iterator<Item = SyntaxContext>, - mut f: F, -) { - let all_data: Vec<_> = HygieneData::with(|data| { - ctxts - .map(|ctxt| { - (ctxt, { - let item = data.syntax_context_data[ctxt.0 as usize]; - debug_assert!(!item.is_decode_placeholder()); - item.key() - }) - }) - .collect() - }); - for (ctxt, data) in all_data.into_iter() { - f(ctxt.0, ctxt, &data); - } -} - -fn for_all_expns_in( - expns: impl Iterator<Item = ExpnId>, - mut f: impl FnMut(ExpnId, &ExpnData, ExpnHash), -) { - let all_data: Vec<_> = HygieneData::with(|data| { - expns.map(|expn| (expn, data.expn_data(expn).clone(), data.expn_hash(expn))).collect() - }); - for (expn, data, hash) in all_data.into_iter() { - f(expn, &data, hash); - } -} - impl<E: SpanEncoder> Encodable<E> for LocalExpnId { fn encode(&self, e: &mut E) { self.to_expn_id().encode(e); @@ -1486,10 +1422,10 @@ impl<D: SpanDecoder> Decodable<D> for LocalExpnId { } } -pub fn raw_encode_syntax_context<E: Encoder>( +pub fn raw_encode_syntax_context( ctxt: SyntaxContext, context: &HygieneEncodeContext, - e: &mut E, + e: &mut impl Encoder, ) { if !context.serialized_ctxts.lock().contains(&ctxt) { context.latest_ctxts.lock().insert(ctxt); diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index f788fd48037..fccdaed21a2 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -17,6 +17,7 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(array_windows)] @@ -24,7 +25,6 @@ #![feature(core_io_borrowed_buf)] #![feature(hash_set_entry)] #![feature(if_let_guard)] -#![feature(let_chains)] #![feature(map_try_insert)] #![feature(negative_impls)] #![feature(read_buf)] diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index cc33974cc62..ca8918e06aa 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -89,9 +89,9 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] -#![feature(let_chains)] #![feature(rustdoc_internals)] // tidy-alphabetical-end diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index 7ecc46cc69d..ae366e29e32 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -144,6 +144,7 @@ pub struct ArgAttributes { /// (corresponding to LLVM's dereferenceable_or_null attributes, i.e., it is okay for this to be /// set on a null pointer, but all non-null pointers must be dereferenceable). pub pointee_size: Size, + /// The minimum alignment of the pointee, if any. pub pointee_align: Option<Align>, } diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs index df99280f571..922c18448d5 100644 --- a/compiler/rustc_target/src/lib.rs +++ b/compiler/rustc_target/src/lib.rs @@ -9,12 +9,12 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(assert_matches)] #![feature(debug_closure_helpers)] #![feature(iter_intersperse)] -#![feature(let_chains)] #![feature(rustc_attrs)] #![feature(rustdoc_internals)] // tidy-alphabetical-end diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 3c769dad630..37ea0d6e7b5 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -42,7 +42,9 @@ use std::path::{Path, PathBuf}; use std::str::FromStr; use std::{fmt, io}; -use rustc_abi::{Endian, ExternAbi, Integer, Size, TargetDataLayout, TargetDataLayoutErrors}; +use rustc_abi::{ + Align, Endian, ExternAbi, Integer, Size, TargetDataLayout, TargetDataLayoutErrors, +}; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_fs_util::try_canonicalize; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; @@ -3599,6 +3601,25 @@ impl Target { _ => return None, }) } + + /// Returns whether this target is known to have unreliable alignment: + /// native C code for the target fails to align some data to the degree + /// required by the C standard. We can't *really* do anything about that + /// since unsafe Rust code may assume alignment any time, but we can at least + /// inhibit some optimizations, and we suppress the alignment checks that + /// would detect this unsoundness. + /// + /// Every target that returns less than `Align::MAX` here is still has a soundness bug. + pub fn max_reliable_alignment(&self) -> Align { + // FIXME(#112480) MSVC on x86-32 is unsound and fails to properly align many types with + // more-than-4-byte-alignment on the stack. This makes alignments larger than 4 generally + // unreliable on 32bit Windows. + if self.is_like_windows && self.arch == "x86" { + Align::from_bytes(4).unwrap() + } else { + Align::MAX + } + } } /// Either a target tuple string or a path to a JSON file. diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 07005215e4f..69c8b9119ab 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -516,7 +516,7 @@ static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("zawrs", Unstable(sym::riscv_target_feature), &[]), ("zba", Stable, &[]), ("zbb", Stable, &[]), - ("zbc", Stable, &[]), + ("zbc", Stable, &["zbkc"]), // Zbc ⊃ Zbkc ("zbkb", Stable, &[]), ("zbkc", Stable, &[]), ("zbkx", Stable, &[]), @@ -545,20 +545,20 @@ static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("zknd", Stable, &[]), ("zkne", Stable, &[]), ("zknh", Stable, &[]), - ("zkr", Stable, &["zicsr"]), + ("zkr", Stable, &[]), ("zks", Stable, &["zbkb", "zbkc", "zbkx", "zksed", "zksh"]), ("zksed", Stable, &[]), ("zksh", Stable, &[]), ("zkt", Stable, &[]), ("ztso", Unstable(sym::riscv_target_feature), &[]), - ("zvbb", Unstable(sym::riscv_target_feature), &["zvkb"]), + ("zvbb", Unstable(sym::riscv_target_feature), &["zvkb"]), // Zvbb ⊃ Zvkb ("zvbc", Unstable(sym::riscv_target_feature), &["zve64x"]), ("zve32f", Unstable(sym::riscv_target_feature), &["zve32x", "f"]), ("zve32x", Unstable(sym::riscv_target_feature), &["zvl32b", "zicsr"]), ("zve64d", Unstable(sym::riscv_target_feature), &["zve64f", "d"]), ("zve64f", Unstable(sym::riscv_target_feature), &["zve32f", "zve64x"]), ("zve64x", Unstable(sym::riscv_target_feature), &["zve32x", "zvl64b"]), - ("zvfh", Unstable(sym::riscv_target_feature), &["zvfhmin", "zfhmin"]), + ("zvfh", Unstable(sym::riscv_target_feature), &["zvfhmin", "zve32f", "zfhmin"]), // Zvfh ⊃ Zvfhmin ("zvfhmin", Unstable(sym::riscv_target_feature), &["zve32f"]), ("zvkb", Unstable(sym::riscv_target_feature), &["zve32x"]), ("zvkg", Unstable(sym::riscv_target_feature), &["zve32x"]), @@ -567,7 +567,7 @@ static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("zvkned", Unstable(sym::riscv_target_feature), &["zve32x"]), ("zvkng", Unstable(sym::riscv_target_feature), &["zvkn", "zvkg"]), ("zvknha", Unstable(sym::riscv_target_feature), &["zve32x"]), - ("zvknhb", Unstable(sym::riscv_target_feature), &["zve64x"]), + ("zvknhb", Unstable(sym::riscv_target_feature), &["zvknha", "zve64x"]), // Zvknhb ⊃ Zvknha ("zvks", Unstable(sym::riscv_target_feature), &["zvksed", "zvksh", "zvkb", "zvkt"]), ("zvksc", Unstable(sym::riscv_target_feature), &["zvks", "zvbc"]), ("zvksed", Unstable(sym::riscv_target_feature), &["zve32x"]), 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 f45e3904212..eba195cb99c 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 @@ -14,8 +14,8 @@ use rustc_middle::hir::nested_filter; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Print, Printer}; use rustc_middle::ty::{ - self, GenericArg, GenericArgKind, GenericArgsRef, InferConst, IsSuggestable, Ty, TyCtxt, - TypeFoldable, TypeFolder, TypeSuperFoldable, TypeckResults, + self, GenericArg, GenericArgKind, GenericArgsRef, InferConst, IsSuggestable, Term, TermKind, + Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeckResults, }; use rustc_span::{BytePos, DUMMY_SP, FileName, Ident, Span, sym}; use rustc_type_ir::TypeVisitableExt; @@ -344,12 +344,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// which were stuck during inference. pub fn extract_inference_diagnostics_data( &self, - arg: GenericArg<'tcx>, + term: Term<'tcx>, highlight: ty::print::RegionHighlightMode<'tcx>, ) -> InferenceDiagnosticsData { let tcx = self.tcx; - match arg.unpack() { - GenericArgKind::Type(ty) => { + match term.unpack() { + TermKind::Ty(ty) => { if let ty::Infer(ty::TyVar(ty_vid)) = *ty.kind() { let var_origin = self.infcx.type_var_origin(ty_vid); if let Some(def_id) = var_origin.param_def_id @@ -375,7 +375,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { parent: None, } } - GenericArgKind::Const(ct) => { + TermKind::Const(ct) => { if let ty::ConstKind::Infer(InferConst::Var(vid)) = ct.kind() { let origin = self.const_var_origin(vid).expect("expected unresolved const var"); if let Some(def_id) = origin.param_def_id { @@ -411,7 +411,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } } - GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"), } } @@ -472,13 +471,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &self, body_def_id: LocalDefId, failure_span: Span, - arg: GenericArg<'tcx>, + term: Term<'tcx>, error_code: TypeAnnotationNeeded, should_label_span: bool, ) -> Diag<'a> { - let arg = self.resolve_vars_if_possible(arg); - let arg_data = - self.extract_inference_diagnostics_data(arg, ty::print::RegionHighlightMode::default()); + let term = self.resolve_vars_if_possible(term); + let arg_data = self + .extract_inference_diagnostics_data(term, ty::print::RegionHighlightMode::default()); let Some(typeck_results) = &self.typeck_results else { // If we don't have any typeck results we're outside @@ -487,7 +486,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return self.bad_inference_failure_err(failure_span, arg_data, error_code); }; - let mut local_visitor = FindInferSourceVisitor::new(self, typeck_results, arg); + let mut local_visitor = FindInferSourceVisitor::new(self, typeck_results, term); if let Some(body) = self.tcx.hir_maybe_body_owned_by( self.tcx.typeck_root_def_id(body_def_id.to_def_id()).expect_local(), ) { @@ -542,7 +541,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { have_turbofish, } => { let generics = self.tcx.generics_of(generics_def_id); - let is_type = matches!(arg.unpack(), GenericArgKind::Type(_)); + let is_type = term.as_type().is_some(); let (parent_exists, parent_prefix, parent_name) = InferenceDiagnosticsParentData::for_parent_def_id(self.tcx, generics_def_id) @@ -811,7 +810,7 @@ struct FindInferSourceVisitor<'a, 'tcx> { tecx: &'a TypeErrCtxt<'a, 'tcx>, typeck_results: &'a TypeckResults<'tcx>, - target: GenericArg<'tcx>, + target: Term<'tcx>, attempt: usize, infer_source_cost: usize, @@ -822,7 +821,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { fn new( tecx: &'a TypeErrCtxt<'a, 'tcx>, typeck_results: &'a TypeckResults<'tcx>, - target: GenericArg<'tcx>, + target: Term<'tcx>, ) -> Self { FindInferSourceVisitor { tecx, @@ -938,12 +937,12 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { // Check whether this generic argument is the inference variable we // are looking for. fn generic_arg_is_target(&self, arg: GenericArg<'tcx>) -> bool { - if arg == self.target { + if arg == self.target.into() { return true; } match (arg.unpack(), self.target.unpack()) { - (GenericArgKind::Type(inner_ty), GenericArgKind::Type(target_ty)) => { + (GenericArgKind::Type(inner_ty), TermKind::Ty(target_ty)) => { use ty::{Infer, TyVar}; match (inner_ty.kind(), target_ty.kind()) { (&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => { @@ -952,7 +951,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { _ => false, } } - (GenericArgKind::Const(inner_ct), GenericArgKind::Const(target_ct)) => { + (GenericArgKind::Const(inner_ct), TermKind::Const(target_ct)) => { use ty::InferConst::*; match (inner_ct.kind(), target_ct.kind()) { (ty::ConstKind::Infer(Var(a_vid)), ty::ConstKind::Infer(Var(b_vid))) => { diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs index 54b50851b74..275b580d794 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs @@ -228,13 +228,18 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Pick the first generic parameter that still contains inference variables as the one // we're going to emit an error for. If there are none (see above), fall back to // a more general error. - let arg = data.trait_ref.args.iter().find(|s| s.has_non_region_infer()); + let term = data + .trait_ref + .args + .iter() + .filter_map(ty::GenericArg::as_term) + .find(|s| s.has_non_region_infer()); - let mut err = if let Some(arg) = arg { + let mut err = if let Some(term) = term { self.emit_inference_failure_err( obligation.cause.body_id, span, - arg, + term, TypeAnnotationNeeded::E0283, true, ) @@ -276,7 +281,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } if ambiguities.len() > 1 && ambiguities.len() < 10 && has_non_region_infer { if let Some(e) = self.tainted_by_errors() - && arg.is_none() + && term.is_none() { // If `arg.is_none()`, then this is probably two param-env // candidates or impl candidates that are equal modulo lifetimes. @@ -313,7 +318,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.suggest_fully_qualified_path(&mut err, def_id, span, trait_pred.def_id()); } - if let Some(ty::GenericArgKind::Type(_)) = arg.map(|arg| arg.unpack()) + if term.is_some_and(|term| term.as_type().is_some()) && let Some(body) = self.tcx.hir_maybe_body_owned_by(obligation.cause.body_id) { let mut expr_finder = FindExprBySpan::new(span, self.tcx); @@ -464,11 +469,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err } - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => { // Same hacky approach as above to avoid deluging user // with error messages. - if let Err(e) = arg.error_reported() { + if let Err(e) = term.error_reported() { return e; } if let Some(e) = self.tainted_by_errors() { @@ -478,7 +483,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.emit_inference_failure_err( obligation.cause.body_id, span, - arg, + term, TypeAnnotationNeeded::E0282, false, ) @@ -519,18 +524,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // other `Foo` impls are incoherent. return guar; } - let arg = data + let term = data .projection_term .args .iter() - .chain(Some(data.term.into_arg())) + .filter_map(ty::GenericArg::as_term) + .chain([data.term]) .find(|g| g.has_non_region_infer()); let predicate = self.tcx.short_string(predicate, &mut file); - if let Some(arg) = arg { + if let Some(term) = term { self.emit_inference_failure_err( obligation.cause.body_id, span, - arg, + term, TypeAnnotationNeeded::E0284, true, ) @@ -554,12 +560,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let Some(e) = self.tainted_by_errors() { return e; } - let arg = data.walk().find(|g| g.is_non_region_infer()); - if let Some(arg) = arg { + let term = + data.walk().filter_map(ty::GenericArg::as_term).find(|term| term.is_infer()); + if let Some(term) = term { let err = self.emit_inference_failure_err( obligation.cause.body_id, span, - arg, + term, TypeAnnotationNeeded::E0284, true, ); 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 df6e8fc4503..d8e05d9ad97 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 @@ -813,38 +813,17 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { obligation: &PredicateObligation<'tcx>, mut trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> Option<ErrorGuaranteed> { - // If `AsyncFnKindHelper` is not implemented, that means that the closure kind - // doesn't extend the goal kind. This is worth reporting, but we can only do so - // if we actually know which closure this goal comes from, so look at the cause - // to see if we can extract that information. - if self.tcx.is_lang_item(trait_pred.def_id(), LangItem::AsyncFnKindHelper) - && let Some(found_kind) = - trait_pred.skip_binder().trait_ref.args.type_at(0).to_opt_closure_kind() - && let Some(expected_kind) = - trait_pred.skip_binder().trait_ref.args.type_at(1).to_opt_closure_kind() - && !found_kind.extends(expected_kind) - { - if let Some((_, Some(parent))) = obligation.cause.code().parent_with_predicate() { - // If we have a derived obligation, then the parent will be a `AsyncFn*` goal. + // If we end up on an `AsyncFnKindHelper` goal, try to unwrap the parent + // `AsyncFn*` goal. + if self.tcx.is_lang_item(trait_pred.def_id(), LangItem::AsyncFnKindHelper) { + let mut code = obligation.cause.code(); + // Unwrap a `FunctionArg` cause, which has been refined from a derived obligation. + if let ObligationCauseCode::FunctionArg { parent_code, .. } = code { + code = &**parent_code; + } + // If we have a derived obligation, then the parent will be a `AsyncFn*` goal. + if let Some((_, Some(parent))) = code.parent_with_predicate() { trait_pred = parent; - } else if let &ObligationCauseCode::FunctionArg { arg_hir_id, .. } = - obligation.cause.code() - && let Some(typeck_results) = &self.typeck_results - && let ty::Closure(closure_def_id, _) | ty::CoroutineClosure(closure_def_id, _) = - *typeck_results.node_type(arg_hir_id).kind() - { - // Otherwise, extract the closure kind from the obligation, - // but only if we actually have an argument to deduce the - // closure type from... - let mut err = self.report_closure_error( - &obligation, - closure_def_id, - found_kind, - expected_kind, - "Async", - ); - self.note_obligation_cause(&mut err, &obligation); - return Some(err.emit()); } } @@ -1523,19 +1502,17 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return None; }; - let trait_assoc_item = self.tcx.opt_associated_item(proj.projection_term.def_id)?; - let trait_assoc_ident = trait_assoc_item.ident(self.tcx); - let mut associated_items = vec![]; self.tcx.for_each_relevant_impl( self.tcx.trait_of_item(proj.projection_term.def_id)?, proj.projection_term.self_ty(), |impl_def_id| { associated_items.extend( - self.tcx - .associated_items(impl_def_id) - .in_definition_order() - .find(|assoc| assoc.ident(self.tcx) == trait_assoc_ident), + self.tcx.associated_items(impl_def_id).in_definition_order().find( + |assoc| { + assoc.trait_item_def_id == Some(proj.projection_term.def_id) + }, + ), ); }, ); diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index 93c11805304..7613a0cef52 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -14,6 +14,7 @@ #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(assert_matches)] @@ -23,7 +24,6 @@ #![feature(if_let_guard)] #![feature(iter_intersperse)] #![feature(iterator_try_reduce)] -#![feature(let_chains)] #![feature(never_type)] #![feature(rustdoc_internals)] #![feature(try_blocks)] diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index e0b425fa739..a87c5ad6db9 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -92,12 +92,16 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< fn well_formed_goals( &self, param_env: ty::ParamEnv<'tcx>, - arg: ty::GenericArg<'tcx>, + term: ty::Term<'tcx>, ) -> Option<Vec<Goal<'tcx, ty::Predicate<'tcx>>>> { - crate::traits::wf::unnormalized_obligations(&self.0, param_env, arg, DUMMY_SP, CRATE_DEF_ID) - .map(|obligations| { - obligations.into_iter().map(|obligation| obligation.as_goal()).collect() - }) + crate::traits::wf::unnormalized_obligations( + &self.0, + param_env, + term, + DUMMY_SP, + CRATE_DEF_ID, + ) + .map(|obligations| obligations.into_iter().map(|obligation| obligation.as_goal()).collect()) } fn clone_opaque_types_for_query_response(&self) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> { diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 848d0646d00..1f4fa5aac10 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -163,15 +163,15 @@ where fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E> { assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); let mut errors = Vec::new(); - for i in 0.. { - if !infcx.tcx.recursion_limit().value_within_limit(i) { - self.obligations.on_fulfillment_overflow(infcx); - // Only return true errors that we have accumulated while processing. - return errors; - } - + loop { let mut has_changed = false; - for obligation in self.obligations.drain_pending(|_| true) { + for mut obligation in self.obligations.drain_pending(|_| true) { + if !infcx.tcx.recursion_limit().value_within_limit(obligation.recursion_depth) { + self.obligations.on_fulfillment_overflow(infcx); + // Only return true errors that we have accumulated while processing. + return errors; + } + let goal = obligation.as_goal(); let result = <&SolverDelegate<'tcx>>::from(infcx) .evaluate_root_goal(goal, GenerateProofTree::No, obligation.cause.span) @@ -189,6 +189,13 @@ where }; if changed == HasChanged::Yes { + // We increment the recursion depth here to track the number of times + // this goal has resulted in inference progress. This doesn't precisely + // model the way that we track recursion depth in the old solver due + // to the fact that we only process root obligations, but it is a good + // approximation and should only result in fulfillment overflow in + // pathological cases. + obligation.recursion_depth += 1; has_changed = true; } 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 f6c650c68c0..a024f432450 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -241,13 +241,13 @@ impl<'tcx> BestObligation<'tcx> { fn visit_well_formed_goal( &mut self, candidate: &inspect::InspectCandidate<'_, 'tcx>, - arg: ty::GenericArg<'tcx>, + term: ty::Term<'tcx>, ) -> ControlFlow<PredicateObligation<'tcx>> { let infcx = candidate.goal().infcx(); let param_env = candidate.goal().goal().param_env; let body_id = self.obligation.cause.body_id; - for obligation in wf::unnormalized_obligations(infcx, param_env, arg, self.span(), body_id) + for obligation in wf::unnormalized_obligations(infcx, param_env, term, self.span(), body_id) .into_iter() .flatten() { @@ -443,8 +443,8 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { polarity: ty::PredicatePolarity::Positive, })) } - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { - return self.visit_well_formed_goal(candidate, arg); + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => { + return self.visit_well_formed_goal(candidate, term); } _ => ChildMode::PassThrough, }; diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index a11f8d3a9ec..34c3c905bd9 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -540,18 +540,18 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { self.selcx.infcx.err_ctxt().report_overflow_obligation(&obligation, false); } - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => { match wf::obligations( self.selcx.infcx, obligation.param_env, obligation.cause.body_id, obligation.recursion_depth + 1, - arg, + term, obligation.cause.span, ) { None => { pending_obligation.stalled_on = - vec![TyOrConstInferVar::maybe_from_generic_arg(arg).unwrap()]; + vec![TyOrConstInferVar::maybe_from_term(term).unwrap()]; ProcessResult::Unchanged } Some(os) => ProcessResult::Changed(mk_pending(obligation, os)), diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs index f059bd00768..81b5a131a32 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs @@ -131,12 +131,12 @@ fn relate_mir_and_user_args<'tcx>( // const CONST: () = { /* arbitrary code that depends on T being WF */ }; // } // ``` - for arg in args { + for term in args.iter().filter_map(ty::GenericArg::as_term) { ocx.register_obligation(Obligation::new( tcx, cause.clone(), param_env, - ty::ClauseKind::WellFormed(arg), + ty::ClauseKind::WellFormed(term), )); } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index f98529860ff..1df69932c64 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -113,8 +113,8 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( | ty::PredicateKind::AliasRelate(..) => {} // We need to search through *all* WellFormed predicates - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { - wf_args.push(arg); + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => { + wf_args.push(term); } // We need to register region relationships diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs index 87fc532adaf..18971c47831 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs @@ -19,10 +19,10 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { return Some(()); } - if let ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) = + if let ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) = key.value.predicate.kind().skip_binder() { - match arg.as_type()?.kind() { + match term.as_type()?.kind() { ty::Param(_) | ty::Bool | ty::Char 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 cf6d2bc151f..00101010f14 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -240,8 +240,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if !drcx.args_may_unify(obligation_args, bound_trait_ref.skip_binder().args) { continue; } - // FIXME(oli-obk): it is suspicious that we are dropping the constness and - // polarity here. let wc = self.where_clause_may_apply(stack, bound_trait_ref)?; if wc.may_apply() { candidates.vec.push(ParamCandidate(bound)); diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index c7ce13c8014..89febd6ce3d 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -658,7 +658,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => { // So, there is a bit going on here. First, `WellFormed` predicates // are coinductive, like trait predicates with auto traits. // This means that we need to detect if we have recursively @@ -682,11 +682,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let cache = previous_stack.cache; let dfn = cache.next_dfn(); - for stack_arg in previous_stack.cache.wf_args.borrow().iter().rev() { - if stack_arg.0 != arg { + for stack_term in previous_stack.cache.wf_args.borrow().iter().rev() { + if stack_term.0 != term { continue; } - debug!("WellFormed({:?}) on stack", arg); + debug!("WellFormed({:?}) on stack", term); if let Some(stack) = previous_stack.head { // Okay, let's imagine we have two different stacks: // `T: NonAutoTrait -> WF(T) -> T: NonAutoTrait` @@ -702,11 +702,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // would contain `(T, 1)`. We want to check all // trait predicates greater than `1`. The previous // stack would be `T: Auto`. - let cycle = stack.iter().take_while(|s| s.depth > stack_arg.1); + let cycle = stack.iter().take_while(|s| s.depth > stack_term.1); let tcx = self.tcx(); let cycle = cycle.map(|stack| stack.obligation.predicate.upcast(tcx)); if self.coinductive_match(cycle) { - stack.update_reached_depth(stack_arg.1); + stack.update_reached_depth(stack_term.1); return Ok(EvaluatedToOk); } else { return Ok(EvaluatedToAmbigStackDependent); @@ -720,11 +720,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation.param_env, obligation.cause.body_id, obligation.recursion_depth + 1, - arg, + term, obligation.cause.span, ) { Some(obligations) => { - cache.wf_args.borrow_mut().push((arg, previous_stack.depth())); + cache.wf_args.borrow_mut().push((term, previous_stack.depth())); let result = self.evaluate_predicates_recursively(previous_stack, obligations); cache.wf_args.borrow_mut().pop(); @@ -2969,14 +2969,14 @@ struct ProvisionalEvaluationCache<'tcx> { /// means the cached value for `F`. map: RefCell<FxIndexMap<ty::PolyTraitPredicate<'tcx>, ProvisionalEvaluation>>, - /// The stack of args that we assume to be true because a `WF(arg)` predicate + /// The stack of terms that we assume to be well-formed because a `WF(term)` predicate /// is on the stack above (and because of wellformedness is coinductive). /// In an "ideal" world, this would share a stack with trait predicates in /// `TraitObligationStack`. However, trait predicates are *much* hotter than /// `WellFormed` predicates, and it's very likely that the additional matches /// will have a perf effect. The value here is the well-formed `GenericArg` /// and the depth of the trait predicate *above* that well-formed predicate. - wf_args: RefCell<Vec<(ty::GenericArg<'tcx>, usize)>>, + wf_args: RefCell<Vec<(ty::Term<'tcx>, usize)>>, } /// A cache value for the provisional cache: contains the depth-first diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 54b6c22b2d8..62bd8e1af05 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -1,3 +1,8 @@ +//! Core logic responsible for determining what it means for various type system +//! primitives to be "well formed". Actually checking whether these primitives are +//! well formed is performed elsewhere (e.g. during type checking or item well formedness +//! checking). + use std::iter; use rustc_hir as hir; @@ -5,8 +10,8 @@ use rustc_hir::lang_items::LangItem; use rustc_infer::traits::{ObligationCauseCode, PredicateObligations}; use rustc_middle::bug; use rustc_middle::ty::{ - self, GenericArg, GenericArgKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, - TypeVisitable, TypeVisitableExt, TypeVisitor, + self, GenericArgsRef, Term, TermKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, TypeVisitor, }; use rustc_session::parse::feature_err; use rustc_span::def_id::{DefId, LocalDefId}; @@ -15,28 +20,29 @@ use tracing::{debug, instrument, trace}; use crate::infer::InferCtxt; use crate::traits; + /// Returns the set of obligations needed to make `arg` well-formed. /// If `arg` contains unresolved inference variables, this may include /// further WF obligations. However, if `arg` IS an unresolved /// inference variable, returns `None`, because we are not able to -/// make any progress at all. This is to prevent "livelock" where we -/// say "$0 is WF if $0 is WF". +/// make any progress at all. This is to prevent cycles where we +/// say "?0 is WF if ?0 is WF". pub fn obligations<'tcx>( infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, body_id: LocalDefId, recursion_depth: usize, - arg: GenericArg<'tcx>, + term: Term<'tcx>, span: Span, ) -> Option<PredicateObligations<'tcx>> { - // Handle the "livelock" case (see comment above) by bailing out if necessary. - let arg = match arg.unpack() { - GenericArgKind::Type(ty) => { + // Handle the "cycle" case (see comment above) by bailing out if necessary. + let term = match term.unpack() { + TermKind::Ty(ty) => { match ty.kind() { ty::Infer(ty::TyVar(_)) => { let resolved_ty = infcx.shallow_resolve(ty); if resolved_ty == ty { - // No progress, bail out to prevent "livelock". + // No progress, bail out to prevent cycles. return None; } else { resolved_ty @@ -46,12 +52,12 @@ pub fn obligations<'tcx>( } .into() } - GenericArgKind::Const(ct) => { + TermKind::Const(ct) => { match ct.kind() { ty::ConstKind::Infer(_) => { let resolved = infcx.shallow_resolve_const(ct); if resolved == ct { - // No progress. + // No progress, bail out to prevent cycles. return None; } else { resolved @@ -61,8 +67,6 @@ pub fn obligations<'tcx>( } .into() } - // There is nothing we have to do for lifetimes. - GenericArgKind::Lifetime(..) => return Some(PredicateObligations::new()), }; let mut wf = WfPredicates { @@ -74,11 +78,11 @@ pub fn obligations<'tcx>( recursion_depth, item: None, }; - wf.compute(arg); - debug!("wf::obligations({:?}, body_id={:?}) = {:?}", arg, body_id, wf.out); + wf.add_wf_preds_for_term(term); + debug!("wf::obligations({:?}, body_id={:?}) = {:?}", term, body_id, wf.out); let result = wf.normalize(infcx); - debug!("wf::obligations({:?}, body_id={:?}) ~~> {:?}", arg, body_id, result); + debug!("wf::obligations({:?}, body_id={:?}) ~~> {:?}", term, body_id, result); Some(result) } @@ -89,23 +93,19 @@ pub fn obligations<'tcx>( pub fn unnormalized_obligations<'tcx>( infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, - arg: GenericArg<'tcx>, + term: Term<'tcx>, span: Span, body_id: LocalDefId, ) -> Option<PredicateObligations<'tcx>> { - debug_assert_eq!(arg, infcx.resolve_vars_if_possible(arg)); + debug_assert_eq!(term, infcx.resolve_vars_if_possible(term)); // However, if `arg` IS an unresolved inference variable, returns `None`, // because we are not able to make any progress at all. This is to prevent - // "livelock" where we say "$0 is WF if $0 is WF". - if arg.is_non_region_infer() { + // cycles where we say "?0 is WF if ?0 is WF". + if term.is_infer() { return None; } - if let ty::GenericArgKind::Lifetime(..) = arg.unpack() { - return Some(PredicateObligations::new()); - } - let mut wf = WfPredicates { infcx, param_env, @@ -115,7 +115,7 @@ pub fn unnormalized_obligations<'tcx>( recursion_depth: 0, item: None, }; - wf.compute(arg); + wf.add_wf_preds_for_term(term); Some(wf.out) } @@ -140,7 +140,7 @@ pub fn trait_obligations<'tcx>( recursion_depth: 0, item: Some(item), }; - wf.compute_trait_pred(trait_pred, Elaborate::All); + wf.add_wf_preds_for_trait_pred(trait_pred, Elaborate::All); debug!(obligations = ?wf.out); wf.normalize(infcx) } @@ -171,7 +171,7 @@ pub fn clause_obligations<'tcx>( // It's ok to skip the binder here because wf code is prepared for it match clause.kind().skip_binder() { ty::ClauseKind::Trait(t) => { - wf.compute_trait_pred(t, Elaborate::None); + wf.add_wf_preds_for_trait_pred(t, Elaborate::None); } ty::ClauseKind::HostEffect(..) => { // Technically the well-formedness of this predicate is implied by @@ -179,22 +179,22 @@ pub fn clause_obligations<'tcx>( } ty::ClauseKind::RegionOutlives(..) => {} ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, _reg)) => { - wf.compute(ty.into()); + wf.add_wf_preds_for_term(ty.into()); } ty::ClauseKind::Projection(t) => { - wf.compute_alias_term(t.projection_term); - wf.compute(t.term.into_arg()); + wf.add_wf_preds_for_alias_term(t.projection_term); + wf.add_wf_preds_for_term(t.term); } ty::ClauseKind::ConstArgHasType(ct, ty) => { - wf.compute(ct.into()); - wf.compute(ty.into()); + wf.add_wf_preds_for_term(ct.into()); + wf.add_wf_preds_for_term(ty.into()); } - ty::ClauseKind::WellFormed(arg) => { - wf.compute(arg); + ty::ClauseKind::WellFormed(term) => { + wf.add_wf_preds_for_term(term); } ty::ClauseKind::ConstEvaluatable(ct) => { - wf.compute(ct.into()); + wf.add_wf_preds_for_term(ct.into()); } } @@ -372,14 +372,18 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { } /// Pushes the obligations required for `trait_ref` to be WF into `self.out`. - fn compute_trait_pred(&mut self, trait_pred: ty::TraitPredicate<'tcx>, elaborate: Elaborate) { + fn add_wf_preds_for_trait_pred( + &mut self, + trait_pred: ty::TraitPredicate<'tcx>, + elaborate: Elaborate, + ) { let tcx = self.tcx(); let trait_ref = trait_pred.trait_ref; // Negative trait predicates don't require supertraits to hold, just // that their args are WF. if trait_pred.polarity == ty::PredicatePolarity::Negative { - self.compute_negative_trait_pred(trait_ref); + self.add_wf_preds_for_negative_trait_pred(trait_ref); return; } @@ -416,11 +420,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { .args .iter() .enumerate() - .filter(|(_, arg)| { - matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..)) - }) - .filter(|(_, arg)| !arg.has_escaping_bound_vars()) - .map(|(i, arg)| { + .filter_map(|(i, arg)| arg.as_term().map(|t| (i, t))) + .filter(|(_, term)| !term.has_escaping_bound_vars()) + .map(|(i, term)| { let mut cause = traits::ObligationCause::misc(self.span, self.body_id); // The first arg is the self ty - use the correct span for it. if i == 0 { @@ -435,9 +437,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { cause, depth, param_env, - ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed( - arg, - ))), + ty::ClauseKind::WellFormed(term), ) }), ); @@ -445,15 +445,17 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // Compute the obligations that are required for `trait_ref` to be WF, // given that it is a *negative* trait predicate. - fn compute_negative_trait_pred(&mut self, trait_ref: ty::TraitRef<'tcx>) { + fn add_wf_preds_for_negative_trait_pred(&mut self, trait_ref: ty::TraitRef<'tcx>) { for arg in trait_ref.args { - self.compute(arg); + if let Some(term) = arg.as_term() { + self.add_wf_preds_for_term(term); + } } } /// Pushes the obligations required for an alias (except inherent) to be WF /// into `self.out`. - fn compute_alias_term(&mut self, data: ty::AliasTerm<'tcx>) { + fn add_wf_preds_for_alias_term(&mut self, data: ty::AliasTerm<'tcx>) { // A projection is well-formed if // // (a) its predicates hold (*) @@ -478,13 +480,13 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { let obligations = self.nominal_obligations(data.def_id, data.args); self.out.extend(obligations); - self.compute_projection_args(data.args); + self.add_wf_preds_for_projection_args(data.args); } /// Pushes the obligations required for an inherent alias to be WF /// into `self.out`. // FIXME(inherent_associated_types): Merge this function with `fn compute_alias`. - fn compute_inherent_projection(&mut self, data: ty::AliasTy<'tcx>) { + fn add_wf_preds_for_inherent_projection(&mut self, data: ty::AliasTy<'tcx>) { // An inherent projection is well-formed if // // (a) its predicates hold (*) @@ -511,7 +513,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { data.args.visit_with(self); } - fn compute_projection_args(&mut self, args: GenericArgsRef<'tcx>) { + fn add_wf_preds_for_projection_args(&mut self, args: GenericArgsRef<'tcx>) { let tcx = self.tcx(); let cause = self.cause(ObligationCauseCode::WellFormed(None)); let param_env = self.param_env; @@ -519,19 +521,15 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { self.out.extend( args.iter() - .filter(|arg| { - matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..)) - }) - .filter(|arg| !arg.has_escaping_bound_vars()) - .map(|arg| { + .filter_map(|arg| arg.as_term()) + .filter(|term| !term.has_escaping_bound_vars()) + .map(|term| { traits::Obligation::with_depth( tcx, cause.clone(), depth, param_env, - ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed( - arg, - ))), + ty::ClauseKind::WellFormed(term), ) }), ); @@ -555,10 +553,10 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { } } - /// Pushes all the predicates needed to validate that `ty` is WF into `out`. + /// Pushes all the predicates needed to validate that `term` is WF into `out`. #[instrument(level = "debug", skip(self))] - fn compute(&mut self, arg: GenericArg<'tcx>) { - arg.visit_with(self); + fn add_wf_preds_for_term(&mut self, term: Term<'tcx>) { + term.visit_with(self); debug!(?self.out); } @@ -596,7 +594,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { .collect() } - fn from_object_ty( + fn add_wf_preds_for_dyn_ty( &mut self, ty: Ty<'tcx>, data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, @@ -651,6 +649,13 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { outlives, )); } + + // We don't add any wf predicates corresponding to the trait ref's generic arguments + // which allows code like this to compile: + // ```rust + // trait Trait<T: Sized> {} + // fn foo(_: &dyn Trait<[u32]>) {} + // ``` } } } @@ -761,7 +766,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> { self.out.extend(obligations); } ty::Alias(ty::Inherent, data) => { - self.compute_inherent_projection(data); + self.add_wf_preds_for_inherent_projection(data); return; // Subtree handled by compute_inherent_projection. } @@ -895,7 +900,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> { // // Here, we defer WF checking due to higher-ranked // regions. This is perhaps not ideal. - self.from_object_ty(t, data, r); + self.add_wf_preds_for_dyn_ty(t, data, r); // FIXME(#27579) RFC also considers adding trait // obligations that don't refer to Self and @@ -917,11 +922,11 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> { // 1. Check if they have been resolved, and if so proceed with // THAT type. // 2. If not, we've at least simplified things (e.g., we went - // from `Vec<$0>: WF` to `$0: WF`), so we can + // from `Vec?0>: WF` to `?0: WF`), so we can // register a pending obligation and keep // moving. (Goal is that an "inductive hypothesis" // is satisfied to ensure termination.) - // See also the comment on `fn obligations`, describing "livelock" + // See also the comment on `fn obligations`, describing cycle // prevention, which happens before this can be reached. ty::Infer(_) => { let cause = self.cause(ObligationCauseCode::WellFormed(None)); diff --git a/compiler/rustc_transmute/src/layout/mod.rs b/compiler/rustc_transmute/src/layout/mod.rs index d555ea702a9..4d5f630ae22 100644 --- a/compiler/rustc_transmute/src/layout/mod.rs +++ b/compiler/rustc_transmute/src/layout/mod.rs @@ -65,7 +65,12 @@ impl fmt::Debug for Byte { } } -#[cfg(test)] +impl From<RangeInclusive<u8>> for Byte { + fn from(src: RangeInclusive<u8>) -> Self { + Self::new(src) + } +} + impl From<u8> for Byte { fn from(src: u8) -> Self { Self::from_val(src) diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index 6a09be18ef9..7cf712ce9e9 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -1,4 +1,4 @@ -use std::ops::ControlFlow; +use std::ops::{ControlFlow, RangeInclusive}; use super::{Byte, Def, Ref}; @@ -32,6 +32,22 @@ where Byte(Byte), } +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub(crate) enum Endian { + Little, + Big, +} + +#[cfg(feature = "rustc")] +impl From<rustc_abi::Endian> for Endian { + fn from(order: rustc_abi::Endian) -> Endian { + match order { + rustc_abi::Endian::Little => Endian::Little, + rustc_abi::Endian::Big => Endian::Big, + } + } +} + impl<D, R> Tree<D, R> where D: Def, @@ -59,22 +75,60 @@ where /// A `Tree` representing the layout of `bool`. pub(crate) fn bool() -> Self { - Self::Byte(Byte::new(0x00..=0x01)) + Self::byte(0x00..=0x01) } /// A `Tree` whose layout matches that of a `u8`. pub(crate) fn u8() -> Self { - Self::Byte(Byte::new(0x00..=0xFF)) + Self::byte(0x00..=0xFF) + } + + /// A `Tree` whose layout matches that of a `char`. + pub(crate) fn char(order: Endian) -> Self { + // `char`s can be in the following ranges: + // - [0, 0xD7FF] + // - [0xE000, 10FFFF] + // + // All other `char` values are illegal. We can thus represent a `char` + // as a union of three possible layouts: + // - 00 00 [00, D7] XX + // - 00 00 [E0, FF] XX + // - 00 [01, 10] XX XX + + const _0: RangeInclusive<u8> = 0..=0; + const BYTE: RangeInclusive<u8> = 0x00..=0xFF; + let x = Self::from_big_endian(order, [_0, _0, 0x00..=0xD7, BYTE]); + let y = Self::from_big_endian(order, [_0, _0, 0xE0..=0xFF, BYTE]); + let z = Self::from_big_endian(order, [_0, 0x01..=0x10, BYTE, BYTE]); + Self::alt([x, y, z]) } - /// A `Tree` whose layout accepts exactly the given bit pattern. - pub(crate) fn from_bits(bits: u8) -> Self { - Self::Byte(Byte::from_val(bits)) + /// A `Tree` whose layout matches `std::num::NonZeroXxx`. + #[allow(dead_code)] + pub(crate) fn nonzero(width_in_bytes: u64) -> Self { + const BYTE: RangeInclusive<u8> = 0x00..=0xFF; + const NONZERO: RangeInclusive<u8> = 0x01..=0xFF; + + (0..width_in_bytes) + .map(|nz_idx| { + (0..width_in_bytes) + .map(|pos| Self::byte(if pos == nz_idx { NONZERO } else { BYTE })) + .fold(Self::unit(), Self::then) + }) + .fold(Self::uninhabited(), Self::or) + } + + pub(crate) fn bytes<const N: usize, B: Into<Byte>>(bytes: [B; N]) -> Self { + Self::seq(bytes.map(B::into).map(Self::Byte)) + } + + pub(crate) fn byte(byte: impl Into<Byte>) -> Self { + Self::Byte(byte.into()) } /// A `Tree` whose layout is a number of the given width. - pub(crate) fn number(width_in_bytes: usize) -> Self { - Self::Seq(vec![Self::u8(); width_in_bytes]) + pub(crate) fn number(width_in_bytes: u64) -> Self { + Self::Seq(vec![Self::u8(); width_in_bytes.try_into().unwrap()]) } /// A `Tree` whose layout is entirely padding of the given width. @@ -125,13 +179,35 @@ where Self::Byte(..) | Self::Ref(..) | Self::Def(..) => true, } } -} -impl<D, R> Tree<D, R> -where - D: Def, - R: Ref, -{ + /// Produces a `Tree` which represents a sequence of bytes stored in + /// `order`. + /// + /// `bytes` is taken to be in big-endian byte order, and its order will be + /// swapped if `order == Endian::Little`. + pub(crate) fn from_big_endian<const N: usize, B: Into<Byte>>( + order: Endian, + mut bytes: [B; N], + ) -> Self { + if order == Endian::Little { + (&mut bytes[..]).reverse(); + } + + Self::bytes(bytes) + } + + /// Produces a `Tree` where each of the trees in `trees` are sequenced one + /// after another. + pub(crate) fn seq<const N: usize>(trees: [Tree<D, R>; N]) -> Self { + trees.into_iter().fold(Tree::unit(), Self::then) + } + + /// Produces a `Tree` where each of the trees in `trees` are accepted as + /// alternative layouts. + pub(crate) fn alt<const N: usize>(trees: [Tree<D, R>; N]) -> Self { + trees.into_iter().fold(Tree::uninhabited(), Self::or) + } + /// Produces a new `Tree` where `other` is sequenced after `self`. pub(crate) fn then(self, other: Self) -> Self { match (self, other) { @@ -222,17 +298,17 @@ pub(crate) mod rustc { ty::Float(nty) => { let width = nty.bit_width() / 8; - Ok(Self::number(width as _)) + Ok(Self::number(width.try_into().unwrap())) } ty::Int(nty) => { let width = nty.normalize(pointer_size.bits() as _).bit_width().unwrap() / 8; - Ok(Self::number(width as _)) + Ok(Self::number(width.try_into().unwrap())) } ty::Uint(nty) => { let width = nty.normalize(pointer_size.bits() as _).bit_width().unwrap() / 8; - Ok(Self::number(width as _)) + Ok(Self::number(width.try_into().unwrap())) } ty::Tuple(members) => Self::from_tuple((ty, layout), members, cx), @@ -249,11 +325,33 @@ pub(crate) mod rustc { .fold(Tree::unit(), |tree, elt| tree.then(elt))) } - ty::Adt(adt_def, _args_ref) if !ty.is_box() => match adt_def.adt_kind() { - AdtKind::Struct => Self::from_struct((ty, layout), *adt_def, cx), - AdtKind::Enum => Self::from_enum((ty, layout), *adt_def, cx), - AdtKind::Union => Self::from_union((ty, layout), *adt_def, cx), - }, + ty::Adt(adt_def, _args_ref) if !ty.is_box() => { + let (lo, hi) = cx.tcx().layout_scalar_valid_range(adt_def.did()); + + use core::ops::Bound::*; + let is_transparent = adt_def.repr().transparent(); + match (adt_def.adt_kind(), lo, hi) { + (AdtKind::Struct, Unbounded, Unbounded) => { + Self::from_struct((ty, layout), *adt_def, cx) + } + (AdtKind::Struct, Included(1), Included(_hi)) if is_transparent => { + // FIXME(@joshlf): Support `NonZero` types: + // - Check to make sure that the first field is + // numerical + // - Check to make sure that the upper bound is the + // maximum value for the field's type + // - Construct `Self::nonzero` + Err(Err::NotYetSupported) + } + (AdtKind::Enum, Unbounded, Unbounded) => { + Self::from_enum((ty, layout), *adt_def, cx) + } + (AdtKind::Union, Unbounded, Unbounded) => { + Self::from_union((ty, layout), *adt_def, cx) + } + _ => Err(Err::NotYetSupported), + } + } ty::Ref(lifetime, ty, mutability) => { let layout = layout_of(cx, *ty)?; @@ -268,6 +366,8 @@ pub(crate) mod rustc { })) } + ty::Char => Ok(Self::char(cx.tcx().data_layout.endian.into())), + _ => Err(Err::NotYetSupported), } } @@ -450,7 +550,7 @@ pub(crate) mod rustc { &bytes[bytes.len() - size.bytes_usize()..] } }; - Self::Seq(bytes.iter().map(|&b| Self::from_bits(b)).collect()) + Self::Seq(bytes.iter().map(|&b| Self::byte(b)).collect()) } /// Constructs a `Tree` from a union. diff --git a/compiler/rustc_transmute/src/layout/tree/tests.rs b/compiler/rustc_transmute/src/layout/tree/tests.rs index 44f50a25c93..8c3dbbe37ab 100644 --- a/compiler/rustc_transmute/src/layout/tree/tests.rs +++ b/compiler/rustc_transmute/src/layout/tree/tests.rs @@ -20,23 +20,18 @@ mod prune { #[test] fn seq_1() { - let layout: Tree<Def, !> = - Tree::def(Def::NoSafetyInvariants).then(Tree::from_bits(0x00)); - assert_eq!( - layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), - Tree::from_bits(0x00) - ); + let layout: Tree<Def, !> = Tree::def(Def::NoSafetyInvariants).then(Tree::byte(0x00)); + assert_eq!(layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), Tree::byte(0x00)); } #[test] fn seq_2() { - let layout: Tree<Def, !> = Tree::from_bits(0x00) - .then(Tree::def(Def::NoSafetyInvariants)) - .then(Tree::from_bits(0x01)); + let layout: Tree<Def, !> = + Tree::byte(0x00).then(Tree::def(Def::NoSafetyInvariants)).then(Tree::byte(0x01)); assert_eq!( layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), - Tree::from_bits(0x00).then(Tree::from_bits(0x01)) + Tree::byte(0x00).then(Tree::byte(0x01)) ); } } @@ -66,7 +61,7 @@ mod prune { #[test] fn invisible_def_in_seq_len_3() { let layout: Tree<Def, !> = Tree::def(Def::NoSafetyInvariants) - .then(Tree::from_bits(0x00)) + .then(Tree::byte(0x00)) .then(Tree::def(Def::HasSafetyInvariants)); assert_eq!( layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), @@ -94,12 +89,9 @@ mod prune { #[test] fn visible_def_in_seq_len_3() { let layout: Tree<Def, !> = Tree::def(Def::NoSafetyInvariants) - .then(Tree::from_bits(0x00)) + .then(Tree::byte(0x00)) .then(Tree::def(Def::NoSafetyInvariants)); - assert_eq!( - layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), - Tree::from_bits(0x00) - ); + assert_eq!(layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), Tree::byte(0x00)); } } } diff --git a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs index 24e2a1acadd..992fcb7cc4c 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs @@ -177,9 +177,9 @@ mod bool { #[test] fn should_permit_validity_expansion_and_reject_contraction() { - let b0 = layout::Tree::<Def, !>::from_bits(0); - let b1 = layout::Tree::<Def, !>::from_bits(1); - let b2 = layout::Tree::<Def, !>::from_bits(2); + let b0 = layout::Tree::<Def, !>::byte(0); + let b1 = layout::Tree::<Def, !>::byte(1); + let b2 = layout::Tree::<Def, !>::byte(2); let alts = [b0, b1, b2]; @@ -279,8 +279,8 @@ mod alt { fn should_permit_identity_transmutation() { type Tree = layout::Tree<Def, !>; - let x = Tree::Seq(vec![Tree::from_bits(0), Tree::from_bits(0)]); - let y = Tree::Seq(vec![Tree::bool(), Tree::from_bits(1)]); + let x = Tree::Seq(vec![Tree::byte(0), Tree::byte(0)]); + let y = Tree::Seq(vec![Tree::bool(), Tree::byte(1)]); let layout = Tree::Alt(vec![x, y]); let answer = crate::maybe_transmutable::MaybeTransmutableQuery::new( @@ -323,6 +323,76 @@ mod union { } } +mod char { + use super::*; + use crate::layout::tree::Endian; + + #[test] + fn should_permit_valid_transmutation() { + for order in [Endian::Big, Endian::Little] { + use Answer::*; + let char_layout = layout::Tree::<Def, !>::char(order); + + // `char`s can be in the following ranges: + // - [0, 0xD7FF] + // - [0xE000, 10FFFF] + // + // This loop synthesizes a singleton-validity type for the extremes + // of each range, and for one past the end of the extremes of each + // range. + let no = No(Reason::DstIsBitIncompatible); + for (src, answer) in [ + (0u32, Yes), + (0xD7FF, Yes), + (0xD800, no.clone()), + (0xDFFF, no.clone()), + (0xE000, Yes), + (0x10FFFF, Yes), + (0x110000, no.clone()), + (0xFFFF0000, no.clone()), + (0xFFFFFFFF, no), + ] { + let src_layout = + layout::tree::Tree::<Def, !>::from_big_endian(order, src.to_be_bytes()); + + let a = is_transmutable(&src_layout, &char_layout, Assume::default()); + assert_eq!(a, answer, "endian:{order:?},\nsrc:{src:x}"); + } + } + } +} + +mod nonzero { + use super::*; + use crate::{Answer, Reason}; + + const NONZERO_BYTE_WIDTHS: [u64; 5] = [1, 2, 4, 8, 16]; + + #[test] + fn should_permit_identity_transmutation() { + for width in NONZERO_BYTE_WIDTHS { + let layout = layout::Tree::<Def, !>::nonzero(width); + assert_eq!(is_transmutable(&layout, &layout, Assume::default()), Answer::Yes); + } + } + + #[test] + fn should_permit_valid_transmutation() { + for width in NONZERO_BYTE_WIDTHS { + use Answer::*; + + let num = layout::Tree::<Def, !>::number(width); + let nz = layout::Tree::<Def, !>::nonzero(width); + + let a = is_transmutable(&num, &nz, Assume::default()); + assert_eq!(a, No(Reason::DstIsBitIncompatible), "width:{width}"); + + let a = is_transmutable(&nz, &num, Assume::default()); + assert_eq!(a, Yes, "width:{width}"); + } + } +} + mod r#ref { use super::*; @@ -330,7 +400,7 @@ mod r#ref { fn should_permit_identity_transmutation() { type Tree = crate::layout::Tree<Def, [(); 1]>; - let layout = Tree::Seq(vec![Tree::from_bits(0), Tree::Ref([()])]); + let layout = Tree::Seq(vec![Tree::byte(0x00), Tree::Ref([()])]); let answer = crate::maybe_transmutable::MaybeTransmutableQuery::new( layout.clone(), diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 3d4ab33240a..63ea035bd0e 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -347,7 +347,8 @@ fn adjust_for_rust_scalar<'tcx>( None }; if let Some(kind) = kind { - attrs.pointee_align = Some(pointee.align); + attrs.pointee_align = + Some(pointee.align.min(cx.tcx().sess.target.max_reliable_alignment())); // `Box` are not necessarily dereferenceable for the entire duration of the function as // they can be deallocated at any time. Same for non-frozen shared references (see diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs index 57051e0df55..ea0f6b8dfba 100644 --- a/compiler/rustc_ty_utils/src/lib.rs +++ b/compiler/rustc_ty_utils/src/lib.rs @@ -6,6 +6,7 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(assert_matches)] @@ -13,7 +14,6 @@ #![feature(box_patterns)] #![feature(if_let_guard)] #![feature(iterator_try_collect)] -#![feature(let_chains)] #![feature(never_type)] #![feature(rustdoc_internals)] // tidy-alphabetical-end diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index e9d3a149a73..74fb148a7cc 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -1,5 +1,3 @@ -use std::slice; - use crate::inherent::*; use crate::visit::Flags; use crate::{self as ty, Interner}; @@ -388,8 +386,8 @@ impl<I: Interner> FlagComputation<I> { self.add_alias_term(projection_term); self.add_term(term); } - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { - self.add_args(slice::from_ref(&arg)); + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => { + self.add_term(term); } ty::PredicateKind::DynCompatible(_def_id) => {} ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(uv)) => { diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 417803e75ea..9b066b6869f 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -442,6 +442,14 @@ pub trait Predicate<I: Interner<Predicate = Self>>: { fn as_clause(self) -> Option<I::Clause>; + fn as_normalizes_to(self) -> Option<ty::Binder<I, ty::NormalizesTo<I>>> { + let kind = self.kind(); + match kind.skip_binder() { + ty::PredicateKind::NormalizesTo(pred) => Some(kind.rebind(pred)), + _ => None, + } + } + // FIXME: Eventually uplift the impl out of rustc and make this defaulted. fn allow_normalization(self) -> bool; } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index ab38556589e..9758cecaf6a 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -286,6 +286,8 @@ pub trait Interner: fn has_item_definition(self, def_id: Self::DefId) -> bool; + fn impl_specializes(self, impl_def_id: Self::DefId, victim_def_id: Self::DefId) -> bool; + fn impl_is_default(self, impl_def_id: Self::DefId) -> bool; fn impl_trait_ref(self, impl_def_id: Self::DefId) -> ty::EarlyBinder<Self, ty::TraitRef<Self>>; diff --git a/compiler/rustc_type_ir/src/predicate_kind.rs b/compiler/rustc_type_ir/src/predicate_kind.rs index 847dff156fe..4e41fd16ffd 100644 --- a/compiler/rustc_type_ir/src/predicate_kind.rs +++ b/compiler/rustc_type_ir/src/predicate_kind.rs @@ -36,7 +36,7 @@ pub enum ClauseKind<I: Interner> { ConstArgHasType(I::Const, I::Ty), /// No syntax: `T` well-formed. - WellFormed(I::GenericArg), + WellFormed(I::Term), /// Constant initializer must evaluate successfully. ConstEvaluatable(I::Const), diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index d5b19470e31..8b448a18402 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -351,9 +351,14 @@ impl CString { /// # Safety /// /// This should only ever be called with a pointer that was earlier - /// obtained by calling [`CString::into_raw`]. Other usage (e.g., trying to take - /// ownership of a string that was allocated by foreign code) is likely to lead - /// to undefined behavior or allocator corruption. + /// obtained by calling [`CString::into_raw`], and the memory it points to must not be accessed + /// through any other pointer during the lifetime of reconstructed `CString`. + /// Other usage (e.g., trying to take ownership of a string that was allocated by foreign code) + /// is likely to lead to undefined behavior or allocator corruption. + /// + /// This function does not validate ownership of the raw pointer's memory. + /// A double-free may occur if the function is called twice on the same raw pointer. + /// Additionally, the caller must ensure the pointer is not dangling. /// /// It should be noted that the length isn't just "recomputed," but that /// the recomputed length must match the original length from the @@ -818,6 +823,7 @@ impl From<Vec<NonZero<u8>>> for CString { } } +#[stable(feature = "c_string_from_str", since = "1.85.0")] impl FromStr for CString { type Err = NulError; @@ -830,6 +836,7 @@ impl FromStr for CString { } } +#[stable(feature = "c_string_from_str", since = "1.85.0")] impl TryFrom<CString> for String { type Error = IntoStringError; diff --git a/library/alloc/src/ffi/mod.rs b/library/alloc/src/ffi/mod.rs index 695d7ad07cf..05a2763a225 100644 --- a/library/alloc/src/ffi/mod.rs +++ b/library/alloc/src/ffi/mod.rs @@ -87,5 +87,5 @@ pub use self::c_str::CString; #[stable(feature = "alloc_c_string", since = "1.64.0")] pub use self::c_str::{FromVecWithNulError, IntoStringError, NulError}; -#[unstable(feature = "c_str_module", issue = "112134")] +#[stable(feature = "c_str_module", since = "CURRENT_RUSTC_VERSION")] pub mod c_str; diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index 0664f2c3cf2..24c5d4c92f7 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -603,6 +603,10 @@ impl str { /// Converts a boxed slice of bytes to a boxed string slice without checking /// that the string contains valid UTF-8. /// +/// # Safety +/// +/// * The provided bytes must contain a valid UTF-8 sequence. +/// /// # Examples /// /// ``` diff --git a/library/alloctests/tests/fmt.rs b/library/alloctests/tests/fmt.rs index c13074c53b7..a20e8c62336 100644 --- a/library/alloctests/tests/fmt.rs +++ b/library/alloctests/tests/fmt.rs @@ -1,6 +1,7 @@ #![deny(warnings)] // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] +#![cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] use std::cell::RefCell; use std::fmt::{self, Write}; diff --git a/library/core/src/char/convert.rs b/library/core/src/char/convert.rs index ac808038f89..d820965a746 100644 --- a/library/core/src/char/convert.rs +++ b/library/core/src/char/convert.rs @@ -21,6 +21,7 @@ pub(super) const fn from_u32(i: u32) -> Option<char> { /// Converts a `u32` to a `char`, ignoring validity. See [`char::from_u32_unchecked`]. #[inline] #[must_use] +#[cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] pub(super) const unsafe fn from_u32_unchecked(i: u32) -> char { // SAFETY: the caller must guarantee that `i` is a valid char value. unsafe { @@ -221,6 +222,7 @@ impl FromStr for char { } #[inline] +#[cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] const fn char_try_from_u32(i: u32) -> Result<char, CharTryFromError> { // This is an optimized version of the check // (i > MAX as u32) || (i >= 0xD800 && i <= 0xDFFF), diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 85e87445a1b..ac07c645c01 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -79,8 +79,9 @@ use crate::{fmt, ops, slice, str}; /// /// fn my_string_safe() -> String { /// let cstr = unsafe { CStr::from_ptr(my_string()) }; -/// // Get copy-on-write Cow<'_, str>, then guarantee a freshly-owned String allocation -/// String::from_utf8_lossy(cstr.to_bytes()).to_string() +/// // Get a copy-on-write Cow<'_, str>, then extract the +/// // allocated String (or allocate a fresh one if needed). +/// cstr.to_string_lossy().into_owned() /// } /// /// println!("string: {}", my_string_safe()); diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index 9bae5fd466a..c9c73a25d89 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -20,7 +20,7 @@ pub use self::c_str::FromBytesUntilNulError; pub use self::c_str::FromBytesWithNulError; use crate::fmt; -#[unstable(feature = "c_str_module", issue = "112134")] +#[stable(feature = "c_str_module", since = "CURRENT_RUSTC_VERSION")] pub mod c_str; #[unstable( diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index a01efb2adeb..a7563f918a2 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -1497,6 +1497,7 @@ pub const fn forget<T: ?Sized>(_: T); /// Turning raw bytes (`[u8; SZ]`) into `u32`, `f64`, etc.: /// /// ``` +/// # #![cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] /// let raw_bytes = [0x78, 0x56, 0x34, 0x12]; /// /// let num = unsafe { @@ -2429,35 +2430,35 @@ pub unsafe fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> In /// Stabilized as [`f16::algebraic_add`], [`f32::algebraic_add`], [`f64::algebraic_add`] and [`f128::algebraic_add`]. #[rustc_nounwind] #[rustc_intrinsic] -pub fn fadd_algebraic<T: Copy>(a: T, b: T) -> T; +pub const fn fadd_algebraic<T: Copy>(a: T, b: T) -> T; /// Float subtraction that allows optimizations based on algebraic rules. /// /// Stabilized as [`f16::algebraic_sub`], [`f32::algebraic_sub`], [`f64::algebraic_sub`] and [`f128::algebraic_sub`]. #[rustc_nounwind] #[rustc_intrinsic] -pub fn fsub_algebraic<T: Copy>(a: T, b: T) -> T; +pub const fn fsub_algebraic<T: Copy>(a: T, b: T) -> T; /// Float multiplication that allows optimizations based on algebraic rules. /// /// Stabilized as [`f16::algebraic_mul`], [`f32::algebraic_mul`], [`f64::algebraic_mul`] and [`f128::algebraic_mul`]. #[rustc_nounwind] #[rustc_intrinsic] -pub fn fmul_algebraic<T: Copy>(a: T, b: T) -> T; +pub const fn fmul_algebraic<T: Copy>(a: T, b: T) -> T; /// Float division that allows optimizations based on algebraic rules. /// /// Stabilized as [`f16::algebraic_div`], [`f32::algebraic_div`], [`f64::algebraic_div`] and [`f128::algebraic_div`]. #[rustc_nounwind] #[rustc_intrinsic] -pub fn fdiv_algebraic<T: Copy>(a: T, b: T) -> T; +pub const fn fdiv_algebraic<T: Copy>(a: T, b: T) -> T; /// Float remainder that allows optimizations based on algebraic rules. /// /// Stabilized as [`f16::algebraic_rem`], [`f32::algebraic_rem`], [`f64::algebraic_rem`] and [`f128::algebraic_rem`]. #[rustc_nounwind] #[rustc_intrinsic] -pub fn frem_algebraic<T: Copy>(a: T, b: T) -> T; +pub const fn frem_algebraic<T: Copy>(a: T, b: T) -> T; /// Returns the number of bits set in an integer type `T` /// diff --git a/library/core/src/iter/adapters/peekable.rs b/library/core/src/iter/adapters/peekable.rs index cc12cd9c356..a6522659620 100644 --- a/library/core/src/iter/adapters/peekable.rs +++ b/library/core/src/iter/adapters/peekable.rs @@ -271,7 +271,7 @@ impl<I: Iterator> Peekable<I> { /// assert_eq!(iter.next_if(|&x| x == 0), Some(0)); /// // The next item returned is now 1, so `next_if` will return `None`. /// assert_eq!(iter.next_if(|&x| x == 0), None); - /// // `next_if` saves the value of the next item if it was not equal to `expected`. + /// // `next_if` retains the next item if the predicate evaluates to `false` for it. /// assert_eq!(iter.next(), Some(1)); /// ``` /// @@ -304,9 +304,9 @@ impl<I: Iterator> Peekable<I> { /// let mut iter = (0..5).peekable(); /// // The first item of the iterator is 0; consume it. /// assert_eq!(iter.next_if_eq(&0), Some(0)); - /// // The next item returned is now 1, so `next_if` will return `None`. + /// // The next item returned is now 1, so `next_if_eq` will return `None`. /// assert_eq!(iter.next_if_eq(&0), None); - /// // `next_if_eq` saves the value of the next item if it was not equal to `expected`. + /// // `next_if_eq` retains the next item if it was not equal to `expected`. /// assert_eq!(iter.next(), Some(1)); /// ``` #[stable(feature = "peekable_next_if", since = "1.51.0")] diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 330b4098764..16c0c118040 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1138,6 +1138,10 @@ pub(crate) mod builtin { issue = "29599", reason = "`concat_idents` is not stable enough for use and is subject to change" )] + #[deprecated( + since = "1.88.0", + note = "use `${concat(...)}` with the `macro_metavar_expr_concat` feature instead" + )] #[rustc_builtin_macro] #[macro_export] macro_rules! concat_idents { diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 2ee25969289..b1119d4899b 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -197,16 +197,22 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] pub const MAX: f128 = 1.18973149535723176508575932662800702e+4932_f128; - /// One greater than the minimum possible normal power of 2 exponent. + /// One greater than the minimum possible *normal* power of 2 exponent + /// for a significand bounded by 1 ≤ x < 2 (i.e. the IEEE definition). /// - /// If <i>x</i> = `MIN_EXP`, then normal numbers - /// ≥ 0.5 × 2<sup><i>x</i></sup>. + /// This corresponds to the exact minimum possible *normal* power of 2 exponent + /// for a significand bounded by 0.5 ≤ x < 1 (i.e. the C definition). + /// In other words, all normal numbers representable by this type are + /// greater than or equal to 0.5 × 2<sup><i>MIN_EXP</i></sup>. #[unstable(feature = "f128", issue = "116909")] pub const MIN_EXP: i32 = -16_381; - /// Maximum possible power of 2 exponent. + /// One greater than the maximum possible power of 2 exponent + /// for a significand bounded by 1 ≤ x < 2 (i.e. the IEEE definition). /// - /// If <i>x</i> = `MAX_EXP`, then normal numbers - /// < 1 × 2<sup><i>x</i></sup>. + /// This corresponds to the exact maximum possible power of 2 exponent + /// for a significand bounded by 0.5 ≤ x < 1 (i.e. the C definition). + /// In other words, all numbers representable by this type are + /// strictly less than 2<sup><i>MAX_EXP</i></sup>. #[unstable(feature = "f128", issue = "116909")] pub const MAX_EXP: i32 = 16_384; @@ -804,7 +810,7 @@ impl f128 { } } - /// Calculates the middle point of `self` and `rhs`. + /// Calculates the midpoint (average) between `self` and `rhs`. /// /// This returns NaN when *either* argument is NaN or if a combination of /// +inf and -inf is provided as arguments. @@ -821,6 +827,7 @@ impl f128 { /// # } /// ``` #[inline] + #[doc(alias = "average")] #[unstable(feature = "f128", issue = "116909")] #[rustc_const_unstable(feature = "f128", issue = "116909")] pub const fn midpoint(self, other: f128) -> f128 { @@ -903,6 +910,7 @@ impl f128 { #[inline] #[unstable(feature = "f128", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] + #[cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] pub const fn to_bits(self) -> u128 { // SAFETY: `u128` is a plain old datatype so we can always transmute to it. unsafe { mem::transmute(self) } @@ -950,6 +958,7 @@ impl f128 { #[inline] #[must_use] #[unstable(feature = "f128", issue = "116909")] + #[cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] pub const fn from_bits(v: u128) -> Self { // It turns out the safety issues with sNaN were overblown! Hooray! // SAFETY: `u128` is a plain old datatype so we can always transmute from it. @@ -1373,8 +1382,9 @@ impl f128 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_add(self, rhs: f128) -> f128 { + pub const fn algebraic_add(self, rhs: f128) -> f128 { intrinsics::fadd_algebraic(self, rhs) } @@ -1383,8 +1393,9 @@ impl f128 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_sub(self, rhs: f128) -> f128 { + pub const fn algebraic_sub(self, rhs: f128) -> f128 { intrinsics::fsub_algebraic(self, rhs) } @@ -1393,8 +1404,9 @@ impl f128 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_mul(self, rhs: f128) -> f128 { + pub const fn algebraic_mul(self, rhs: f128) -> f128 { intrinsics::fmul_algebraic(self, rhs) } @@ -1403,8 +1415,9 @@ impl f128 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_div(self, rhs: f128) -> f128 { + pub const fn algebraic_div(self, rhs: f128) -> f128 { intrinsics::fdiv_algebraic(self, rhs) } @@ -1413,8 +1426,9 @@ impl f128 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_rem(self, rhs: f128) -> f128 { + pub const fn algebraic_rem(self, rhs: f128) -> f128 { intrinsics::frem_algebraic(self, rhs) } } diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 69882d13c17..54e38d9e1a6 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -192,16 +192,22 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] pub const MAX: f16 = 6.5504e+4_f16; - /// One greater than the minimum possible normal power of 2 exponent. + /// One greater than the minimum possible *normal* power of 2 exponent + /// for a significand bounded by 1 ≤ x < 2 (i.e. the IEEE definition). /// - /// If <i>x</i> = `MIN_EXP`, then normal numbers - /// ≥ 0.5 × 2<sup><i>x</i></sup>. + /// This corresponds to the exact minimum possible *normal* power of 2 exponent + /// for a significand bounded by 0.5 ≤ x < 1 (i.e. the C definition). + /// In other words, all normal numbers representable by this type are + /// greater than or equal to 0.5 × 2<sup><i>MIN_EXP</i></sup>. #[unstable(feature = "f16", issue = "116909")] pub const MIN_EXP: i32 = -13; - /// Maximum possible power of 2 exponent. + /// One greater than the maximum possible power of 2 exponent + /// for a significand bounded by 1 ≤ x < 2 (i.e. the IEEE definition). /// - /// If <i>x</i> = `MAX_EXP`, then normal numbers - /// < 1 × 2<sup><i>x</i></sup>. + /// This corresponds to the exact maximum possible power of 2 exponent + /// for a significand bounded by 0.5 ≤ x < 1 (i.e. the C definition). + /// In other words, all numbers representable by this type are + /// strictly less than 2<sup><i>MAX_EXP</i></sup>. #[unstable(feature = "f16", issue = "116909")] pub const MAX_EXP: i32 = 16; @@ -792,7 +798,7 @@ impl f16 { } } - /// Calculates the middle point of `self` and `rhs`. + /// Calculates the midpoint (average) between `self` and `rhs`. /// /// This returns NaN when *either* argument is NaN or if a combination of /// +inf and -inf is provided as arguments. @@ -808,6 +814,7 @@ impl f16 { /// # } /// ``` #[inline] + #[doc(alias = "average")] #[unstable(feature = "f16", issue = "116909")] #[rustc_const_unstable(feature = "f16", issue = "116909")] pub const fn midpoint(self, other: f16) -> f16 { @@ -891,6 +898,7 @@ impl f16 { #[inline] #[unstable(feature = "f16", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] + #[cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] pub const fn to_bits(self) -> u16 { // SAFETY: `u16` is a plain old datatype so we can always transmute to it. unsafe { mem::transmute(self) } @@ -937,6 +945,7 @@ impl f16 { #[inline] #[must_use] #[unstable(feature = "f16", issue = "116909")] + #[cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] pub const fn from_bits(v: u16) -> Self { // It turns out the safety issues with sNaN were overblown! Hooray! // SAFETY: `u16` is a plain old datatype so we can always transmute from it. @@ -1349,8 +1358,9 @@ impl f16 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_add(self, rhs: f16) -> f16 { + pub const fn algebraic_add(self, rhs: f16) -> f16 { intrinsics::fadd_algebraic(self, rhs) } @@ -1359,8 +1369,9 @@ impl f16 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_sub(self, rhs: f16) -> f16 { + pub const fn algebraic_sub(self, rhs: f16) -> f16 { intrinsics::fsub_algebraic(self, rhs) } @@ -1369,8 +1380,9 @@ impl f16 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_mul(self, rhs: f16) -> f16 { + pub const fn algebraic_mul(self, rhs: f16) -> f16 { intrinsics::fmul_algebraic(self, rhs) } @@ -1379,8 +1391,9 @@ impl f16 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_div(self, rhs: f16) -> f16 { + pub const fn algebraic_div(self, rhs: f16) -> f16 { intrinsics::fdiv_algebraic(self, rhs) } @@ -1389,8 +1402,9 @@ impl f16 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_rem(self, rhs: f16) -> f16 { + pub const fn algebraic_rem(self, rhs: f16) -> f16 { intrinsics::frem_algebraic(self, rhs) } } diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 7e056a6c1f3..e66fd3bb52b 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -443,16 +443,22 @@ impl f32 { #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const MAX: f32 = 3.40282347e+38_f32; - /// One greater than the minimum possible normal power of 2 exponent. + /// One greater than the minimum possible *normal* power of 2 exponent + /// for a significand bounded by 1 ≤ x < 2 (i.e. the IEEE definition). /// - /// If <i>x</i> = `MIN_EXP`, then normal numbers - /// ≥ 0.5 × 2<sup><i>x</i></sup>. + /// This corresponds to the exact minimum possible *normal* power of 2 exponent + /// for a significand bounded by 0.5 ≤ x < 1 (i.e. the C definition). + /// In other words, all normal numbers representable by this type are + /// greater than or equal to 0.5 × 2<sup><i>MIN_EXP</i></sup>. #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const MIN_EXP: i32 = -125; - /// Maximum possible power of 2 exponent. + /// One greater than the maximum possible power of 2 exponent + /// for a significand bounded by 1 ≤ x < 2 (i.e. the IEEE definition). /// - /// If <i>x</i> = `MAX_EXP`, then normal numbers - /// < 1 × 2<sup><i>x</i></sup>. + /// This corresponds to the exact maximum possible power of 2 exponent + /// for a significand bounded by 0.5 ≤ x < 1 (i.e. the C definition). + /// In other words, all numbers representable by this type are + /// strictly less than 2<sup><i>MAX_EXP</i></sup>. #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const MAX_EXP: i32 = 128; @@ -710,8 +716,7 @@ impl f32 { pub const fn is_sign_negative(self) -> bool { // IEEE754 says: isSignMinus(x) is true if and only if x has negative sign. isSignMinus // applies to zeros and NaNs as well. - // SAFETY: This is just transmuting to get the sign bit, it's fine. - unsafe { mem::transmute::<f32, u32>(self) & 0x8000_0000 != 0 } + self.to_bits() & 0x8000_0000 != 0 } /// Returns the least number greater than `self`. @@ -986,7 +991,7 @@ impl f32 { } } - /// Calculates the middle point of `self` and `rhs`. + /// Calculates the midpoint (average) between `self` and `rhs`. /// /// This returns NaN when *either* argument is NaN or if a combination of /// +inf and -inf is provided as arguments. @@ -998,6 +1003,7 @@ impl f32 { /// assert_eq!((-5.5f32).midpoint(8.0), 1.25); /// ``` #[inline] + #[doc(alias = "average")] #[stable(feature = "num_midpoint", since = "1.85.0")] #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] pub const fn midpoint(self, other: f32) -> f32 { @@ -1096,6 +1102,7 @@ impl f32 { #[stable(feature = "float_bits_conv", since = "1.20.0")] #[rustc_const_stable(feature = "const_float_bits_conv", since = "1.83.0")] #[inline] + #[cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] pub const fn to_bits(self) -> u32 { // SAFETY: `u32` is a plain old datatype so we can always transmute to it. unsafe { mem::transmute(self) } @@ -1141,6 +1148,7 @@ impl f32 { #[rustc_const_stable(feature = "const_float_bits_conv", since = "1.83.0")] #[must_use] #[inline] + #[cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] pub const fn from_bits(v: u32) -> Self { // It turns out the safety issues with sNaN were overblown! Hooray! // SAFETY: `u32` is a plain old datatype so we can always transmute from it. @@ -1515,8 +1523,9 @@ impl f32 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_add(self, rhs: f32) -> f32 { + pub const fn algebraic_add(self, rhs: f32) -> f32 { intrinsics::fadd_algebraic(self, rhs) } @@ -1525,8 +1534,9 @@ impl f32 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_sub(self, rhs: f32) -> f32 { + pub const fn algebraic_sub(self, rhs: f32) -> f32 { intrinsics::fsub_algebraic(self, rhs) } @@ -1535,8 +1545,9 @@ impl f32 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_mul(self, rhs: f32) -> f32 { + pub const fn algebraic_mul(self, rhs: f32) -> f32 { intrinsics::fmul_algebraic(self, rhs) } @@ -1545,8 +1556,9 @@ impl f32 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_div(self, rhs: f32) -> f32 { + pub const fn algebraic_div(self, rhs: f32) -> f32 { intrinsics::fdiv_algebraic(self, rhs) } @@ -1555,8 +1567,9 @@ impl f32 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_rem(self, rhs: f32) -> f32 { + pub const fn algebraic_rem(self, rhs: f32) -> f32 { intrinsics::frem_algebraic(self, rhs) } } diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index b9ebbb1d764..2d791437b28 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -442,16 +442,22 @@ impl f64 { #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const MAX: f64 = 1.7976931348623157e+308_f64; - /// One greater than the minimum possible normal power of 2 exponent. + /// One greater than the minimum possible *normal* power of 2 exponent + /// for a significand bounded by 1 ≤ x < 2 (i.e. the IEEE definition). /// - /// If <i>x</i> = `MIN_EXP`, then normal numbers - /// ≥ 0.5 × 2<sup><i>x</i></sup>. + /// This corresponds to the exact minimum possible *normal* power of 2 exponent + /// for a significand bounded by 0.5 ≤ x < 1 (i.e. the C definition). + /// In other words, all normal numbers representable by this type are + /// greater than or equal to 0.5 × 2<sup><i>MIN_EXP</i></sup>. #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const MIN_EXP: i32 = -1021; - /// Maximum possible power of 2 exponent. + /// One greater than the maximum possible power of 2 exponent + /// for a significand bounded by 1 ≤ x < 2 (i.e. the IEEE definition). /// - /// If <i>x</i> = `MAX_EXP`, then normal numbers - /// < 1 × 2<sup><i>x</i></sup>. + /// This corresponds to the exact maximum possible power of 2 exponent + /// for a significand bounded by 0.5 ≤ x < 1 (i.e. the C definition). + /// In other words, all numbers representable by this type are + /// strictly less than 2<sup><i>MAX_EXP</i></sup>. #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const MAX_EXP: i32 = 1024; @@ -718,8 +724,7 @@ impl f64 { pub const fn is_sign_negative(self) -> bool { // IEEE754 says: isSignMinus(x) is true if and only if x has negative sign. isSignMinus // applies to zeros and NaNs as well. - // SAFETY: This is just transmuting to get the sign bit, it's fine. - unsafe { mem::transmute::<f64, u64>(self) & Self::SIGN_MASK != 0 } + self.to_bits() & Self::SIGN_MASK != 0 } #[must_use] @@ -1004,7 +1009,7 @@ impl f64 { } } - /// Calculates the middle point of `self` and `rhs`. + /// Calculates the midpoint (average) between `self` and `rhs`. /// /// This returns NaN when *either* argument is NaN or if a combination of /// +inf and -inf is provided as arguments. @@ -1016,6 +1021,7 @@ impl f64 { /// assert_eq!((-5.5f64).midpoint(8.0), 1.25); /// ``` #[inline] + #[doc(alias = "average")] #[stable(feature = "num_midpoint", since = "1.85.0")] #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] pub const fn midpoint(self, other: f64) -> f64 { @@ -1094,6 +1100,7 @@ impl f64 { without modifying the original"] #[stable(feature = "float_bits_conv", since = "1.20.0")] #[rustc_const_stable(feature = "const_float_bits_conv", since = "1.83.0")] + #[cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] #[inline] pub const fn to_bits(self) -> u64 { // SAFETY: `u64` is a plain old datatype so we can always transmute to it. @@ -1140,6 +1147,7 @@ impl f64 { #[rustc_const_stable(feature = "const_float_bits_conv", since = "1.83.0")] #[must_use] #[inline] + #[cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] pub const fn from_bits(v: u64) -> Self { // It turns out the safety issues with sNaN were overblown! Hooray! // SAFETY: `u64` is a plain old datatype so we can always transmute from it. @@ -1514,8 +1522,9 @@ impl f64 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_add(self, rhs: f64) -> f64 { + pub const fn algebraic_add(self, rhs: f64) -> f64 { intrinsics::fadd_algebraic(self, rhs) } @@ -1524,8 +1533,9 @@ impl f64 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_sub(self, rhs: f64) -> f64 { + pub const fn algebraic_sub(self, rhs: f64) -> f64 { intrinsics::fsub_algebraic(self, rhs) } @@ -1534,8 +1544,9 @@ impl f64 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_mul(self, rhs: f64) -> f64 { + pub const fn algebraic_mul(self, rhs: f64) -> f64 { intrinsics::fmul_algebraic(self, rhs) } @@ -1544,8 +1555,9 @@ impl f64 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_div(self, rhs: f64) -> f64 { + pub const fn algebraic_div(self, rhs: f64) -> f64 { intrinsics::fdiv_algebraic(self, rhs) } @@ -1554,8 +1566,9 @@ impl f64 { /// See [algebraic operators](primitive@f32#algebraic-operators) for more info. #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "float_algebraic", issue = "136469")] + #[rustc_const_unstable(feature = "float_algebraic", issue = "136469")] #[inline] - pub fn algebraic_rem(self, rhs: f64) -> f64 { + pub const fn algebraic_rem(self, rhs: f64) -> f64 { intrinsics::frem_algebraic(self, rhs) } } diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 05d8216ac27..8d31a7b697a 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -3675,6 +3675,7 @@ macro_rules! int_impl { /// ``` #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] // SAFETY: const sound because integers are plain old datatypes so we can always // transmute them to arrays of bytes #[must_use = "this returns the result of the operation, \ @@ -3778,6 +3779,7 @@ macro_rules! int_impl { /// ``` #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] #[must_use] // SAFETY: const sound because integers are plain old datatypes so we can always // transmute to them diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 348457e2c00..ecc1c7bf902 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -130,7 +130,7 @@ depending on the target pointer size. macro_rules! midpoint_impl { ($SelfT:ty, unsigned) => { - /// Calculates the middle point of `self` and `rhs`. + /// Calculates the midpoint (average) between `self` and `rhs`. /// /// `midpoint(a, b)` is `(a + b) / 2` as if it were performed in a /// sufficiently-large unsigned integral type. This implies that the result is @@ -146,6 +146,8 @@ macro_rules! midpoint_impl { #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[doc(alias = "average_floor")] + #[doc(alias = "average")] #[inline] pub const fn midpoint(self, rhs: $SelfT) -> $SelfT { // Use the well known branchless algorithm from Hacker's Delight to compute @@ -154,7 +156,7 @@ macro_rules! midpoint_impl { } }; ($SelfT:ty, signed) => { - /// Calculates the middle point of `self` and `rhs`. + /// Calculates the midpoint (average) between `self` and `rhs`. /// /// `midpoint(a, b)` is `(a + b) / 2` as if it were performed in a /// sufficiently-large signed integral type. This implies that the result is @@ -173,6 +175,9 @@ macro_rules! midpoint_impl { #[rustc_const_stable(feature = "num_midpoint_signed", since = "1.87.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[doc(alias = "average_floor")] + #[doc(alias = "average_ceil")] + #[doc(alias = "average")] #[inline] pub const fn midpoint(self, rhs: Self) -> Self { // Use the well known branchless algorithm from Hacker's Delight to compute @@ -184,7 +189,7 @@ macro_rules! midpoint_impl { } }; ($SelfT:ty, $WideT:ty, unsigned) => { - /// Calculates the middle point of `self` and `rhs`. + /// Calculates the midpoint (average) between `self` and `rhs`. /// /// `midpoint(a, b)` is `(a + b) / 2` as if it were performed in a /// sufficiently-large unsigned integral type. This implies that the result is @@ -200,13 +205,15 @@ macro_rules! midpoint_impl { #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[doc(alias = "average_floor")] + #[doc(alias = "average")] #[inline] pub const fn midpoint(self, rhs: $SelfT) -> $SelfT { ((self as $WideT + rhs as $WideT) / 2) as $SelfT } }; ($SelfT:ty, $WideT:ty, signed) => { - /// Calculates the middle point of `self` and `rhs`. + /// Calculates the midpoint (average) between `self` and `rhs`. /// /// `midpoint(a, b)` is `(a + b) / 2` as if it were performed in a /// sufficiently-large signed integral type. This implies that the result is @@ -225,6 +232,9 @@ macro_rules! midpoint_impl { #[rustc_const_stable(feature = "num_midpoint_signed", since = "1.87.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[doc(alias = "average_floor")] + #[doc(alias = "average_ceil")] + #[doc(alias = "average")] #[inline] pub const fn midpoint(self, rhs: $SelfT) -> $SelfT { ((self as $WideT + rhs as $WideT) / 2) as $SelfT diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 1b79112aec1..8a8b2733d5e 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -1589,7 +1589,7 @@ macro_rules! nonzero_integer_signedness_dependent_methods { super::int_log10::$Int(self.get()) } - /// Calculates the middle point of `self` and `rhs`. + /// Calculates the midpoint (average) between `self` and `rhs`. /// /// `midpoint(a, b)` is `(a + b) >> 1` as if it were performed in a /// sufficiently-large signed integral type. This implies that the result is @@ -1615,6 +1615,8 @@ macro_rules! nonzero_integer_signedness_dependent_methods { #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[doc(alias = "average_floor")] + #[doc(alias = "average")] #[inline] pub const fn midpoint(self, rhs: Self) -> Self { // SAFETY: The only way to get `0` with midpoint is to have two opposite or diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 3678bb091e7..bc6cb950816 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -3523,6 +3523,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] // SAFETY: const sound because integers are plain old datatypes so we can always // transmute them to arrays of bytes #[inline] @@ -3624,6 +3625,7 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "int_to_from_bytes", since = "1.32.0")] #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[cfg_attr(not(bootstrap), allow(unnecessary_transmutes))] #[must_use] // SAFETY: const sound because integers are plain old datatypes so we can always // transmute to them diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 7ec0ac71271..aed5a043c11 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -162,8 +162,14 @@ //! The [`is_some`] and [`is_none`] methods return [`true`] if the [`Option`] //! is [`Some`] or [`None`], respectively. //! +//! The [`is_some_and`] and [`is_none_or`] methods apply the provided function +//! to the contents of the [`Option`] to produce a boolean value. +//! If this is [`None`] then a default result is returned instead without executing the function. +//! //! [`is_none`]: Option::is_none //! [`is_some`]: Option::is_some +//! [`is_some_and`]: Option::is_some_and +//! [`is_none_or`]: Option::is_none_or //! //! ## Adapters for working with references //! @@ -177,6 +183,10 @@ //! <code>[Option]<[Pin]<[&]T>></code> //! * [`as_pin_mut`] converts from <code>[Pin]<[&mut] [Option]\<T>></code> to //! <code>[Option]<[Pin]<[&mut] T>></code> +//! * [`as_slice`] returns a one-element slice of the contained value, if any. +//! If this is [`None`], an empty slice is returned. +//! * [`as_mut_slice`] returns a mutable one-element slice of the contained value, if any. +//! If this is [`None`], an empty slice is returned. //! //! [&]: reference "shared reference" //! [&mut]: reference "mutable reference" @@ -187,6 +197,8 @@ //! [`as_pin_mut`]: Option::as_pin_mut //! [`as_pin_ref`]: Option::as_pin_ref //! [`as_ref`]: Option::as_ref +//! [`as_slice`]: Option::as_slice +//! [`as_mut_slice`]: Option::as_mut_slice //! //! ## Extracting the contained value //! @@ -200,12 +212,15 @@ //! (which must implement the [`Default`] trait) //! * [`unwrap_or_else`] returns the result of evaluating the provided //! function +//! * [`unwrap_unchecked`] produces *[undefined behavior]* //! //! [`expect`]: Option::expect //! [`unwrap`]: Option::unwrap //! [`unwrap_or`]: Option::unwrap_or //! [`unwrap_or_default`]: Option::unwrap_or_default //! [`unwrap_or_else`]: Option::unwrap_or_else +//! [`unwrap_unchecked`]: Option::unwrap_unchecked +//! [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html //! //! ## Transforming contained values //! @@ -230,8 +245,9 @@ //! * [`filter`] calls the provided predicate function on the contained //! value `t` if the [`Option`] is [`Some(t)`], and returns [`Some(t)`] //! if the function returns `true`; otherwise, returns [`None`] -//! * [`flatten`] removes one level of nesting from an -//! [`Option<Option<T>>`] +//! * [`flatten`] removes one level of nesting from an [`Option<Option<T>>`] +//! * [`inspect`] method takes ownership of the [`Option`] and applies +//! the provided function to the contained value by reference if [`Some`] //! * [`map`] transforms [`Option<T>`] to [`Option<U>`] by applying the //! provided function to the contained value of [`Some`] and leaving //! [`None`] values unchanged @@ -239,6 +255,7 @@ //! [`Some(t)`]: Some //! [`filter`]: Option::filter //! [`flatten`]: Option::flatten +//! [`inspect`]: Option::inspect //! [`map`]: Option::map //! //! These methods transform [`Option<T>`] to a value of a possibly @@ -621,6 +638,10 @@ impl<T> Option<T> { /// /// let x: Option<u32> = None; /// assert_eq!(x.is_some_and(|x| x > 1), false); + /// + /// let x: Option<String> = Some("ownership".to_string()); + /// assert_eq!(x.as_ref().is_some_and(|x| x.len() > 1), true); + /// println!("still alive {:?}", x); /// ``` #[must_use] #[inline] @@ -665,6 +686,10 @@ impl<T> Option<T> { /// /// let x: Option<u32> = None; /// assert_eq!(x.is_none_or(|x| x > 1), true); + /// + /// let x: Option<String> = Some("ownership".to_string()); + /// assert_eq!(x.as_ref().is_none_or(|x| x.len() > 1), true); + /// println!("still alive {:?}", x); /// ``` #[must_use] #[inline] diff --git a/library/core/src/prelude/v1.rs b/library/core/src/prelude/v1.rs index 8f1b5275871..9737d0baec7 100644 --- a/library/core/src/prelude/v1.rs +++ b/library/core/src/prelude/v1.rs @@ -59,6 +59,7 @@ pub use crate::hash::macros::Hash; #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] #[allow(deprecated)] +#[cfg_attr(bootstrap, allow(deprecated_in_future))] #[doc(no_inline)] pub use crate::{ assert, cfg, column, compile_error, concat, concat_idents, env, file, format_args, diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 48ab9267f21..736ffb7d0ca 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -259,8 +259,14 @@ //! The [`is_ok`] and [`is_err`] methods return [`true`] if the [`Result`] //! is [`Ok`] or [`Err`], respectively. //! +//! The [`is_ok_and`] and [`is_err_and`] methods apply the provided function +//! to the contents of the [`Result`] to produce a boolean value. If the [`Result`] does not have the expected variant +//! then [`false`] is returned instead without executing the function. +//! //! [`is_err`]: Result::is_err //! [`is_ok`]: Result::is_ok +//! [`is_ok_and`]: Result::is_ok_and +//! [`is_err_and`]: Result::is_err_and //! //! ## Adapters for working with references //! @@ -287,6 +293,7 @@ //! (which must implement the [`Default`] trait) //! * [`unwrap_or_else`] returns the result of evaluating the provided //! function +//! * [`unwrap_unchecked`] produces *[undefined behavior]* //! //! The panicking methods [`expect`] and [`unwrap`] require `E` to //! implement the [`Debug`] trait. @@ -297,6 +304,8 @@ //! [`unwrap_or`]: Result::unwrap_or //! [`unwrap_or_default`]: Result::unwrap_or_default //! [`unwrap_or_else`]: Result::unwrap_or_else +//! [`unwrap_unchecked`]: Result::unwrap_unchecked +//! [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html //! //! These methods extract the contained value in a [`Result<T, E>`] when it //! is the [`Err`] variant. They require `T` to implement the [`Debug`] @@ -304,10 +313,13 @@ //! //! * [`expect_err`] panics with a provided custom message //! * [`unwrap_err`] panics with a generic message +//! * [`unwrap_err_unchecked`] produces *[undefined behavior]* //! //! [`Debug`]: crate::fmt::Debug //! [`expect_err`]: Result::expect_err //! [`unwrap_err`]: Result::unwrap_err +//! [`unwrap_err_unchecked`]: Result::unwrap_err_unchecked +//! [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html //! //! ## Transforming contained values //! @@ -330,21 +342,29 @@ //! [`Some(v)`]: Option::Some //! [`transpose`]: Result::transpose //! -//! This method transforms the contained value of the [`Ok`] variant: +//! These methods transform the contained value of the [`Ok`] variant: //! //! * [`map`] transforms [`Result<T, E>`] into [`Result<U, E>`] by applying //! the provided function to the contained value of [`Ok`] and leaving //! [`Err`] values unchanged +//! * [`inspect`] takes ownership of the [`Result`], applies the +//! provided function to the contained value by reference, +//! and then returns the [`Result`] //! //! [`map`]: Result::map +//! [`inspect`]: Result::inspect //! -//! This method transforms the contained value of the [`Err`] variant: +//! These methods transform the contained value of the [`Err`] variant: //! //! * [`map_err`] transforms [`Result<T, E>`] into [`Result<T, F>`] by //! applying the provided function to the contained value of [`Err`] and //! leaving [`Ok`] values unchanged +//! * [`inspect_err`] takes ownership of the [`Result`], applies the +//! provided function to the contained value of [`Err`] by reference, +//! and then returns the [`Result`] //! //! [`map_err`]: Result::map_err +//! [`inspect_err`]: Result::inspect_err //! //! These methods transform a [`Result<T, E>`] into a value of a possibly //! different type `U`: @@ -578,6 +598,10 @@ impl<T, E> Result<T, E> { /// /// let x: Result<u32, &str> = Err("hey"); /// assert_eq!(x.is_ok_and(|x| x > 1), false); + /// + /// let x: Result<String, &str> = Ok("ownership".to_string()); + /// assert_eq!(x.as_ref().is_ok_and(|x| x.len() > 1), true); + /// println!("still alive {:?}", x); /// ``` #[must_use] #[inline] @@ -623,6 +647,10 @@ impl<T, E> Result<T, E> { /// /// let x: Result<u32, Error> = Ok(123); /// assert_eq!(x.is_err_and(|x| x.kind() == ErrorKind::NotFound), false); + /// + /// let x: Result<u32, String> = Err("ownership".to_string()); + /// assert_eq!(x.as_ref().is_err_and(|x| x.len() > 1), true); + /// println!("still alive {:?}", x); /// ``` #[must_use] #[inline] diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 79b4953fcc1..fe35bfdbdf7 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -2115,7 +2115,7 @@ impl str { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "str_trim"] pub fn trim(&self) -> &str { - self.trim_matches(|c: char| c.is_whitespace()) + self.trim_matches(char::is_whitespace) } /// Returns a string slice with leading whitespace removed. @@ -2154,7 +2154,7 @@ impl str { #[stable(feature = "trim_direction", since = "1.30.0")] #[rustc_diagnostic_item = "str_trim_start"] pub fn trim_start(&self) -> &str { - self.trim_start_matches(|c: char| c.is_whitespace()) + self.trim_start_matches(char::is_whitespace) } /// Returns a string slice with trailing whitespace removed. @@ -2193,7 +2193,7 @@ impl str { #[stable(feature = "trim_direction", since = "1.30.0")] #[rustc_diagnostic_item = "str_trim_end"] pub fn trim_end(&self) -> &str { - self.trim_end_matches(|c: char| c.is_whitespace()) + self.trim_end_matches(char::is_whitespace) } /// Returns a string slice with leading whitespace removed. diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index c46dcebedca..36a1c57b020 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -513,13 +513,13 @@ impl Span { } /// Creates an empty span pointing to directly before this span. - #[unstable(feature = "proc_macro_span", issue = "54725")] + #[stable(feature = "proc_macro_span_location", since = "CURRENT_RUSTC_VERSION")] pub fn start(&self) -> Span { Span(self.0.start()) } /// Creates an empty span pointing to directly after this span. - #[unstable(feature = "proc_macro_span", issue = "54725")] + #[stable(feature = "proc_macro_span_location", since = "CURRENT_RUSTC_VERSION")] pub fn end(&self) -> Span { Span(self.0.end()) } @@ -527,7 +527,7 @@ impl Span { /// The one-indexed line of the source file where the span starts. /// /// To obtain the line of the span's end, use `span.end().line()`. - #[unstable(feature = "proc_macro_span", issue = "54725")] + #[stable(feature = "proc_macro_span_location", since = "CURRENT_RUSTC_VERSION")] pub fn line(&self) -> usize { self.0.line() } @@ -535,7 +535,7 @@ impl Span { /// The one-indexed column of the source file where the span starts. /// /// To obtain the column of the span's end, use `span.end().column()`. - #[unstable(feature = "proc_macro_span", issue = "54725")] + #[stable(feature = "proc_macro_span_location", since = "CURRENT_RUSTC_VERSION")] pub fn column(&self) -> usize { self.0.column() } diff --git a/library/std/src/env.rs b/library/std/src/env.rs index c84a72c4fad..1593969e114 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -13,7 +13,7 @@ use crate::error::Error; use crate::ffi::{OsStr, OsString}; use crate::path::{Path, PathBuf}; -use crate::sys::os as os_imp; +use crate::sys::{env as env_imp, os as os_imp}; use crate::{fmt, io, sys}; /// Returns the current working directory as a [`PathBuf`]. @@ -96,7 +96,7 @@ pub struct Vars { /// [`env::vars_os()`]: vars_os #[stable(feature = "env", since = "1.0.0")] pub struct VarsOs { - inner: os_imp::Env, + inner: env_imp::Env, } /// Returns an iterator of (variable, value) pairs of strings, for all the @@ -150,7 +150,7 @@ pub fn vars() -> Vars { #[must_use] #[stable(feature = "env", since = "1.0.0")] pub fn vars_os() -> VarsOs { - VarsOs { inner: os_imp::env() } + VarsOs { inner: env_imp::env() } } #[stable(feature = "env", since = "1.0.0")] @@ -259,7 +259,7 @@ pub fn var_os<K: AsRef<OsStr>>(key: K) -> Option<OsString> { } fn _var_os(key: &OsStr) -> Option<OsString> { - os_imp::getenv(key) + env_imp::getenv(key) } /// The error type for operations interacting with environment variables. @@ -363,7 +363,7 @@ impl Error for VarError { #[stable(feature = "env", since = "1.0.0")] pub unsafe fn set_var<K: AsRef<OsStr>, V: AsRef<OsStr>>(key: K, value: V) { let (key, value) = (key.as_ref(), value.as_ref()); - unsafe { os_imp::setenv(key, value) }.unwrap_or_else(|e| { + unsafe { env_imp::setenv(key, value) }.unwrap_or_else(|e| { panic!("failed to set environment variable `{key:?}` to `{value:?}`: {e}") }) } @@ -434,7 +434,7 @@ pub unsafe fn set_var<K: AsRef<OsStr>, V: AsRef<OsStr>>(key: K, value: V) { #[stable(feature = "env", since = "1.0.0")] pub unsafe fn remove_var<K: AsRef<OsStr>>(key: K) { let key = key.as_ref(); - unsafe { os_imp::unsetenv(key) } + unsafe { env_imp::unsetenv(key) } .unwrap_or_else(|e| panic!("failed to remove environment variable `{key:?}`: {e}")) } diff --git a/library/std/src/ffi/mod.rs b/library/std/src/ffi/mod.rs index d34e3ca00b9..bd9446f5aba 100644 --- a/library/std/src/ffi/mod.rs +++ b/library/std/src/ffi/mod.rs @@ -161,7 +161,7 @@ #![stable(feature = "rust1", since = "1.0.0")] -#[unstable(feature = "c_str_module", issue = "112134")] +#[stable(feature = "c_str_module", since = "CURRENT_RUSTC_VERSION")] pub mod c_str; #[stable(feature = "core_c_void", since = "1.30.0")] diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 5242261cf93..96fac4f6bde 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -2319,9 +2319,9 @@ pub trait BufRead: Read { /// Checks if there is any data left to be `read`. /// /// This function may fill the buffer to check for data, - /// so this functions returns `Result<bool>`, not `bool`. + /// so this function returns `Result<bool>`, not `bool`. /// - /// Default implementation calls `fill_buf` and checks that + /// The default implementation calls `fill_buf` and checks that the /// returned slice is empty (which means that there is no data left, /// since EOF is reached). /// diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index c07c391892d..c9595b051e2 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -119,7 +119,7 @@ mod break_keyword {} #[doc(keyword = "const")] // -/// Compile-time constants, compile-time evaluable functions, and raw pointers. +/// Compile-time constants, compile-time blocks, compile-time evaluable functions, and raw pointers. /// /// ## Compile-time constants /// @@ -166,6 +166,12 @@ mod break_keyword {} /// /// For more detail on `const`, see the [Rust Book] or the [Reference]. /// +/// ## Compile-time blocks +/// +/// The `const` keyword can also be used to define a block of code that is evaluated at compile time. +/// This is useful for ensuring certain computations are completed before optimizations happen, as well as +/// before runtime. For more details, see the [Reference][const-blocks]. +/// /// ## Compile-time evaluable functions /// /// The other main use of the `const` keyword is in `const fn`. This marks a function as being @@ -184,6 +190,7 @@ mod break_keyword {} /// [pointer primitive]: pointer /// [Rust Book]: ../book/ch03-01-variables-and-mutability.html#constants /// [Reference]: ../reference/items/constant-items.html +/// [const-blocks]: ../reference/expressions/block-expr.html#const-blocks /// [const-eval]: ../reference/const_eval.html mod const_keyword {} @@ -381,11 +388,15 @@ mod enum_keyword {} /// lazy_static;`. The other use is in foreign function interfaces (FFI). /// /// `extern` is used in two different contexts within FFI. The first is in the form of external -/// blocks, for declaring function interfaces that Rust code can call foreign code by. +/// blocks, for declaring function interfaces that Rust code can call foreign code by. This use +/// of `extern` is unsafe, since we are asserting to the compiler that all function declarations +/// are correct. If they are not, using these items may lead to undefined behavior. /// /// ```rust ignore +/// // SAFETY: The function declarations given below are in +/// // line with the header files of `my_c_library`. /// #[link(name = "my_c_library")] -/// extern "C" { +/// unsafe extern "C" { /// fn my_c_function(x: i32) -> bool; /// } /// ``` diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 3a52b779037..3c22b8a5589 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -329,7 +329,6 @@ #![feature(array_chunks)] #![feature(bstr)] #![feature(bstr_internals)] -#![feature(c_str_module)] #![feature(char_internals)] #![feature(clone_to_uninit)] #![feature(core_intrinsics)] @@ -709,6 +708,7 @@ pub use core::primitive; // Re-export built-in macros defined through core. #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] #[allow(deprecated)] +#[cfg_attr(bootstrap, allow(deprecated_in_future))] pub use core::{ assert, assert_matches, cfg, column, compile_error, concat, concat_idents, const_format_args, env, file, format_args, format_args_nl, include, include_bytes, include_str, line, log_syntax, diff --git a/library/std/src/prelude/v1.rs b/library/std/src/prelude/v1.rs index c15d8c40085..68c9ac1e414 100644 --- a/library/std/src/prelude/v1.rs +++ b/library/std/src/prelude/v1.rs @@ -46,6 +46,7 @@ pub use crate::result::Result::{self, Err, Ok}; // Re-exported built-in macros #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] #[allow(deprecated)] +#[cfg_attr(bootstrap, allow(deprecated_in_future))] #[doc(no_inline)] pub use core::prelude::v1::{ assert, cfg, column, compile_error, concat, concat_idents, env, file, format_args, diff --git a/library/std/src/sys/args/mod.rs b/library/std/src/sys/args/mod.rs index 6a37b32d229..0011f55dc14 100644 --- a/library/std/src/sys/args/mod.rs +++ b/library/std/src/sys/args/mod.rs @@ -2,6 +2,16 @@ #![forbid(unsafe_op_in_unsafe_fn)] +#[cfg(any( + all(target_family = "unix", not(any(target_os = "espidf", target_os = "vita"))), + target_family = "windows", + target_os = "hermit", + target_os = "uefi", + target_os = "wasi", + target_os = "xous", +))] +mod common; + cfg_if::cfg_if! { if #[cfg(any( all(target_family = "unix", not(any(target_os = "espidf", target_os = "vita"))), diff --git a/library/std/src/sys/args/uefi.rs b/library/std/src/sys/args/uefi.rs index 84406c7f69d..02dada382ef 100644 --- a/library/std/src/sys/args/uefi.rs +++ b/library/std/src/sys/args/uefi.rs @@ -1,14 +1,11 @@ use r_efi::protocols::loaded_image; +pub use super::common::Args; use crate::env::current_exe; use crate::ffi::OsString; use crate::iter::Iterator; use crate::sys::pal::helpers; -#[path = "common.rs"] -mod common; -pub use common::Args; - pub fn args() -> Args { let lazy_current_exe = || Vec::from([current_exe().map(Into::into).unwrap_or_default()]); diff --git a/library/std/src/sys/args/unix.rs b/library/std/src/sys/args/unix.rs index c087fd62965..a7b79ad396e 100644 --- a/library/std/src/sys/args/unix.rs +++ b/library/std/src/sys/args/unix.rs @@ -5,16 +5,13 @@ #![allow(dead_code)] // runtime init functions not used during testing +pub use super::common::Args; use crate::ffi::CStr; #[cfg(target_os = "hermit")] use crate::os::hermit::ffi::OsStringExt; #[cfg(not(target_os = "hermit"))] use crate::os::unix::ffi::OsStringExt; -#[path = "common.rs"] -mod common; -pub use common::Args; - /// One-time global initialization. pub unsafe fn init(argc: isize, argv: *const *const u8) { unsafe { imp::init(argc, argv) } diff --git a/library/std/src/sys/args/wasi.rs b/library/std/src/sys/args/wasi.rs index 4795789e4c7..72063a87dc9 100644 --- a/library/std/src/sys/args/wasi.rs +++ b/library/std/src/sys/args/wasi.rs @@ -1,12 +1,9 @@ #![forbid(unsafe_op_in_unsafe_fn)] +pub use super::common::Args; use crate::ffi::{CStr, OsStr, OsString}; use crate::os::wasi::ffi::OsStrExt; -#[path = "common.rs"] -mod common; -pub use common::Args; - /// Returns the command line arguments pub fn args() -> Args { Args::new(maybe_args().unwrap_or(Vec::new())) diff --git a/library/std/src/sys/args/windows.rs b/library/std/src/sys/args/windows.rs index 47f0e5f2d05..81c44fabdcc 100644 --- a/library/std/src/sys/args/windows.rs +++ b/library/std/src/sys/args/windows.rs @@ -6,6 +6,7 @@ #[cfg(test)] mod tests; +pub use super::common::Args; use crate::ffi::{OsStr, OsString}; use crate::num::NonZero; use crate::os::windows::prelude::*; @@ -18,10 +19,6 @@ use crate::sys_common::AsInner; use crate::sys_common::wstr::WStrUnits; use crate::{io, iter, ptr}; -#[path = "common.rs"] -mod common; -pub use common::Args; - pub fn args() -> Args { // SAFETY: `GetCommandLineW` returns a pointer to a null terminated UTF-16 // string so it's safe for `WStrUnits` to use. diff --git a/library/std/src/sys/args/xous.rs b/library/std/src/sys/args/xous.rs index 09a47283d65..2010bad14d1 100644 --- a/library/std/src/sys/args/xous.rs +++ b/library/std/src/sys/args/xous.rs @@ -1,10 +1,7 @@ +pub use super::common::Args; use crate::sys::pal::os::get_application_parameters; use crate::sys::pal::os::params::ArgumentList; -#[path = "common.rs"] -mod common; -pub use common::Args; - pub fn args() -> Args { let Some(params) = get_application_parameters() else { return Args::new(vec![]); diff --git a/library/std/src/sys/env/common.rs b/library/std/src/sys/env/common.rs new file mode 100644 index 00000000000..f161ff073f3 --- /dev/null +++ b/library/std/src/sys/env/common.rs @@ -0,0 +1,48 @@ +use crate::ffi::OsString; +use crate::{fmt, vec}; + +pub struct Env { + iter: vec::IntoIter<(OsString, OsString)>, +} + +// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt. +pub struct EnvStrDebug<'a> { + slice: &'a [(OsString, OsString)], +} + +impl fmt::Debug for EnvStrDebug<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list() + .entries(self.slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) + .finish() + } +} + +impl Env { + pub(super) fn new(env: Vec<(OsString, OsString)>) -> Self { + Env { iter: env.into_iter() } + } + + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + EnvStrDebug { slice: self.iter.as_slice() } + } +} + +impl fmt::Debug for Env { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.iter.as_slice()).finish() + } +} + +impl !Send for Env {} +impl !Sync for Env {} + +impl Iterator for Env { + type Item = (OsString, OsString); + fn next(&mut self) -> Option<(OsString, OsString)> { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option<usize>) { + self.iter.size_hint() + } +} diff --git a/library/std/src/sys/env/hermit.rs b/library/std/src/sys/env/hermit.rs new file mode 100644 index 00000000000..445ecdeb6a3 --- /dev/null +++ b/library/std/src/sys/env/hermit.rs @@ -0,0 +1,72 @@ +use core::slice::memchr; + +pub use super::common::Env; +use crate::collections::HashMap; +use crate::ffi::{CStr, OsStr, OsString, c_char}; +use crate::io; +use crate::os::hermit::ffi::OsStringExt; +use crate::sync::Mutex; + +static ENV: Mutex<Option<HashMap<OsString, OsString>>> = Mutex::new(None); + +pub fn init(env: *const *const c_char) { + let mut guard = ENV.lock().unwrap(); + let map = guard.insert(HashMap::new()); + + if env.is_null() { + return; + } + + unsafe { + let mut environ = env; + while !(*environ).is_null() { + if let Some((key, value)) = parse(CStr::from_ptr(*environ).to_bytes()) { + map.insert(key, value); + } + environ = environ.add(1); + } + } + + fn parse(input: &[u8]) -> Option<(OsString, OsString)> { + // Strategy (copied from glibc): Variable name and value are separated + // by an ASCII equals sign '='. Since a variable name must not be + // empty, allow variable names starting with an equals sign. Skip all + // malformed lines. + if input.is_empty() { + return None; + } + let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1); + pos.map(|p| { + ( + OsStringExt::from_vec(input[..p].to_vec()), + OsStringExt::from_vec(input[p + 1..].to_vec()), + ) + }) + } +} + +/// Returns a vector of (variable, value) byte-vector pairs for all the +/// environment variables of the current process. +pub fn env() -> Env { + let guard = ENV.lock().unwrap(); + let env = guard.as_ref().unwrap(); + + let result = env.iter().map(|(key, value)| (key.clone(), value.clone())).collect(); + + Env::new(result) +} + +pub fn getenv(k: &OsStr) -> Option<OsString> { + ENV.lock().unwrap().as_ref().unwrap().get(k).cloned() +} + +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + let (k, v) = (k.to_owned(), v.to_owned()); + ENV.lock().unwrap().as_mut().unwrap().insert(k, v); + Ok(()) +} + +pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> { + ENV.lock().unwrap().as_mut().unwrap().remove(k); + Ok(()) +} diff --git a/library/std/src/sys/env/mod.rs b/library/std/src/sys/env/mod.rs new file mode 100644 index 00000000000..d81ff875c83 --- /dev/null +++ b/library/std/src/sys/env/mod.rs @@ -0,0 +1,48 @@ +//! Platform-dependent environment variables abstraction. + +#![forbid(unsafe_op_in_unsafe_fn)] + +#[cfg(any( + target_family = "unix", + target_os = "hermit", + all(target_vendor = "fortanix", target_env = "sgx"), + target_os = "solid_asp3", + target_os = "uefi", + target_os = "wasi", + target_os = "xous", +))] +mod common; + +cfg_if::cfg_if! { + if #[cfg(target_family = "unix")] { + mod unix; + pub use unix::*; + } else if #[cfg(target_family = "windows")] { + mod windows; + pub use windows::*; + } else if #[cfg(target_os = "hermit")] { + mod hermit; + pub use hermit::*; + } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + mod sgx; + pub use sgx::*; + } else if #[cfg(target_os = "solid_asp3")] { + mod solid; + pub use solid::*; + } else if #[cfg(target_os = "uefi")] { + mod uefi; + pub use uefi::*; + } else if #[cfg(target_os = "wasi")] { + mod wasi; + pub use wasi::*; + } else if #[cfg(target_os = "xous")] { + mod xous; + pub use xous::*; + } else if #[cfg(target_os = "zkvm")] { + mod zkvm; + pub use zkvm::*; + } else { + mod unsupported; + pub use unsupported::*; + } +} diff --git a/library/std/src/sys/env/sgx.rs b/library/std/src/sys/env/sgx.rs new file mode 100644 index 00000000000..85be9cd6ad4 --- /dev/null +++ b/library/std/src/sys/env/sgx.rs @@ -0,0 +1,55 @@ +#![allow(fuzzy_provenance_casts)] // FIXME: this module systematically confuses pointers and integers + +pub use super::common::Env; +use crate::collections::HashMap; +use crate::ffi::{OsStr, OsString}; +use crate::io; +use crate::sync::atomic::{AtomicUsize, Ordering}; +use crate::sync::{Mutex, Once}; + +// 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: AtomicUsize = 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<HashMap<OsString, OsString>>; + +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) } +} + +pub fn env() -> Env { + let clone_to_vec = |map: &HashMap<OsString, OsString>| -> 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(); + Env::new(env) +} + +pub fn getenv(k: &OsStr) -> Option<OsString> { + get_env_store().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); + Ok(()) +} + +pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> { + if let Some(env) = get_env_store() { + env.lock().unwrap().remove(k); + } + Ok(()) +} diff --git a/library/std/src/sys/env/solid.rs b/library/std/src/sys/env/solid.rs new file mode 100644 index 00000000000..ea77fc3c119 --- /dev/null +++ b/library/std/src/sys/env/solid.rs @@ -0,0 +1,96 @@ +use core::slice::memchr; + +pub use super::common::Env; +use crate::ffi::{CStr, OsStr, OsString}; +use crate::io; +use crate::os::raw::{c_char, c_int}; +use crate::os::solid::ffi::{OsStrExt, OsStringExt}; +use crate::sync::{PoisonError, RwLock}; +use crate::sys::common::small_c_string::run_with_cstr; + +static ENV_LOCK: RwLock<()> = RwLock::new(()); + +pub fn env_read_lock() -> impl Drop { + ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner) +} + +/// Returns a vector of (variable, value) byte-vector pairs for all the +/// environment variables of the current process. +pub fn env() -> Env { + unsafe extern "C" { + static mut environ: *const *const c_char; + } + + unsafe { + let _guard = env_read_lock(); + let mut result = Vec::new(); + if !environ.is_null() { + while !(*environ).is_null() { + if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) { + result.push(key_value); + } + environ = environ.add(1); + } + } + return Env::new(result); + } + + fn parse(input: &[u8]) -> Option<(OsString, OsString)> { + // Strategy (copied from glibc): Variable name and value are separated + // by an ASCII equals sign '='. Since a variable name must not be + // empty, allow variable names starting with an equals sign. Skip all + // malformed lines. + if input.is_empty() { + return None; + } + let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1); + pos.map(|p| { + ( + OsStringExt::from_vec(input[..p].to_vec()), + OsStringExt::from_vec(input[p + 1..].to_vec()), + ) + }) + } +} + +pub fn getenv(k: &OsStr) -> Option<OsString> { + // environment variables with a nul byte can't be set, so their value is + // always None as well + run_with_cstr(k.as_bytes(), &|k| { + let _guard = env_read_lock(); + let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; + + if v.is_null() { + Ok(None) + } else { + // SAFETY: `v` cannot be mutated while executing this line since we've a read lock + let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec(); + + Ok(Some(OsStringExt::from_vec(bytes))) + } + }) + .ok() + .flatten() +} + +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + run_with_cstr(k.as_bytes(), &|k| { + run_with_cstr(v.as_bytes(), &|v| { + let _guard = ENV_LOCK.write(); + cvt_env(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop) + }) + }) +} + +pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { + run_with_cstr(n.as_bytes(), &|nbuf| { + let _guard = ENV_LOCK.write(); + cvt_env(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop) + }) +} + +/// In kmclib, `setenv` and `unsetenv` don't always set `errno`, so this +/// function just returns a generic error. +fn cvt_env(t: c_int) -> io::Result<c_int> { + if t == -1 { Err(io::const_error!(io::ErrorKind::Uncategorized, "failure")) } else { Ok(t) } +} diff --git a/library/std/src/sys/env/uefi.rs b/library/std/src/sys/env/uefi.rs new file mode 100644 index 00000000000..1561df41cac --- /dev/null +++ b/library/std/src/sys/env/uefi.rs @@ -0,0 +1,102 @@ +pub use super::common::Env; +use crate::ffi::{OsStr, OsString}; +use crate::io; + +pub fn env() -> Env { + let env = uefi_env::get_all().expect("not supported on this platform"); + Env::new(env) +} + +pub fn getenv(key: &OsStr) -> Option<OsString> { + uefi_env::get(key) +} + +pub unsafe fn setenv(key: &OsStr, val: &OsStr) -> io::Result<()> { + uefi_env::set(key, val) +} + +pub unsafe fn unsetenv(key: &OsStr) -> io::Result<()> { + uefi_env::unset(key) +} + +mod uefi_env { + use crate::ffi::{OsStr, OsString}; + use crate::io; + use crate::os::uefi::ffi::OsStringExt; + use crate::ptr::NonNull; + use crate::sys::{helpers, unsupported_err}; + + pub(crate) fn get(key: &OsStr) -> Option<OsString> { + let shell = helpers::open_shell()?; + let mut key_ptr = helpers::os_string_to_raw(key)?; + unsafe { get_raw(shell, key_ptr.as_mut_ptr()) } + } + + pub(crate) fn set(key: &OsStr, val: &OsStr) -> io::Result<()> { + let mut key_ptr = helpers::os_string_to_raw(key) + .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid key"))?; + let mut val_ptr = helpers::os_string_to_raw(val) + .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid value"))?; + unsafe { set_raw(key_ptr.as_mut_ptr(), val_ptr.as_mut_ptr()) } + } + + pub(crate) fn unset(key: &OsStr) -> io::Result<()> { + let mut key_ptr = helpers::os_string_to_raw(key) + .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid key"))?; + unsafe { set_raw(key_ptr.as_mut_ptr(), crate::ptr::null_mut()) } + } + + pub(crate) fn get_all() -> io::Result<Vec<(OsString, OsString)>> { + let shell = helpers::open_shell().ok_or(unsupported_err())?; + + let mut vars = Vec::new(); + let val = unsafe { ((*shell.as_ptr()).get_env)(crate::ptr::null_mut()) }; + + if val.is_null() { + return Ok(vars); + } + + let mut start = 0; + + // UEFI Shell returns all keys separated by NULL. + // End of string is denoted by two NULLs + for i in 0.. { + if unsafe { *val.add(i) } == 0 { + // Two NULL signal end of string + if i == start { + break; + } + + let key = OsString::from_wide(unsafe { + crate::slice::from_raw_parts(val.add(start), i - start) + }); + // SAFETY: val.add(start) is always NULL terminated + let val = unsafe { get_raw(shell, val.add(start)) } + .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid value"))?; + + vars.push((key, val)); + start = i + 1; + } + } + + Ok(vars) + } + + unsafe fn get_raw( + shell: NonNull<r_efi::efi::protocols::shell::Protocol>, + key_ptr: *mut r_efi::efi::Char16, + ) -> Option<OsString> { + let val = unsafe { ((*shell.as_ptr()).get_env)(key_ptr) }; + helpers::os_string_from_raw(val) + } + + unsafe fn set_raw( + key_ptr: *mut r_efi::efi::Char16, + val_ptr: *mut r_efi::efi::Char16, + ) -> io::Result<()> { + let shell = helpers::open_shell().ok_or(unsupported_err())?; + let r = + unsafe { ((*shell.as_ptr()).set_env)(key_ptr, val_ptr, r_efi::efi::Boolean::FALSE) }; + if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } + } +} diff --git a/library/std/src/sys/env/unix.rs b/library/std/src/sys/env/unix.rs new file mode 100644 index 00000000000..78c7af65f9e --- /dev/null +++ b/library/std/src/sys/env/unix.rs @@ -0,0 +1,126 @@ +use core::slice::memchr; + +use libc::c_char; + +pub use super::common::Env; +use crate::ffi::{CStr, OsStr, OsString}; +use crate::io; +use crate::os::unix::prelude::*; +use crate::sync::{PoisonError, RwLock}; +use crate::sys::common::small_c_string::run_with_cstr; +use crate::sys::cvt; + +// Use `_NSGetEnviron` on Apple platforms. +// +// `_NSGetEnviron` is the documented alternative (see `man environ`), and has +// been available since the first versions of both macOS and iOS. +// +// Nowadays, specifically since macOS 10.8, `environ` has been exposed through +// `libdyld.dylib`, which is linked via. `libSystem.dylib`: +// <https://github.com/apple-oss-distributions/dyld/blob/dyld-1160.6/libdyld/libdyldGlue.cpp#L913> +// +// So in the end, it likely doesn't really matter which option we use, but the +// performance cost of using `_NSGetEnviron` is extremely miniscule, and it +// might be ever so slightly more supported, so let's just use that. +// +// NOTE: The header where this is defined (`crt_externs.h`) was added to the +// iOS 13.0 SDK, which has been the source of a great deal of confusion in the +// past about the availability of this API. +// +// NOTE(madsmtm): Neither this nor using `environ` has been verified to not +// cause App Store rejections; if this is found to be the case, an alternative +// implementation of this is possible using `[NSProcessInfo environment]` +// - which internally uses `_NSGetEnviron` and a system-wide lock on the +// environment variables to protect against `setenv`, so using that might be +// desirable anyhow? Though it also means that we have to link to Foundation. +#[cfg(target_vendor = "apple")] +pub unsafe fn environ() -> *mut *const *const c_char { + unsafe { libc::_NSGetEnviron() as *mut *const *const c_char } +} + +// Use the `environ` static which is part of POSIX. +#[cfg(not(target_vendor = "apple"))] +pub unsafe fn environ() -> *mut *const *const c_char { + unsafe extern "C" { + static mut environ: *const *const c_char; + } + &raw mut environ +} + +static ENV_LOCK: RwLock<()> = RwLock::new(()); + +pub fn env_read_lock() -> impl Drop { + ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner) +} + +/// Returns a vector of (variable, value) byte-vector pairs for all the +/// environment variables of the current process. +pub fn env() -> Env { + unsafe { + let _guard = env_read_lock(); + let mut environ = *environ(); + let mut result = Vec::new(); + if !environ.is_null() { + while !(*environ).is_null() { + if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) { + result.push(key_value); + } + environ = environ.add(1); + } + } + return Env::new(result); + } + + fn parse(input: &[u8]) -> Option<(OsString, OsString)> { + // Strategy (copied from glibc): Variable name and value are separated + // by an ASCII equals sign '='. Since a variable name must not be + // empty, allow variable names starting with an equals sign. Skip all + // malformed lines. + if input.is_empty() { + return None; + } + let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1); + pos.map(|p| { + ( + OsStringExt::from_vec(input[..p].to_vec()), + OsStringExt::from_vec(input[p + 1..].to_vec()), + ) + }) + } +} + +pub fn getenv(k: &OsStr) -> Option<OsString> { + // environment variables with a nul byte can't be set, so their value is + // always None as well + run_with_cstr(k.as_bytes(), &|k| { + let _guard = env_read_lock(); + let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; + + if v.is_null() { + Ok(None) + } else { + // SAFETY: `v` cannot be mutated while executing this line since we've a read lock + let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec(); + + Ok(Some(OsStringExt::from_vec(bytes))) + } + }) + .ok() + .flatten() +} + +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + run_with_cstr(k.as_bytes(), &|k| { + run_with_cstr(v.as_bytes(), &|v| { + let _guard = ENV_LOCK.write(); + cvt(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop) + }) + }) +} + +pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { + run_with_cstr(n.as_bytes(), &|nbuf| { + let _guard = ENV_LOCK.write(); + cvt(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop) + }) +} diff --git a/library/std/src/sys/env/unsupported.rs b/library/std/src/sys/env/unsupported.rs new file mode 100644 index 00000000000..98905e64827 --- /dev/null +++ b/library/std/src/sys/env/unsupported.rs @@ -0,0 +1,40 @@ +use crate::ffi::{OsStr, OsString}; +use crate::{fmt, io}; + +pub struct Env(!); + +impl Env { + // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt. + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + self.0 + } +} + +impl fmt::Debug for Env { + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +impl Iterator for Env { + type Item = (OsString, OsString); + fn next(&mut self) -> Option<(OsString, OsString)> { + self.0 + } +} + +pub fn env() -> Env { + panic!("not supported on this platform") +} + +pub fn getenv(_: &OsStr) -> Option<OsString> { + None +} + +pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { + Err(io::const_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) +} + +pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { + Err(io::const_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) +} diff --git a/library/std/src/sys/env/wasi.rs b/library/std/src/sys/env/wasi.rs new file mode 100644 index 00000000000..3719f9db51e --- /dev/null +++ b/library/std/src/sys/env/wasi.rs @@ -0,0 +1,102 @@ +use core::slice::memchr; + +pub use super::common::Env; +use crate::ffi::{CStr, OsStr, OsString}; +use crate::io; +use crate::os::wasi::prelude::*; +use crate::sys::common::small_c_string::run_with_cstr; +use crate::sys::pal::os::{cvt, libc}; + +cfg_if::cfg_if! { + if #[cfg(target_feature = "atomics")] { + // Access to the environment must be protected by a lock in multi-threaded scenarios. + use crate::sync::{PoisonError, RwLock}; + static ENV_LOCK: RwLock<()> = RwLock::new(()); + pub fn env_read_lock() -> impl Drop { + ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner) + } + pub fn env_write_lock() -> impl Drop { + ENV_LOCK.write().unwrap_or_else(PoisonError::into_inner) + } + } else { + // No need for a lock if we are single-threaded. + pub fn env_read_lock() -> impl Drop { + Box::new(()) + } + pub fn env_write_lock() -> impl Drop { + Box::new(()) + } + } +} + +pub fn env() -> Env { + unsafe { + let _guard = env_read_lock(); + + // Use `__wasilibc_get_environ` instead of `environ` here so that we + // don't require wasi-libc to eagerly initialize the environment + // variables. + let mut environ = libc::__wasilibc_get_environ(); + + let mut result = Vec::new(); + if !environ.is_null() { + while !(*environ).is_null() { + if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) { + result.push(key_value); + } + environ = environ.add(1); + } + } + return Env::new(result); + } + + // See src/libstd/sys/pal/unix/os.rs, same as that + fn parse(input: &[u8]) -> Option<(OsString, OsString)> { + if input.is_empty() { + return None; + } + let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1); + pos.map(|p| { + ( + OsStringExt::from_vec(input[..p].to_vec()), + OsStringExt::from_vec(input[p + 1..].to_vec()), + ) + }) + } +} + +pub fn getenv(k: &OsStr) -> Option<OsString> { + // environment variables with a nul byte can't be set, so their value is + // always None as well + run_with_cstr(k.as_bytes(), &|k| { + let _guard = env_read_lock(); + let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; + + if v.is_null() { + Ok(None) + } else { + // SAFETY: `v` cannot be mutated while executing this line since we've a read lock + let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec(); + + Ok(Some(OsStringExt::from_vec(bytes))) + } + }) + .ok() + .flatten() +} + +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + run_with_cstr(k.as_bytes(), &|k| { + run_with_cstr(v.as_bytes(), &|v| unsafe { + let _guard = env_write_lock(); + cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) + }) + }) +} + +pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { + run_with_cstr(n.as_bytes(), &|nbuf| unsafe { + let _guard = env_write_lock(); + cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) + }) +} diff --git a/library/std/src/sys/env/windows.rs b/library/std/src/sys/env/windows.rs new file mode 100644 index 00000000000..3c4d4a84cfd --- /dev/null +++ b/library/std/src/sys/env/windows.rs @@ -0,0 +1,133 @@ +use crate::ffi::{OsStr, OsString}; +use crate::os::windows::prelude::*; +use crate::sys::pal::{c, cvt, fill_utf16_buf, to_u16s}; +use crate::{fmt, io, ptr, slice}; + +pub struct Env { + base: *mut c::WCHAR, + iter: EnvIterator, +} + +// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt. +pub struct EnvStrDebug<'a> { + iter: &'a EnvIterator, +} + +impl fmt::Debug for EnvStrDebug<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { iter } = self; + let iter: EnvIterator = (*iter).clone(); + let mut list = f.debug_list(); + for (a, b) in iter { + list.entry(&(a.to_str().unwrap(), b.to_str().unwrap())); + } + list.finish() + } +} + +impl Env { + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + let Self { base: _, iter } = self; + EnvStrDebug { iter } + } +} + +impl fmt::Debug for Env { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { base: _, iter } = self; + f.debug_list().entries(iter.clone()).finish() + } +} + +impl Iterator for Env { + type Item = (OsString, OsString); + + fn next(&mut self) -> Option<(OsString, OsString)> { + let Self { base: _, iter } = self; + iter.next() + } +} + +#[derive(Clone)] +struct EnvIterator(*mut c::WCHAR); + +impl Iterator for EnvIterator { + type Item = (OsString, OsString); + + fn next(&mut self) -> Option<(OsString, OsString)> { + let Self(cur) = self; + loop { + unsafe { + if **cur == 0 { + return None; + } + let p = *cur as *const u16; + let mut len = 0; + while *p.add(len) != 0 { + len += 1; + } + let s = slice::from_raw_parts(p, len); + *cur = cur.add(len + 1); + + // Windows allows environment variables to start with an equals + // symbol (in any other position, this is the separator between + // variable name and value). Since`s` has at least length 1 at + // this point (because the empty string terminates the array of + // environment variables), we can safely slice. + let pos = match s[1..].iter().position(|&u| u == b'=' as u16).map(|p| p + 1) { + Some(p) => p, + None => continue, + }; + return Some(( + OsStringExt::from_wide(&s[..pos]), + OsStringExt::from_wide(&s[pos + 1..]), + )); + } + } + } +} + +impl Drop for Env { + fn drop(&mut self) { + unsafe { + c::FreeEnvironmentStringsW(self.base); + } + } +} + +pub fn env() -> Env { + unsafe { + let ch = c::GetEnvironmentStringsW(); + if ch.is_null() { + panic!("failure getting env string from OS: {}", io::Error::last_os_error()); + } + Env { base: ch, iter: EnvIterator(ch) } + } +} + +pub fn getenv(k: &OsStr) -> Option<OsString> { + let k = to_u16s(k).ok()?; + fill_utf16_buf( + |buf, sz| unsafe { c::GetEnvironmentVariableW(k.as_ptr(), buf, sz) }, + OsStringExt::from_wide, + ) + .ok() +} + +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + // SAFETY: We ensure that k and v are null-terminated wide strings. + unsafe { + let k = to_u16s(k)?; + let v = to_u16s(v)?; + + cvt(c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr())).map(drop) + } +} + +pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { + // SAFETY: We ensure that v is a null-terminated wide strings. + unsafe { + let v = to_u16s(n)?; + cvt(c::SetEnvironmentVariableW(v.as_ptr(), ptr::null())).map(drop) + } +} diff --git a/library/std/src/sys/env/xous.rs b/library/std/src/sys/env/xous.rs new file mode 100644 index 00000000000..232a3dafb0b --- /dev/null +++ b/library/std/src/sys/env/xous.rs @@ -0,0 +1,54 @@ +pub use super::common::Env; +use crate::collections::HashMap; +use crate::ffi::{OsStr, OsString}; +use crate::io; +use crate::sync::atomic::{AtomicUsize, Ordering}; +use crate::sync::{Mutex, Once}; +use crate::sys::pal::os::{get_application_parameters, params}; + +static ENV: AtomicUsize = AtomicUsize::new(0); +static ENV_INIT: Once = Once::new(); +type EnvStore = Mutex<HashMap<OsString, OsString>>; + +fn get_env_store() -> &'static EnvStore { + ENV_INIT.call_once(|| { + let env_store = EnvStore::default(); + if let Some(params) = get_application_parameters() { + for param in params { + if let Ok(envs) = params::EnvironmentBlock::try_from(¶m) { + let mut env_store = env_store.lock().unwrap(); + for env in envs { + env_store.insert(env.key.into(), env.value.into()); + } + break; + } + } + } + ENV.store(Box::into_raw(Box::new(env_store)) as _, Ordering::Relaxed) + }); + unsafe { &*core::ptr::with_exposed_provenance::<EnvStore>(ENV.load(Ordering::Relaxed)) } +} + +pub fn env() -> Env { + let clone_to_vec = |map: &HashMap<OsString, OsString>| -> Vec<_> { + map.iter().map(|(k, v)| (k.clone(), v.clone())).collect() + }; + + let env = clone_to_vec(&*get_env_store().lock().unwrap()); + Env::new(env) +} + +pub fn getenv(k: &OsStr) -> Option<OsString> { + get_env_store().lock().unwrap().get(k).cloned() +} + +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + let (k, v) = (k.to_owned(), v.to_owned()); + get_env_store().lock().unwrap().insert(k, v); + Ok(()) +} + +pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> { + get_env_store().lock().unwrap().remove(k); + Ok(()) +} diff --git a/library/std/src/sys/env/zkvm.rs b/library/std/src/sys/env/zkvm.rs new file mode 100644 index 00000000000..2eb7005ba12 --- /dev/null +++ b/library/std/src/sys/env/zkvm.rs @@ -0,0 +1,32 @@ +#[expect(dead_code)] +#[path = "unsupported.rs"] +mod unsupported_env; +pub use unsupported_env::{Env, env, setenv, unsetenv}; + +use crate::ffi::{OsStr, OsString}; +use crate::sys::os_str; +use crate::sys::pal::{WORD_SIZE, abi}; +use crate::sys_common::FromInner; + +pub fn getenv(varname: &OsStr) -> Option<OsString> { + let varname = varname.as_encoded_bytes(); + let nbytes = + unsafe { abi::sys_getenv(crate::ptr::null_mut(), 0, varname.as_ptr(), varname.len()) }; + if nbytes == usize::MAX { + return None; + } + + let nwords = (nbytes + WORD_SIZE - 1) / WORD_SIZE; + let words = unsafe { abi::sys_alloc_words(nwords) }; + + let nbytes2 = unsafe { abi::sys_getenv(words, nwords, varname.as_ptr(), varname.len()) }; + debug_assert_eq!(nbytes, nbytes2); + + // Convert to OsString. + // + // FIXME: We can probably get rid of the extra copy here if we + // reimplement "os_str" instead of just using the generic unix + // "os_str". + let u8s: &[u8] = unsafe { crate::slice::from_raw_parts(words.cast() as *const u8, nbytes) }; + Some(OsString::from_inner(os_str::Buf { inner: u8s.to_vec() })) +} diff --git a/library/std/src/sys/env_consts.rs b/library/std/src/sys/env_consts.rs index 018d7954db2..9683fd47cf9 100644 --- a/library/std/src/sys/env_consts.rs +++ b/library/std/src/sys/env_consts.rs @@ -389,6 +389,17 @@ pub mod os { pub const EXE_EXTENSION: &str = "exe"; } +#[cfg(target_os = "zkvm")] +pub mod os { + pub const FAMILY: &str = ""; + pub const OS: &str = ""; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ".elf"; + pub const DLL_EXTENSION: &str = "elf"; + pub const EXE_SUFFIX: &str = ".elf"; + pub const EXE_EXTENSION: &str = "elf"; +} + // The fallback when none of the other gates match. #[else] pub mod os { diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index e7b631999e0..f9a02b522e5 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -12,6 +12,7 @@ pub mod anonymous_pipe; pub mod args; pub mod backtrace; pub mod cmath; +pub mod env; pub mod env_consts; pub mod exit_guard; pub mod fd; diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index 70636760a83..ea636938d70 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -16,7 +16,10 @@ #![deny(unsafe_op_in_unsafe_fn)] #![allow(missing_docs, nonstandard_style)] +use crate::io::ErrorKind; +use crate::os::hermit::hermit_abi; use crate::os::raw::c_char; +use crate::sys::env; pub mod futex; pub mod os; @@ -25,9 +28,6 @@ pub mod pipe; pub mod thread; pub mod time; -use crate::io::ErrorKind; -use crate::os::hermit::hermit_abi; - pub fn unsupported<T>() -> crate::io::Result<T> { Err(unsupported_err()) } @@ -76,7 +76,7 @@ pub unsafe extern "C" fn runtime_entry( } // initialize environment - os::init_environment(env); + env::init(env); let result = unsafe { main(argc as isize, argv) }; diff --git a/library/std/src/sys/pal/hermit/os.rs b/library/std/src/sys/pal/hermit/os.rs index 791cdb1e57e..a998c3165e5 100644 --- a/library/std/src/sys/pal/hermit/os.rs +++ b/library/std/src/sys/pal/hermit/os.rs @@ -1,15 +1,10 @@ -use core::slice::memchr; - use super::hermit_abi; -use crate::collections::HashMap; use crate::error::Error as StdError; -use crate::ffi::{CStr, OsStr, OsString, c_char}; +use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; -use crate::os::hermit::ffi::OsStringExt; use crate::path::{self, PathBuf}; -use crate::sync::Mutex; use crate::sys::unsupported; -use crate::{fmt, io, str, vec}; +use crate::{fmt, io, str}; pub fn errno() -> i32 { unsafe { hermit_abi::get_errno() } @@ -68,115 +63,6 @@ pub fn current_exe() -> io::Result<PathBuf> { unsupported() } -static ENV: Mutex<Option<HashMap<OsString, OsString>>> = Mutex::new(None); - -pub fn init_environment(env: *const *const c_char) { - let mut guard = ENV.lock().unwrap(); - let map = guard.insert(HashMap::new()); - - if env.is_null() { - return; - } - - unsafe { - let mut environ = env; - while !(*environ).is_null() { - if let Some((key, value)) = parse(CStr::from_ptr(*environ).to_bytes()) { - map.insert(key, value); - } - environ = environ.add(1); - } - } - - fn parse(input: &[u8]) -> Option<(OsString, OsString)> { - // Strategy (copied from glibc): Variable name and value are separated - // by an ASCII equals sign '='. Since a variable name must not be - // empty, allow variable names starting with an equals sign. Skip all - // malformed lines. - if input.is_empty() { - return None; - } - let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1); - pos.map(|p| { - ( - OsStringExt::from_vec(input[..p].to_vec()), - OsStringExt::from_vec(input[p + 1..].to_vec()), - ) - }) - } -} - -pub struct Env { - iter: vec::IntoIter<(OsString, OsString)>, -} - -// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt. -pub struct EnvStrDebug<'a> { - slice: &'a [(OsString, OsString)], -} - -impl fmt::Debug for EnvStrDebug<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { slice } = self; - f.debug_list() - .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) - .finish() - } -} - -impl Env { - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self { iter } = self; - EnvStrDebug { slice: iter.as_slice() } - } -} - -impl fmt::Debug for Env { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { iter } = self; - f.debug_list().entries(iter.as_slice()).finish() - } -} - -impl !Send for Env {} -impl !Sync for Env {} - -impl Iterator for Env { - type Item = (OsString, OsString); - fn next(&mut self) -> Option<(OsString, OsString)> { - self.iter.next() - } - fn size_hint(&self) -> (usize, Option<usize>) { - self.iter.size_hint() - } -} - -/// Returns a vector of (variable, value) byte-vector pairs for all the -/// environment variables of the current process. -pub fn env() -> Env { - let guard = ENV.lock().unwrap(); - let env = guard.as_ref().unwrap(); - - let result = env.iter().map(|(key, value)| (key.clone(), value.clone())).collect::<Vec<_>>(); - - Env { iter: result.into_iter() } -} - -pub fn getenv(k: &OsStr) -> Option<OsString> { - ENV.lock().unwrap().as_ref().unwrap().get(k).cloned() -} - -pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - let (k, v) = (k.to_owned(), v.to_owned()); - ENV.lock().unwrap().as_mut().unwrap().insert(k, v); - Ok(()) -} - -pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> { - ENV.lock().unwrap().as_mut().unwrap().remove(k); - Ok(()) -} - pub fn temp_dir() -> PathBuf { PathBuf::from("/tmp") } diff --git a/library/std/src/sys/pal/sgx/os.rs b/library/std/src/sys/pal/sgx/os.rs index 010634cf310..70f838679c9 100644 --- a/library/std/src/sys/pal/sgx/os.rs +++ b/library/std/src/sys/pal/sgx/os.rs @@ -1,14 +1,11 @@ use fortanix_sgx_abi::{Error, RESULT_SUCCESS}; -use crate::collections::HashMap; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; use crate::path::{self, PathBuf}; -use crate::sync::atomic::{AtomicUsize, Ordering}; -use crate::sync::{Mutex, Once}; use crate::sys::{decode_error_kind, sgx_ineffective, unsupported}; -use crate::{fmt, io, str, vec}; +use crate::{fmt, io, str}; pub fn errno() -> i32 { RESULT_SUCCESS @@ -73,101 +70,6 @@ pub fn current_exe() -> io::Result<PathBuf> { unsupported() } -// 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: AtomicUsize = 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<HashMap<OsString, OsString>>; - -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) } -} - -pub struct Env { - iter: vec::IntoIter<(OsString, OsString)>, -} - -// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt. -pub struct EnvStrDebug<'a> { - slice: &'a [(OsString, OsString)], -} - -impl fmt::Debug for EnvStrDebug<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { slice } = self; - f.debug_list() - .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) - .finish() - } -} - -impl Env { - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self { iter } = self; - EnvStrDebug { slice: iter.as_slice() } - } -} - -impl fmt::Debug for Env { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { iter } = self; - f.debug_list().entries(iter.as_slice()).finish() - } -} - -impl !Send for Env {} -impl !Sync for Env {} - -impl Iterator for Env { - type Item = (OsString, OsString); - fn next(&mut self) -> Option<(OsString, OsString)> { - self.iter.next() - } - fn size_hint(&self) -> (usize, Option<usize>) { - self.iter.size_hint() - } -} - -pub fn env() -> Env { - let clone_to_vec = |map: &HashMap<OsString, OsString>| -> Vec<_> { - map.iter().map(|(k, v)| (k.clone(), v.clone())).collect() - }; - - let iter = get_env_store() - .map(|env| clone_to_vec(&env.lock().unwrap())) - .unwrap_or_default() - .into_iter(); - Env { iter } -} - -pub fn getenv(k: &OsStr) -> Option<OsString> { - get_env_store().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); - Ok(()) -} - -pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> { - if let Some(env) = get_env_store() { - env.lock().unwrap().remove(k); - } - Ok(()) -} - pub fn temp_dir() -> PathBuf { panic!("no filesystem in SGX") } diff --git a/library/std/src/sys/pal/solid/os.rs b/library/std/src/sys/pal/solid/os.rs index e3b2e0aa50f..8f5976b0592 100644 --- a/library/std/src/sys/pal/solid/os.rs +++ b/library/std/src/sys/pal/solid/os.rs @@ -1,14 +1,8 @@ -use core::slice::memchr; - use super::{error, itron, unsupported}; use crate::error::Error as StdError; -use crate::ffi::{CStr, OsStr, OsString}; -use crate::os::raw::{c_char, c_int}; -use crate::os::solid::ffi::{OsStrExt, OsStringExt}; +use crate::ffi::{OsStr, OsString}; use crate::path::{self, PathBuf}; -use crate::sync::{PoisonError, RwLock}; -use crate::sys::common::small_c_string::run_with_cstr; -use crate::{fmt, io, vec}; +use crate::{fmt, io}; // `solid` directly maps `errno`s to μITRON error codes. impl itron::error::ItronError { @@ -75,138 +69,6 @@ pub fn current_exe() -> io::Result<PathBuf> { unsupported() } -static ENV_LOCK: RwLock<()> = RwLock::new(()); - -pub fn env_read_lock() -> impl Drop { - ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner) -} - -pub struct Env { - iter: vec::IntoIter<(OsString, OsString)>, -} - -// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt. -pub struct EnvStrDebug<'a> { - slice: &'a [(OsString, OsString)], -} - -impl fmt::Debug for EnvStrDebug<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { slice } = self; - f.debug_list() - .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) - .finish() - } -} - -impl Env { - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self { iter } = self; - EnvStrDebug { slice: iter.as_slice() } - } -} - -impl fmt::Debug for Env { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { iter } = self; - f.debug_list().entries(iter.as_slice()).finish() - } -} - -impl !Send for Env {} -impl !Sync for Env {} - -impl Iterator for Env { - type Item = (OsString, OsString); - fn next(&mut self) -> Option<(OsString, OsString)> { - self.iter.next() - } - fn size_hint(&self) -> (usize, Option<usize>) { - self.iter.size_hint() - } -} - -/// Returns a vector of (variable, value) byte-vector pairs for all the -/// environment variables of the current process. -pub fn env() -> Env { - unsafe extern "C" { - static mut environ: *const *const c_char; - } - - unsafe { - let _guard = env_read_lock(); - let mut result = Vec::new(); - if !environ.is_null() { - while !(*environ).is_null() { - if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) { - result.push(key_value); - } - environ = environ.add(1); - } - } - return Env { iter: result.into_iter() }; - } - - fn parse(input: &[u8]) -> Option<(OsString, OsString)> { - // Strategy (copied from glibc): Variable name and value are separated - // by an ASCII equals sign '='. Since a variable name must not be - // empty, allow variable names starting with an equals sign. Skip all - // malformed lines. - if input.is_empty() { - return None; - } - let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1); - pos.map(|p| { - ( - OsStringExt::from_vec(input[..p].to_vec()), - OsStringExt::from_vec(input[p + 1..].to_vec()), - ) - }) - } -} - -pub fn getenv(k: &OsStr) -> Option<OsString> { - // environment variables with a nul byte can't be set, so their value is - // always None as well - run_with_cstr(k.as_bytes(), &|k| { - let _guard = env_read_lock(); - let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; - - if v.is_null() { - Ok(None) - } else { - // SAFETY: `v` cannot be mutated while executing this line since we've a read lock - let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec(); - - Ok(Some(OsStringExt::from_vec(bytes))) - } - }) - .ok() - .flatten() -} - -pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - run_with_cstr(k.as_bytes(), &|k| { - run_with_cstr(v.as_bytes(), &|v| { - let _guard = ENV_LOCK.write(); - cvt_env(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop) - }) - }) -} - -pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { - run_with_cstr(n.as_bytes(), &|nbuf| { - let _guard = ENV_LOCK.write(); - cvt_env(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop) - }) -} - -/// In kmclib, `setenv` and `unsetenv` don't always set `errno`, so this -/// function just returns a generic error. -fn cvt_env(t: c_int) -> io::Result<c_int> { - if t == -1 { Err(io::const_error!(io::ErrorKind::Uncategorized, "failure")) } else { Ok(t) } -} - pub fn temp_dir() -> PathBuf { panic!("no standard temporary directory on this platform") } diff --git a/library/std/src/sys/pal/teeos/os.rs b/library/std/src/sys/pal/teeos/os.rs index bf6945811ab..03f3c72b022 100644 --- a/library/std/src/sys/pal/teeos/os.rs +++ b/library/std/src/sys/pal/teeos/os.rs @@ -73,47 +73,6 @@ pub fn current_exe() -> io::Result<PathBuf> { unsupported() } -pub struct Env(!); - -impl Env { - // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt. - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self(inner) = self; - match *inner {} - } -} - -impl fmt::Debug for Env { - fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self(inner) = self; - match *inner {} - } -} - -impl Iterator for Env { - type Item = (OsString, OsString); - fn next(&mut self) -> Option<(OsString, OsString)> { - let Self(inner) = self; - match *inner {} - } -} - -pub fn env() -> Env { - panic!("not supported on this platform") -} - -pub fn getenv(_: &OsStr) -> Option<OsString> { - None -} - -pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) -} - -pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) -} - pub fn temp_dir() -> PathBuf { panic!("no filesystem on this platform") } diff --git a/library/std/src/sys/pal/uefi/os.rs b/library/std/src/sys/pal/uefi/os.rs index d26d61890c1..bfd4dc81cb4 100644 --- a/library/std/src/sys/pal/uefi/os.rs +++ b/library/std/src/sys/pal/uefi/os.rs @@ -131,60 +131,6 @@ pub fn current_exe() -> io::Result<PathBuf> { helpers::device_path_to_text(protocol).map(PathBuf::from) } -pub struct EnvStrDebug<'a> { - iter: &'a [(OsString, OsString)], -} - -impl fmt::Debug for EnvStrDebug<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut list = f.debug_list(); - for (a, b) in self.iter { - list.entry(&(a.to_str().unwrap(), b.to_str().unwrap())); - } - list.finish() - } -} - -pub struct Env(crate::vec::IntoIter<(OsString, OsString)>); - -impl Env { - // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt. - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - EnvStrDebug { iter: self.0.as_slice() } - } -} - -impl Iterator for Env { - type Item = (OsString, OsString); - - fn next(&mut self) -> Option<(OsString, OsString)> { - self.0.next() - } -} - -impl fmt::Debug for Env { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -pub fn env() -> Env { - let env = uefi_env::get_all().expect("not supported on this platform"); - Env(env.into_iter()) -} - -pub fn getenv(key: &OsStr) -> Option<OsString> { - uefi_env::get(key) -} - -pub unsafe fn setenv(key: &OsStr, val: &OsStr) -> io::Result<()> { - uefi_env::set(key, val) -} - -pub unsafe fn unsetenv(key: &OsStr) -> io::Result<()> { - uefi_env::unset(key) -} - pub fn temp_dir() -> PathBuf { panic!("no filesystem on this platform") } @@ -213,85 +159,3 @@ pub fn exit(code: i32) -> ! { pub fn getpid() -> u32 { panic!("no pids on this platform") } - -mod uefi_env { - use crate::ffi::{OsStr, OsString}; - use crate::io; - use crate::os::uefi::ffi::OsStringExt; - use crate::ptr::NonNull; - use crate::sys::{helpers, unsupported_err}; - - pub(crate) fn get(key: &OsStr) -> Option<OsString> { - let shell = helpers::open_shell()?; - let mut key_ptr = helpers::os_string_to_raw(key)?; - unsafe { get_raw(shell, key_ptr.as_mut_ptr()) } - } - - pub(crate) fn set(key: &OsStr, val: &OsStr) -> io::Result<()> { - let mut key_ptr = helpers::os_string_to_raw(key) - .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid key"))?; - let mut val_ptr = helpers::os_string_to_raw(val) - .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid value"))?; - unsafe { set_raw(key_ptr.as_mut_ptr(), val_ptr.as_mut_ptr()) } - } - - pub(crate) fn unset(key: &OsStr) -> io::Result<()> { - let mut key_ptr = helpers::os_string_to_raw(key) - .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid key"))?; - unsafe { set_raw(key_ptr.as_mut_ptr(), crate::ptr::null_mut()) } - } - - pub(crate) fn get_all() -> io::Result<Vec<(OsString, OsString)>> { - let shell = helpers::open_shell().ok_or(unsupported_err())?; - - let mut vars = Vec::new(); - let val = unsafe { ((*shell.as_ptr()).get_env)(crate::ptr::null_mut()) }; - - if val.is_null() { - return Ok(vars); - } - - let mut start = 0; - - // UEFI Shell returns all keys separated by NULL. - // End of string is denoted by two NULLs - for i in 0.. { - if unsafe { *val.add(i) } == 0 { - // Two NULL signal end of string - if i == start { - break; - } - - let key = OsString::from_wide(unsafe { - crate::slice::from_raw_parts(val.add(start), i - start) - }); - // SAFETY: val.add(start) is always NULL terminated - let val = unsafe { get_raw(shell, val.add(start)) } - .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "invalid value"))?; - - vars.push((key, val)); - start = i + 1; - } - } - - Ok(vars) - } - - unsafe fn get_raw( - shell: NonNull<r_efi::efi::protocols::shell::Protocol>, - key_ptr: *mut r_efi::efi::Char16, - ) -> Option<OsString> { - let val = unsafe { ((*shell.as_ptr()).get_env)(key_ptr) }; - helpers::os_string_from_raw(val) - } - - unsafe fn set_raw( - key_ptr: *mut r_efi::efi::Char16, - val_ptr: *mut r_efi::efi::Char16, - ) -> io::Result<()> { - let shell = helpers::open_shell().ok_or(unsupported_err())?; - let r = - unsafe { ((*shell.as_ptr()).set_env)(key_ptr, val_ptr, r_efi::efi::Boolean::FALSE) }; - if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } - } -} diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index f47421c6705..4883303b88e 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -5,20 +5,15 @@ #[cfg(test)] mod tests; -use core::slice::memchr; - use libc::{c_char, c_int, c_void}; use crate::error::Error as StdError; -use crate::ffi::{CStr, CString, OsStr, OsString}; +use crate::ffi::{CStr, OsStr, OsString}; use crate::os::unix::prelude::*; use crate::path::{self, PathBuf}; -use crate::sync::{PoisonError, RwLock}; -use crate::sys::common::small_c_string::{run_path_with_cstr, run_with_cstr}; -#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))] -use crate::sys::weak::weak; -use crate::sys::{cvt, fd}; -use crate::{fmt, io, iter, mem, ptr, slice, str, vec}; +use crate::sys::common::small_c_string::run_path_with_cstr; +use crate::sys::cvt; +use crate::{fmt, io, iter, mem, ptr, slice, str}; const TMPBUF_SZ: usize = 128; @@ -552,166 +547,6 @@ pub fn current_exe() -> io::Result<PathBuf> { if !path.is_absolute() { getcwd().map(|cwd| cwd.join(path)) } else { Ok(path) } } -pub struct Env { - iter: vec::IntoIter<(OsString, OsString)>, -} - -// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt. -pub struct EnvStrDebug<'a> { - slice: &'a [(OsString, OsString)], -} - -impl fmt::Debug for EnvStrDebug<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { slice } = self; - f.debug_list() - .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) - .finish() - } -} - -impl Env { - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self { iter } = self; - EnvStrDebug { slice: iter.as_slice() } - } -} - -impl fmt::Debug for Env { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { iter } = self; - f.debug_list().entries(iter.as_slice()).finish() - } -} - -impl !Send for Env {} -impl !Sync for Env {} - -impl Iterator for Env { - type Item = (OsString, OsString); - fn next(&mut self) -> Option<(OsString, OsString)> { - self.iter.next() - } - fn size_hint(&self) -> (usize, Option<usize>) { - self.iter.size_hint() - } -} - -// Use `_NSGetEnviron` on Apple platforms. -// -// `_NSGetEnviron` is the documented alternative (see `man environ`), and has -// been available since the first versions of both macOS and iOS. -// -// Nowadays, specifically since macOS 10.8, `environ` has been exposed through -// `libdyld.dylib`, which is linked via. `libSystem.dylib`: -// <https://github.com/apple-oss-distributions/dyld/blob/dyld-1160.6/libdyld/libdyldGlue.cpp#L913> -// -// So in the end, it likely doesn't really matter which option we use, but the -// performance cost of using `_NSGetEnviron` is extremely miniscule, and it -// might be ever so slightly more supported, so let's just use that. -// -// NOTE: The header where this is defined (`crt_externs.h`) was added to the -// iOS 13.0 SDK, which has been the source of a great deal of confusion in the -// past about the availability of this API. -// -// NOTE(madsmtm): Neither this nor using `environ` has been verified to not -// cause App Store rejections; if this is found to be the case, an alternative -// implementation of this is possible using `[NSProcessInfo environment]` -// - which internally uses `_NSGetEnviron` and a system-wide lock on the -// environment variables to protect against `setenv`, so using that might be -// desirable anyhow? Though it also means that we have to link to Foundation. -#[cfg(target_vendor = "apple")] -pub unsafe fn environ() -> *mut *const *const c_char { - libc::_NSGetEnviron() as *mut *const *const c_char -} - -// Use the `environ` static which is part of POSIX. -#[cfg(not(target_vendor = "apple"))] -pub unsafe fn environ() -> *mut *const *const c_char { - unsafe extern "C" { - static mut environ: *const *const c_char; - } - &raw mut environ -} - -static ENV_LOCK: RwLock<()> = RwLock::new(()); - -pub fn env_read_lock() -> impl Drop { - ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner) -} - -/// Returns a vector of (variable, value) byte-vector pairs for all the -/// environment variables of the current process. -pub fn env() -> Env { - unsafe { - let _guard = env_read_lock(); - let mut environ = *environ(); - let mut result = Vec::new(); - if !environ.is_null() { - while !(*environ).is_null() { - if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) { - result.push(key_value); - } - environ = environ.add(1); - } - } - return Env { iter: result.into_iter() }; - } - - fn parse(input: &[u8]) -> Option<(OsString, OsString)> { - // Strategy (copied from glibc): Variable name and value are separated - // by an ASCII equals sign '='. Since a variable name must not be - // empty, allow variable names starting with an equals sign. Skip all - // malformed lines. - if input.is_empty() { - return None; - } - let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1); - pos.map(|p| { - ( - OsStringExt::from_vec(input[..p].to_vec()), - OsStringExt::from_vec(input[p + 1..].to_vec()), - ) - }) - } -} - -pub fn getenv(k: &OsStr) -> Option<OsString> { - // environment variables with a nul byte can't be set, so their value is - // always None as well - run_with_cstr(k.as_bytes(), &|k| { - let _guard = env_read_lock(); - let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; - - if v.is_null() { - Ok(None) - } else { - // SAFETY: `v` cannot be mutated while executing this line since we've a read lock - let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec(); - - Ok(Some(OsStringExt::from_vec(bytes))) - } - }) - .ok() - .flatten() -} - -pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - run_with_cstr(k.as_bytes(), &|k| { - run_with_cstr(v.as_bytes(), &|v| { - let _guard = ENV_LOCK.write(); - cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) - }) - }) -} - -pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { - run_with_cstr(n.as_bytes(), &|nbuf| { - let _guard = ENV_LOCK.write(); - cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) - }) -} - #[cfg(not(target_os = "espidf"))] pub fn page_size() -> usize { unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize } diff --git a/library/std/src/sys/pal/unix/sync/condvar.rs b/library/std/src/sys/pal/unix/sync/condvar.rs index 73631053e9f..efa6f8d7765 100644 --- a/library/std/src/sys/pal/unix/sync/condvar.rs +++ b/library/std/src/sys/pal/unix/sync/condvar.rs @@ -64,7 +64,10 @@ impl Condvar { // https://gist.github.com/stepancheg/198db4623a20aad2ad7cddb8fda4a63c // // To work around this issue, the timeout is clamped to 1000 years. - #[cfg(target_vendor = "apple")] + // + // Cygwin implementation is based on NT API and a super large timeout + // makes the syscall block forever. + #[cfg(any(target_vendor = "apple", target_os = "cygwin"))] let dur = Duration::min(dur, Duration::from_secs(1000 * 365 * 86400)); let timeout = Timespec::now(Self::CLOCK).checked_add_duration(&dur); diff --git a/library/std/src/sys/pal/unsupported/os.rs b/library/std/src/sys/pal/unsupported/os.rs index 48de4312885..a8ef97ecf67 100644 --- a/library/std/src/sys/pal/unsupported/os.rs +++ b/library/std/src/sys/pal/unsupported/os.rs @@ -62,47 +62,6 @@ pub fn current_exe() -> io::Result<PathBuf> { unsupported() } -pub struct Env(!); - -impl Env { - // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt. - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self(inner) = self; - match *inner {} - } -} - -impl fmt::Debug for Env { - fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self(inner) = self; - match *inner {} - } -} - -impl Iterator for Env { - type Item = (OsString, OsString); - fn next(&mut self) -> Option<(OsString, OsString)> { - let Self(inner) = self; - match *inner {} - } -} - -pub fn env() -> Env { - panic!("not supported on this platform") -} - -pub fn getenv(_: &OsStr) -> Option<OsString> { - None -} - -pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) -} - -pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) -} - pub fn temp_dir() -> PathBuf { panic!("no filesystem on this platform") } diff --git a/library/std/src/sys/pal/wasi/os.rs b/library/std/src/sys/pal/wasi/os.rs index ba2b65a1f40..672cf70d1a5 100644 --- a/library/std/src/sys/pal/wasi/os.rs +++ b/library/std/src/sys/pal/wasi/os.rs @@ -1,19 +1,16 @@ #![forbid(unsafe_op_in_unsafe_fn)] -use core::slice::memchr; - use crate::error::Error as StdError; use crate::ffi::{CStr, OsStr, OsString}; use crate::marker::PhantomData; -use crate::ops::Drop; use crate::os::wasi::prelude::*; use crate::path::{self, PathBuf}; -use crate::sys::common::small_c_string::{run_path_with_cstr, run_with_cstr}; +use crate::sys::common::small_c_string::run_path_with_cstr; use crate::sys::unsupported; -use crate::{fmt, io, str, vec}; +use crate::{fmt, io, str}; // Add a few symbols not in upstream `libc` just yet. -mod libc { +pub mod libc { pub use libc::*; unsafe extern "C" { @@ -23,28 +20,6 @@ mod libc { } } -cfg_if::cfg_if! { - if #[cfg(target_feature = "atomics")] { - // Access to the environment must be protected by a lock in multi-threaded scenarios. - use crate::sync::{PoisonError, RwLock}; - static ENV_LOCK: RwLock<()> = RwLock::new(()); - pub fn env_read_lock() -> impl Drop { - ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner) - } - pub fn env_write_lock() -> impl Drop { - ENV_LOCK.write().unwrap_or_else(PoisonError::into_inner) - } - } else { - // No need for a lock if we are single-threaded. - pub fn env_read_lock() -> impl Drop { - Box::new(()) - } - pub fn env_write_lock() -> impl Drop { - Box::new(()) - } - } -} - pub fn errno() -> i32 { unsafe extern "C" { #[thread_local] @@ -141,123 +116,6 @@ pub fn current_exe() -> io::Result<PathBuf> { unsupported() } -pub struct Env { - iter: vec::IntoIter<(OsString, OsString)>, -} - -// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt. -pub struct EnvStrDebug<'a> { - slice: &'a [(OsString, OsString)], -} - -impl fmt::Debug for EnvStrDebug<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { slice } = self; - f.debug_list() - .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) - .finish() - } -} - -impl Env { - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self { iter } = self; - EnvStrDebug { slice: iter.as_slice() } - } -} - -impl fmt::Debug for Env { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { iter } = self; - f.debug_list().entries(iter.as_slice()).finish() - } -} - -impl !Send for Env {} -impl !Sync for Env {} - -impl Iterator for Env { - type Item = (OsString, OsString); - fn next(&mut self) -> Option<(OsString, OsString)> { - self.iter.next() - } - fn size_hint(&self) -> (usize, Option<usize>) { - self.iter.size_hint() - } -} - -pub fn env() -> Env { - unsafe { - let _guard = env_read_lock(); - - // Use `__wasilibc_get_environ` instead of `environ` here so that we - // don't require wasi-libc to eagerly initialize the environment - // variables. - let mut environ = libc::__wasilibc_get_environ(); - - let mut result = Vec::new(); - if !environ.is_null() { - while !(*environ).is_null() { - if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) { - result.push(key_value); - } - environ = environ.add(1); - } - } - return Env { iter: result.into_iter() }; - } - - // See src/libstd/sys/pal/unix/os.rs, same as that - fn parse(input: &[u8]) -> Option<(OsString, OsString)> { - if input.is_empty() { - return None; - } - let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1); - pos.map(|p| { - ( - OsStringExt::from_vec(input[..p].to_vec()), - OsStringExt::from_vec(input[p + 1..].to_vec()), - ) - }) - } -} - -pub fn getenv(k: &OsStr) -> Option<OsString> { - // environment variables with a nul byte can't be set, so their value is - // always None as well - run_with_cstr(k.as_bytes(), &|k| { - let _guard = env_read_lock(); - let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; - - if v.is_null() { - Ok(None) - } else { - // SAFETY: `v` cannot be mutated while executing this line since we've a read lock - let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec(); - - Ok(Some(OsStringExt::from_vec(bytes))) - } - }) - .ok() - .flatten() -} - -pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - run_with_cstr(k.as_bytes(), &|k| { - run_with_cstr(v.as_bytes(), &|v| unsafe { - let _guard = env_write_lock(); - cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) - }) - }) -} - -pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { - run_with_cstr(n.as_bytes(), &|nbuf| unsafe { - let _guard = env_write_lock(); - cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) - }) -} - #[allow(dead_code)] pub fn page_size() -> usize { unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize } @@ -294,6 +152,6 @@ macro_rules! impl_is_minus_one { impl_is_minus_one! { i8 i16 i32 i64 isize } -fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> { +pub fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> { if t.is_minus_one() { Err(io::Error::last_os_error()) } else { Ok(t) } } diff --git a/library/std/src/sys/pal/windows/os.rs b/library/std/src/sys/pal/windows/os.rs index 044dc2e8cd8..f331282d2d7 100644 --- a/library/std/src/sys/pal/windows/os.rs +++ b/library/std/src/sys/pal/windows/os.rs @@ -5,16 +5,16 @@ #[cfg(test)] mod tests; +use super::api; #[cfg(not(target_vendor = "uwp"))] use super::api::WinError; -use super::{api, to_u16s}; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::os::windows::ffi::EncodeWide; use crate::os::windows::prelude::*; use crate::path::{self, PathBuf}; -use crate::sys::{c, cvt}; -use crate::{fmt, io, ptr, slice}; +use crate::sys::pal::{c, cvt}; +use crate::{fmt, io, ptr}; pub fn errno() -> i32 { api::get_last_error().code as i32 @@ -76,108 +76,6 @@ pub fn error_string(mut errnum: i32) -> String { } } -pub struct Env { - base: *mut c::WCHAR, - iter: EnvIterator, -} - -// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt. -pub struct EnvStrDebug<'a> { - iter: &'a EnvIterator, -} - -impl fmt::Debug for EnvStrDebug<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { iter } = self; - let iter: EnvIterator = (*iter).clone(); - let mut list = f.debug_list(); - for (a, b) in iter { - list.entry(&(a.to_str().unwrap(), b.to_str().unwrap())); - } - list.finish() - } -} - -impl Env { - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self { base: _, iter } = self; - EnvStrDebug { iter } - } -} - -impl fmt::Debug for Env { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { base: _, iter } = self; - f.debug_list().entries(iter.clone()).finish() - } -} - -impl Iterator for Env { - type Item = (OsString, OsString); - - fn next(&mut self) -> Option<(OsString, OsString)> { - let Self { base: _, iter } = self; - iter.next() - } -} - -#[derive(Clone)] -struct EnvIterator(*mut c::WCHAR); - -impl Iterator for EnvIterator { - type Item = (OsString, OsString); - - fn next(&mut self) -> Option<(OsString, OsString)> { - let Self(cur) = self; - loop { - unsafe { - if **cur == 0 { - return None; - } - let p = *cur as *const u16; - let mut len = 0; - while *p.add(len) != 0 { - len += 1; - } - let s = slice::from_raw_parts(p, len); - *cur = cur.add(len + 1); - - // Windows allows environment variables to start with an equals - // symbol (in any other position, this is the separator between - // variable name and value). Since`s` has at least length 1 at - // this point (because the empty string terminates the array of - // environment variables), we can safely slice. - let pos = match s[1..].iter().position(|&u| u == b'=' as u16).map(|p| p + 1) { - Some(p) => p, - None => continue, - }; - return Some(( - OsStringExt::from_wide(&s[..pos]), - OsStringExt::from_wide(&s[pos + 1..]), - )); - } - } - } -} - -impl Drop for Env { - fn drop(&mut self) { - unsafe { - c::FreeEnvironmentStringsW(self.base); - } - } -} - -pub fn env() -> Env { - unsafe { - let ch = c::GetEnvironmentStringsW(); - if ch.is_null() { - panic!("failure getting env string from OS: {}", io::Error::last_os_error()); - } - Env { base: ch, iter: EnvIterator(ch) } - } -} - pub struct SplitPaths<'a> { data: EncodeWide<'a>, must_yield: bool, @@ -290,33 +188,6 @@ pub fn chdir(p: &path::Path) -> io::Result<()> { cvt(unsafe { c::SetCurrentDirectoryW(p.as_ptr()) }).map(drop) } -pub fn getenv(k: &OsStr) -> Option<OsString> { - let k = to_u16s(k).ok()?; - super::fill_utf16_buf( - |buf, sz| unsafe { c::GetEnvironmentVariableW(k.as_ptr(), buf, sz) }, - OsStringExt::from_wide, - ) - .ok() -} - -pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - // SAFETY: We ensure that k and v are null-terminated wide strings. - unsafe { - let k = to_u16s(k)?; - let v = to_u16s(v)?; - - cvt(c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr())).map(drop) - } -} - -pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { - // SAFETY: We ensure that v is a null-terminated wide strings. - unsafe { - let v = to_u16s(n)?; - cvt(c::SetEnvironmentVariableW(v.as_ptr(), ptr::null())).map(drop) - } -} - pub fn temp_dir() -> PathBuf { super::fill_utf16_buf(|buf, sz| unsafe { c::GetTempPath2W(sz, buf) }, super::os2path).unwrap() } diff --git a/library/std/src/sys/pal/xous/os.rs b/library/std/src/sys/pal/xous/os.rs index 2c87e7d91f2..1b41575358f 100644 --- a/library/std/src/sys/pal/xous/os.rs +++ b/library/std/src/sys/pal/xous/os.rs @@ -1,13 +1,11 @@ use super::unsupported; -use crate::collections::HashMap; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; use crate::os::xous::ffi::Error as XousError; use crate::path::{self, PathBuf}; -use crate::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; -use crate::sync::{Mutex, Once}; -use crate::{fmt, io, vec}; +use crate::sync::atomic::{AtomicPtr, Ordering}; +use crate::{fmt, io}; pub(crate) mod params; @@ -136,100 +134,6 @@ pub(crate) fn get_application_parameters() -> Option<params::ApplicationParamete unsafe { params::ApplicationParameters::new_from_ptr(params_address) } } -// ---------- Environment handling ---------- // -static ENV: AtomicUsize = AtomicUsize::new(0); -static ENV_INIT: Once = Once::new(); -type EnvStore = Mutex<HashMap<OsString, OsString>>; - -fn get_env_store() -> &'static EnvStore { - ENV_INIT.call_once(|| { - let env_store = EnvStore::default(); - if let Some(params) = get_application_parameters() { - for param in params { - if let Ok(envs) = params::EnvironmentBlock::try_from(¶m) { - let mut env_store = env_store.lock().unwrap(); - for env in envs { - env_store.insert(env.key.into(), env.value.into()); - } - break; - } - } - } - ENV.store(Box::into_raw(Box::new(env_store)) as _, Ordering::Relaxed) - }); - unsafe { &*core::ptr::with_exposed_provenance::<EnvStore>(ENV.load(Ordering::Relaxed)) } -} - -pub struct Env { - iter: vec::IntoIter<(OsString, OsString)>, -} - -// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt. -pub struct EnvStrDebug<'a> { - slice: &'a [(OsString, OsString)], -} - -impl fmt::Debug for EnvStrDebug<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { slice } = self; - f.debug_list() - .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) - .finish() - } -} - -impl Env { - // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt. - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self { iter } = self; - EnvStrDebug { slice: iter.as_slice() } - } -} - -impl fmt::Debug for Env { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { iter } = self; - f.debug_list().entries(iter.as_slice()).finish() - } -} - -impl !Send for Env {} -impl !Sync for Env {} - -impl Iterator for Env { - type Item = (OsString, OsString); - fn next(&mut self) -> Option<(OsString, OsString)> { - self.iter.next() - } - fn size_hint(&self) -> (usize, Option<usize>) { - self.iter.size_hint() - } -} - -pub fn env() -> Env { - let clone_to_vec = |map: &HashMap<OsString, OsString>| -> Vec<_> { - map.iter().map(|(k, v)| (k.clone(), v.clone())).collect() - }; - - let iter = clone_to_vec(&*get_env_store().lock().unwrap()).into_iter(); - Env { iter } -} - -pub fn getenv(k: &OsStr) -> Option<OsString> { - get_env_store().lock().unwrap().get(k).cloned() -} - -pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - let (k, v) = (k.to_owned(), v.to_owned()); - get_env_store().lock().unwrap().insert(k, v); - Ok(()) -} - -pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> { - get_env_store().lock().unwrap().remove(k); - Ok(()) -} - pub fn temp_dir() -> PathBuf { panic!("no filesystem on this platform") } diff --git a/library/std/src/sys/pal/zkvm/env.rs b/library/std/src/sys/pal/zkvm/env.rs deleted file mode 100644 index b85153642b1..00000000000 --- a/library/std/src/sys/pal/zkvm/env.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub mod os { - pub const FAMILY: &str = ""; - pub const OS: &str = ""; - pub const DLL_PREFIX: &str = ""; - pub const DLL_SUFFIX: &str = ".elf"; - pub const DLL_EXTENSION: &str = "elf"; - pub const EXE_SUFFIX: &str = ".elf"; - pub const EXE_EXTENSION: &str = "elf"; -} diff --git a/library/std/src/sys/pal/zkvm/os.rs b/library/std/src/sys/pal/zkvm/os.rs index 868b19e33b6..a8ef97ecf67 100644 --- a/library/std/src/sys/pal/zkvm/os.rs +++ b/library/std/src/sys/pal/zkvm/os.rs @@ -1,10 +1,8 @@ -use super::{WORD_SIZE, abi, unsupported}; +use super::unsupported; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; use crate::path::{self, PathBuf}; -use crate::sys::os_str; -use crate::sys_common::FromInner; use crate::{fmt, io}; pub fn errno() -> i32 { @@ -64,64 +62,6 @@ pub fn current_exe() -> io::Result<PathBuf> { unsupported() } -pub struct Env(!); - -impl Iterator for Env { - type Item = (OsString, OsString); - fn next(&mut self) -> Option<(OsString, OsString)> { - self.0 - } -} - -pub fn env() -> Env { - panic!("not supported on this platform") -} - -impl Env { - pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self(inner) = self; - match *inner {} - } -} - -impl fmt::Debug for Env { - fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self(inner) = self; - match *inner {} - } -} - -pub fn getenv(varname: &OsStr) -> Option<OsString> { - let varname = varname.as_encoded_bytes(); - let nbytes = - unsafe { abi::sys_getenv(crate::ptr::null_mut(), 0, varname.as_ptr(), varname.len()) }; - if nbytes == usize::MAX { - return None; - } - - let nwords = (nbytes + WORD_SIZE - 1) / WORD_SIZE; - let words = unsafe { abi::sys_alloc_words(nwords) }; - - let nbytes2 = unsafe { abi::sys_getenv(words, nwords, varname.as_ptr(), varname.len()) }; - debug_assert_eq!(nbytes, nbytes2); - - // Convert to OsString. - // - // FIXME: We can probably get rid of the extra copy here if we - // reimplement "os_str" instead of just using the generic unix - // "os_str". - let u8s: &[u8] = unsafe { crate::slice::from_raw_parts(words.cast() as *const u8, nbytes) }; - Some(OsString::from_inner(os_str::Buf { inner: u8s.to_vec() })) -} - -pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) -} - -pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) -} - pub fn temp_dir() -> PathBuf { panic!("no filesystem on this platform") } diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs index 92bb809d90c..478f6583b1f 100644 --- a/library/std/src/sys/process/unix/unix.rs +++ b/library/std/src/sys/process/unix/unix.rs @@ -88,7 +88,7 @@ impl Command { // in its own process. Thus the parent drops the lock guard immediately. // The child calls `mem::forget` to leak the lock, which is crucial because // releasing a lock is not async-signal-safe. - let env_lock = sys::os::env_read_lock(); + let env_lock = sys::env::env_read_lock(); let pid = unsafe { self.do_fork()? }; if pid == 0 { @@ -237,7 +237,7 @@ impl Command { // Similar to when forking, we want to ensure that access to // the environment is synchronized, so make sure to grab the // environment lock before we try to exec. - let _lock = sys::os::env_read_lock(); + let _lock = sys::env::env_read_lock(); let Err(e) = self.do_exec(theirs, envp.as_ref()); e @@ -386,13 +386,13 @@ impl Command { impl Drop for Reset { fn drop(&mut self) { unsafe { - *sys::os::environ() = self.0; + *sys::env::environ() = self.0; } } } - _reset = Some(Reset(*sys::os::environ())); - *sys::os::environ() = envp.as_ptr(); + _reset = Some(Reset(*sys::env::environ())); + *sys::env::environ() = envp.as_ptr(); } libc::execvp(self.get_program_cstr().as_ptr(), self.get_argv().as_ptr()); @@ -739,8 +739,8 @@ impl Command { cvt_nz(libc::posix_spawnattr_setflags(attrs.0.as_mut_ptr(), flags as _))?; // Make sure we synchronize access to the global `environ` resource - let _env_lock = sys::os::env_read_lock(); - let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::os::environ() as *const _); + 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"))] let spawn_fn = libc::posix_spawnp; diff --git a/library/std/src/sys/process/unix/vxworks.rs b/library/std/src/sys/process/unix/vxworks.rs index 5f1727789a1..b92446f0cf6 100644 --- a/library/std/src/sys/process/unix/vxworks.rs +++ b/library/std/src/sys/process/unix/vxworks.rs @@ -67,7 +67,7 @@ impl Command { let c_envp = envp .as_ref() .map(|c| c.as_ptr()) - .unwrap_or_else(|| *sys::os::environ() as *const _); + .unwrap_or_else(|| *sys::env::environ() as *const _); let stack_size = crate::cmp::max( crate::env::var_os("RUST_MIN_STACK") .and_then(|s| s.to_str().and_then(|s| s.parse().ok())) @@ -76,7 +76,7 @@ impl Command { ); // ensure that access to the environment is synchronized - let _lock = sys::os::env_read_lock(); + let _lock = sys::env::env_read_lock(); let ret = libc::rtpSpawn( self.get_program_cstr().as_ptr(), diff --git a/library/stdarch b/library/stdarch -Subproject 4666c7376f25a265c74535585d622da3da6dfeb +Subproject 1245618ccf5b2df7ab1ebb0279b9f3f72667016 diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index ed90ede7936..3c412683b94 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -421,13 +421,13 @@ impl Step for Rustc { builder.install(&rustdoc, &image.join("bin"), FileType::Executable); } + let ra_proc_macro_srv_compiler = + builder.compiler_for(compiler.stage, builder.config.build, compiler.host); + builder.ensure(compile::Rustc::new(ra_proc_macro_srv_compiler, compiler.host)); + if let Some(ra_proc_macro_srv) = builder.ensure_if_default( tool::RustAnalyzerProcMacroSrv { - compiler: builder.compiler_for( - compiler.stage, - builder.config.build, - compiler.host, - ), + compiler: ra_proc_macro_srv_compiler, target: compiler.host, }, builder.kind, @@ -1178,6 +1178,8 @@ impl Step for Cargo { let compiler = self.compiler; let target = self.target; + builder.ensure(compile::Rustc::new(compiler, target)); + let cargo = builder.ensure(tool::Cargo { compiler, target }); let src = builder.src.join("src/tools/cargo"); let etc = src.join("src/etc"); @@ -1232,6 +1234,8 @@ impl Step for RustAnalyzer { let compiler = self.compiler; let target = self.target; + builder.ensure(compile::Rustc::new(compiler, target)); + let rust_analyzer = builder.ensure(tool::RustAnalyzer { compiler, target }); let mut tarball = Tarball::new(builder, "rust-analyzer", &target.triple); @@ -1274,6 +1278,8 @@ impl Step for Clippy { let compiler = self.compiler; let target = self.target; + builder.ensure(compile::Rustc::new(compiler, target)); + // Prepare the image directory // We expect clippy to build, because we've exited this step above if tool // state for clippy isn't testing. @@ -1324,9 +1330,12 @@ impl Step for Miri { if !builder.build.unstable_features() { return None; } + let compiler = self.compiler; let target = self.target; + builder.ensure(compile::Rustc::new(compiler, target)); + let miri = builder.ensure(tool::Miri { compiler, target }); let cargomiri = builder.ensure(tool::CargoMiri { compiler, target }); @@ -1463,6 +1472,8 @@ impl Step for Rustfmt { let compiler = self.compiler; let target = self.target; + builder.ensure(compile::Rustc::new(compiler, target)); + let rustfmt = builder.ensure(tool::Rustfmt { compiler, target }); let cargofmt = builder.ensure(tool::Cargofmt { compiler, target }); let mut tarball = Tarball::new(builder, "rustfmt", &target.triple); @@ -2328,6 +2339,8 @@ impl Step for LlvmBitcodeLinker { let compiler = self.compiler; let target = self.target; + builder.ensure(compile::Rustc::new(compiler, target)); + let llbc_linker = builder.ensure(tool::LlvmBitcodeLinker { compiler, target, extra_features: vec![] }); diff --git a/src/bootstrap/src/core/build_steps/suggest.rs b/src/bootstrap/src/core/build_steps/suggest.rs index 6a6731cafc5..fd4918961ad 100644 --- a/src/bootstrap/src/core/build_steps/suggest.rs +++ b/src/bootstrap/src/core/build_steps/suggest.rs @@ -13,7 +13,6 @@ pub fn suggest(builder: &Builder<'_>, run: bool) { let git_config = builder.config.git_config(); let suggestions = builder .tool_cmd(Tool::SuggestTests) - .env("SUGGEST_TESTS_GIT_REPOSITORY", git_config.git_repository) .env("SUGGEST_TESTS_NIGHTLY_BRANCH", git_config.nightly_branch) .env("SUGGEST_TESTS_MERGE_COMMIT_EMAIL", git_config.git_merge_commit_email) .run_capture_stdout(builder) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 096f7de6597..d4fccc535a6 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -2064,7 +2064,6 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the } let git_config = builder.config.git_config(); - cmd.arg("--git-repository").arg(git_config.git_repository); cmd.arg("--nightly-branch").arg(git_config.nightly_branch); cmd.arg("--git-merge-commit-email").arg(git_config.git_merge_commit_email); cmd.force_coloring_in_ci(); diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 8e2c6fc52cd..51852099dc3 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -449,6 +449,7 @@ mod dist { use pretty_assertions::assert_eq; use super::{Config, TEST_TRIPLE_1, TEST_TRIPLE_2, TEST_TRIPLE_3, first, run_build}; + use crate::Flags; use crate::core::builder::*; fn configure(host: &[&str], target: &[&str]) -> Config { @@ -687,6 +688,37 @@ mod dist { ); } + /// This also serves as an important regression test for <https://github.com/rust-lang/rust/issues/138123> + /// and <https://github.com/rust-lang/rust/issues/138004>. + #[test] + fn dist_all_cross() { + let cmd_args = + &["dist", "--stage", "2", "--dry-run", "--config=/does/not/exist"].map(str::to_owned); + let config_str = r#" + [rust] + channel = "nightly" + + [build] + extended = true + + build = "i686-unknown-haiku" + host = ["i686-unknown-netbsd"] + target = ["i686-unknown-netbsd"] + "#; + let config = Config::parse_inner(Flags::parse(cmd_args), |&_| toml::from_str(config_str)); + let mut cache = run_build(&[], config); + + // Stage 2 `compile::Rustc` should **NEVER** be cached here. + assert_eq!( + first(cache.all::<compile::Rustc>()), + &[ + rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0), + rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 1), + rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_3, stage = 1), + ] + ); + } + #[test] fn build_all() { let build = Build::new(configure( diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 419976c83b1..23b623d9bab 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -2963,7 +2963,6 @@ impl Config { pub fn git_config(&self) -> GitConfig<'_> { GitConfig { - git_repository: &self.stage0_metadata.config.git_repository, nightly_branch: &self.stage0_metadata.config.nightly_branch, git_merge_commit_email: &self.stage0_metadata.config.git_merge_commit_email, } diff --git a/src/bootstrap/src/utils/tests/git.rs b/src/bootstrap/src/utils/tests/git.rs index 99e0793af46..bd40f398c7b 100644 --- a/src/bootstrap/src/utils/tests/git.rs +++ b/src/bootstrap/src/utils/tests/git.rs @@ -135,7 +135,6 @@ impl GitCtx { fn git_config(&self) -> GitConfig<'_> { GitConfig { - git_repository: &self.git_repo, nightly_branch: &self.nightly_branch, git_merge_commit_email: &self.merge_bot_email, } diff --git a/src/build_helper/src/git.rs b/src/build_helper/src/git.rs index 8d53a83ea31..438cd14389c 100644 --- a/src/build_helper/src/git.rs +++ b/src/build_helper/src/git.rs @@ -5,7 +5,6 @@ use crate::ci::CiEnv; #[derive(Debug)] pub struct GitConfig<'a> { - pub git_repository: &'a str, pub nightly_branch: &'a str, pub git_merge_commit_email: &'a str, } diff --git a/src/build_helper/src/stage0_parser.rs b/src/build_helper/src/stage0_parser.rs index 2a0c12a1c91..2723f4aa7b9 100644 --- a/src/build_helper/src/stage0_parser.rs +++ b/src/build_helper/src/stage0_parser.rs @@ -20,7 +20,6 @@ pub struct Stage0Config { pub artifacts_server: String, pub artifacts_with_llvm_assertions_server: String, pub git_merge_commit_email: String, - pub git_repository: String, pub nightly_branch: String, } @@ -49,7 +48,6 @@ pub fn parse_stage0_file() -> Stage0 { stage0.config.artifacts_with_llvm_assertions_server = value.to_owned() } "git_merge_commit_email" => stage0.config.git_merge_commit_email = value.to_owned(), - "git_repository" => stage0.config.git_repository = value.to_owned(), "nightly_branch" => stage0.config.nightly_branch = value.to_owned(), "compiler_date" => stage0.compiler.date = value.to_owned(), diff --git a/src/ci/citool/src/jobs.rs b/src/ci/citool/src/jobs.rs index 13880ad466a..5600d7b4db5 100644 --- a/src/ci/citool/src/jobs.rs +++ b/src/ci/citool/src/jobs.rs @@ -3,12 +3,15 @@ mod tests; use std::collections::BTreeMap; +use anyhow::Context as _; use serde_yaml::Value; use crate::GitHubContext; +use crate::utils::load_env_var; /// Representation of a job loaded from the `src/ci/github-actions/jobs.yml` file. #[derive(serde::Deserialize, Debug, Clone)] +#[serde(deny_unknown_fields)] pub struct Job { /// Name of the job, e.g. mingw-check pub name: String, @@ -26,6 +29,8 @@ pub struct Job { pub free_disk: Option<bool>, /// Documentation link to a resource that could help people debug this CI job. pub doc_url: Option<String>, + /// Whether the job is executed on AWS CodeBuild. + pub codebuild: Option<bool>, } impl Job { @@ -80,7 +85,7 @@ impl JobDatabase { } pub fn load_job_db(db: &str) -> anyhow::Result<JobDatabase> { - let mut db: Value = serde_yaml::from_str(&db)?; + let mut db: Value = serde_yaml::from_str(db)?; // We need to expand merge keys (<<), because serde_yaml can't deal with them // `apply_merge` only applies the merge once, so do it a few times to unwrap nested merges. @@ -107,6 +112,29 @@ struct GithubActionsJob { free_disk: Option<bool>, #[serde(skip_serializing_if = "Option::is_none")] doc_url: Option<String>, + #[serde(skip_serializing_if = "Option::is_none")] + codebuild: Option<bool>, +} + +/// Replace GitHub context variables with environment variables in job configs. +/// Used for codebuild jobs like +/// `codebuild-ubuntu-22-8c-$github.run_id-$github.run_attempt` +fn substitute_github_vars(jobs: Vec<Job>) -> anyhow::Result<Vec<Job>> { + let run_id = load_env_var("GITHUB_RUN_ID")?; + let run_attempt = load_env_var("GITHUB_RUN_ATTEMPT")?; + + let jobs = jobs + .into_iter() + .map(|mut job| { + job.os = job + .os + .replace("$github.run_id", &run_id) + .replace("$github.run_attempt", &run_attempt); + job + }) + .collect(); + + Ok(jobs) } /// Skip CI jobs that are not supposed to be executed on the given `channel`. @@ -177,6 +205,8 @@ fn calculate_jobs( } RunType::AutoJob => (db.auto_jobs.clone(), "auto", &db.envs.auto_env), }; + let jobs = substitute_github_vars(jobs.clone()) + .context("Failed to substitute GitHub context variables in jobs")?; let jobs = skip_jobs(jobs, channel); let jobs = jobs .into_iter() @@ -207,6 +237,7 @@ fn calculate_jobs( continue_on_error: job.continue_on_error, free_disk: job.free_disk, doc_url: job.doc_url, + codebuild: job.codebuild, } }) .collect(); diff --git a/src/ci/docker/host-x86_64/dist-arm-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-arm-linux/Dockerfile index 420c42bc9d8..3795859f308 100644 --- a/src/ci/docker/host-x86_64/dist-arm-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-arm-linux/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:22.04 +FROM ghcr.io/rust-lang/ubuntu:22.04 COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh diff --git a/src/ci/docker/run.sh b/src/ci/docker/run.sh index 00d791eeb6b..36f7df2b069 100755 --- a/src/ci/docker/run.sh +++ b/src/ci/docker/run.sh @@ -288,7 +288,7 @@ args="$args --privileged" # `LOCAL_USER_ID` (recognized in `src/ci/run.sh`) to ensure that files are all # read/written as the same user as the bare-metal user. if [ -f /.dockerenv ]; then - docker create -v /checkout --name checkout alpine:3.4 /bin/true + docker create -v /checkout --name checkout ghcr.io/rust-lang/alpine:3.4 /bin/true docker cp . checkout:/checkout args="$args --volumes-from checkout" else diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 950a75721c4..88b29d2df56 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -56,6 +56,15 @@ runners: - &job-aarch64-linux-8c os: ubuntu-24.04-arm64-8core-32gb <<: *base-job + + # Codebuild runners are provisioned in + # https://github.com/rust-lang/simpleinfra/blob/b7ddd5e6bec8a93ec30510cdddec02c5666fefe9/terragrunt/accounts/ci-prod/ci-runners/terragrunt.hcl#L2 + - &job-linux-36c-codebuild + free_disk: true + codebuild: true + os: codebuild-ubuntu-22-36c-$github.run_id-$github.run_attempt + <<: *base-job + envs: env-x86_64-apple-tests: &env-x86_64-apple-tests SCRIPT: ./x.py check compiletest --set build.compiletest-use-stage0-libtest=true && ./x.py --stage 2 test --skip tests/ui --skip tests/rustdoc -- --exact @@ -151,7 +160,7 @@ auto: <<: *job-linux-4c - name: dist-arm-linux - <<: *job-linux-8c + <<: *job-linux-36c-codebuild - name: dist-armhf-linux <<: *job-linux-4c diff --git a/src/ci/scripts/free-disk-space.sh b/src/ci/scripts/free-disk-space.sh index 055a6ac2211..ad7ee136e9c 100755 --- a/src/ci/scripts/free-disk-space.sh +++ b/src/ci/scripts/free-disk-space.sh @@ -14,6 +14,17 @@ isX86() { fi } +# Check if we're on a GitHub hosted runner. +# In aws codebuild, the variable RUNNER_ENVIRONMENT is "self-hosted". +isGitHubRunner() { + # `:-` means "use the value of RUNNER_ENVIRONMENT if it exists, otherwise use an empty string". + if [[ "${RUNNER_ENVIRONMENT:-}" == "github-hosted" ]]; then + return 0 + else + return 1 + fi +} + # print a line of the specified character printSeparationLine() { for ((i = 0; i < 80; i++)); do @@ -32,7 +43,7 @@ getAvailableSpace() { # make Kb human readable (assume the input is Kb) # REF: https://unix.stackexchange.com/a/44087/60849 formatByteCount() { - numfmt --to=iec-i --suffix=B --padding=7 "$1"'000' + numfmt --to=iec-i --suffix=B --padding=7 "${1}000" } # macro to output saved space @@ -45,6 +56,11 @@ printSavedSpace() { after=$(getAvailableSpace) local saved=$((after - before)) + if [ "$saved" -lt 0 ]; then + echo "::warning::Saved space is negative: $saved. Using '0' as saved space." + saved=0 + fi + echo "" printSeparationLine "*" if [ -n "${title}" ]; then @@ -118,10 +134,14 @@ removeUnusedFilesAndDirs() { # Azure "/opt/az" "/usr/share/az_"* + ) + if [ -n "${AGENT_TOOLSDIRECTORY:-}" ]; then # Environment variable set by GitHub Actions - "$AGENT_TOOLSDIRECTORY" - ) + to_remove+=( + "${AGENT_TOOLSDIRECTORY}" + ) + fi for element in "${to_remove[@]}"; do if [ ! -e "$element" ]; then @@ -155,20 +175,25 @@ cleanPackages() { '^dotnet-.*' '^llvm-.*' '^mongodb-.*' - 'azure-cli' 'firefox' 'libgl1-mesa-dri' 'mono-devel' 'php.*' ) - if isX86; then + if isGitHubRunner; then packages+=( - 'google-chrome-stable' - 'google-cloud-cli' - 'google-cloud-sdk' - 'powershell' + azure-cli ) + + if isX86; then + packages+=( + 'google-chrome-stable' + 'google-cloud-cli' + 'google-cloud-sdk' + 'powershell' + ) + fi fi sudo apt-get -qq remove -y --fix-missing "${packages[@]}" diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index dd333f98f96..67fa25f2228 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -c02a4f0852e6665cf3df3867982021383f5615df +deb947971c8748f5c6203548ce4af9022f21eaf0 diff --git a/src/doc/rustc-dev-guide/src/tests/minicore.md b/src/doc/rustc-dev-guide/src/tests/minicore.md index e4853b6d40e..507b259e027 100644 --- a/src/doc/rustc-dev-guide/src/tests/minicore.md +++ b/src/doc/rustc-dev-guide/src/tests/minicore.md @@ -6,25 +6,37 @@ ui/codegen/assembly test suites. It provides `core` stubs for tests that need to build for cross-compiled targets but do not need/want to run. +<div class="warning"> +Please note that [`minicore`] is only intended for `core` items, and explicitly +**not** `std` or `alloc` items because `core` items are applicable to a wider +range of tests. +</div> + A test can use [`minicore`] by specifying the `//@ add-core-stubs` directive. Then, mark the test with `#![feature(no_core)]` + `#![no_std]` + `#![no_core]`. Due to Edition 2015 extern prelude rules, you will probably need to declare `minicore` as an extern crate. +## Implied compiler flags + Due to the `no_std` + `no_core` nature of these tests, `//@ add-core-stubs` implies and requires that the test will be built with `-C panic=abort`. -Unwinding panics are not supported. +**Unwinding panics are not supported.** + +Tests will also be built with `-C force-unwind-tables=yes` to preserve CFI +directives in assembly tests. + +TL;DR: `//@ add-core-stubs` implies two compiler flags: + +1. `-C panic=abort` +2. `-C force-unwind-tables=yes` + +## Adding more `core` stubs If you find a `core` item to be missing from the [`minicore`] stub, consider adding it to the test auxiliary if it's likely to be used or is already needed by more than one test. -<div class="warning"> -Please note that [`minicore`] is only intended for `core` items, and explicitly -**not** `std` or `alloc` items because `core` items are applicable to a wider -range of tests. -</div> - ## Example codegen test that uses `minicore` ```rust,no_run diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 9870e5011eb..50c7ae3ef8d 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -34,7 +34,7 @@ target | notes -------|------- [`aarch64-apple-darwin`](platform-support/apple-darwin.md) | ARM64 macOS (11.0+, Big Sur+) `aarch64-unknown-linux-gnu` | ARM64 Linux (kernel 4.1, glibc 2.17+) -`i686-pc-windows-msvc` | 32-bit MSVC (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] +`i686-pc-windows-msvc` | 32-bit MSVC (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] [^win32-msvc-alignment] `i686-unknown-linux-gnu` | 32-bit Linux (kernel 3.2+, glibc 2.17+, Pentium 4) [^x86_32-floats-return-ABI] [`x86_64-apple-darwin`](platform-support/apple-darwin.md) | 64-bit macOS (10.12+, Sierra+) [`x86_64-pc-windows-gnu`](platform-support/windows-gnu.md) | 64-bit MinGW (Windows 10+, Windows Server 2016+) @@ -43,6 +43,8 @@ target | notes [^x86_32-floats-return-ABI]: Due to limitations of the C ABI, floating-point support on `i686` targets is non-compliant: floating-point return values are passed via an x87 register, so NaN payload bits can be lost. Functions with the default Rust ABI are not affected. See [issue #115567][x86-32-float-return-issue]. +[^win32-msvc-alignment]: Due to non-standard behavior of MSVC, native C code on this target can cause types with an alignment of more than 4 bytes to be incorrectly aligned to only 4 bytes (this affects, e.g., `u64` and `i64`). Rust applies some mitigations to reduce the impact of this issue, but this can still cause unsoundness due to unsafe code that (correctly) assumes that references are always properly aligned. See [issue #112480](https://github.com/rust-lang/rust/issues/112480). + [77071]: https://github.com/rust-lang/rust/issues/77071 [x86-32-float-return-issue]: https://github.com/rust-lang/rust/issues/115567 @@ -95,7 +97,7 @@ target | notes [`armv7-unknown-linux-ohos`](platform-support/openharmony.md) | Armv7-A OpenHarmony [`loongarch64-unknown-linux-gnu`](platform-support/loongarch-linux.md) | LoongArch64 Linux, LP64D ABI (kernel 5.19, glibc 2.36) [`loongarch64-unknown-linux-musl`](platform-support/loongarch-linux.md) | LoongArch64 Linux, LP64D ABI (kernel 5.19, musl 1.2.5) -[`i686-pc-windows-gnu`](platform-support/windows-gnu.md) | 32-bit MinGW (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] +[`i686-pc-windows-gnu`](platform-support/windows-gnu.md) | 32-bit MinGW (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] [^win32-msvc-alignment] `powerpc-unknown-linux-gnu` | PowerPC Linux (kernel 3.2, glibc 2.17) `powerpc64-unknown-linux-gnu` | PPC64 Linux (kernel 3.2, glibc 2.17) [`powerpc64le-unknown-linux-gnu`](platform-support/powerpc64le-unknown-linux-gnu.md) | PPC64LE Linux (kernel 3.10, glibc 2.17) @@ -169,7 +171,7 @@ target | std | notes [`i686-pc-windows-gnullvm`](platform-support/windows-gnullvm.md) | ✓ | 32-bit x86 MinGW (Windows 10+, Pentium 4), LLVM ABI [^x86_32-floats-return-ABI] [`i686-unknown-freebsd`](platform-support/freebsd.md) | ✓ | 32-bit x86 FreeBSD (Pentium 4) [^x86_32-floats-return-ABI] `i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.3 (Pentium 4) [^x86_32-floats-return-ABI] -[`i686-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 32-bit UEFI (Pentium 4, softfloat) +[`i686-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 32-bit UEFI (Pentium 4, softfloat) [^win32-msvc-alignment] [`loongarch64-unknown-none`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64D ABI) [`loongarch64-unknown-none-softfloat`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64S ABI) [`nvptx64-nvidia-cuda`](platform-support/nvptx64-nvidia-cuda.md) | * | --emit=asm generates PTX code that [runs on NVIDIA GPUs] @@ -317,9 +319,9 @@ target | std | host | notes [`i686-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD/i386 (Pentium 4) [^x86_32-floats-return-ABI] [`i686-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | 32-bit OpenBSD (Pentium 4) [^x86_32-floats-return-ABI] `i686-uwp-windows-gnu` | ✓ | | [^x86_32-floats-return-ABI] -[`i686-uwp-windows-msvc`](platform-support/uwp-windows-msvc.md) | ✓ | | [^x86_32-floats-return-ABI] +[`i686-uwp-windows-msvc`](platform-support/uwp-windows-msvc.md) | ✓ | | [^x86_32-floats-return-ABI] [^win32-msvc-alignment] [`i686-win7-windows-gnu`](platform-support/win7-windows-gnu.md) | ✓ | | 32-bit Windows 7 support [^x86_32-floats-return-ABI] -[`i686-win7-windows-msvc`](platform-support/win7-windows-msvc.md) | ✓ | | 32-bit Windows 7 support [^x86_32-floats-return-ABI] +[`i686-win7-windows-msvc`](platform-support/win7-windows-msvc.md) | ✓ | | 32-bit Windows 7 support [^x86_32-floats-return-ABI] [^win32-msvc-alignment] [`i686-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | [^x86_32-floats-return-ABI] [`loongarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | | LoongArch64 OpenHarmony [`m68k-unknown-linux-gnu`](platform-support/m68k-unknown-linux-gnu.md) | ? | | Motorola 680x0 Linux diff --git a/src/doc/rustc/src/platform-support/x86_64-pc-cygwin.md b/src/doc/rustc/src/platform-support/x86_64-pc-cygwin.md index 60aaa371bbc..e98aa996a6e 100644 --- a/src/doc/rustc/src/platform-support/x86_64-pc-cygwin.md +++ b/src/doc/rustc/src/platform-support/x86_64-pc-cygwin.md @@ -20,8 +20,9 @@ The `target_os` of the target is `cygwin`, and it is `unix`. ## Building the target -For cross-compilation you want LLVM with [llvm/llvm-project#121439 (merged)](https://github.com/llvm/llvm-project/pull/121439) applied to fix the LLVM codegen on importing external global variables from DLLs. -No native builds on Cygwin now. It should be possible theoretically though, but might need a lot of patches. +For cross-compilation you want LLVM at least 20.1.0-rc1. +No native builds on Cygwin now. +The tracking issue for host tools on Cygwin is [#137819](https://github.com/rust-lang/rust/issues/137819). ## Building Rust programs diff --git a/src/doc/rustdoc/src/command-line-arguments.md b/src/doc/rustdoc/src/command-line-arguments.md index 872592d669d..b55ddf6e0e1 100644 --- a/src/doc/rustdoc/src/command-line-arguments.md +++ b/src/doc/rustdoc/src/command-line-arguments.md @@ -222,6 +222,28 @@ For more, see [the chapter on documentation tests](write-documentation/documenta See also `--test`. +## `--test-runtool`, `--test-runtool-arg`: program to run tests with; args to pass to it + +A doctest wrapper program can be specified with the `--test-runtool` flag. +Rustdoc will execute that wrapper instead of the doctest executable when +running tests. The first arguments to the wrapper will be any arguments +specified with the `--test-runtool-arg` flag, followed by the path to the +doctest executable to run. + +Using these options looks like this: + +```bash +$ rustdoc src/lib.rs --test-runtool path/to/runner --test-runtool-arg --do-thing --test-runtool-arg --do-other-thing +``` + +For example, if you want to run your doctests under valgrind you might run: + +```bash +$ rustdoc src/lib.rs --test-runtool valgrind +``` + +Another use case would be to run a test inside an emulator, or through a Virtual Machine. + ## `--target`: generate documentation for the specified target triple Using this flag looks like this: diff --git a/src/doc/rustdoc/src/read-documentation/search.md b/src/doc/rustdoc/src/read-documentation/search.md index bace2f5f953..5635f68b1b3 100644 --- a/src/doc/rustdoc/src/read-documentation/search.md +++ b/src/doc/rustdoc/src/read-documentation/search.md @@ -89,7 +89,7 @@ the standard library and functions that are included in the results list: ### Non-functions in type-based search Certain items that are not functions are treated as though they -were a semantically equivelent function. +were a semantically equivalent function. For example, struct fields are treated as though they were getter methods. This means that a search for `CpuidResult -> u32` will show diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index d4ff69a9933..69e5a5adbec 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -631,60 +631,6 @@ The generated output (formatted) will look like this: `--output-format html` has no effect, as the default output is HTML. This is accepted on stable, even though the other options for this flag aren't. -## `--enable-per-target-ignores`: allow `ignore-foo` style filters for doctests - - * Tracking issue: [#64245](https://github.com/rust-lang/rust/issues/64245) - -Using this flag looks like this: - -```bash -$ rustdoc src/lib.rs -Z unstable-options --enable-per-target-ignores -``` - -This flag allows you to tag doctests with compiletest style `ignore-foo` filters that prevent -rustdoc from running that test if the target triple string contains foo. For example: - -```rust -///```ignore-foo,ignore-bar -///assert!(2 == 2); -///``` -struct Foo; -``` - -This will not be run when the build target is `super-awesome-foo` or `less-bar-awesome`. -If the flag is not enabled, then rustdoc will consume the filter, but do nothing with it, and -the above example will be run for all targets. -If you want to preserve backwards compatibility for older versions of rustdoc, you can use - -```rust -///```ignore,ignore-foo -///assert!(2 == 2); -///``` -struct Foo; -``` - -In older versions, this will be ignored on all targets, but on newer versions `ignore-gnu` will -override `ignore`. - -## `--runtool`, `--runtool-arg`: program to run tests with; args to pass to it - - * Tracking issue: [#64245](https://github.com/rust-lang/rust/issues/64245) - -Using these options looks like this: - -```bash -$ rustdoc src/lib.rs -Z unstable-options --runtool runner --runtool-arg --do-thing --runtool-arg --do-other-thing -``` - -These options can be used to run the doctest under a program, and also pass arguments to -that program. For example, if you want to run your doctests under valgrind you might run - -```bash -$ rustdoc src/lib.rs -Z unstable-options --runtool valgrind -``` - -Another use case would be to run a test inside an emulator, or through a Virtual Machine. - ## `--with-examples`: include examples of uses of items as documentation * Tracking issue: [#88791](https://github.com/rust-lang/rust/issues/88791) diff --git a/src/doc/rustdoc/src/write-documentation/documentation-tests.md b/src/doc/rustdoc/src/write-documentation/documentation-tests.md index b921f677857..077b02d603d 100644 --- a/src/doc/rustdoc/src/write-documentation/documentation-tests.md +++ b/src/doc/rustdoc/src/write-documentation/documentation-tests.md @@ -427,6 +427,43 @@ should not be merged with the others. So the previous code should use it: In this case, it means that the line information will not change if you add/remove other doctests. +### Ignoring targets + +Attributes starting with `ignore-` can be used to ignore doctests for specific +targets. For example, `ignore-x86_64` will avoid building doctests when the +target name contains `x86_64`. + +```rust +/// ```ignore-x86_64 +/// assert!(2 == 2); +/// ``` +struct Foo; +``` + +This doctest will not be built for targets such as `x86_64-unknown-linux-gnu`. + +Multiple ignore attributes can be specified to ignore multiple targets: + +```rust +/// ```ignore-x86_64,ignore-windows +/// assert!(2 == 2); +/// ``` +struct Foo; +``` + +If you want to preserve backwards compatibility for older versions of rustdoc, +you can specify both `ignore` and `ignore-`, such as: + +```rust +/// ```ignore,ignore-x86_64 +/// assert!(2 == 2); +/// ``` +struct Foo; +``` + +In older versions, this will be ignored on all targets, but starting with +version CURRENT_RUSTC_VERSION, `ignore-x86_64` will override `ignore`. + ### Custom CSS classes for code blocks ```rust diff --git a/src/doc/unstable-book/src/library-features/concat-idents.md b/src/doc/unstable-book/src/library-features/concat-idents.md index 4366172fb99..8a38d155e3d 100644 --- a/src/doc/unstable-book/src/library-features/concat-idents.md +++ b/src/doc/unstable-book/src/library-features/concat-idents.md @@ -2,7 +2,10 @@ The tracking issue for this feature is: [#29599] +This feature is deprecated, to be replaced by [`macro_metavar_expr_concat`]. + [#29599]: https://github.com/rust-lang/rust/issues/29599 +[`macro_metavar_expr_concat`]: https://github.com/rust-lang/rust/issues/124225 ------------------------ diff --git a/src/gcc b/src/gcc -Subproject 13cc8243226a9028bb08ab6c5e1c5fe6d533bcd +Subproject 0ea98a1365b81f7488073512c850e8ee951a4af diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 23a2bcd9011..4ef73ff48ed 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -124,13 +124,9 @@ pub(crate) struct Options { /// temporary directory if not set. pub(crate) persist_doctests: Option<PathBuf>, /// Runtool to run doctests with - pub(crate) runtool: Option<String>, + pub(crate) test_runtool: Option<String>, /// Arguments to pass to the runtool - pub(crate) runtool_args: Vec<String>, - /// Whether to allow ignoring doctests on a per-target basis - /// For example, using ignore-foo to ignore running the doctest on any target that - /// contains "foo" as a substring - pub(crate) enable_per_target_ignores: bool, + pub(crate) test_runtool_args: Vec<String>, /// Do not run doctests, compile them if should_test is active. pub(crate) no_run: bool, /// What sources are being mapped. @@ -215,9 +211,8 @@ impl fmt::Debug for Options { .field("persist_doctests", &self.persist_doctests) .field("show_coverage", &self.show_coverage) .field("crate_version", &self.crate_version) - .field("runtool", &self.runtool) - .field("runtool_args", &self.runtool_args) - .field("enable-per-target-ignores", &self.enable_per_target_ignores) + .field("test_runtool", &self.test_runtool) + .field("test_runtool_args", &self.test_runtool_args) .field("run_check", &self.run_check) .field("no_run", &self.no_run) .field("test_builder_wrappers", &self.test_builder_wrappers) @@ -779,9 +774,8 @@ impl Options { let unstable_opts_strs = matches.opt_strs("Z"); let lib_strs = matches.opt_strs("L"); let extern_strs = matches.opt_strs("extern"); - let runtool = matches.opt_str("runtool"); - let runtool_args = matches.opt_strs("runtool-arg"); - let enable_per_target_ignores = matches.opt_present("enable-per-target-ignores"); + let test_runtool = matches.opt_str("test-runtool"); + let test_runtool_args = matches.opt_strs("test-runtool-arg"); let document_private = matches.opt_present("document-private-items"); let document_hidden = matches.opt_present("document-hidden-items"); let run_check = matches.opt_present("check"); @@ -843,9 +837,8 @@ impl Options { crate_version, test_run_directory, persist_doctests, - runtool, - runtool_args, - enable_per_target_ignores, + test_runtool, + test_runtool_args, test_builder, run_check, no_run, diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 88eaa52c6de..829a9ca6e7d 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -219,11 +219,9 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions let crate_name = tcx.crate_name(LOCAL_CRATE).to_string(); let crate_attrs = tcx.hir_attrs(CRATE_HIR_ID); let opts = scrape_test_config(crate_name, crate_attrs, args_path); - let enable_per_target_ignores = options.enable_per_target_ignores; let hir_collector = HirCollector::new( ErrorCodes::from(compiler.sess.opts.unstable_features.is_nightly_build()), - enable_per_target_ignores, tcx, ); let tests = hir_collector.collect_crate(); @@ -782,10 +780,10 @@ fn run_test( let mut cmd; let output_file = make_maybe_absolute_path(output_file); - if let Some(tool) = &rustdoc_options.runtool { + if let Some(tool) = &rustdoc_options.test_runtool { let tool = make_maybe_absolute_path(tool.into()); cmd = Command::new(tool); - cmd.args(&rustdoc_options.runtool_args); + cmd.args(&rustdoc_options.test_runtool_args); cmd.arg(&output_file); } else { cmd = Command::new(&output_file); diff --git a/src/librustdoc/doctest/markdown.rs b/src/librustdoc/doctest/markdown.rs index a0d39ce749d..497a8d7c4a7 100644 --- a/src/librustdoc/doctest/markdown.rs +++ b/src/librustdoc/doctest/markdown.rs @@ -104,13 +104,7 @@ pub(crate) fn test(input: &Input, options: Options) -> Result<(), String> { }; let codes = ErrorCodes::from(options.unstable_features.is_nightly_build()); - find_testable_code( - &input_str, - &mut md_collector, - codes, - options.enable_per_target_ignores, - None, - ); + find_testable_code(&input_str, &mut md_collector, codes, None); let mut collector = CreateRunnableDocTests::new(options.clone(), opts); md_collector.tests.into_iter().for_each(|t| collector.add_test(t)); diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index 784d628805d..39a4f23560a 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -131,7 +131,22 @@ mod __doctest_mod {{ .output() .expect(\"failed to run command\"); if !out.status.success() {{ - eprint!(\"{{}}\", String::from_utf8_lossy(&out.stderr)); + if let Some(code) = out.status.code() {{ + eprintln!(\"Test executable failed (exit status: {{code}}).\"); + }} else {{ + eprintln!(\"Test executable failed (terminated by signal).\"); + }} + if !out.stdout.is_empty() || !out.stderr.is_empty() {{ + eprintln!(); + }} + if !out.stdout.is_empty() {{ + eprintln!(\"stdout:\"); + eprintln!(\"{{}}\", String::from_utf8_lossy(&out.stdout)); + }} + if !out.stderr.is_empty() {{ + eprintln!(\"stderr:\"); + eprintln!(\"{{}}\", String::from_utf8_lossy(&out.stderr)); + }} ExitCode::FAILURE }} else {{ ExitCode::SUCCESS diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs index be2dea641d7..43dcfab880b 100644 --- a/src/librustdoc/doctest/rust.rs +++ b/src/librustdoc/doctest/rust.rs @@ -63,19 +63,18 @@ impl DocTestVisitor for RustCollector { pub(super) struct HirCollector<'tcx> { codes: ErrorCodes, tcx: TyCtxt<'tcx>, - enable_per_target_ignores: bool, collector: RustCollector, } impl<'tcx> HirCollector<'tcx> { - pub fn new(codes: ErrorCodes, enable_per_target_ignores: bool, tcx: TyCtxt<'tcx>) -> Self { + pub fn new(codes: ErrorCodes, tcx: TyCtxt<'tcx>) -> Self { let collector = RustCollector { source_map: tcx.sess.psess.clone_source_map(), cur_path: vec![], position: DUMMY_SP, tests: vec![], }; - Self { codes, enable_per_target_ignores, tcx, collector } + Self { codes, tcx, collector } } pub fn collect_crate(mut self) -> Vec<ScrapedDocTest> { @@ -131,7 +130,6 @@ impl HirCollector<'_> { &doc, &mut self.collector, self.codes, - self.enable_per_target_ignores, Some(&crate::html::markdown::ExtraInfo::new(self.tcx, def_id, span)), ); } diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 44134bda5ea..fc46293e7ea 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -246,7 +246,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> { match kind { CodeBlockKind::Fenced(ref lang) => { let parse_result = - LangString::parse_without_check(lang, self.check_error_codes, false); + LangString::parse_without_check(lang, self.check_error_codes); if !parse_result.rust { let added_classes = parse_result.added_classes; let lang_string = if let Some(lang) = parse_result.unknown.first() { @@ -707,17 +707,15 @@ pub(crate) fn find_testable_code<T: doctest::DocTestVisitor>( doc: &str, tests: &mut T, error_codes: ErrorCodes, - enable_per_target_ignores: bool, extra_info: Option<&ExtraInfo<'_>>, ) { - find_codes(doc, tests, error_codes, enable_per_target_ignores, extra_info, false) + find_codes(doc, tests, error_codes, extra_info, false) } pub(crate) fn find_codes<T: doctest::DocTestVisitor>( doc: &str, tests: &mut T, error_codes: ErrorCodes, - enable_per_target_ignores: bool, extra_info: Option<&ExtraInfo<'_>>, include_non_rust: bool, ) { @@ -733,12 +731,7 @@ pub(crate) fn find_codes<T: doctest::DocTestVisitor>( if lang.is_empty() { Default::default() } else { - LangString::parse( - lang, - error_codes, - enable_per_target_ignores, - extra_info, - ) + LangString::parse(lang, error_codes, extra_info) } } CodeBlockKind::Indented => Default::default(), @@ -1162,18 +1155,13 @@ impl Default for LangString { } impl LangString { - fn parse_without_check( - string: &str, - allow_error_code_check: ErrorCodes, - enable_per_target_ignores: bool, - ) -> Self { - Self::parse(string, allow_error_code_check, enable_per_target_ignores, None) + fn parse_without_check(string: &str, allow_error_code_check: ErrorCodes) -> Self { + Self::parse(string, allow_error_code_check, None) } fn parse( string: &str, allow_error_code_check: ErrorCodes, - enable_per_target_ignores: bool, extra: Option<&ExtraInfo<'_>>, ) -> Self { let allow_error_code_check = allow_error_code_check.as_bool(); @@ -1203,10 +1191,8 @@ impl LangString { LangStringToken::LangToken(x) if let Some(ignore) = x.strip_prefix("ignore-") => { - if enable_per_target_ignores { - ignores.push(ignore.to_owned()); - seen_rust_tags = !seen_other_tags; - } + ignores.push(ignore.to_owned()); + seen_rust_tags = !seen_other_tags; } LangStringToken::LangToken("rust") => { data.rust = true; @@ -1968,7 +1954,7 @@ pub(crate) fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec<Rust let lang_string = if syntax.is_empty() { Default::default() } else { - LangString::parse(syntax, ErrorCodes::Yes, false, Some(extra_info)) + LangString::parse(syntax, ErrorCodes::Yes, Some(extra_info)) }; if !lang_string.rust { continue; diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs index bb42b877a2c..784d0c5d21e 100644 --- a/src/librustdoc/html/markdown/tests.rs +++ b/src/librustdoc/html/markdown/tests.rs @@ -49,7 +49,7 @@ fn test_unique_id() { fn test_lang_string_parse() { fn t(lg: LangString) { let s = &lg.original; - assert_eq!(LangString::parse(s, ErrorCodes::Yes, true, None), lg) + assert_eq!(LangString::parse(s, ErrorCodes::Yes, None), lg) } t(Default::default()); @@ -479,7 +479,7 @@ fn test_markdown_html_escape() { fn test_find_testable_code_line() { fn t(input: &str, expect: &[usize]) { let mut lines = Vec::<usize>::new(); - find_testable_code(input, &mut lines, ErrorCodes::No, false, None); + find_testable_code(input, &mut lines, ErrorCodes::No, None); assert_eq!(lines, expect); } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 7e17f09aecd..beaa6497b8c 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -2086,6 +2086,7 @@ fn render_impl( .split_summary_and_content() }) .unwrap_or((None, None)); + write!( w, "{}", @@ -2097,24 +2098,19 @@ fn render_impl( use_absolute, aliases, before_dox.as_deref(), + trait_.is_none() && impl_.items.is_empty(), ) )?; if toggled { w.write_str("</summary>")?; } - if before_dox.is_some() { - if trait_.is_none() && impl_.items.is_empty() { - w.write_str( - "<div class=\"item-info\">\ - <div class=\"stab empty-impl\">This impl block contains no items.</div>\ - </div>", - )?; - } - if let Some(after_dox) = after_dox { - write!(w, "<div class=\"docblock\">{after_dox}</div>")?; - } + if before_dox.is_some() + && let Some(after_dox) = after_dox + { + write!(w, "<div class=\"docblock\">{after_dox}</div>")?; } + if !default_impl_items.is_empty() || !impl_items.is_empty() { w.write_str("<div class=\"impl-items\">")?; close_tags.push("</div>"); @@ -2182,6 +2178,7 @@ fn render_impl_summary( // in documentation pages for trait with automatic implementations like "Send" and "Sync". aliases: &[String], doc: Option<&str>, + impl_is_empty: bool, ) -> impl fmt::Display { fmt::from_fn(move |w| { let inner_impl = i.inner_impl(); @@ -2237,6 +2234,13 @@ fn render_impl_summary( } if let Some(doc) = doc { + if impl_is_empty { + w.write_str( + "<div class=\"item-info\">\ + <div class=\"stab empty-impl\">This impl block contains no items.</div>\ + </div>", + )?; + } write!(w, "<div class=\"docblock\">{doc}</div>")?; } diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index a6dd06b76ea..19ac24a5d6e 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -2319,7 +2319,10 @@ details.toggle > summary:not(.hideme)::before { doc block while aligning it with the impl block items. */ .implementors-toggle > .docblock, /* We indent trait items as well. */ -#main-content > .methods > :not(.item-info) { +#main-content > .methods > :not(.item-info), +.impl > .item-info, +.impl > .docblock, +.impl + .docblock { margin-left: var(--impl-items-indent); } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 4fe5e13c3af..44bd96a7e45 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -507,28 +507,20 @@ fn opts() -> Vec<RustcOptGroup> { "", ), opt( - Unstable, - FlagMulti, - "", - "enable-per-target-ignores", - "parse ignore-foo for ignoring doctests on a per-target basis", - "", - ), - opt( - Unstable, + Stable, Opt, "", - "runtool", + "test-runtool", "", "The tool to run tests with when building for a different target than host", ), opt( - Unstable, + Stable, Multi, "", - "runtool-arg", + "test-runtool-arg", "", - "One (of possibly many) arguments to pass to the runtool", + "One argument (of possibly many) to pass to the runtool", ), opt( Unstable, diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs index 761282bde7c..c9f0baaaa4c 100644 --- a/src/librustdoc/passes/calculate_doc_coverage.rs +++ b/src/librustdoc/passes/calculate_doc_coverage.rs @@ -212,7 +212,7 @@ impl DocVisitor<'_> for CoverageCalculator<'_, '_> { let has_docs = !i.attrs.doc_strings.is_empty(); let mut tests = Tests { found_tests: 0 }; - find_testable_code(&i.doc_value(), &mut tests, ErrorCodes::No, false, None); + find_testable_code(&i.doc_value(), &mut tests, ErrorCodes::No, None); let has_doc_example = tests.found_tests != 0; let hir_id = DocContext::as_local_hir_id(self.ctx.tcx, i.item_id).unwrap(); diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs index 70dbb944d4c..8028afea363 100644 --- a/src/librustdoc/passes/check_doc_test_visibility.rs +++ b/src/librustdoc/passes/check_doc_test_visibility.rs @@ -122,7 +122,7 @@ pub(crate) fn look_for_tests(cx: &DocContext<'_>, dox: &str, item: &Item) { let mut tests = Tests { found_tests: 0 }; - find_testable_code(dox, &mut tests, ErrorCodes::No, false, None); + find_testable_code(dox, &mut tests, ErrorCodes::No, None); if tests.found_tests == 0 && cx.tcx.features().rustdoc_missing_doc_code_examples() { if should_have_doc_example(cx, item) { diff --git a/src/stage0 b/src/stage0 index 6e86501a72a..06080e3a8c1 100644 --- a/src/stage0 +++ b/src/stage0 @@ -2,7 +2,6 @@ dist_server=https://static.rust-lang.org artifacts_server=https://ci-artifacts.rust-lang.org/rustc-builds artifacts_with_llvm_assertions_server=https://ci-artifacts.rust-lang.org/rustc-builds-alt git_merge_commit_email=bors@rust-lang.org -git_repository=rust-lang/rust nightly_branch=master # The configuration above this comment is editable, and can be changed diff --git a/src/tools/bump-stage0/src/main.rs b/src/tools/bump-stage0/src/main.rs index d679084ae44..680437cce4f 100644 --- a/src/tools/bump-stage0/src/main.rs +++ b/src/tools/bump-stage0/src/main.rs @@ -61,7 +61,6 @@ impl Tool { artifacts_server, artifacts_with_llvm_assertions_server, git_merge_commit_email, - git_repository, nightly_branch, } = &self.config; @@ -72,7 +71,6 @@ impl Tool { artifacts_with_llvm_assertions_server )); file_content.push_str(&format!("git_merge_commit_email={}\n", git_merge_commit_email)); - file_content.push_str(&format!("git_repository={}\n", git_repository)); file_content.push_str(&format!("nightly_branch={}\n", nightly_branch)); file_content.push_str("\n"); diff --git a/src/tools/cargo b/src/tools/cargo -Subproject d811228b14ae2707323f37346aee3f4147e247e +Subproject 7918c7eb59614c39f1c4e27e99d557720976bdd diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions.fixed b/src/tools/clippy/tests/ui/blocks_in_conditions.fixed index c82276b358e..e696896538e 100644 --- a/src/tools/clippy/tests/ui/blocks_in_conditions.fixed +++ b/src/tools/clippy/tests/ui/blocks_in_conditions.fixed @@ -1,7 +1,13 @@ //@aux-build:proc_macro_attr.rs #![warn(clippy::blocks_in_conditions)] -#![allow(unused, clippy::needless_if, clippy::missing_transmute_annotations)] +#![allow( + unused, + unnecessary_transmutes, + clippy::let_and_return, + clippy::needless_if, + clippy::missing_transmute_annotations +)] #![warn(clippy::nonminimal_bool)] macro_rules! blocky { diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions.rs b/src/tools/clippy/tests/ui/blocks_in_conditions.rs index 6a4a7c62106..8c8f3249b8a 100644 --- a/src/tools/clippy/tests/ui/blocks_in_conditions.rs +++ b/src/tools/clippy/tests/ui/blocks_in_conditions.rs @@ -1,7 +1,13 @@ //@aux-build:proc_macro_attr.rs #![warn(clippy::blocks_in_conditions)] -#![allow(unused, clippy::needless_if, clippy::missing_transmute_annotations)] +#![allow( + unused, + unnecessary_transmutes, + clippy::let_and_return, + clippy::needless_if, + clippy::missing_transmute_annotations +)] #![warn(clippy::nonminimal_bool)] macro_rules! blocky { diff --git a/src/tools/clippy/tests/ui/blocks_in_conditions.stderr b/src/tools/clippy/tests/ui/blocks_in_conditions.stderr index e57eca5dcee..41ff59c683e 100644 --- a/src/tools/clippy/tests/ui/blocks_in_conditions.stderr +++ b/src/tools/clippy/tests/ui/blocks_in_conditions.stderr @@ -1,5 +1,5 @@ error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> tests/ui/blocks_in_conditions.rs:25:5 + --> tests/ui/blocks_in_conditions.rs:31:5 | LL | / if { LL | | @@ -20,13 +20,13 @@ LL ~ }; if res { | error: omit braces around single expression condition - --> tests/ui/blocks_in_conditions.rs:37:8 + --> tests/ui/blocks_in_conditions.rs:43:8 | LL | if { true } { 6 } else { 10 } | ^^^^^^^^ help: try: `true` error: this boolean expression can be simplified - --> tests/ui/blocks_in_conditions.rs:43:8 + --> tests/ui/blocks_in_conditions.rs:49:8 | LL | if true && x == 3 { 6 } else { 10 } | ^^^^^^^^^^^^^^ help: try: `x == 3` diff --git a/src/tools/clippy/tests/ui/crashes/ice-1782.rs b/src/tools/clippy/tests/ui/crashes/ice-1782.rs index 4a1886c08af..776b0a93bf7 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-1782.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-1782.rs @@ -1,6 +1,6 @@ //@ check-pass -#![allow(dead_code, unused_variables, invalid_null_arguments)] +#![allow(dead_code, unused_variables, invalid_null_arguments, unnecessary_transmutes)] #![allow(clippy::unnecessary_cast, clippy::missing_transmute_annotations)] /// Should not trigger an ICE in `SpanlessEq` / `consts::constant` diff --git a/src/tools/clippy/tests/ui/transmute.rs b/src/tools/clippy/tests/ui/transmute.rs index 8c8674ac356..2b8b6c539ad 100644 --- a/src/tools/clippy/tests/ui/transmute.rs +++ b/src/tools/clippy/tests/ui/transmute.rs @@ -3,6 +3,7 @@ #![allow( dead_code, clippy::borrow_as_ptr, + unnecessary_transmutes, clippy::needless_lifetimes, clippy::missing_transmute_annotations )] diff --git a/src/tools/clippy/tests/ui/transmute.stderr b/src/tools/clippy/tests/ui/transmute.stderr index 4219e09d2ab..1bb70151965 100644 --- a/src/tools/clippy/tests/ui/transmute.stderr +++ b/src/tools/clippy/tests/ui/transmute.stderr @@ -1,5 +1,5 @@ error: transmute from a reference to a pointer - --> tests/ui/transmute.rs:32:27 + --> tests/ui/transmute.rs:33:27 | LL | let _: *const T = core::mem::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T` @@ -8,61 +8,61 @@ LL | let _: *const T = core::mem::transmute(t); = help: to override `-D warnings` add `#[allow(clippy::useless_transmute)]` error: transmute from a reference to a pointer - --> tests/ui/transmute.rs:35:25 + --> tests/ui/transmute.rs:36:25 | LL | let _: *mut T = core::mem::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T` error: transmute from a reference to a pointer - --> tests/ui/transmute.rs:38:27 + --> tests/ui/transmute.rs:39:27 | LL | let _: *const U = core::mem::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U` error: transmute from a type (`std::vec::Vec<i32>`) to itself - --> tests/ui/transmute.rs:46:27 + --> tests/ui/transmute.rs:47:27 | LL | let _: Vec<i32> = core::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec<i32>`) to itself - --> tests/ui/transmute.rs:49:27 + --> tests/ui/transmute.rs:50:27 | LL | let _: Vec<i32> = core::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec<i32>`) to itself - --> tests/ui/transmute.rs:52:27 + --> tests/ui/transmute.rs:53:27 | LL | let _: Vec<i32> = std::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec<i32>`) to itself - --> tests/ui/transmute.rs:55:27 + --> tests/ui/transmute.rs:56:27 | LL | let _: Vec<i32> = std::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec<i32>`) to itself - --> tests/ui/transmute.rs:58:27 + --> tests/ui/transmute.rs:59:27 | LL | let _: Vec<i32> = my_transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^ error: transmute from an integer to a pointer - --> tests/ui/transmute.rs:61:31 + --> tests/ui/transmute.rs:62:31 | LL | let _: *const usize = std::mem::transmute(5_isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `5_isize as *const usize` error: transmute from an integer to a pointer - --> tests/ui/transmute.rs:66:31 + --> tests/ui/transmute.rs:67:31 | LL | let _: *const usize = std::mem::transmute(1 + 1usize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(1 + 1usize) as *const usize` error: transmute from a type (`*const Usize`) to the type that it points to (`Usize`) - --> tests/ui/transmute.rs:98:24 + --> tests/ui/transmute.rs:99:24 | LL | let _: Usize = core::mem::transmute(int_const_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -71,25 +71,25 @@ LL | let _: Usize = core::mem::transmute(int_const_ptr); = help: to override `-D warnings` add `#[allow(clippy::crosspointer_transmute)]` error: transmute from a type (`*mut Usize`) to the type that it points to (`Usize`) - --> tests/ui/transmute.rs:101:24 + --> tests/ui/transmute.rs:102:24 | LL | let _: Usize = core::mem::transmute(int_mut_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*const Usize`) - --> tests/ui/transmute.rs:104:31 + --> tests/ui/transmute.rs:105:31 | LL | let _: *const Usize = core::mem::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`) - --> tests/ui/transmute.rs:107:29 + --> tests/ui/transmute.rs:108:29 | LL | let _: *mut Usize = core::mem::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a `u8` to a `bool` - --> tests/ui/transmute.rs:114:28 + --> tests/ui/transmute.rs:115:28 | LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0` @@ -98,7 +98,7 @@ LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_bool)]` error: transmute from a `u16` to a `f16` - --> tests/ui/transmute.rs:121:31 + --> tests/ui/transmute.rs:122:31 | LL | let _: f16 = unsafe { std::mem::transmute(0_u16) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f16::from_bits(0_u16)` @@ -107,97 +107,97 @@ LL | let _: f16 = unsafe { std::mem::transmute(0_u16) }; = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_float)]` error: transmute from a `i16` to a `f16` - --> tests/ui/transmute.rs:124:31 + --> tests/ui/transmute.rs:125:31 | LL | let _: f16 = unsafe { std::mem::transmute(0_i16) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f16::from_bits(0_i16 as u16)` error: transmute from a `u32` to a `f32` - --> tests/ui/transmute.rs:127:31 + --> tests/ui/transmute.rs:128:31 | LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` error: transmute from a `i32` to a `f32` - --> tests/ui/transmute.rs:130:31 + --> tests/ui/transmute.rs:131:31 | LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` error: transmute from a `u64` to a `f64` - --> tests/ui/transmute.rs:133:31 + --> tests/ui/transmute.rs:134:31 | LL | let _: f64 = unsafe { std::mem::transmute(0_u64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_u64)` error: transmute from a `i64` to a `f64` - --> tests/ui/transmute.rs:136:31 + --> tests/ui/transmute.rs:137:31 | LL | let _: f64 = unsafe { std::mem::transmute(0_i64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)` error: transmute from a `u128` to a `f128` - --> tests/ui/transmute.rs:139:32 + --> tests/ui/transmute.rs:140:32 | LL | let _: f128 = unsafe { std::mem::transmute(0_u128) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f128::from_bits(0_u128)` error: transmute from a `i128` to a `f128` - --> tests/ui/transmute.rs:142:32 + --> tests/ui/transmute.rs:143:32 | LL | let _: f128 = unsafe { std::mem::transmute(0_i128) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f128::from_bits(0_i128 as u128)` error: transmute from a `u16` to a `f16` - --> tests/ui/transmute.rs:147:39 + --> tests/ui/transmute.rs:148:39 | LL | const VALUE16: f16 = unsafe { std::mem::transmute(0_u16) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f16::from_bits(0_u16)` error: transmute from a `u32` to a `f32` - --> tests/ui/transmute.rs:150:39 + --> tests/ui/transmute.rs:151:39 | LL | const VALUE32: f32 = unsafe { std::mem::transmute(0_u32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` error: transmute from a `i64` to a `f64` - --> tests/ui/transmute.rs:153:39 + --> tests/ui/transmute.rs:154:39 | LL | const VALUE64: f64 = unsafe { std::mem::transmute(0_i64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)` error: transmute from a `i128` to a `f128` - --> tests/ui/transmute.rs:156:41 + --> tests/ui/transmute.rs:157:41 | LL | const VALUE128: f128 = unsafe { std::mem::transmute(0_i128) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f128::from_bits(0_i128 as u128)` error: transmute from a `i16` to a `f16` - --> tests/ui/transmute.rs:160:22 + --> tests/ui/transmute.rs:161:22 | LL | unsafe { std::mem::transmute(v) } | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f16::from_bits(v as u16)` error: transmute from a `i32` to a `f32` - --> tests/ui/transmute.rs:165:22 + --> tests/ui/transmute.rs:166:22 | LL | unsafe { std::mem::transmute(v) } | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(v as u32)` error: transmute from a `u64` to a `f64` - --> tests/ui/transmute.rs:170:22 + --> tests/ui/transmute.rs:171:22 | LL | unsafe { std::mem::transmute(v) } | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(v)` error: transmute from a `u128` to a `f128` - --> tests/ui/transmute.rs:175:22 + --> tests/ui/transmute.rs:176:22 | LL | unsafe { std::mem::transmute(v) } | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f128::from_bits(v)` error: transmute from a `u8` to a `[u8; 1]` - --> tests/ui/transmute.rs:184:30 + --> tests/ui/transmute.rs:185:30 | LL | let _: [u8; 1] = std::mem::transmute(0u8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` @@ -206,121 +206,121 @@ LL | let _: [u8; 1] = std::mem::transmute(0u8); = help: to override `-D warnings` add `#[allow(clippy::transmute_num_to_bytes)]` error: transmute from a `u32` to a `[u8; 4]` - --> tests/ui/transmute.rs:187:30 + --> tests/ui/transmute.rs:188:30 | LL | let _: [u8; 4] = std::mem::transmute(0u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` error: transmute from a `u128` to a `[u8; 16]` - --> tests/ui/transmute.rs:190:31 + --> tests/ui/transmute.rs:191:31 | LL | let _: [u8; 16] = std::mem::transmute(0u128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` error: transmute from a `i8` to a `[u8; 1]` - --> tests/ui/transmute.rs:193:30 + --> tests/ui/transmute.rs:194:30 | LL | let _: [u8; 1] = std::mem::transmute(0i8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` error: transmute from a `i32` to a `[u8; 4]` - --> tests/ui/transmute.rs:196:30 + --> tests/ui/transmute.rs:197:30 | LL | let _: [u8; 4] = std::mem::transmute(0i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` error: transmute from a `i128` to a `[u8; 16]` - --> tests/ui/transmute.rs:199:31 + --> tests/ui/transmute.rs:200:31 | LL | let _: [u8; 16] = std::mem::transmute(0i128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` error: transmute from a `f16` to a `[u8; 2]` - --> tests/ui/transmute.rs:202:30 + --> tests/ui/transmute.rs:203:30 | LL | let _: [u8; 2] = std::mem::transmute(0.0f16); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f16.to_ne_bytes()` error: transmute from a `f32` to a `[u8; 4]` - --> tests/ui/transmute.rs:205:30 + --> tests/ui/transmute.rs:206:30 | LL | let _: [u8; 4] = std::mem::transmute(0.0f32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f32.to_ne_bytes()` error: transmute from a `f64` to a `[u8; 8]` - --> tests/ui/transmute.rs:208:30 + --> tests/ui/transmute.rs:209:30 | LL | let _: [u8; 8] = std::mem::transmute(0.0f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f64.to_ne_bytes()` error: transmute from a `f128` to a `[u8; 16]` - --> tests/ui/transmute.rs:211:31 + --> tests/ui/transmute.rs:212:31 | LL | let _: [u8; 16] = std::mem::transmute(0.0f128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f128.to_ne_bytes()` error: transmute from a `u8` to a `[u8; 1]` - --> tests/ui/transmute.rs:217:30 + --> tests/ui/transmute.rs:218:30 | LL | let _: [u8; 1] = std::mem::transmute(0u8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` error: transmute from a `u32` to a `[u8; 4]` - --> tests/ui/transmute.rs:220:30 + --> tests/ui/transmute.rs:221:30 | LL | let _: [u8; 4] = std::mem::transmute(0u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` error: transmute from a `u128` to a `[u8; 16]` - --> tests/ui/transmute.rs:223:31 + --> tests/ui/transmute.rs:224:31 | LL | let _: [u8; 16] = std::mem::transmute(0u128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` error: transmute from a `i8` to a `[u8; 1]` - --> tests/ui/transmute.rs:226:30 + --> tests/ui/transmute.rs:227:30 | LL | let _: [u8; 1] = std::mem::transmute(0i8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` error: transmute from a `i32` to a `[u8; 4]` - --> tests/ui/transmute.rs:229:30 + --> tests/ui/transmute.rs:230:30 | LL | let _: [u8; 4] = std::mem::transmute(0i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` error: transmute from a `i128` to a `[u8; 16]` - --> tests/ui/transmute.rs:232:31 + --> tests/ui/transmute.rs:233:31 | LL | let _: [u8; 16] = std::mem::transmute(0i128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` error: transmute from a `f16` to a `[u8; 2]` - --> tests/ui/transmute.rs:235:30 + --> tests/ui/transmute.rs:236:30 | LL | let _: [u8; 2] = std::mem::transmute(0.0f16); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f16.to_ne_bytes()` error: transmute from a `f32` to a `[u8; 4]` - --> tests/ui/transmute.rs:238:30 + --> tests/ui/transmute.rs:239:30 | LL | let _: [u8; 4] = std::mem::transmute(0.0f32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f32.to_ne_bytes()` error: transmute from a `f64` to a `[u8; 8]` - --> tests/ui/transmute.rs:241:30 + --> tests/ui/transmute.rs:242:30 | LL | let _: [u8; 8] = std::mem::transmute(0.0f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f64.to_ne_bytes()` error: transmute from a `f128` to a `[u8; 16]` - --> tests/ui/transmute.rs:244:31 + --> tests/ui/transmute.rs:245:31 | LL | let _: [u8; 16] = std::mem::transmute(0.0f128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f128.to_ne_bytes()` error: transmute from a `&[u8]` to a `&str` - --> tests/ui/transmute.rs:253:28 + --> tests/ui/transmute.rs:254:28 | LL | let _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(B).unwrap()` @@ -329,13 +329,13 @@ LL | let _: &str = unsafe { std::mem::transmute(B) }; = help: to override `-D warnings` add `#[allow(clippy::transmute_bytes_to_str)]` error: transmute from a `&mut [u8]` to a `&mut str` - --> tests/ui/transmute.rs:256:32 + --> tests/ui/transmute.rs:257:32 | LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` error: transmute from a `&[u8]` to a `&str` - --> tests/ui/transmute.rs:259:30 + --> tests/ui/transmute.rs:260:30 | LL | const _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_unchecked(B)` diff --git a/src/tools/clippy/tests/ui/transmute_float_to_int.fixed b/src/tools/clippy/tests/ui/transmute_float_to_int.fixed index 1f97b997eaa..844445907d7 100644 --- a/src/tools/clippy/tests/ui/transmute_float_to_int.fixed +++ b/src/tools/clippy/tests/ui/transmute_float_to_int.fixed @@ -1,5 +1,5 @@ #![warn(clippy::transmute_float_to_int)] -#![allow(clippy::missing_transmute_annotations)] +#![allow(clippy::missing_transmute_annotations, unnecessary_transmutes)] #![feature(f128)] #![feature(f16)] diff --git a/src/tools/clippy/tests/ui/transmute_float_to_int.rs b/src/tools/clippy/tests/ui/transmute_float_to_int.rs index 788a7e1026c..a1f3b15bbfe 100644 --- a/src/tools/clippy/tests/ui/transmute_float_to_int.rs +++ b/src/tools/clippy/tests/ui/transmute_float_to_int.rs @@ -1,5 +1,5 @@ #![warn(clippy::transmute_float_to_int)] -#![allow(clippy::missing_transmute_annotations)] +#![allow(clippy::missing_transmute_annotations, unnecessary_transmutes)] #![feature(f128)] #![feature(f16)] diff --git a/src/tools/clippy/tests/ui/transmute_int_to_char.fixed b/src/tools/clippy/tests/ui/transmute_int_to_char.fixed index b5425a2e9e8..28644aa9ebb 100644 --- a/src/tools/clippy/tests/ui/transmute_int_to_char.fixed +++ b/src/tools/clippy/tests/ui/transmute_int_to_char.fixed @@ -1,5 +1,5 @@ #![warn(clippy::transmute_int_to_char)] -#![allow(clippy::missing_transmute_annotations)] +#![allow(clippy::missing_transmute_annotations, unnecessary_transmutes)] fn int_to_char() { let _: char = unsafe { std::char::from_u32(0_u32).unwrap() }; diff --git a/src/tools/clippy/tests/ui/transmute_int_to_char.rs b/src/tools/clippy/tests/ui/transmute_int_to_char.rs index b24bb177c9f..8c83ecc8914 100644 --- a/src/tools/clippy/tests/ui/transmute_int_to_char.rs +++ b/src/tools/clippy/tests/ui/transmute_int_to_char.rs @@ -1,5 +1,5 @@ #![warn(clippy::transmute_int_to_char)] -#![allow(clippy::missing_transmute_annotations)] +#![allow(clippy::missing_transmute_annotations, unnecessary_transmutes)] fn int_to_char() { let _: char = unsafe { std::mem::transmute(0_u32) }; diff --git a/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.fixed b/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.fixed index e525751e306..e6e09a2be4b 100644 --- a/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.fixed +++ b/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.fixed @@ -1,7 +1,7 @@ #![no_std] #![feature(lang_items)] #![warn(clippy::transmute_int_to_char)] -#![allow(clippy::missing_transmute_annotations)] +#![allow(clippy::missing_transmute_annotations, unnecessary_transmutes)] use core::panic::PanicInfo; diff --git a/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.rs b/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.rs index 7cb508ceaf3..0f2106df00e 100644 --- a/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.rs +++ b/src/tools/clippy/tests/ui/transmute_int_to_char_no_std.rs @@ -1,7 +1,7 @@ #![no_std] #![feature(lang_items)] #![warn(clippy::transmute_int_to_char)] -#![allow(clippy::missing_transmute_annotations)] +#![allow(clippy::missing_transmute_annotations, unnecessary_transmutes)] use core::panic::PanicInfo; diff --git a/src/tools/compiletest/Cargo.toml b/src/tools/compiletest/Cargo.toml index ba1b8f25658..93f7b1cb7cf 100644 --- a/src/tools/compiletest/Cargo.toml +++ b/src/tools/compiletest/Cargo.toml @@ -18,6 +18,7 @@ glob = "0.3.0" home = "0.5.5" indexmap = "2.0.0" miropt-test-tools = { path = "../miropt-test-tools" } +rayon = "1.10.0" regex = "1.0" rustfix = "0.8.1" semver = { version = "1.0.23", features = ["serde"] } diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index de93e2b99ee..e0132056d6c 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -399,7 +399,6 @@ pub struct Config { pub nocapture: bool, // Needed both to construct build_helper::git::GitConfig - pub git_repository: String, pub nightly_branch: String, pub git_merge_commit_email: String, @@ -514,7 +513,6 @@ impl Config { pub fn git_config(&self) -> GitConfig<'_> { GitConfig { - git_repository: &self.git_repository, nightly_branch: &self.nightly_branch, git_merge_commit_email: &self.git_merge_commit_email, } diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs index 2525e0adc83..e7e5ff0ab00 100644 --- a/src/tools/compiletest/src/header/tests.rs +++ b/src/tools/compiletest/src/header/tests.rs @@ -175,7 +175,6 @@ impl ConfigBuilder { self.host.as_deref().unwrap_or("x86_64-unknown-linux-gnu"), "--target", self.target.as_deref().unwrap_or("x86_64-unknown-linux-gnu"), - "--git-repository=", "--nightly-branch=", "--git-merge-commit-email=", "--minicore-path=", diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 7948a273c1e..788bafaa724 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -33,6 +33,7 @@ use std::{env, fs, vec}; use build_helper::git::{get_git_modified_files, get_git_untracked_files}; use camino::{Utf8Path, Utf8PathBuf}; use getopts::Options; +use rayon::iter::{ParallelBridge, ParallelIterator}; use tracing::*; use walkdir::WalkDir; @@ -188,7 +189,6 @@ pub fn parse_config(args: Vec<String>) -> Config { "run tests which rely on commit version being compiled into the binaries", ) .optopt("", "edition", "default Rust edition", "EDITION") - .reqopt("", "git-repository", "name of the git repository", "ORG/REPO") .reqopt("", "nightly-branch", "name of the git branch for nightly", "BRANCH") .reqopt( "", @@ -440,7 +440,6 @@ pub fn parse_config(args: Vec<String>) -> Config { nocapture: matches.opt_present("no-capture"), - git_repository: matches.opt_str("git-repository").unwrap(), nightly_branch: matches.opt_str("nightly-branch").unwrap(), git_merge_commit_email: matches.opt_str("git-merge-commit-email").unwrap(), @@ -641,6 +640,18 @@ struct TestCollector { poisoned: bool, } +impl TestCollector { + fn new() -> Self { + TestCollector { tests: vec![], found_path_stems: HashSet::new(), poisoned: false } + } + + fn merge(&mut self, mut other: Self) { + self.tests.append(&mut other.tests); + self.found_path_stems.extend(other.found_path_stems); + self.poisoned |= other.poisoned; + } +} + /// Creates test structures for every test/revision in the test suite directory. /// /// This always inspects _all_ test files in the suite (e.g. all 17k+ ui tests), @@ -659,10 +670,7 @@ pub(crate) fn collect_and_make_tests(config: Arc<Config>) -> Vec<CollectedTest> let cache = HeadersCache::load(&config); let cx = TestCollectorCx { config, cache, common_inputs_stamp, modified_tests }; - let mut collector = - TestCollector { tests: vec![], found_path_stems: HashSet::new(), poisoned: false }; - - collect_tests_from_dir(&cx, &mut collector, &cx.config.src_test_suite_root, Utf8Path::new("")) + let collector = collect_tests_from_dir(&cx, &cx.config.src_test_suite_root, Utf8Path::new("")) .unwrap_or_else(|reason| { panic!("Could not read tests from {}: {reason}", cx.config.src_test_suite_root) }); @@ -768,25 +776,25 @@ fn modified_tests(config: &Config, dir: &Utf8Path) -> Result<Vec<Utf8PathBuf>, S /// that will be handed over to libtest. fn collect_tests_from_dir( cx: &TestCollectorCx, - collector: &mut TestCollector, dir: &Utf8Path, relative_dir_path: &Utf8Path, -) -> io::Result<()> { +) -> io::Result<TestCollector> { // Ignore directories that contain a file named `compiletest-ignore-dir`. if dir.join("compiletest-ignore-dir").exists() { - return Ok(()); + return Ok(TestCollector::new()); } // For run-make tests, a "test file" is actually a directory that contains an `rmake.rs`. if cx.config.mode == Mode::RunMake { + let mut collector = TestCollector::new(); if dir.join("rmake.rs").exists() { let paths = TestPaths { file: dir.to_path_buf(), relative_dir: relative_dir_path.parent().unwrap().to_path_buf(), }; - make_test(cx, collector, &paths); + make_test(cx, &mut collector, &paths); // This directory is a test, so don't try to find other tests inside it. - return Ok(()); + return Ok(collector); } } @@ -803,36 +811,47 @@ fn collect_tests_from_dir( // subdirectories we find, except for `auxiliary` directories. // FIXME: this walks full tests tree, even if we have something to ignore // use walkdir/ignore like in tidy? - for file in fs::read_dir(dir.as_std_path())? { - let file = file?; - let file_path = Utf8PathBuf::try_from(file.path()).unwrap(); - let file_name = file_path.file_name().unwrap(); - - if is_test(file_name) - && (!cx.config.only_modified || cx.modified_tests.contains(&file_path)) - { - // We found a test file, so create the corresponding libtest structures. - debug!(%file_path, "found test file"); - - // Record the stem of the test file, to check for overlaps later. - let rel_test_path = relative_dir_path.join(file_path.file_stem().unwrap()); - collector.found_path_stems.insert(rel_test_path); - - let paths = - TestPaths { file: file_path, relative_dir: relative_dir_path.to_path_buf() }; - make_test(cx, collector, &paths); - } else if file_path.is_dir() { - // Recurse to find more tests in a subdirectory. - let relative_file_path = relative_dir_path.join(file_name); - if file_name != "auxiliary" { - debug!(%file_path, "found directory"); - collect_tests_from_dir(cx, collector, &file_path, &relative_file_path)?; + fs::read_dir(dir.as_std_path())? + .par_bridge() + .map(|file| { + let mut collector = TestCollector::new(); + let file = file?; + let file_path = Utf8PathBuf::try_from(file.path()).unwrap(); + let file_name = file_path.file_name().unwrap(); + + if is_test(file_name) + && (!cx.config.only_modified || cx.modified_tests.contains(&file_path)) + { + // We found a test file, so create the corresponding libtest structures. + debug!(%file_path, "found test file"); + + // Record the stem of the test file, to check for overlaps later. + let rel_test_path = relative_dir_path.join(file_path.file_stem().unwrap()); + collector.found_path_stems.insert(rel_test_path); + + let paths = + TestPaths { file: file_path, relative_dir: relative_dir_path.to_path_buf() }; + make_test(cx, &mut collector, &paths); + } else if file_path.is_dir() { + // Recurse to find more tests in a subdirectory. + let relative_file_path = relative_dir_path.join(file_name); + if file_name != "auxiliary" { + debug!(%file_path, "found directory"); + collector.merge(collect_tests_from_dir(cx, &file_path, &relative_file_path)?); + } + } else { + debug!(%file_path, "found other file/directory"); } - } else { - debug!(%file_path, "found other file/directory"); - } - } - Ok(()) + Ok(collector) + }) + .reduce( + || Ok(TestCollector::new()), + |a, b| { + let mut a = a?; + a.merge(b?); + Ok(a) + }, + ) } /// Returns true if `file_name` looks like a proper test file name. diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index cc09463c358..fe23cce81e9 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1710,12 +1710,16 @@ impl<'test> TestCx<'test> { rustc.args(&self.props.compile_flags); // FIXME(jieyouxu): we should report a fatal error or warning if user wrote `-Cpanic=` with - // something that's not `abort`, however, by moving this last we should override previous - // `-Cpanic=`s + // something that's not `abort` and `-Cforce-unwind-tables` with a value that is not `yes`, + // however, by moving this last we should override previous `-Cpanic`s and + // `-Cforce-unwind-tables`s. Note that checking here is very fragile, because we'd have to + // account for all possible compile flag splittings (they have some... intricacies and are + // not yet normalized). // // `minicore` requires `#![no_std]` and `#![no_core]`, which means no unwinding panics. if self.props.add_core_stubs { rustc.arg("-Cpanic=abort"); + rustc.arg("-Cforce-unwind-tables=yes"); } rustc diff --git a/src/tools/lint-docs/src/lib.rs b/src/tools/lint-docs/src/lib.rs index 9fd33e23204..d6c69d39e17 100644 --- a/src/tools/lint-docs/src/lib.rs +++ b/src/tools/lint-docs/src/lib.rs @@ -444,21 +444,15 @@ impl<'a> LintExtractor<'a> { fs::write(&tempfile, source) .map_err(|e| format!("failed to write {}: {}", tempfile.display(), e))?; let mut cmd = Command::new(self.rustc_path); - if options.contains(&"edition2024") { - cmd.arg("--edition=2024"); - cmd.arg("-Zunstable-options"); - } else if options.contains(&"edition2021") { - cmd.arg("--edition=2021"); - } else if options.contains(&"edition2018") { - cmd.arg("--edition=2018"); - } else if options.contains(&"edition2015") { - cmd.arg("--edition=2015"); - } else if options.contains(&"edition") { - panic!("lint-docs: unknown edition"); - } else { + let edition = options + .iter() + .filter_map(|opt| opt.strip_prefix("edition")) + .next() // defaults to latest edition - cmd.arg("--edition=2021"); - } + .unwrap_or("2024"); + cmd.arg(format!("--edition={edition}")); + // Just in case this is an unstable edition. + cmd.arg("-Zunstable-options"); cmd.arg("--error-format=json"); cmd.arg("--target").arg(self.rustc_target); if let Some(target_linker) = self.rustc_linker { diff --git a/src/tools/miri/cargo-miri/src/main.rs b/src/tools/miri/cargo-miri/src/main.rs index 7d9f77f3752..322ef0a6c2a 100644 --- a/src/tools/miri/cargo-miri/src/main.rs +++ b/src/tools/miri/cargo-miri/src/main.rs @@ -53,7 +53,7 @@ fn main() { // with `RustcPhase::Rustdoc`. There we perform a check-build (needed to get the expected // build failures for `compile_fail` doctests) and then store a JSON file with the // information needed to run this test. - // - We also set `--runtool` to ourselves, which ends up in `phase_runner` with + // - We also set `--test-runtool` to ourselves, which ends up in `phase_runner` with // `RunnerPhase::Rustdoc`. There we parse the JSON file written in `phase_rustc` and invoke // the Miri driver for interpretation. diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index 71ea07f3463..cb62e12413c 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -666,8 +666,8 @@ pub fn phase_rustdoc(mut args: impl Iterator<Item = String>) { if arg == "--extern" { // Patch --extern arguments to use *.rmeta files, since phase_cargo_rustc only creates stub *.rlib files. forward_patched_extern_arg(&mut args, &mut cmd); - } else if arg == "--runtool" { - // An existing --runtool flag indicates cargo is running in cross-target mode, which we don't support. + } else if arg == "--test-runtool" { + // An existing --test-runtool flag indicates cargo is running in cross-target mode, which we don't support. // Note that this is only passed when cargo is run with the unstable -Zdoctest-xcompile flag; // otherwise, we won't be called as rustdoc at all. show_error!("cross-interpreting doctests is not currently supported by Miri."); @@ -693,8 +693,8 @@ pub fn phase_rustdoc(mut args: impl Iterator<Item = String>) { // to let phase_cargo_rustc know to expect that. We'll use this environment variable as a flag: cmd.env("MIRI_CALLED_FROM_RUSTDOC", "1"); - // The `--test-builder` and `--runtool` arguments are unstable rustdoc features, - // which are disabled by default. We first need to enable them explicitly: + // The `--test-builder` is an unstable rustdoc features, + // which is disabled by default. We first need to enable them explicitly: cmd.arg("-Zunstable-options"); // rustdoc needs to know the right sysroot. @@ -705,7 +705,7 @@ pub fn phase_rustdoc(mut args: impl Iterator<Item = String>) { // Make rustdoc call us back. let cargo_miri_path = env::current_exe().expect("current executable path invalid"); cmd.arg("--test-builder").arg(&cargo_miri_path); // invoked by forwarding most arguments - cmd.arg("--runtool").arg(&cargo_miri_path); // invoked with just a single path argument + cmd.arg("--test-runtool").arg(&cargo_miri_path); // invoked with just a single path argument debug_cmd("[cargo-miri rustdoc]", verbose, &cmd); exec(cmd) diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index a3525dcc77a..7d60a7e5c48 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -392,32 +392,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } #[rustfmt::skip] - | "fadd_algebraic" - | "fsub_algebraic" - | "fmul_algebraic" - | "fdiv_algebraic" - | "frem_algebraic" - => { - let [a, b] = check_intrinsic_arg_count(args)?; - let a = this.read_immediate(a)?; - let b = this.read_immediate(b)?; - let op = match intrinsic_name { - "fadd_algebraic" => mir::BinOp::Add, - "fsub_algebraic" => mir::BinOp::Sub, - "fmul_algebraic" => mir::BinOp::Mul, - "fdiv_algebraic" => mir::BinOp::Div, - "frem_algebraic" => mir::BinOp::Rem, - _ => bug!(), - }; - let res = this.binary_op(op, &a, &b)?; - // `binary_op` already called `generate_nan` if needed. - // Apply a relative error of 4ULP to simulate non-deterministic precision loss - // due to optimizations. - let res = apply_random_float_error_to_imm(this, res, 2 /* log2(4) */)?; - this.write_immediate(*res, dest)?; - } - - #[rustfmt::skip] | "fadd_fast" | "fsub_fast" | "fmul_fast" diff --git a/src/tools/miri/tests/fail/validity/invalid_bool.rs b/src/tools/miri/tests/fail/validity/invalid_bool.rs index 4f11bb2629f..bd448af834d 100644 --- a/src/tools/miri/tests/fail/validity/invalid_bool.rs +++ b/src/tools/miri/tests/fail/validity/invalid_bool.rs @@ -1,3 +1,4 @@ +#![allow(unnecessary_transmutes)] fn main() { let _b = unsafe { std::mem::transmute::<u8, bool>(2) }; //~ ERROR: expected a boolean } diff --git a/src/tools/miri/tests/fail/validity/invalid_bool_op.rs b/src/tools/miri/tests/fail/validity/invalid_bool_op.rs index fe9bb3bed7f..0cbe2d76dc6 100644 --- a/src/tools/miri/tests/fail/validity/invalid_bool_op.rs +++ b/src/tools/miri/tests/fail/validity/invalid_bool_op.rs @@ -2,6 +2,7 @@ // Make sure we find these even with many checks disabled. //@compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation +#![allow(unnecessary_transmutes)] fn main() { let b = unsafe { std::mem::transmute::<u8, bool>(2) }; let _x = b == std::hint::black_box(true); //~ ERROR: interpreting an invalid 8-bit value as a bool diff --git a/src/tools/miri/tests/fail/validity/invalid_char.rs b/src/tools/miri/tests/fail/validity/invalid_char.rs index 568892e5910..d57c933dac1 100644 --- a/src/tools/miri/tests/fail/validity/invalid_char.rs +++ b/src/tools/miri/tests/fail/validity/invalid_char.rs @@ -1,3 +1,4 @@ +#![allow(unnecessary_transmutes)] fn main() { assert!(std::char::from_u32(-1_i32 as u32).is_none()); let _val = match unsafe { std::mem::transmute::<i32, char>(-1) } { diff --git a/src/tools/miri/tests/fail/validity/invalid_char_op.rs b/src/tools/miri/tests/fail/validity/invalid_char_op.rs index 69924822944..e3a5f837e18 100644 --- a/src/tools/miri/tests/fail/validity/invalid_char_op.rs +++ b/src/tools/miri/tests/fail/validity/invalid_char_op.rs @@ -2,6 +2,7 @@ // Make sure we find these even with many checks disabled. //@compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation +#![allow(unnecessary_transmutes)] fn main() { let c = 0xFFFFFFu32; assert!(std::char::from_u32(c).is_none()); diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs index cd60b6bd563..575d70579a4 100644 --- a/src/tools/miri/tests/pass/float.rs +++ b/src/tools/miri/tests/pass/float.rs @@ -6,6 +6,7 @@ #![feature(f16)] #![allow(arithmetic_overflow)] #![allow(internal_features)] +#![allow(unnecessary_transmutes)] use std::any::type_name; use std::cmp::min; diff --git a/src/tools/miri/tests/pass/issues/issue-miri-184.rs b/src/tools/miri/tests/pass/issues/issue-miri-184.rs index 39c841403ef..964d850298f 100644 --- a/src/tools/miri/tests/pass/issues/issue-miri-184.rs +++ b/src/tools/miri/tests/pass/issues/issue-miri-184.rs @@ -1,3 +1,4 @@ +#![allow(unnecessary_transmutes)] pub fn main() { let bytes: [u8; 8] = unsafe { ::std::mem::transmute(0u64) }; let _val: &[u8] = &bytes; 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 a629e2acfe9..882b5e3f795 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 @@ -368,7 +368,7 @@ unsafe fn load_m256i_word<T>(data: &[T], word_index: usize) -> __m256i { #[target_feature(enable = "avx512f")] unsafe fn load_m512i_word<T>(data: &[T], word_index: usize) -> __m512i { let byte_offset = word_index * 64 / size_of::<T>(); - let pointer = data.as_ptr().add(byte_offset) as *const i32; + let pointer = data.as_ptr().add(byte_offset) as *const __m512i; _mm512_loadu_si512(black_box(pointer)) } 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 6f7ab3b3c9f..be3f961e10f 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 +#![allow(unnecessary_transmutes)] #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/tools/suggest-tests/src/main.rs b/src/tools/suggest-tests/src/main.rs index ee212af3626..d84f8e9fa1b 100644 --- a/src/tools/suggest-tests/src/main.rs +++ b/src/tools/suggest-tests/src/main.rs @@ -6,7 +6,6 @@ use suggest_tests::get_suggestions; fn main() -> ExitCode { let modified_files = get_git_modified_files( &GitConfig { - git_repository: &env("SUGGEST_TESTS_GIT_REPOSITORY"), nightly_branch: &env("SUGGEST_TESTS_NIGHTLY_BRANCH"), git_merge_commit_email: &env("SUGGEST_TESTS_MERGE_COMMIT_EMAIL"), }, diff --git a/src/tools/tidy/src/gcc_submodule.rs b/src/tools/tidy/src/gcc_submodule.rs new file mode 100644 index 00000000000..952ebe9e0cf --- /dev/null +++ b/src/tools/tidy/src/gcc_submodule.rs @@ -0,0 +1,47 @@ +//! Tidy check to ensure that the commit SHA of the `src/gcc` submodule is the same as the +//! required GCC version of the GCC codegen backend. + +use std::path::Path; +use std::process::Command; + +pub fn check(root_path: &Path, compiler_path: &Path, bad: &mut bool) { + let cg_gcc_version_path = compiler_path.join("rustc_codegen_gcc/libgccjit.version"); + let cg_gcc_version = std::fs::read_to_string(&cg_gcc_version_path) + .expect(&format!("Cannot read GCC version from {}", cg_gcc_version_path.display())) + .trim() + .to_string(); + + let git_output = Command::new("git") + .current_dir(root_path) + .arg("submodule") + .arg("status") + // --cached asks for the version that is actually committed in the repository, not the one + // that is currently checked out. + .arg("--cached") + .arg("src/gcc") + .output() + .expect("Cannot determine git SHA of the src/gcc checkout"); + + // This can return e.g. + // -e607be166673a8de9fc07f6f02c60426e556c5f2 src/gcc + // e607be166673a8de9fc07f6f02c60426e556c5f2 src/gcc (master-e607be166673a8de9fc07f6f02c60426e556c5f2.e607be) + // +e607be166673a8de9fc07f6f02c60426e556c5f2 src/gcc (master-e607be166673a8de9fc07f6f02c60426e556c5f2.e607be) + let git_output = String::from_utf8_lossy(&git_output.stdout) + .trim() + .split_whitespace() + .next() + .unwrap_or_default() + .to_string(); + + // The SHA can start with + if the submodule is modified or - if it is not checked out. + let gcc_submodule_sha = git_output.trim_start_matches(&['+', '-']); + if gcc_submodule_sha != cg_gcc_version { + *bad = true; + eprintln!( + r#"Commit SHA of the src/gcc submodule (`{gcc_submodule_sha}`) does not match the required GCC version of the GCC codegen backend (`{cg_gcc_version}`). +Make sure to set the src/gcc submodule to commit {cg_gcc_version}. +The GCC codegen backend commit is configured at {}."#, + cg_gcc_version_path.display(), + ); + } +} diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index 66856f5247b..ca45f8bb84b 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -75,6 +75,7 @@ pub mod features; pub mod fluent_alphabetical; pub mod fluent_period; mod fluent_used; +pub mod gcc_submodule; pub(crate) mod iter_header; pub mod known_bug; pub mod mir_opt_tests; diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index 4078d462f55..48122129b01 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -116,6 +116,7 @@ fn main() { check!(fluent_alphabetical, &compiler_path, bless); check!(fluent_period, &compiler_path); check!(target_policy, &root_path); + check!(gcc_submodule, &root_path, &compiler_path); // Checks that only make sense for the std libs. check!(pal, &library_path); diff --git a/src/tools/wasm-component-ld/Cargo.toml b/src/tools/wasm-component-ld/Cargo.toml index f8ae91f9e6f..642d48b9952 100644 --- a/src/tools/wasm-component-ld/Cargo.toml +++ b/src/tools/wasm-component-ld/Cargo.toml @@ -10,4 +10,4 @@ name = "wasm-component-ld" path = "src/main.rs" [dependencies] -wasm-component-ld = "0.5.12" +wasm-component-ld = "0.5.13" diff --git a/tests/assembly/x86-return-float.rs b/tests/assembly/x86-return-float.rs index 2c39c830684..165c11d2280 100644 --- a/tests/assembly/x86-return-float.rs +++ b/tests/assembly/x86-return-float.rs @@ -35,6 +35,7 @@ use minicore::*; pub fn return_f32(x: f32) -> f32 { // CHECK: movss {{.*}}(%ebp), %xmm0 // CHECK-NEXT: popl %ebp + // linux-NEXT: .cfi_def_cfa // CHECK-NEXT: retl x } @@ -44,6 +45,7 @@ pub fn return_f32(x: f32) -> f32 { pub fn return_f64(x: f64) -> f64 { // CHECK: movsd {{.*}}(%ebp), %xmm0 // CHECK-NEXT: popl %ebp + // linux-NEXT: .cfi_def_cfa // CHECK-NEXT: retl x } @@ -313,9 +315,13 @@ pub unsafe fn call_other_f64(x: &mut (usize, f64)) { #[no_mangle] pub fn return_f16(x: f16) -> f16 { // CHECK: pushl %ebp + // linux-NEXT: .cfi_def_cfa_offset + // linux-NEXT: .cfi_offset // CHECK-NEXT: movl %esp, %ebp + // linux-NEXT: .cfi_def_cfa_register // CHECK-NEXT: pinsrw $0, 8(%ebp), %xmm0 // CHECK-NEXT: popl %ebp + // linux-NEXT: .cfi_def_cfa // CHECK-NEXT: retl x } @@ -324,10 +330,14 @@ pub fn return_f16(x: f16) -> f16 { #[no_mangle] pub fn return_f128(x: f128) -> f128 { // CHECK: pushl %ebp + // linux-NEXT: .cfi_def_cfa_offset + // linux-NEXT: .cfi_offset // CHECK-NEXT: movl %esp, %ebp + // linux-NEXT: .cfi_def_cfa_register // linux-NEXT: movaps 8(%ebp), %xmm0 // win-NEXT: movups 8(%ebp), %xmm0 // CHECK-NEXT: popl %ebp + // linux-NEXT: .cfi_def_cfa // CHECK-NEXT: retl x } diff --git a/tests/codegen/align-struct.rs b/tests/codegen/align-struct.rs index cc65b08a922..402a184d4c0 100644 --- a/tests/codegen/align-struct.rs +++ b/tests/codegen/align-struct.rs @@ -1,5 +1,7 @@ //@ compile-flags: -C no-prepopulate-passes -Z mir-opt-level=0 -// +// 32bit MSVC does not align things properly so we suppress high alignment annotations (#112480) +//@ ignore-i686-pc-windows-msvc +//@ ignore-i686-pc-windows-gnu #![crate_type = "lib"] diff --git a/tests/codegen/autodiff/identical_fnc.rs b/tests/codegen/autodiff/identical_fnc.rs new file mode 100644 index 00000000000..1c3277f52b4 --- /dev/null +++ b/tests/codegen/autodiff/identical_fnc.rs @@ -0,0 +1,45 @@ +//@ compile-flags: -Zautodiff=Enable -C opt-level=3 -Clto=fat +//@ no-prefer-dynamic +//@ needs-enzyme +// +// Each autodiff invocation creates a new placeholder function, which we will replace on llvm-ir +// level. If a user tries to differentiate two identical functions within the same compilation unit, +// then LLVM might merge them in release mode before AD. In that case we can't rewrite one of the +// merged placeholder function anymore, and compilation would fail. We prevent this by disabling +// LLVM's merge_function pass before AD. Here we implicetely test that our solution keeps working. +// We also explicetly test that we keep running merge_function after AD, by checking for two +// identical function calls in the LLVM-IR, while having two different calls in the Rust code. +#![feature(autodiff)] + +use std::autodiff::autodiff; + +#[autodiff(d_square, Reverse, Duplicated, Active)] +fn square(x: &f64) -> f64 { + x * x +} + +#[autodiff(d_square2, Reverse, Duplicated, Active)] +fn square2(x: &f64) -> f64 { + x * x +} + +// CHECK:; identical_fnc::main +// CHECK-NEXT:; Function Attrs: +// CHECK-NEXT:define internal void @_ZN13identical_fnc4main17hf4dbc69c8d2f9130E() +// CHECK-NEXT:start: +// CHECK-NOT:br +// CHECK-NOT:ret +// CHECK:; call identical_fnc::d_square +// CHECK-NEXT: call fastcc void @_ZN13identical_fnc8d_square17h4c364207a2f8e06dE(double %x.val, ptr noalias noundef nonnull align 8 dereferenceable(8) %dx1) +// CHECK-NEXT:; call identical_fnc::d_square +// CHECK-NEXT: call fastcc void @_ZN13identical_fnc8d_square17h4c364207a2f8e06dE(double %x.val, ptr noalias noundef nonnull align 8 dereferenceable(8) %dx2) + +fn main() { + let x = std::hint::black_box(3.0); + let mut dx1 = std::hint::black_box(1.0); + let mut dx2 = std::hint::black_box(1.0); + let _ = d_square(&x, &mut dx1, 1.0); + let _ = d_square2(&x, &mut dx2, 1.0); + assert_eq!(dx1, 6.0); + assert_eq!(dx2, 6.0); +} diff --git a/tests/codegen/issues/issue-56927.rs b/tests/codegen/issues/issue-56927.rs index a40718689b3..415ef073e03 100644 --- a/tests/codegen/issues/issue-56927.rs +++ b/tests/codegen/issues/issue-56927.rs @@ -1,4 +1,7 @@ //@ compile-flags: -C no-prepopulate-passes +// 32bit MSVC does not align things properly so we suppress high alignment annotations (#112480) +//@ ignore-i686-pc-windows-msvc +//@ ignore-i686-pc-windows-gnu #![crate_type = "rlib"] diff --git a/tests/pretty/never-pattern.pp b/tests/pretty/never-pattern.pp new file mode 100644 index 00000000000..923ad9b82c7 --- /dev/null +++ b/tests/pretty/never-pattern.pp @@ -0,0 +1,17 @@ +#![feature(prelude_import)] +#![no_std] +//@ pretty-mode:expanded +//@ pp-exact:never-pattern.pp +//@ only-x86_64 + +#![allow(incomplete_features)] +#![feature(never_patterns)] +#![feature(never_type)] +#[prelude_import] +use ::std::prelude::rust_2015::*; +#[macro_use] +extern crate std; + +fn f(x: Result<u32, !>) { _ = match x { Ok(x) => x, Err(!) , }; } + +fn main() {} diff --git a/tests/pretty/never-pattern.rs b/tests/pretty/never-pattern.rs new file mode 100644 index 00000000000..fe170bafc66 --- /dev/null +++ b/tests/pretty/never-pattern.rs @@ -0,0 +1,16 @@ +//@ pretty-mode:expanded +//@ pp-exact:never-pattern.pp +//@ only-x86_64 + +#![allow(incomplete_features)] +#![feature(never_patterns)] +#![feature(never_type)] + +fn f(x: Result<u32, !>) { + _ = match x { + Ok(x) => x, + Err(!), + }; +} + +fn main() {} diff --git a/tests/run-make/c-link-to-rust-dylib/rmake.rs b/tests/run-make/c-link-to-rust-dylib/rmake.rs index ab9aa445402..3a48af8a366 100644 --- a/tests/run-make/c-link-to-rust-dylib/rmake.rs +++ b/tests/run-make/c-link-to-rust-dylib/rmake.rs @@ -23,7 +23,11 @@ fn main() { if path.is_file() && path.extension().is_some_and(|ext| ext == expected_extension) && path.file_name().and_then(|name| name.to_str()).is_some_and(|name| { - name.ends_with(".so") || name.ends_with(".dll") || name.ends_with(".dylib") + if cfg!(target_os = "aix") { + name.ends_with(".a") + } else { + name.ends_with(".so") || name.ends_with(".dll") || name.ends_with(".dylib") + } }) { rfs::remove_file(path); diff --git a/tests/run-make/doctests-runtool/rmake.rs b/tests/run-make/doctests-runtool/rmake.rs index c7be829c215..817001c514b 100644 --- a/tests/run-make/doctests-runtool/rmake.rs +++ b/tests/run-make/doctests-runtool/rmake.rs @@ -1,4 +1,4 @@ -// Tests behavior of rustdoc `--runtool`. +// Tests behavior of rustdoc `--test-runtool`. use std::path::PathBuf; @@ -11,7 +11,7 @@ fn mkdir(name: &str) -> PathBuf { dir } -// Behavior with --runtool with relative paths and --test-run-directory. +// Behavior with --test-runtool with relative paths and --test-run-directory. fn main() { let run_dir_name = "rundir"; let run_dir = mkdir(run_dir_name); @@ -27,7 +27,7 @@ fn main() { .arg("--test") .arg("--test-run-directory") .arg(run_dir_name) - .arg("--runtool") + .arg("--test-runtool") .arg(&run_tool_binary) .extern_("t", "libt.rlib") .run(); diff --git a/tests/run-make/rustc-help/help-v.diff b/tests/run-make/rustc-help/help-v.diff index 0ea79f3e557..60a9dfbe201 100644 --- a/tests/run-make/rustc-help/help-v.diff +++ b/tests/run-make/rustc-help/help-v.diff @@ -1,22 +1,23 @@ -@@ -63,10 +63,27 @@ +@@ -65,10 +65,28 @@ Set a codegen option -V, --version Print version info and exit -v, --verbose Use verbose output -+ --extern NAME[=PATH] ++ --extern <NAME>[=<PATH>] + Specify where an external rust library is located -+ --sysroot PATH Override the system root -+ --error-format human|json|short ++ --sysroot <PATH> ++ Override the system root ++ --error-format <human|json|short> + How errors and other messages are produced -+ --json CONFIG Configure the JSON output of the compiler -+ --color auto|always|never ++ --json <CONFIG> Configure the JSON output of the compiler ++ --color <auto|always|never> + Configure coloring of output: -+ auto = colorize, if output goes to a tty (default); -+ always = always colorize output; -+ never = never colorize output -+ --diagnostic-width WIDTH ++ * auto = colorize, if output goes to a tty (default); ++ * always = always colorize output; ++ * never = never colorize output ++ --diagnostic-width <WIDTH> + Inform rustc of the width of the output so that + diagnostics can be truncated to fit -+ --remap-path-prefix FROM=TO ++ --remap-path-prefix <FROM>=<TO> + Remap source names in all output (compiler messages + and output files) + @path Read newline separated options from `path` diff --git a/tests/run-make/rustc-help/help-v.stdout b/tests/run-make/rustc-help/help-v.stdout index 744453d6e85..3fc297fb08e 100644 --- a/tests/run-make/rustc-help/help-v.stdout +++ b/tests/run-make/rustc-help/help-v.stdout @@ -2,31 +2,32 @@ Usage: rustc [OPTIONS] INPUT Options: -h, --help Display this message - --cfg SPEC Configure the compilation environment. - SPEC supports the syntax `NAME[="VALUE"]`. - --check-cfg SPEC + --cfg <SPEC> Configure the compilation environment. + SPEC supports the syntax `<NAME>[="<VALUE>"]`. + --check-cfg <SPEC> Provide list of expected cfgs for checking - -L [KIND=]PATH Add a directory to the library search path. The - optional KIND can be one of dependency, crate, native, - framework, or all (the default). - -l [KIND[:MODIFIERS]=]NAME[:RENAME] + -L [<KIND>=]<PATH> Add a directory to the library search path. The + optional KIND can be one of + <dependency|crate|native|framework|all> (default: + all). + -l [<KIND>[:<MODIFIERS>]=]<NAME>[:<RENAME>] Link the generated crate(s) to the specified native library NAME. The optional KIND can be one of - static, framework, or dylib (the default). + <static|framework|dylib> (default: dylib). Optional comma separated MODIFIERS - (bundle|verbatim|whole-archive|as-needed) + <bundle|verbatim|whole-archive|as-needed> may be specified each with a prefix of either '+' to enable or '-' to disable. - --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro] + --crate-type <bin|lib|rlib|dylib|cdylib|staticlib|proc-macro> Comma separated list of types of crates for the compiler to emit - --crate-name NAME + --crate-name <NAME> Specify the name of the crate being built - --edition 2015|2018|2021|2024 + --edition <2015|2018|2021|2024|future> Specify which edition of the compiler to use when compiling code. The default is 2015 and the latest stable edition is 2024. - --emit TYPE[=FILE] + --emit <TYPE>[=<FILE>] Comma separated list of types of output for the compiler to emit. Each TYPE has the default FILE name: @@ -39,45 +40,47 @@ Options: * mir - CRATE_NAME.mir * obj - CRATE_NAME.o * thin-link-bitcode - CRATE_NAME.indexing.o - --print INFO[=FILE] + --print <INFO>[=<FILE>] Compiler information to print on stdout (or to a file) INFO may be one of - (all-target-specs-json|calling-conventions|cfg|check-cfg|code-models|crate-name|crate-root-lint-levels|deployment-target|file-names|host-tuple|link-args|native-static-libs|relocation-models|split-debuginfo|stack-protector-strategies|supported-crate-types|sysroot|target-cpus|target-features|target-libdir|target-list|target-spec-json|tls-models). + <all-target-specs-json|calling-conventions|cfg|check-cfg|code-models|crate-name|crate-root-lint-levels|deployment-target|file-names|host-tuple|link-args|native-static-libs|relocation-models|split-debuginfo|stack-protector-strategies|supported-crate-types|sysroot|target-cpus|target-features|target-libdir|target-list|target-spec-json|tls-models>. -g Equivalent to -C debuginfo=2 -O Equivalent to -C opt-level=3 - -o FILENAME Write output to <filename> - --out-dir DIR Write output to compiler-chosen filename in <dir> - --explain OPT Provide a detailed explanation of an error message + -o <FILENAME> Write output to FILENAME + --out-dir <DIR> Write output to compiler-chosen filename in DIR + --explain <OPT> Provide a detailed explanation of an error message --test Build a test harness - --target TARGET Target triple for which the code is compiled - -A, --allow LINT Set lint allowed - -W, --warn LINT Set lint warnings - --force-warn LINT + --target <TARGET> + Target triple for which the code is compiled + -A, --allow <LINT> Set lint allowed + -W, --warn <LINT> Set lint warnings + --force-warn <LINT> Set lint force-warn - -D, --deny LINT Set lint denied - -F, --forbid LINT Set lint forbidden - --cap-lints LEVEL + -D, --deny <LINT> Set lint denied + -F, --forbid <LINT> Set lint forbidden + --cap-lints <LEVEL> Set the most restrictive lint level. More restrictive lints are capped at this level - -C, --codegen OPT[=VALUE] + -C, --codegen <OPT>[=<VALUE>] Set a codegen option -V, --version Print version info and exit -v, --verbose Use verbose output - --extern NAME[=PATH] + --extern <NAME>[=<PATH>] Specify where an external rust library is located - --sysroot PATH Override the system root - --error-format human|json|short + --sysroot <PATH> + Override the system root + --error-format <human|json|short> How errors and other messages are produced - --json CONFIG Configure the JSON output of the compiler - --color auto|always|never + --json <CONFIG> Configure the JSON output of the compiler + --color <auto|always|never> Configure coloring of output: - auto = colorize, if output goes to a tty (default); - always = always colorize output; - never = never colorize output - --diagnostic-width WIDTH + * auto = colorize, if output goes to a tty (default); + * always = always colorize output; + * never = never colorize output + --diagnostic-width <WIDTH> Inform rustc of the width of the output so that diagnostics can be truncated to fit - --remap-path-prefix FROM=TO + --remap-path-prefix <FROM>=<TO> Remap source names in all output (compiler messages and output files) @path Read newline separated options from `path` diff --git a/tests/run-make/rustc-help/help.stdout b/tests/run-make/rustc-help/help.stdout index 3043755207a..caffe28f498 100644 --- a/tests/run-make/rustc-help/help.stdout +++ b/tests/run-make/rustc-help/help.stdout @@ -2,31 +2,32 @@ Usage: rustc [OPTIONS] INPUT Options: -h, --help Display this message - --cfg SPEC Configure the compilation environment. - SPEC supports the syntax `NAME[="VALUE"]`. - --check-cfg SPEC + --cfg <SPEC> Configure the compilation environment. + SPEC supports the syntax `<NAME>[="<VALUE>"]`. + --check-cfg <SPEC> Provide list of expected cfgs for checking - -L [KIND=]PATH Add a directory to the library search path. The - optional KIND can be one of dependency, crate, native, - framework, or all (the default). - -l [KIND[:MODIFIERS]=]NAME[:RENAME] + -L [<KIND>=]<PATH> Add a directory to the library search path. The + optional KIND can be one of + <dependency|crate|native|framework|all> (default: + all). + -l [<KIND>[:<MODIFIERS>]=]<NAME>[:<RENAME>] Link the generated crate(s) to the specified native library NAME. The optional KIND can be one of - static, framework, or dylib (the default). + <static|framework|dylib> (default: dylib). Optional comma separated MODIFIERS - (bundle|verbatim|whole-archive|as-needed) + <bundle|verbatim|whole-archive|as-needed> may be specified each with a prefix of either '+' to enable or '-' to disable. - --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro] + --crate-type <bin|lib|rlib|dylib|cdylib|staticlib|proc-macro> Comma separated list of types of crates for the compiler to emit - --crate-name NAME + --crate-name <NAME> Specify the name of the crate being built - --edition 2015|2018|2021|2024 + --edition <2015|2018|2021|2024|future> Specify which edition of the compiler to use when compiling code. The default is 2015 and the latest stable edition is 2024. - --emit TYPE[=FILE] + --emit <TYPE>[=<FILE>] Comma separated list of types of output for the compiler to emit. Each TYPE has the default FILE name: @@ -39,27 +40,28 @@ Options: * mir - CRATE_NAME.mir * obj - CRATE_NAME.o * thin-link-bitcode - CRATE_NAME.indexing.o - --print INFO[=FILE] + --print <INFO>[=<FILE>] Compiler information to print on stdout (or to a file) INFO may be one of - (all-target-specs-json|calling-conventions|cfg|check-cfg|code-models|crate-name|crate-root-lint-levels|deployment-target|file-names|host-tuple|link-args|native-static-libs|relocation-models|split-debuginfo|stack-protector-strategies|supported-crate-types|sysroot|target-cpus|target-features|target-libdir|target-list|target-spec-json|tls-models). + <all-target-specs-json|calling-conventions|cfg|check-cfg|code-models|crate-name|crate-root-lint-levels|deployment-target|file-names|host-tuple|link-args|native-static-libs|relocation-models|split-debuginfo|stack-protector-strategies|supported-crate-types|sysroot|target-cpus|target-features|target-libdir|target-list|target-spec-json|tls-models>. -g Equivalent to -C debuginfo=2 -O Equivalent to -C opt-level=3 - -o FILENAME Write output to <filename> - --out-dir DIR Write output to compiler-chosen filename in <dir> - --explain OPT Provide a detailed explanation of an error message + -o <FILENAME> Write output to FILENAME + --out-dir <DIR> Write output to compiler-chosen filename in DIR + --explain <OPT> Provide a detailed explanation of an error message --test Build a test harness - --target TARGET Target triple for which the code is compiled - -A, --allow LINT Set lint allowed - -W, --warn LINT Set lint warnings - --force-warn LINT + --target <TARGET> + Target triple for which the code is compiled + -A, --allow <LINT> Set lint allowed + -W, --warn <LINT> Set lint warnings + --force-warn <LINT> Set lint force-warn - -D, --deny LINT Set lint denied - -F, --forbid LINT Set lint forbidden - --cap-lints LEVEL + -D, --deny <LINT> Set lint denied + -F, --forbid <LINT> Set lint forbidden + --cap-lints <LEVEL> Set the most restrictive lint level. More restrictive lints are capped at this level - -C, --codegen OPT[=VALUE] + -C, --codegen <OPT>[=<VALUE>] Set a codegen option -V, --version Print version info and exit -v, --verbose Use verbose output diff --git a/tests/run-make/rustdoc-default-output/output-default.stdout b/tests/run-make/rustdoc-default-output/output-default.stdout index 01f470f6e16..78ca8c863eb 100644 --- a/tests/run-make/rustdoc-default-output/output-default.stdout +++ b/tests/run-make/rustdoc-default-output/output-default.stdout @@ -11,7 +11,7 @@ Options: -o, --out-dir PATH which directory to place the output --crate-name NAME specify the name of this crate - --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro] + --crate-type <bin|lib|rlib|dylib|cdylib|staticlib|proc-macro> Comma separated list of types of crates for the compiler to emit -L, --library-path DIR @@ -138,12 +138,9 @@ Options: --show-coverage calculate percentage of public items with documentation - --enable-per-target-ignores - parse ignore-foo for ignoring doctests on a per-target - basis - --runtool The tool to run tests with when building for a different target than host + --test-runtool The tool to run tests with when building for a different target than host - --runtool-arg One (of possibly many) arguments to pass to the runtool + --test-runtool-arg One argument (of possibly many) to pass to the runtool --test-builder PATH The rustc-like binary to use as the test builder diff --git a/tests/rustdoc-gui/docblock-table-overflow.goml b/tests/rustdoc-gui/docblock-table-overflow.goml index 18e5b4d7f35..e603c3a4d22 100644 --- a/tests/rustdoc-gui/docblock-table-overflow.goml +++ b/tests/rustdoc-gui/docblock-table-overflow.goml @@ -11,7 +11,7 @@ assert-property: (".top-doc .docblock table", {"scrollWidth": "1572"}) // Checking it works on other doc blocks as well... // Logically, the ".docblock" and the "<p>" should have the same scroll width (if we exclude the margin). -assert-property: ("#implementations-list > details .docblock", {"scrollWidth": 816}) +assert-property: ("#implementations-list > details .docblock", {"scrollWidth": 835}) assert-property: ("#implementations-list > details .docblock > p", {"scrollWidth": 835}) // However, since there is overflow in the <table>, its scroll width is bigger. assert-property: ("#implementations-list > details .docblock table", {"scrollWidth": "1572"}) diff --git a/tests/rustdoc-gui/impl-doc-indent.goml b/tests/rustdoc-gui/impl-doc-indent.goml new file mode 100644 index 00000000000..d647fec6d73 --- /dev/null +++ b/tests/rustdoc-gui/impl-doc-indent.goml @@ -0,0 +1,16 @@ +// Checks the impl block docs have the correct indent. +go-to: "file://" + |DOC_PATH| + "/test_docs/impls_indent/struct.Context.html" + +// First we ensure that the impl items are indent (more on the right of the screen) than the +// impl itself. +store-position: ("#impl-Context", {"x": impl_x}) +store-position: ("#impl-Context > .item-info", {"x": impl_item_x}) +assert: |impl_x| < |impl_item_x| + +// And we ensure that all impl items have the same indent. +assert-position: ("#impl-Context > .docblock", {"x": |impl_item_x|}) +assert-position: ("#impl-Context + .docblock", {"x": |impl_item_x|}) + +// Same with the collapsible impl block. +assert-position: ("#impl-Context-1 > .docblock", {"x": |impl_item_x|}) +assert-position: (".implementors-toggle > summary + .docblock", {"x": |impl_item_x|}) diff --git a/tests/rustdoc-gui/item-info-overflow.goml b/tests/rustdoc-gui/item-info-overflow.goml index c325beb6d06..2c4e06e297c 100644 --- a/tests/rustdoc-gui/item-info-overflow.goml +++ b/tests/rustdoc-gui/item-info-overflow.goml @@ -21,7 +21,7 @@ compare-elements-property: ( ) assert-property: ( "#impl-SimpleTrait-for-LongItemInfo2 .item-info", - {"scrollWidth": "916"}, + {"scrollWidth": "935"}, ) // Just to be sure we're comparing the correct "item-info": assert-text: ( diff --git a/tests/rustdoc-gui/src/test_docs/lib.rs b/tests/rustdoc-gui/src/test_docs/lib.rs index 31f6b7f09b7..bb0015b8f9c 100644 --- a/tests/rustdoc-gui/src/test_docs/lib.rs +++ b/tests/rustdoc-gui/src/test_docs/lib.rs @@ -740,3 +740,29 @@ pub mod SidebarSort { impl Sort for Cell<u8> {} impl<'a> Sort for &'a str {} } + +pub mod impls_indent { + pub struct Context; + + /// Working with objects. + /// + /// # Safety + /// + /// Functions that take indices of locals do not check bounds on these indices; + /// the caller must ensure that the indices are less than the number of locals + /// in the current stack frame. + impl Context { + } + + /// Working with objects. + /// + /// # Safety + /// + /// Functions that take indices of locals do not check bounds on these indices; + /// the caller must ensure that the indices are less than the number of locals + /// in the current stack frame. + impl Context { + /// bla + pub fn bar() {} + } +} diff --git a/tests/rustdoc-ui/doctest/edition-2024-error-output.stdout b/tests/rustdoc-ui/doctest/edition-2024-error-output.stdout index 8f056a5f703..273d7071237 100644 --- a/tests/rustdoc-ui/doctest/edition-2024-error-output.stdout +++ b/tests/rustdoc-ui/doctest/edition-2024-error-output.stdout @@ -5,6 +5,9 @@ test $DIR/edition-2024-error-output.rs - (line 12) ... FAILED failures: ---- $DIR/edition-2024-error-output.rs - (line 12) stdout ---- +Test executable failed (exit status: 101). + +stderr: thread 'main' panicked at $TMP:6:1: assertion `left == right` failed @@ -13,6 +16,7 @@ assertion `left == right` failed note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace + failures: $DIR/edition-2024-error-output.rs - (line 12) diff --git a/tests/rustdoc-ui/doctest/stdout-and-stderr.rs b/tests/rustdoc-ui/doctest/stdout-and-stderr.rs new file mode 100644 index 00000000000..9b0c69d8839 --- /dev/null +++ b/tests/rustdoc-ui/doctest/stdout-and-stderr.rs @@ -0,0 +1,26 @@ +// This test ensures that the output is correctly generated when the +// doctest fails. It checks when there is stderr and stdout, no stdout +// and no stderr/stdout. +// +// This is a regression test for <https://github.com/rust-lang/rust/issues/140289>. + +//@ edition: 2024 +//@ compile-flags:--test --test-args=--test-threads=1 +//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR" +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ normalize-stdout: "panicked at .+rs:" -> "panicked at $$TMP:" +//@ failure-status: 101 +//@ rustc-env:RUST_BACKTRACE=0 + +//! ``` +//! println!("######## from a DOC TEST ########"); +//! assert_eq!("doc", "test"); +//! ``` +//! +//! ``` +//! assert_eq!("doc", "test"); +//! ``` +//! +//! ``` +//! std::process::exit(1); +//! ``` diff --git a/tests/rustdoc-ui/doctest/stdout-and-stderr.stdout b/tests/rustdoc-ui/doctest/stdout-and-stderr.stdout new file mode 100644 index 00000000000..b2febe1344f --- /dev/null +++ b/tests/rustdoc-ui/doctest/stdout-and-stderr.stdout @@ -0,0 +1,46 @@ + +running 3 tests +test $DIR/stdout-and-stderr.rs - (line 15) ... FAILED +test $DIR/stdout-and-stderr.rs - (line 20) ... FAILED +test $DIR/stdout-and-stderr.rs - (line 24) ... FAILED + +failures: + +---- $DIR/stdout-and-stderr.rs - (line 15) stdout ---- +Test executable failed (exit status: 101). + +stdout: +######## from a DOC TEST ######## + +stderr: + +thread 'main' panicked at $TMP:7:1: +assertion `left == right` failed + left: "doc" + right: "test" +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace + + +---- $DIR/stdout-and-stderr.rs - (line 20) stdout ---- +Test executable failed (exit status: 101). + +stderr: + +thread 'main' panicked at $TMP:15:1: +assertion `left == right` failed + left: "doc" + right: "test" +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace + + +---- $DIR/stdout-and-stderr.rs - (line 24) stdout ---- +Test executable failed (exit status: 1). + + +failures: + $DIR/stdout-and-stderr.rs - (line 15) + $DIR/stdout-and-stderr.rs - (line 20) + $DIR/stdout-and-stderr.rs - (line 24) + +test result: FAILED. 0 passed; 3 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc/doctest/auxiliary/doctest-runtool.rs b/tests/rustdoc/doctest/auxiliary/doctest-runtool.rs new file mode 100644 index 00000000000..a21ae0664fe --- /dev/null +++ b/tests/rustdoc/doctest/auxiliary/doctest-runtool.rs @@ -0,0 +1,21 @@ +// For some reason on Windows, the PATH to the libstd dylib doesn't seem to +// carry over to running the runtool. +//@ no-prefer-dynamic + +use std::path::Path; +use std::process::Command; + +fn main() { + let args: Vec<_> = std::env::args().collect(); + eprintln!("{args:#?}"); + assert_eq!(args.len(), 4); + assert_eq!(args[1], "arg1"); + assert_eq!(args[2], "arg2 with space"); + let path = Path::new(&args[3]); + let output = Command::new(path).output().unwrap(); + // Should fail without env var. + assert!(!output.status.success()); + let output = Command::new(path).env("DOCTEST_RUNTOOL_CHECK", "xyz").output().unwrap(); + // Should pass with env var. + assert!(output.status.success()); +} diff --git a/tests/rustdoc/doctest/doctest-runtool.rs b/tests/rustdoc/doctest/doctest-runtool.rs new file mode 100644 index 00000000000..c4fb02e5228 --- /dev/null +++ b/tests/rustdoc/doctest/doctest-runtool.rs @@ -0,0 +1,13 @@ +// Tests that the --test-runtool argument works. + +//@ ignore-cross-compile +//@ aux-bin: doctest-runtool.rs +//@ compile-flags: --test +//@ compile-flags: --test-runtool=auxiliary/bin/doctest-runtool +//@ compile-flags: --test-runtool-arg=arg1 --test-runtool-arg +//@ compile-flags: 'arg2 with space' + +/// ``` +/// assert_eq!(std::env::var("DOCTEST_RUNTOOL_CHECK"), Ok("xyz".to_string())); +/// ``` +pub fn main() {} diff --git a/tests/ui/asm/naked-invalid-attr.rs b/tests/ui/asm/naked-invalid-attr.rs index c3a3131ee46..6ac9cb9e3a9 100644 --- a/tests/ui/asm/naked-invalid-attr.rs +++ b/tests/ui/asm/naked-invalid-attr.rs @@ -51,3 +51,12 @@ fn main() { #[unsafe(naked)] //~ ERROR should be applied to a function definition || {}; } + +// Check that the path of an attribute without a name is printed correctly (issue #140082) +#[::a] +//~^ ERROR attribute incompatible with `#[unsafe(naked)]` +//~| ERROR failed to resolve: use of unresolved module or unlinked crate `a` +#[unsafe(naked)] +extern "C" fn issue_140082() { + naked_asm!("") +} diff --git a/tests/ui/asm/naked-invalid-attr.stderr b/tests/ui/asm/naked-invalid-attr.stderr index 81d30e6475d..ef389e7d921 100644 --- a/tests/ui/asm/naked-invalid-attr.stderr +++ b/tests/ui/asm/naked-invalid-attr.stderr @@ -1,3 +1,9 @@ +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `a` + --> $DIR/naked-invalid-attr.rs:56:5 + | +LL | #[::a] + | ^ use of unresolved module or unlinked crate `a` + error: attribute should be applied to a function definition --> $DIR/naked-invalid-attr.rs:13:1 | @@ -27,6 +33,15 @@ LL | #[unsafe(naked)] LL | || {}; | ----- not a function definition +error[E0736]: attribute incompatible with `#[unsafe(naked)]` + --> $DIR/naked-invalid-attr.rs:56:1 + | +LL | #[::a] + | ^^^^^^ the `{{root}}::a` attribute is incompatible with `#[unsafe(naked)]` +... +LL | #[unsafe(naked)] + | ---------------- function marked with `#[unsafe(naked)]` here + error: attribute should be applied to a function definition --> $DIR/naked-invalid-attr.rs:22:5 | @@ -49,5 +64,7 @@ error: attribute should be applied to a function definition LL | #![unsafe(naked)] | ^^^^^^^^^^^^^^^^^ cannot be applied to crates -error: aborting due to 6 previous errors +error: aborting due to 8 previous errors +Some errors have detailed explanations: E0433, E0736. +For more information about an error, try `rustc --explain E0433`. diff --git a/tests/ui/async-await/async-closures/kind-due-to-arg-with-box-wrap.rs b/tests/ui/async-await/async-closures/kind-due-to-arg-with-box-wrap.rs new file mode 100644 index 00000000000..650fb10d94b --- /dev/null +++ b/tests/ui/async-await/async-closures/kind-due-to-arg-with-box-wrap.rs @@ -0,0 +1,22 @@ +//@ edition: 2024 + +// Regression test for <https://github.com/rust-lang/rust/issues/140292>. + +struct Test; + +impl Test { + async fn an_async_fn(&mut self) { + todo!() + } + + pub async fn uses_takes_asyncfn(&mut self) { + takes_asyncfn(Box::new(async || self.an_async_fn().await)); + //~^ ERROR expected a closure that implements the `AsyncFn` trait, but this closure only implements `AsyncFnMut` + } +} + +async fn takes_asyncfn(_: impl AsyncFn()) { + todo!() +} + +fn main() {} diff --git a/tests/ui/async-await/async-closures/kind-due-to-arg-with-box-wrap.stderr b/tests/ui/async-await/async-closures/kind-due-to-arg-with-box-wrap.stderr new file mode 100644 index 00000000000..e79f95c197b --- /dev/null +++ b/tests/ui/async-await/async-closures/kind-due-to-arg-with-box-wrap.stderr @@ -0,0 +1,21 @@ +error[E0525]: expected a closure that implements the `AsyncFn` trait, but this closure only implements `AsyncFnMut` + --> $DIR/kind-due-to-arg-with-box-wrap.rs:13:32 + | +LL | takes_asyncfn(Box::new(async || self.an_async_fn().await)); + | ------------- ---------^^^^^^^^-------------------------- + | | | | | + | | | | closure is `AsyncFnMut` because it mutates the variable `*self` here + | | | this closure implements `AsyncFnMut`, not `AsyncFn` + | | the requirement to implement `AsyncFn` derives from here + | required by a bound introduced by this call + | + = note: required for `Box<{async closure@$DIR/kind-due-to-arg-with-box-wrap.rs:13:32: 13:40}>` to implement `AsyncFn()` +note: required by a bound in `takes_asyncfn` + --> $DIR/kind-due-to-arg-with-box-wrap.rs:18:32 + | +LL | async fn takes_asyncfn(_: impl AsyncFn()) { + | ^^^^^^^^^ required by this bound in `takes_asyncfn` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0525`. diff --git a/tests/ui/coercion/issue-73886.stderr b/tests/ui/coercion/issue-73886.stderr index a6f8ba65ab5..0d4c90017cf 100644 --- a/tests/ui/coercion/issue-73886.stderr +++ b/tests/ui/coercion/issue-73886.stderr @@ -8,9 +8,14 @@ error[E0605]: non-primitive cast: `u32` as `Option<_>` --> $DIR/issue-73886.rs:4:13 | LL | let _ = 7u32 as Option<_>; - | ^^^^^^^^^^^^^^^^^ help: consider using the `From` trait instead: `Option<_>::from(7u32)` + | ^^^^^^^^^^^^^^^^^ | = note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object +help: consider using the `From` trait instead + | +LL - let _ = 7u32 as Option<_>; +LL + let _ = Option::<_>::from(7u32); + | error: aborting due to 2 previous errors diff --git a/tests/ui/coercion/non-primitive-cast-135412.fixed b/tests/ui/coercion/non-primitive-cast-135412.fixed new file mode 100644 index 00000000000..5cadc9368d5 --- /dev/null +++ b/tests/ui/coercion/non-primitive-cast-135412.fixed @@ -0,0 +1,10 @@ +//@ run-rustfix + +use std::sync::Arc; + +fn main() { + let _ = Option::<_>::from(7u32); + //~^ ERROR non-primitive cast: `u32` as `Option<_>` + let _ = Arc::<str>::from("String"); + //~^ ERROR non-primitive cast: `&'static str` as `Arc<str>` +} diff --git a/tests/ui/coercion/non-primitive-cast-135412.rs b/tests/ui/coercion/non-primitive-cast-135412.rs new file mode 100644 index 00000000000..67a3ef340d2 --- /dev/null +++ b/tests/ui/coercion/non-primitive-cast-135412.rs @@ -0,0 +1,10 @@ +//@ run-rustfix + +use std::sync::Arc; + +fn main() { + let _ = 7u32 as Option<_>; + //~^ ERROR non-primitive cast: `u32` as `Option<_>` + let _ = "String" as Arc<str>; + //~^ ERROR non-primitive cast: `&'static str` as `Arc<str>` +} diff --git a/tests/ui/coercion/non-primitive-cast-135412.stderr b/tests/ui/coercion/non-primitive-cast-135412.stderr new file mode 100644 index 00000000000..7e5861f83e9 --- /dev/null +++ b/tests/ui/coercion/non-primitive-cast-135412.stderr @@ -0,0 +1,29 @@ +error[E0605]: non-primitive cast: `u32` as `Option<_>` + --> $DIR/non-primitive-cast-135412.rs:6:13 + | +LL | let _ = 7u32 as Option<_>; + | ^^^^^^^^^^^^^^^^^ + | + = note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object +help: consider using the `From` trait instead + | +LL - let _ = 7u32 as Option<_>; +LL + let _ = Option::<_>::from(7u32); + | + +error[E0605]: non-primitive cast: `&'static str` as `Arc<str>` + --> $DIR/non-primitive-cast-135412.rs:8:13 + | +LL | let _ = "String" as Arc<str>; + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object +help: consider using the `From` trait instead + | +LL - let _ = "String" as Arc<str>; +LL + let _ = Arc::<str>::from("String"); + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0605`. diff --git a/tests/ui/compiletest-self-test/compile-flags-last.stderr b/tests/ui/compiletest-self-test/compile-flags-last.stderr index 72d92206e2b..5a48361cfc0 100644 --- a/tests/ui/compiletest-self-test/compile-flags-last.stderr +++ b/tests/ui/compiletest-self-test/compile-flags-last.stderr @@ -1,5 +1,5 @@ error: Argument to option 'cap-lints' missing Usage: - --cap-lints LEVEL Set the most restrictive lint level. More restrictive + --cap-lints <LEVEL> Set the most restrictive lint level. More restrictive lints are capped at this level diff --git a/tests/ui/const-generics/defaults/concrete-const-param-type.rs b/tests/ui/const-generics/defaults/concrete-const-param-type.rs new file mode 100644 index 00000000000..c411f81192b --- /dev/null +++ b/tests/ui/const-generics/defaults/concrete-const-param-type.rs @@ -0,0 +1,13 @@ +#![feature(generic_const_parameter_types, unsized_const_params, adt_const_params)] +//~^ WARN the feature `generic_const_parameter_types` is incomplete +//~| WARN the feature `unsized_const_params` is incomplete +// Make sure that we test the const param type of default const parameters +// if both the type of the default and the type of the parameter are concrete. + +use std::marker::ConstParamTy_; + +struct Foo<const N: u32, const M: u64 = N>; //~ ERROR the constant `N` is not of type `u64` +struct Bar<T: ConstParamTy_, const N: T, const M: u64 = N>(T); // ok +struct Baz<T: ConstParamTy_, const N: u32, const M: T = N>(T); // ok + +fn main() {} diff --git a/tests/ui/const-generics/defaults/concrete-const-param-type.stderr b/tests/ui/const-generics/defaults/concrete-const-param-type.stderr new file mode 100644 index 00000000000..ad077f87e5d --- /dev/null +++ b/tests/ui/const-generics/defaults/concrete-const-param-type.stderr @@ -0,0 +1,25 @@ +warning: the feature `generic_const_parameter_types` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/concrete-const-param-type.rs:1:12 + | +LL | #![feature(generic_const_parameter_types, unsized_const_params, adt_const_params)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #137626 <https://github.com/rust-lang/rust/issues/137626> for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: the feature `unsized_const_params` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/concrete-const-param-type.rs:1:43 + | +LL | #![feature(generic_const_parameter_types, unsized_const_params, adt_const_params)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #95174 <https://github.com/rust-lang/rust/issues/95174> for more information + +error: the constant `N` is not of type `u64` + --> $DIR/concrete-const-param-type.rs:9:26 + | +LL | struct Foo<const N: u32, const M: u64 = N>; + | ^^^^^^^^^^^^^^^^ expected `u64`, found `u32` + +error: aborting due to 1 previous error; 2 warnings emitted + diff --git a/tests/ui/consts/const-eval/raw-bytes.rs b/tests/ui/consts/const-eval/raw-bytes.rs index 9187de56362..20f1a9aae70 100644 --- a/tests/ui/consts/const-eval/raw-bytes.rs +++ b/tests/ui/consts/const-eval/raw-bytes.rs @@ -2,7 +2,7 @@ //@ ignore-endian-big // ignore-tidy-linelength //@ normalize-stderr: "╾─*ALLOC[0-9]+(\+[a-z0-9]+)?(<imm>)?─*╼" -> "╾ALLOC_ID$1╼" -#![allow(invalid_value)] +#![allow(invalid_value, unnecessary_transmutes)] #![feature(never_type, rustc_attrs, ptr_metadata, slice_from_ptr_range, const_slice_from_ptr_range)] use std::mem; diff --git a/tests/ui/consts/const-eval/transmute-const-promotion.rs b/tests/ui/consts/const-eval/transmute-const-promotion.rs index 1f0240d4b5a..840334f43c0 100644 --- a/tests/ui/consts/const-eval/transmute-const-promotion.rs +++ b/tests/ui/consts/const-eval/transmute-const-promotion.rs @@ -1,3 +1,4 @@ +#![allow(unnecessary_transmutes)] use std::mem; fn main() { diff --git a/tests/ui/consts/const-eval/transmute-const-promotion.stderr b/tests/ui/consts/const-eval/transmute-const-promotion.stderr index 3603db03bb2..eb2fed091c3 100644 --- a/tests/ui/consts/const-eval/transmute-const-promotion.stderr +++ b/tests/ui/consts/const-eval/transmute-const-promotion.stderr @@ -1,5 +1,5 @@ error[E0716]: temporary value dropped while borrowed - --> $DIR/transmute-const-promotion.rs:4:37 + --> $DIR/transmute-const-promotion.rs:5:37 | LL | let x: &'static u32 = unsafe { &mem::transmute(3.0f32) }; | ------------ ^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use diff --git a/tests/ui/consts/const-eval/transmute-const.rs b/tests/ui/consts/const-eval/transmute-const.rs index 1cfad00ca76..fb6cb77675f 100644 --- a/tests/ui/consts/const-eval/transmute-const.rs +++ b/tests/ui/consts/const-eval/transmute-const.rs @@ -1,3 +1,4 @@ +#![allow(unnecessary_transmutes)] use std::mem; static FOO: bool = unsafe { mem::transmute(3u8) }; diff --git a/tests/ui/consts/const-eval/transmute-const.stderr b/tests/ui/consts/const-eval/transmute-const.stderr index d72289487d7..35a5cabaa67 100644 --- a/tests/ui/consts/const-eval/transmute-const.stderr +++ b/tests/ui/consts/const-eval/transmute-const.stderr @@ -1,5 +1,5 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/transmute-const.rs:3:1 + --> $DIR/transmute-const.rs:4:1 | LL | static FOO: bool = unsafe { mem::transmute(3u8) }; | ^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0x03, but expected a boolean diff --git a/tests/ui/consts/const-eval/ub-enum.rs b/tests/ui/consts/const-eval/ub-enum.rs index 5be444e667a..a5255ef95aa 100644 --- a/tests/ui/consts/const-eval/ub-enum.rs +++ b/tests/ui/consts/const-eval/ub-enum.rs @@ -3,7 +3,7 @@ //@ normalize-stderr: "([0-9a-f][0-9a-f] |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?(<imm>)?─*╼ )+ *│.*" -> "HEX_DUMP" //@ normalize-stderr: "0x0+" -> "0x0" #![feature(never_type)] -#![allow(invalid_value)] +#![allow(invalid_value, unnecessary_transmutes)] use std::mem; diff --git a/tests/ui/consts/const-eval/ub-wide-ptr.rs b/tests/ui/consts/const-eval/ub-wide-ptr.rs index a071a44272b..4e2defc1a09 100644 --- a/tests/ui/consts/const-eval/ub-wide-ptr.rs +++ b/tests/ui/consts/const-eval/ub-wide-ptr.rs @@ -1,5 +1,5 @@ // ignore-tidy-linelength -#![allow(unused)] +#![allow(unused, unnecessary_transmutes)] #![feature(ptr_metadata)] use std::{ptr, mem}; diff --git a/tests/ui/consts/extra-const-ub/detect-extra-ub.rs b/tests/ui/consts/extra-const-ub/detect-extra-ub.rs index 481f2ff88df..c3bd8301d5c 100644 --- a/tests/ui/consts/extra-const-ub/detect-extra-ub.rs +++ b/tests/ui/consts/extra-const-ub/detect-extra-ub.rs @@ -2,6 +2,7 @@ //@ [no_flag] check-pass //@ [with_flag] compile-flags: -Zextra-const-ub-checks #![feature(never_type)] +#![allow(unnecessary_transmutes)] use std::mem::transmute; use std::ptr::addr_of; diff --git a/tests/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr b/tests/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr index 0100aafb6b7..ea3b0e70b82 100644 --- a/tests/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr +++ b/tests/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr @@ -1,11 +1,11 @@ error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:29:20 + --> $DIR/detect-extra-ub.rs:30:20 | LL | let _x: bool = transmute(3u8); | ^^^^^^^^^^^^^^ constructing invalid value: encountered 0x03, but expected a boolean error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:35:21 + --> $DIR/detect-extra-ub.rs:36:21 | LL | let _x: usize = transmute(&3u8); | ^^^^^^^^^^^^^^^ constructing invalid value: encountered a pointer, but expected an integer @@ -14,7 +14,7 @@ LL | let _x: usize = transmute(&3u8); = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:41:28 + --> $DIR/detect-extra-ub.rs:42:28 | LL | let _x: PtrSizedEnum = transmute(&3u8); | ^^^^^^^^^^^^^^^ constructing invalid value at .<enum-tag>: encountered a pointer, but expected an integer @@ -23,7 +23,7 @@ LL | let _x: PtrSizedEnum = transmute(&3u8); = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:48:30 + --> $DIR/detect-extra-ub.rs:49:30 | LL | let _x: (usize, usize) = transmute(x); | ^^^^^^^^^^^^ constructing invalid value at .0: encountered a pointer, but expected an integer @@ -32,19 +32,19 @@ LL | let _x: (usize, usize) = transmute(x); = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:54:20 + --> $DIR/detect-extra-ub.rs:55:20 | LL | let _x: &u32 = transmute(&[0u8; 4]); | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered an unaligned reference (required 4 byte alignment but found 1) error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:62:13 + --> $DIR/detect-extra-ub.rs:63:13 | LL | let v = *addr_of!(data).cast::<UninhDiscriminant>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<enum-tag>: encountered an uninhabited enum variant error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:82:16 + --> $DIR/detect-extra-ub.rs:83:16 | LL | let _val = *(&mem as *const Align as *const [*const u8; 2]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at [0]: encountered a partial pointer or a mix of pointers @@ -53,7 +53,7 @@ LL | let _val = *(&mem as *const Align as *const [*const u8; 2]); = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:97:16 + --> $DIR/detect-extra-ub.rs:98:16 | LL | let _val = &*slice; | ^^^^^^^ constructing invalid value: encountered invalid reference metadata: slice is bigger than largest supported object diff --git a/tests/ui/consts/issue-69532.rs b/tests/ui/consts/issue-69532.rs index 285cfe7213b..43ab1d6cca7 100644 --- a/tests/ui/consts/issue-69532.rs +++ b/tests/ui/consts/issue-69532.rs @@ -1,8 +1,8 @@ //@ run-pass const fn make_nans() -> (f64, f64, f32, f32) { - let nan1: f64 = unsafe { std::mem::transmute(0x7FF0_0001_0000_0001u64) }; - let nan2: f64 = unsafe { std::mem::transmute(0x7FF0_0000_0000_0001u64) }; + let nan1 = f64::from_bits(0x7FF0_0001_0000_0001); + let nan2 = f64::from_bits(0x7FF0_0000_0000_0001); let nan1_32 = nan1 as f32; let nan2_32 = nan2 as f32; diff --git a/tests/ui/copy-a-resource.rs b/tests/ui/copy-a-resource.rs deleted file mode 100644 index 55f2dd4ee6d..00000000000 --- a/tests/ui/copy-a-resource.rs +++ /dev/null @@ -1,21 +0,0 @@ -#[derive(Debug)] -struct Foo { - i: isize, -} - -impl Drop for Foo { - fn drop(&mut self) {} -} - -fn foo(i:isize) -> Foo { - Foo { - i: i - } -} - -fn main() { - let x = foo(10); - let _y = x.clone(); - //~^ ERROR no method named `clone` found - println!("{:?}", x); -} diff --git a/tests/ui/coroutine/dont-drop-stalled-generators.rs b/tests/ui/coroutine/dont-drop-stalled-generators.rs new file mode 100644 index 00000000000..8e0c45a9773 --- /dev/null +++ b/tests/ui/coroutine/dont-drop-stalled-generators.rs @@ -0,0 +1,25 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-pass +//@ edition: 2024 + +// This test previously used the `is_copy_raw` query during +// HIR typeck, dropping the list of generators from the current +// body. This then caused a query cycle. + +struct W<T>(*const T); + +impl<T: Send> Clone for W<T> { + fn clone(&self) -> Self { W(self.0) } +} + +impl<T: Send> Copy for W<T> {} + +fn main() { + let coro = async {}; + let x = W(&raw const coro); + let c = || { + let x = x; + }; +} diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr index 6170250992c..8178f54b2aa 100644 --- a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr +++ b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr @@ -18,13 +18,16 @@ LL | where LL | T: AsExpression<Self::SqlType>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Foo::check` -error[E0271]: type mismatch resolving `Integer == Text` +error[E0277]: the trait bound `&str: AsExpression<Integer>` is not satisfied --> $DIR/as_expression.rs:56:5 | LL | SelectInt.check("bar"); - | ^^^^^^^^^^^^^^^^^^^^^^ types differ + | ^^^^^^^^^^^^^^^^^^^^^^ the trait `AsExpression<Integer>` is not implemented for `&str` + | + = help: the trait `AsExpression<Integer>` is not implemented for `&str` + but trait `AsExpression<Text>` is implemented for it + = help: for that trait implementation, expected `Text`, found `Integer` error: aborting due to 2 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 E0277`. diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs index 673adb82870..86f39e43484 100644 --- a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs +++ b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs @@ -55,5 +55,5 @@ impl<T> Foo for T where T: Expression {} fn main() { SelectInt.check("bar"); //~^ ERROR the trait bound `&str: AsExpression<Integer>` is not satisfied - //[next]~| ERROR type mismatch + //[next]~| ERROR the trait bound `&str: AsExpression<Integer>` is not satisfied } diff --git a/tests/ui/feature-gates/feature-gate-concat_idents.rs b/tests/ui/feature-gates/feature-gate-concat_idents.rs index 68caf3d71e9..4fc3b691597 100644 --- a/tests/ui/feature-gates/feature-gate-concat_idents.rs +++ b/tests/ui/feature-gates/feature-gate-concat_idents.rs @@ -1,3 +1,5 @@ +#![expect(deprecated)] // concat_idents is deprecated + const XY_1: i32 = 10; fn main() { diff --git a/tests/ui/feature-gates/feature-gate-concat_idents.stderr b/tests/ui/feature-gates/feature-gate-concat_idents.stderr index d0f4fe62d04..6399424eecd 100644 --- a/tests/ui/feature-gates/feature-gate-concat_idents.stderr +++ b/tests/ui/feature-gates/feature-gate-concat_idents.stderr @@ -1,5 +1,5 @@ error[E0658]: use of unstable library feature `concat_idents`: `concat_idents` is not stable enough for use and is subject to change - --> $DIR/feature-gate-concat_idents.rs:5:13 + --> $DIR/feature-gate-concat_idents.rs:7:13 | LL | let a = concat_idents!(X, Y_1); | ^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | let a = concat_idents!(X, Y_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 `concat_idents`: `concat_idents` is not stable enough for use and is subject to change - --> $DIR/feature-gate-concat_idents.rs:6:13 + --> $DIR/feature-gate-concat_idents.rs:8:13 | LL | let b = concat_idents!(X, Y_2); | ^^^^^^^^^^^^^ diff --git a/tests/ui/feature-gates/feature-gate-concat_idents2.rs b/tests/ui/feature-gates/feature-gate-concat_idents2.rs index 9660ffeafa5..bc2b4f7cddf 100644 --- a/tests/ui/feature-gates/feature-gate-concat_idents2.rs +++ b/tests/ui/feature-gates/feature-gate-concat_idents2.rs @@ -1,3 +1,5 @@ +#![expect(deprecated)] // concat_idents is deprecated + fn main() { concat_idents!(a, b); //~ ERROR `concat_idents` is not stable enough //~| ERROR cannot find value `ab` in this scope diff --git a/tests/ui/feature-gates/feature-gate-concat_idents2.stderr b/tests/ui/feature-gates/feature-gate-concat_idents2.stderr index b42a1d999e4..a770c1a348b 100644 --- a/tests/ui/feature-gates/feature-gate-concat_idents2.stderr +++ b/tests/ui/feature-gates/feature-gate-concat_idents2.stderr @@ -1,5 +1,5 @@ error[E0658]: use of unstable library feature `concat_idents`: `concat_idents` is not stable enough for use and is subject to change - --> $DIR/feature-gate-concat_idents2.rs:2:5 + --> $DIR/feature-gate-concat_idents2.rs:4:5 | LL | concat_idents!(a, b); | ^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | concat_idents!(a, b); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0425]: cannot find value `ab` in this scope - --> $DIR/feature-gate-concat_idents2.rs:2:5 + --> $DIR/feature-gate-concat_idents2.rs:4:5 | LL | concat_idents!(a, b); | ^^^^^^^^^^^^^^^^^^^^ not found in this scope diff --git a/tests/ui/feature-gates/feature-gate-concat_idents3.rs b/tests/ui/feature-gates/feature-gate-concat_idents3.rs index 81710fd9fb0..d4a0d2e6bb0 100644 --- a/tests/ui/feature-gates/feature-gate-concat_idents3.rs +++ b/tests/ui/feature-gates/feature-gate-concat_idents3.rs @@ -1,3 +1,5 @@ +#![expect(deprecated)] // concat_idents is deprecated + const XY_1: i32 = 10; fn main() { diff --git a/tests/ui/feature-gates/feature-gate-concat_idents3.stderr b/tests/ui/feature-gates/feature-gate-concat_idents3.stderr index b186601d0ed..7d929322bc0 100644 --- a/tests/ui/feature-gates/feature-gate-concat_idents3.stderr +++ b/tests/ui/feature-gates/feature-gate-concat_idents3.stderr @@ -1,5 +1,5 @@ error[E0658]: use of unstable library feature `concat_idents`: `concat_idents` is not stable enough for use and is subject to change - --> $DIR/feature-gate-concat_idents3.rs:5:20 + --> $DIR/feature-gate-concat_idents3.rs:7:20 | LL | assert_eq!(10, concat_idents!(X, Y_1)); | ^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | assert_eq!(10, concat_idents!(X, Y_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 `concat_idents`: `concat_idents` is not stable enough for use and is subject to change - --> $DIR/feature-gate-concat_idents3.rs:6:20 + --> $DIR/feature-gate-concat_idents3.rs:8:20 | LL | assert_eq!(20, concat_idents!(X, Y_2)); | ^^^^^^^^^^^^^ diff --git a/tests/ui/generics/single-colon-path-not-const-generics.stderr b/tests/ui/generics/single-colon-path-not-const-generics.stderr index 9eb62de2756..163ea4bbda6 100644 --- a/tests/ui/generics/single-colon-path-not-const-generics.stderr +++ b/tests/ui/generics/single-colon-path-not-const-generics.stderr @@ -1,13 +1,15 @@ error: path separator must be a double colon --> $DIR/single-colon-path-not-const-generics.rs:8:18 | +LL | pub struct Foo { + | --- while parsing this struct LL | a: Vec<foo::bar:A>, | ^ | help: use a double colon instead | LL | a: Vec<foo::bar::A>, - | + + | + error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/in-trait/dont-probe-missing-item-name-4.rs b/tests/ui/impl-trait/in-trait/dont-probe-missing-item-name-4.rs new file mode 100644 index 00000000000..1ee3bfd1233 --- /dev/null +++ b/tests/ui/impl-trait/in-trait/dont-probe-missing-item-name-4.rs @@ -0,0 +1,23 @@ +trait ServerFn { + type Output; + fn run_body() -> impl Sized; +} +struct MyServerFn {} + +macro_rules! f { + () => { + impl ServerFn for MyServerFn { + type Output = (); + fn run_body() -> impl Sized {} + } + }; +} + +f! {} + +fn problem<T: ServerFn<Output = i64>>(_: T) {} + +fn main() { + problem(MyServerFn {}); + //~^ ERROR type mismatch resolving `<MyServerFn as ServerFn>::Output == i64` +} diff --git a/tests/ui/impl-trait/in-trait/dont-probe-missing-item-name-4.stderr b/tests/ui/impl-trait/in-trait/dont-probe-missing-item-name-4.stderr new file mode 100644 index 00000000000..b4c022d3521 --- /dev/null +++ b/tests/ui/impl-trait/in-trait/dont-probe-missing-item-name-4.stderr @@ -0,0 +1,26 @@ +error[E0271]: type mismatch resolving `<MyServerFn as ServerFn>::Output == i64` + --> $DIR/dont-probe-missing-item-name-4.rs:21:13 + | +LL | problem(MyServerFn {}); + | ------- ^^^^^^^^^^^^^ type mismatch resolving `<MyServerFn as ServerFn>::Output == i64` + | | + | required by a bound introduced by this call + | +note: expected this to be `i64` + --> $DIR/dont-probe-missing-item-name-4.rs:10:27 + | +LL | type Output = (); + | ^^ +... +LL | f! {} + | ----- in this macro invocation +note: required by a bound in `problem` + --> $DIR/dont-probe-missing-item-name-4.rs:18:24 + | +LL | fn problem<T: ServerFn<Output = i64>>(_: T) {} + | ^^^^^^^^^^^^ required by this bound in `problem` + = note: this error originates in the macro `f` (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 E0271`. diff --git a/tests/ui/invalid-compile-flags/emit-output-types-without-args.stderr b/tests/ui/invalid-compile-flags/emit-output-types-without-args.stderr index 669913b5765..9e4486a272f 100644 --- a/tests/ui/invalid-compile-flags/emit-output-types-without-args.stderr +++ b/tests/ui/invalid-compile-flags/emit-output-types-without-args.stderr @@ -1,6 +1,7 @@ error: Argument to option 'emit' missing Usage: - --emit TYPE[=FILE] Comma separated list of types of output for the + --emit <TYPE>[=<FILE>] + Comma separated list of types of output for the compiler to emit. Each TYPE has the default FILE name: * asm - CRATE_NAME.s diff --git a/tests/ui/invalid-compile-flags/print-without-arg.stderr b/tests/ui/invalid-compile-flags/print-without-arg.stderr index fd2a36e761b..3048a59d0d0 100644 --- a/tests/ui/invalid-compile-flags/print-without-arg.stderr +++ b/tests/ui/invalid-compile-flags/print-without-arg.stderr @@ -1,6 +1,7 @@ error: Argument to option 'print' missing Usage: - --print INFO[=FILE] Compiler information to print on stdout (or to a file) + --print <INFO>[=<FILE>] + Compiler information to print on stdout (or to a file) INFO may be one of - (all-target-specs-json|calling-conventions|cfg|check-cfg|code-models|crate-name|crate-root-lint-levels|deployment-target|file-names|host-tuple|link-args|native-static-libs|relocation-models|split-debuginfo|stack-protector-strategies|supported-crate-types|sysroot|target-cpus|target-features|target-libdir|target-list|target-spec-json|tls-models). + <all-target-specs-json|calling-conventions|cfg|check-cfg|code-models|crate-name|crate-root-lint-levels|deployment-target|file-names|host-tuple|link-args|native-static-libs|relocation-models|split-debuginfo|stack-protector-strategies|supported-crate-types|sysroot|target-cpus|target-features|target-libdir|target-list|target-spec-json|tls-models>. diff --git a/tests/ui/issues/issue-25746-bool-transmute.rs b/tests/ui/issues/issue-25746-bool-transmute.rs index f8cdc980daa..046dcf83f62 100644 --- a/tests/ui/issues/issue-25746-bool-transmute.rs +++ b/tests/ui/issues/issue-25746-bool-transmute.rs @@ -1,4 +1,5 @@ //@ run-pass +#![allow(unnecessary_transmutes)] use std::mem::transmute; fn main() { diff --git a/tests/ui/issues/issue-32950.rs b/tests/ui/issues/issue-32950.rs index 27d68a11c1f..b51ac296776 100644 --- a/tests/ui/issues/issue-32950.rs +++ b/tests/ui/issues/issue-32950.rs @@ -1,4 +1,5 @@ #![feature(concat_idents)] +#![expect(deprecated)] // concat_idents is deprecated #[derive(Debug)] struct Baz<T>( diff --git a/tests/ui/issues/issue-32950.stderr b/tests/ui/issues/issue-32950.stderr index 3cdf35af1d8..38a82542f89 100644 --- a/tests/ui/issues/issue-32950.stderr +++ b/tests/ui/issues/issue-32950.stderr @@ -1,11 +1,11 @@ error: `derive` cannot be used on items with type macros - --> $DIR/issue-32950.rs:5:5 + --> $DIR/issue-32950.rs:6:5 | LL | concat_idents!(Foo, Bar) | ^^^^^^^^^^^^^^^^^^^^^^^^ error[E0412]: cannot find type `FooBar` in this scope - --> $DIR/issue-32950.rs:5:5 + --> $DIR/issue-32950.rs:6:5 | LL | concat_idents!(Foo, Bar) | ^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope diff --git a/tests/ui/issues/issue-50403.rs b/tests/ui/issues/issue-50403.rs index ab22aff26d9..f14958afc34 100644 --- a/tests/ui/issues/issue-50403.rs +++ b/tests/ui/issues/issue-50403.rs @@ -1,4 +1,5 @@ #![feature(concat_idents)] +#![expect(deprecated)] // concat_idents is deprecated fn main() { let x = concat_idents!(); //~ ERROR `concat_idents!()` takes 1 or more arguments diff --git a/tests/ui/issues/issue-50403.stderr b/tests/ui/issues/issue-50403.stderr index 193d815d519..e7dd05bb018 100644 --- a/tests/ui/issues/issue-50403.stderr +++ b/tests/ui/issues/issue-50403.stderr @@ -1,5 +1,5 @@ error: `concat_idents!()` takes 1 or more arguments - --> $DIR/issue-50403.rs:4:13 + --> $DIR/issue-50403.rs:5:13 | LL | let x = concat_idents!(); | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/linking/cdylib-no-mangle.rs b/tests/ui/linking/cdylib-no-mangle.rs new file mode 100644 index 00000000000..f442c3f584d --- /dev/null +++ b/tests/ui/linking/cdylib-no-mangle.rs @@ -0,0 +1,20 @@ +//@ only-apple +//@ build-fail +//@ dont-check-compiler-stderr +//@ dont-check-compiler-stdout + +// Regression test for <https://github.com/rust-lang/rust/issues/139744>. +// Functions in the dynamic library marked with no_mangle should not be GC-ed. + +#![crate_type = "cdylib"] + +unsafe extern "C" { + unsafe static THIS_SYMBOL_SHOULD_BE_UNDEFINED: usize; +} + +#[unsafe(no_mangle)] +pub unsafe fn function_marked_with_no_mangle() { + println!("FUNCTION_MARKED_WITH_NO_MANGLE = {}", unsafe { THIS_SYMBOL_SHOULD_BE_UNDEFINED }); +} + +//~? ERROR linking diff --git a/tests/ui/linking/executable-no-mangle-strip.rs b/tests/ui/linking/executable-no-mangle-strip.rs new file mode 100644 index 00000000000..cc283dc53ee --- /dev/null +++ b/tests/ui/linking/executable-no-mangle-strip.rs @@ -0,0 +1,27 @@ +//@ run-pass +//@ ignore-windows-gnu: only statics marked with used can be GC-ed on windows-gnu + +// Regression test for <https://github.com/rust-lang/rust/issues/139744>. +// Functions in the binary marked with no_mangle should be GC-ed if they +// are not indirectly referenced by main. + +#![feature(used_with_arg)] + +#[cfg_attr(windows, link(name = "this_lib_does_not_exist", kind = "raw-dylib"))] +unsafe extern "C" { + unsafe static THIS_SYMBOL_SHOULD_BE_UNDEFINED: usize; +} + +#[unsafe(no_mangle)] +pub unsafe fn function_marked_with_no_mangle() { + println!("FUNCTION_MARKED_WITH_NO_MANGLE = {}", unsafe { THIS_SYMBOL_SHOULD_BE_UNDEFINED }); +} + +#[used(compiler)] +pub static FUNCTION_MARKED_WITH_USED: unsafe fn() = || { + println!("FUNCTION_MARKED_WITH_USED = {}", unsafe { THIS_SYMBOL_SHOULD_BE_UNDEFINED }); +}; + +fn main() { + println!("MAIN"); +} diff --git a/tests/ui/lint/invalid_null_args.rs b/tests/ui/lint/invalid_null_args.rs index 7948f0d86d0..f40f06a0d36 100644 --- a/tests/ui/lint/invalid_null_args.rs +++ b/tests/ui/lint/invalid_null_args.rs @@ -1,19 +1,19 @@ // check-fail // run-rustfix +#![allow(unnecessary_transmutes)] -use std::ptr; -use std::mem; +use std::{mem, ptr}; unsafe fn null_ptr() { ptr::write( - //~^ ERROR calling this function with a null pointer is undefined behavior + //~^ ERROR calling this function with a null pointer is undefined behavior ptr::null_mut() as *mut u32, mem::transmute::<[u8; 4], _>([0, 0, 0, 255]), ); let null_ptr = ptr::null_mut(); ptr::write( - //~^ ERROR calling this function with a null pointer is undefined behavior + //~^ ERROR calling this function with a null pointer is undefined behavior null_ptr as *mut u32, mem::transmute::<[u8; 4], _>([0, 0, 0, 255]), ); @@ -38,10 +38,10 @@ unsafe fn null_ptr() { ptr::copy_nonoverlapping::<usize>(ptr::null(), ptr::NonNull::dangling().as_ptr(), 0); //~^ ERROR calling this function with a null pointer is undefined behavior ptr::copy_nonoverlapping::<usize>( - //~^ ERROR calling this function with a null pointer is undefined behavior + //~^ ERROR calling this function with a null pointer is undefined behavior ptr::NonNull::dangling().as_ptr(), ptr::null_mut(), - 0 + 0, ); #[derive(Copy, Clone)] diff --git a/tests/ui/lint/invalid_null_args.stderr b/tests/ui/lint/invalid_null_args.stderr index f95bc2afa82..11c6270cfb7 100644 --- a/tests/ui/lint/invalid_null_args.stderr +++ b/tests/ui/lint/invalid_null_args.stderr @@ -117,7 +117,7 @@ LL | | LL | | ptr::NonNull::dangling().as_ptr(), LL | | ptr::null_mut(), | | --------------- null pointer originates from here -LL | | 0 +LL | | 0, LL | | ); | |_____^ | diff --git a/tests/ui/lint/lint-ctypes-enum.rs b/tests/ui/lint/lint-ctypes-enum.rs index 0d19d5b5347..b2ef27b833b 100644 --- a/tests/ui/lint/lint-ctypes-enum.rs +++ b/tests/ui/lint/lint-ctypes-enum.rs @@ -2,6 +2,8 @@ #![deny(improper_ctypes)] #![feature(ptr_internals)] #![feature(transparent_unions)] +#![feature(repr128)] +#![allow(incomplete_features)] use std::num; @@ -40,6 +42,20 @@ enum Isize { C, } +#[repr(u128)] +enum U128 { + A, + B, + C, +} + +#[repr(i128)] +enum I128 { + A, + B, + C, +} + #[repr(transparent)] struct TransparentStruct<T>(T, std::marker::PhantomData<Z>); @@ -71,6 +87,8 @@ extern "C" { fn repr_c(x: ReprC); fn repr_u8(x: U8); fn repr_isize(x: Isize); + fn repr_u128(x: U128); //~ ERROR `extern` block uses type `U128` + fn repr_i128(x: I128); //~ ERROR `extern` block uses type `I128` fn option_ref(x: Option<&'static u8>); fn option_fn(x: Option<extern "C" fn()>); fn option_nonnull(x: Option<std::ptr::NonNull<u8>>); diff --git a/tests/ui/lint/lint-ctypes-enum.stderr b/tests/ui/lint/lint-ctypes-enum.stderr index a491bd19605..d5fc844f756 100644 --- a/tests/ui/lint/lint-ctypes-enum.stderr +++ b/tests/ui/lint/lint-ctypes-enum.stderr @@ -1,5 +1,5 @@ error: `extern` block uses type `U`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:68:14 + --> $DIR/lint-ctypes-enum.rs:84:14 | LL | fn uf(x: U); | ^ not FFI-safe @@ -7,7 +7,7 @@ LL | fn uf(x: U); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint note: the type is defined here - --> $DIR/lint-ctypes-enum.rs:9:1 + --> $DIR/lint-ctypes-enum.rs:11:1 | LL | enum U { | ^^^^^^ @@ -18,7 +18,7 @@ LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ error: `extern` block uses type `B`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:69:14 + --> $DIR/lint-ctypes-enum.rs:85:14 | LL | fn bf(x: B); | ^ not FFI-safe @@ -26,13 +26,13 @@ LL | fn bf(x: B); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint note: the type is defined here - --> $DIR/lint-ctypes-enum.rs:12:1 + --> $DIR/lint-ctypes-enum.rs:14:1 | LL | enum B { | ^^^^^^ error: `extern` block uses type `T`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:70:14 + --> $DIR/lint-ctypes-enum.rs:86:14 | LL | fn tf(x: T); | ^ not FFI-safe @@ -40,13 +40,39 @@ LL | fn tf(x: T); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint note: the type is defined here - --> $DIR/lint-ctypes-enum.rs:16:1 + --> $DIR/lint-ctypes-enum.rs:18:1 | LL | enum T { | ^^^^^^ +error: `extern` block uses type `U128`, which is not FFI-safe + --> $DIR/lint-ctypes-enum.rs:90:21 + | +LL | fn repr_u128(x: U128); + | ^^^^ not FFI-safe + | + = note: 128-bit integers don't currently have a known stable ABI +note: the type is defined here + --> $DIR/lint-ctypes-enum.rs:46:1 + | +LL | enum U128 { + | ^^^^^^^^^ + +error: `extern` block uses type `I128`, which is not FFI-safe + --> $DIR/lint-ctypes-enum.rs:91:21 + | +LL | fn repr_i128(x: I128); + | ^^^^ not FFI-safe + | + = note: 128-bit integers don't currently have a known stable ABI +note: the type is defined here + --> $DIR/lint-ctypes-enum.rs:53:1 + | +LL | enum I128 { + | ^^^^^^^^^ + error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:82:31 + --> $DIR/lint-ctypes-enum.rs:100:31 | LL | fn option_nonzero_u128(x: Option<num::NonZero<u128>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -54,7 +80,7 @@ LL | fn option_nonzero_u128(x: Option<num::NonZero<u128>>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:89:31 + --> $DIR/lint-ctypes-enum.rs:107:31 | LL | fn option_nonzero_i128(x: Option<num::NonZero<i128>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -62,7 +88,7 @@ LL | fn option_nonzero_i128(x: Option<num::NonZero<i128>>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `Option<TransparentUnion<NonZero<u8>>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:94:36 + --> $DIR/lint-ctypes-enum.rs:112:36 | LL | fn option_transparent_union(x: Option<TransparentUnion<num::NonZero<u8>>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -71,7 +97,7 @@ LL | fn option_transparent_union(x: Option<TransparentUnion<num::NonZero<u8> = note: enum has no representation hint error: `extern` block uses type `Option<Rust<NonZero<u8>>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:96:28 + --> $DIR/lint-ctypes-enum.rs:114:28 | LL | fn option_repr_rust(x: Option<Rust<num::NonZero<u8>>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -80,7 +106,7 @@ LL | fn option_repr_rust(x: Option<Rust<num::NonZero<u8>>>); = note: enum has no representation hint error: `extern` block uses type `Option<u8>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:97:21 + --> $DIR/lint-ctypes-enum.rs:115:21 | LL | fn option_u8(x: Option<u8>); | ^^^^^^^^^^ not FFI-safe @@ -89,7 +115,7 @@ LL | fn option_u8(x: Option<u8>); = note: enum has no representation hint error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:107:33 + --> $DIR/lint-ctypes-enum.rs:125:33 | LL | fn result_nonzero_u128_t(x: Result<num::NonZero<u128>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -97,7 +123,7 @@ LL | fn result_nonzero_u128_t(x: Result<num::NonZero<u128>, ()>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:114:33 + --> $DIR/lint-ctypes-enum.rs:132:33 | LL | fn result_nonzero_i128_t(x: Result<num::NonZero<i128>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -105,7 +131,7 @@ LL | fn result_nonzero_i128_t(x: Result<num::NonZero<i128>, ()>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `Result<TransparentUnion<NonZero<u8>>, ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:119:38 + --> $DIR/lint-ctypes-enum.rs:137:38 | LL | fn result_transparent_union_t(x: Result<TransparentUnion<num::NonZero<u8>>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -114,7 +140,7 @@ LL | fn result_transparent_union_t(x: Result<TransparentUnion<num::NonZero<u = note: enum has no representation hint error: `extern` block uses type `Result<Rust<NonZero<u8>>, ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:121:30 + --> $DIR/lint-ctypes-enum.rs:139:30 | LL | fn result_repr_rust_t(x: Result<Rust<num::NonZero<u8>>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -123,7 +149,7 @@ LL | fn result_repr_rust_t(x: Result<Rust<num::NonZero<u8>>, ()>); = note: enum has no representation hint error: `extern` block uses type `Result<NonZero<u8>, U>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:125:51 + --> $DIR/lint-ctypes-enum.rs:143:51 | LL | fn result_1zst_exhaustive_single_variant_t(x: Result<num::NonZero<u8>, U>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -132,7 +158,7 @@ LL | fn result_1zst_exhaustive_single_variant_t(x: Result<num::NonZero<u8>, = note: enum has no representation hint error: `extern` block uses type `Result<NonZero<u8>, B>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:127:53 + --> $DIR/lint-ctypes-enum.rs:145:53 | LL | fn result_1zst_exhaustive_multiple_variant_t(x: Result<num::NonZero<u8>, B>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -141,7 +167,7 @@ LL | fn result_1zst_exhaustive_multiple_variant_t(x: Result<num::NonZero<u8> = note: enum has no representation hint error: `extern` block uses type `Result<NonZero<u8>, NonExhaustive>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:129:51 + --> $DIR/lint-ctypes-enum.rs:147:51 | LL | fn result_1zst_non_exhaustive_no_variant_t(x: Result<num::NonZero<u8>, NonExhaustive>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -150,7 +176,7 @@ LL | fn result_1zst_non_exhaustive_no_variant_t(x: Result<num::NonZero<u8>, = note: enum has no representation hint error: `extern` block uses type `Result<NonZero<u8>, Field>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:132:49 + --> $DIR/lint-ctypes-enum.rs:150:49 | LL | fn result_1zst_exhaustive_single_field_t(x: Result<num::NonZero<u8>, Field>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -159,7 +185,7 @@ LL | fn result_1zst_exhaustive_single_field_t(x: Result<num::NonZero<u8>, Fi = note: enum has no representation hint error: `extern` block uses type `Result<Result<(), NonZero<u8>>, ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:134:30 + --> $DIR/lint-ctypes-enum.rs:152:30 | LL | fn result_cascading_t(x: Result<Result<(), num::NonZero<u8>>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -168,7 +194,7 @@ LL | fn result_cascading_t(x: Result<Result<(), num::NonZero<u8>>, ()>); = note: enum has no representation hint error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:145:33 + --> $DIR/lint-ctypes-enum.rs:163:33 | LL | fn result_nonzero_u128_e(x: Result<(), num::NonZero<u128>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -176,7 +202,7 @@ LL | fn result_nonzero_u128_e(x: Result<(), num::NonZero<u128>>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:152:33 + --> $DIR/lint-ctypes-enum.rs:170:33 | LL | fn result_nonzero_i128_e(x: Result<(), num::NonZero<i128>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -184,7 +210,7 @@ LL | fn result_nonzero_i128_e(x: Result<(), num::NonZero<i128>>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `Result<(), TransparentUnion<NonZero<u8>>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:157:38 + --> $DIR/lint-ctypes-enum.rs:175:38 | LL | fn result_transparent_union_e(x: Result<(), TransparentUnion<num::NonZero<u8>>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -193,7 +219,7 @@ LL | fn result_transparent_union_e(x: Result<(), TransparentUnion<num::NonZe = note: enum has no representation hint error: `extern` block uses type `Result<(), Rust<NonZero<u8>>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:159:30 + --> $DIR/lint-ctypes-enum.rs:177:30 | LL | fn result_repr_rust_e(x: Result<(), Rust<num::NonZero<u8>>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -202,7 +228,7 @@ LL | fn result_repr_rust_e(x: Result<(), Rust<num::NonZero<u8>>>); = note: enum has no representation hint error: `extern` block uses type `Result<U, NonZero<u8>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:163:51 + --> $DIR/lint-ctypes-enum.rs:181:51 | LL | fn result_1zst_exhaustive_single_variant_e(x: Result<U, num::NonZero<u8>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -211,7 +237,7 @@ LL | fn result_1zst_exhaustive_single_variant_e(x: Result<U, num::NonZero<u8 = note: enum has no representation hint error: `extern` block uses type `Result<B, NonZero<u8>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:165:53 + --> $DIR/lint-ctypes-enum.rs:183:53 | LL | fn result_1zst_exhaustive_multiple_variant_e(x: Result<B, num::NonZero<u8>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -220,7 +246,7 @@ LL | fn result_1zst_exhaustive_multiple_variant_e(x: Result<B, num::NonZero< = note: enum has no representation hint error: `extern` block uses type `Result<NonExhaustive, NonZero<u8>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:167:51 + --> $DIR/lint-ctypes-enum.rs:185:51 | LL | fn result_1zst_non_exhaustive_no_variant_e(x: Result<NonExhaustive, num::NonZero<u8>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -229,7 +255,7 @@ LL | fn result_1zst_non_exhaustive_no_variant_e(x: Result<NonExhaustive, num = note: enum has no representation hint error: `extern` block uses type `Result<Field, NonZero<u8>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:170:49 + --> $DIR/lint-ctypes-enum.rs:188:49 | LL | fn result_1zst_exhaustive_single_field_e(x: Result<Field, num::NonZero<u8>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -238,7 +264,7 @@ LL | fn result_1zst_exhaustive_single_field_e(x: Result<Field, num::NonZero< = note: enum has no representation hint error: `extern` block uses type `Result<(), Result<(), NonZero<u8>>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:172:30 + --> $DIR/lint-ctypes-enum.rs:190:30 | LL | fn result_cascading_e(x: Result<(), Result<(), num::NonZero<u8>>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -247,7 +273,7 @@ LL | fn result_cascading_e(x: Result<(), Result<(), num::NonZero<u8>>>); = note: enum has no representation hint error: `extern` block uses type `Result<(), ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:174:27 + --> $DIR/lint-ctypes-enum.rs:192:27 | LL | fn result_unit_t_e(x: Result<(), ()>); | ^^^^^^^^^^^^^^ not FFI-safe @@ -255,5 +281,5 @@ LL | fn result_unit_t_e(x: Result<(), ()>); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint -error: aborting due to 27 previous errors +error: aborting due to 29 previous errors diff --git a/tests/ui/macros/macros-nonfatal-errors.rs b/tests/ui/macros/macros-nonfatal-errors.rs index 79beffbe986..091d64ea5d9 100644 --- a/tests/ui/macros/macros-nonfatal-errors.rs +++ b/tests/ui/macros/macros-nonfatal-errors.rs @@ -5,6 +5,7 @@ #![feature(trace_macros, concat_idents)] #![feature(stmt_expr_attributes)] +#![expect(deprecated)] // concat_idents is deprecated use std::arch::asm; diff --git a/tests/ui/macros/macros-nonfatal-errors.stderr b/tests/ui/macros/macros-nonfatal-errors.stderr index 44194b506a4..2f990cb24e2 100644 --- a/tests/ui/macros/macros-nonfatal-errors.stderr +++ b/tests/ui/macros/macros-nonfatal-errors.stderr @@ -1,5 +1,5 @@ error: the `#[default]` attribute may only be used on unit enum variants - --> $DIR/macros-nonfatal-errors.rs:13:5 + --> $DIR/macros-nonfatal-errors.rs:14:5 | LL | #[default] | ^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | #[default] = help: consider a manual implementation of `Default` error: the `#[default]` attribute may only be used on unit enum variants - --> $DIR/macros-nonfatal-errors.rs:18:36 + --> $DIR/macros-nonfatal-errors.rs:19:36 | LL | struct DefaultInnerAttrTupleStruct(#[default] ()); | ^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | struct DefaultInnerAttrTupleStruct(#[default] ()); = help: consider a manual implementation of `Default` error: the `#[default]` attribute may only be used on unit enum variants - --> $DIR/macros-nonfatal-errors.rs:22:1 + --> $DIR/macros-nonfatal-errors.rs:23:1 | LL | #[default] | ^^^^^^^^^^ @@ -23,7 +23,7 @@ LL | #[default] = help: consider a manual implementation of `Default` error: the `#[default]` attribute may only be used on unit enum variants - --> $DIR/macros-nonfatal-errors.rs:26:1 + --> $DIR/macros-nonfatal-errors.rs:27:1 | LL | #[default] | ^^^^^^^^^^ @@ -31,7 +31,7 @@ LL | #[default] = help: consider a manual implementation of `Default` error: the `#[default]` attribute may only be used on unit enum variants - --> $DIR/macros-nonfatal-errors.rs:36:11 + --> $DIR/macros-nonfatal-errors.rs:37:11 | LL | Foo = #[default] 0, | ^^^^^^^^^^ @@ -39,7 +39,7 @@ LL | Foo = #[default] 0, = help: consider a manual implementation of `Default` error: the `#[default]` attribute may only be used on unit enum variants - --> $DIR/macros-nonfatal-errors.rs:37:14 + --> $DIR/macros-nonfatal-errors.rs:38:14 | LL | Bar([u8; #[default] 1]), | ^^^^^^^^^^ @@ -47,7 +47,7 @@ LL | Bar([u8; #[default] 1]), = help: consider a manual implementation of `Default` error[E0665]: `#[derive(Default)]` on enum with no `#[default]` - --> $DIR/macros-nonfatal-errors.rs:42:10 + --> $DIR/macros-nonfatal-errors.rs:43:10 | LL | #[derive(Default)] | ^^^^^^^ @@ -67,7 +67,7 @@ LL | #[default] Bar, | ++++++++++ error[E0665]: `#[derive(Default)]` on enum with no `#[default]` - --> $DIR/macros-nonfatal-errors.rs:48:10 + --> $DIR/macros-nonfatal-errors.rs:49:10 | LL | #[derive(Default)] | ^^^^^^^ @@ -78,7 +78,7 @@ LL | | } | |_- this enum needs a unit variant marked with `#[default]` error: multiple declared defaults - --> $DIR/macros-nonfatal-errors.rs:54:10 + --> $DIR/macros-nonfatal-errors.rs:55:10 | LL | #[derive(Default)] | ^^^^^^^ @@ -95,7 +95,7 @@ LL | Baz, = note: only one variant can be default error: `#[default]` attribute does not accept a value - --> $DIR/macros-nonfatal-errors.rs:66:5 + --> $DIR/macros-nonfatal-errors.rs:67:5 | LL | #[default = 1] | ^^^^^^^^^^^^^^ @@ -103,7 +103,7 @@ LL | #[default = 1] = help: try using `#[default]` error: multiple `#[default]` attributes - --> $DIR/macros-nonfatal-errors.rs:74:5 + --> $DIR/macros-nonfatal-errors.rs:75:5 | LL | #[default] | ---------- `#[default]` used here @@ -114,13 +114,13 @@ LL | Foo, | = note: only one `#[default]` attribute is needed help: try removing this - --> $DIR/macros-nonfatal-errors.rs:73:5 + --> $DIR/macros-nonfatal-errors.rs:74:5 | LL | #[default] | ^^^^^^^^^^ error: multiple `#[default]` attributes - --> $DIR/macros-nonfatal-errors.rs:84:5 + --> $DIR/macros-nonfatal-errors.rs:85:5 | LL | #[default] | ---------- `#[default]` used here @@ -132,7 +132,7 @@ LL | Foo, | = note: only one `#[default]` attribute is needed help: try removing these - --> $DIR/macros-nonfatal-errors.rs:81:5 + --> $DIR/macros-nonfatal-errors.rs:82:5 | LL | #[default] | ^^^^^^^^^^ @@ -142,7 +142,7 @@ LL | #[default] | ^^^^^^^^^^ error: the `#[default]` attribute may only be used on unit enum variants - --> $DIR/macros-nonfatal-errors.rs:91:5 + --> $DIR/macros-nonfatal-errors.rs:92:5 | LL | Foo {}, | ^^^ @@ -150,7 +150,7 @@ LL | Foo {}, = help: consider a manual implementation of `Default` error: default variant must be exhaustive - --> $DIR/macros-nonfatal-errors.rs:99:5 + --> $DIR/macros-nonfatal-errors.rs:100:5 | LL | #[non_exhaustive] | ----------------- declared `#[non_exhaustive]` here @@ -160,37 +160,37 @@ LL | Foo, = help: consider a manual implementation of `Default` error: asm template must be a string literal - --> $DIR/macros-nonfatal-errors.rs:104:10 + --> $DIR/macros-nonfatal-errors.rs:105:10 | LL | asm!(invalid); | ^^^^^^^ error: `concat_idents!()` requires ident args - --> $DIR/macros-nonfatal-errors.rs:107:5 + --> $DIR/macros-nonfatal-errors.rs:108:5 | LL | concat_idents!("not", "idents"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: argument must be a string literal - --> $DIR/macros-nonfatal-errors.rs:109:17 + --> $DIR/macros-nonfatal-errors.rs:110:17 | LL | option_env!(invalid); | ^^^^^^^ error: expected string literal - --> $DIR/macros-nonfatal-errors.rs:110:10 + --> $DIR/macros-nonfatal-errors.rs:111:10 | LL | env!(invalid); | ^^^^^^^ error: `env!()` takes 1 or 2 arguments - --> $DIR/macros-nonfatal-errors.rs:111:5 + --> $DIR/macros-nonfatal-errors.rs:112:5 | LL | env!(foo, abr, baz); | ^^^^^^^^^^^^^^^^^^^ error: environment variable `RUST_HOPEFULLY_THIS_DOESNT_EXIST` not defined at compile time - --> $DIR/macros-nonfatal-errors.rs:112:5 + --> $DIR/macros-nonfatal-errors.rs:113:5 | LL | env!("RUST_HOPEFULLY_THIS_DOESNT_EXIST"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -198,7 +198,7 @@ LL | env!("RUST_HOPEFULLY_THIS_DOESNT_EXIST"); = help: use `std::env::var("RUST_HOPEFULLY_THIS_DOESNT_EXIST")` to read the variable at run time error: format argument must be a string literal - --> $DIR/macros-nonfatal-errors.rs:114:13 + --> $DIR/macros-nonfatal-errors.rs:115:13 | LL | format!(invalid); | ^^^^^^^ @@ -209,43 +209,43 @@ LL | format!("{}", invalid); | +++++ error: argument must be a string literal - --> $DIR/macros-nonfatal-errors.rs:116:14 + --> $DIR/macros-nonfatal-errors.rs:117:14 | LL | include!(invalid); | ^^^^^^^ error: argument must be a string literal - --> $DIR/macros-nonfatal-errors.rs:118:18 + --> $DIR/macros-nonfatal-errors.rs:119:18 | LL | include_str!(invalid); | ^^^^^^^ error: couldn't read `$DIR/i'd be quite surprised if a file with this name existed`: $FILE_NOT_FOUND_MSG - --> $DIR/macros-nonfatal-errors.rs:119:5 + --> $DIR/macros-nonfatal-errors.rs:120:5 | LL | include_str!("i'd be quite surprised if a file with this name existed"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: argument must be a string literal - --> $DIR/macros-nonfatal-errors.rs:120:20 + --> $DIR/macros-nonfatal-errors.rs:121:20 | LL | include_bytes!(invalid); | ^^^^^^^ error: couldn't read `$DIR/i'd be quite surprised if a file with this name existed`: $FILE_NOT_FOUND_MSG - --> $DIR/macros-nonfatal-errors.rs:121:5 + --> $DIR/macros-nonfatal-errors.rs:122:5 | LL | include_bytes!("i'd be quite surprised if a file with this name existed"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: trace_macros! accepts only `true` or `false` - --> $DIR/macros-nonfatal-errors.rs:123:5 + --> $DIR/macros-nonfatal-errors.rs:124:5 | LL | trace_macros!(invalid); | ^^^^^^^^^^^^^^^^^^^^^^ error: default variant must be exhaustive - --> $DIR/macros-nonfatal-errors.rs:133:9 + --> $DIR/macros-nonfatal-errors.rs:134:9 | LL | #[non_exhaustive] | ----------------- declared `#[non_exhaustive]` here @@ -255,7 +255,7 @@ LL | Foo, = help: consider a manual implementation of `Default` error: cannot find macro `llvm_asm` in this scope - --> $DIR/macros-nonfatal-errors.rs:105:5 + --> $DIR/macros-nonfatal-errors.rs:106:5 | LL | llvm_asm!(invalid); | ^^^^^^^^ diff --git a/tests/ui/fail-simple.rs b/tests/ui/macros/no-matching-rule.rs index 55e547ee72b..55e547ee72b 100644 --- a/tests/ui/fail-simple.rs +++ b/tests/ui/macros/no-matching-rule.rs diff --git a/tests/ui/fail-simple.stderr b/tests/ui/macros/no-matching-rule.stderr index 50c350b3ef5..a6312a843f3 100644 --- a/tests/ui/fail-simple.stderr +++ b/tests/ui/macros/no-matching-rule.stderr @@ -1,5 +1,5 @@ error: no rules expected `@` - --> $DIR/fail-simple.rs:2:12 + --> $DIR/no-matching-rule.rs:2:12 | LL | panic!(@); | ^ no rules expected this token in macro call diff --git a/tests/ui/methods/clone-missing.rs b/tests/ui/methods/clone-missing.rs new file mode 100644 index 00000000000..f2e4ad268c6 --- /dev/null +++ b/tests/ui/methods/clone-missing.rs @@ -0,0 +1,19 @@ +// This test checks that calling `.clone()` on a type that does not implement the `Clone` trait +// results in a compilation error. The `Foo` struct does not derive or implement `Clone`, +// so attempting to clone it should fail. + +struct Foo { + i: isize, +} + +fn foo(i:isize) -> Foo { + Foo { + i: i + } +} + +fn main() { + let x = foo(10); + let _y = x.clone(); + //~^ ERROR no method named `clone` found +} diff --git a/tests/ui/copy-a-resource.stderr b/tests/ui/methods/clone-missing.stderr index ff1e28bf961..4ab1aae4934 100644 --- a/tests/ui/copy-a-resource.stderr +++ b/tests/ui/methods/clone-missing.stderr @@ -1,5 +1,5 @@ error[E0599]: no method named `clone` found for struct `Foo` in the current scope - --> $DIR/copy-a-resource.rs:18:16 + --> $DIR/clone-missing.rs:17:16 | LL | struct Foo { | ---------- method `clone` not found for this struct diff --git a/tests/ui/modules/mod-pub-access.rs b/tests/ui/modules/mod-pub-access.rs new file mode 100644 index 00000000000..c07e7a2ff30 --- /dev/null +++ b/tests/ui/modules/mod-pub-access.rs @@ -0,0 +1,11 @@ +//@ run-pass +// This is a name resolution smoke test that ensures paths with more than one +// segment (e.g., `foo::bar`) resolve correctly. +// It also serves as a basic visibility test — confirming that a `pub` item +// inside a private module can still be accessed from outside that module. + +mod foo { + pub fn bar(_offset: usize) {} +} + +fn main() { foo::bar(0); } diff --git a/tests/ui/parser/or-in-let-chain.edition2021.stderr b/tests/ui/parser/or-in-let-chain.edition2021.stderr new file mode 100644 index 00000000000..a97095cc3b8 --- /dev/null +++ b/tests/ui/parser/or-in-let-chain.edition2021.stderr @@ -0,0 +1,28 @@ +error: `||` operators are not supported in let chain conditions + --> $DIR/or-in-let-chain.rs:6:24 + | +LL | if let true = true || false {} + | ^^ + +error: expected expression, found `let` statement + --> $DIR/or-in-let-chain.rs:9:9 + | +LL | if (let true = true) || false {} + | ^^^^^^^^^^^^^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions + +error: `||` operators are not supported in let chain conditions + --> $DIR/or-in-let-chain.rs:12:24 + | +LL | if let true = true || false || true {} + | ^^ + +error: `||` operators are not supported in let chain conditions + --> $DIR/or-in-let-chain.rs:15:33 + | +LL | if let true = true && false || true {} + | ^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/parser/or-in-let-chain.edition2024.stderr b/tests/ui/parser/or-in-let-chain.edition2024.stderr new file mode 100644 index 00000000000..a97095cc3b8 --- /dev/null +++ b/tests/ui/parser/or-in-let-chain.edition2024.stderr @@ -0,0 +1,28 @@ +error: `||` operators are not supported in let chain conditions + --> $DIR/or-in-let-chain.rs:6:24 + | +LL | if let true = true || false {} + | ^^ + +error: expected expression, found `let` statement + --> $DIR/or-in-let-chain.rs:9:9 + | +LL | if (let true = true) || false {} + | ^^^^^^^^^^^^^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions + +error: `||` operators are not supported in let chain conditions + --> $DIR/or-in-let-chain.rs:12:24 + | +LL | if let true = true || false || true {} + | ^^ + +error: `||` operators are not supported in let chain conditions + --> $DIR/or-in-let-chain.rs:15:33 + | +LL | if let true = true && false || true {} + | ^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/parser/or-in-let-chain.rs b/tests/ui/parser/or-in-let-chain.rs new file mode 100644 index 00000000000..4c4372bb00f --- /dev/null +++ b/tests/ui/parser/or-in-let-chain.rs @@ -0,0 +1,17 @@ +//@ revisions: edition2021 edition2024 +//@ [edition2021] edition: 2021 +//@ [edition2024] edition: 2024 + +fn main() { + if let true = true || false {} + //~^ ERROR `||` operators are not supported in let chain conditions + // With parentheses + if (let true = true) || false {} + //~^ ERROR expected expression, found `let` statement + // Multiple || operators + if let true = true || false || true {} + //~^ ERROR `||` operators are not supported in let chain conditions + // Mixed operators (should still show error for ||) + if let true = true && false || true {} + //~^ ERROR `||` operators are not supported in let chain conditions +} diff --git a/tests/ui/parser/ty-path-followed-by-single-colon.rs b/tests/ui/parser/ty-path-followed-by-single-colon.rs new file mode 100644 index 00000000000..a9082ea317a --- /dev/null +++ b/tests/ui/parser/ty-path-followed-by-single-colon.rs @@ -0,0 +1,22 @@ +// Paths in type contexts may be followed by single colons. +// This means we can't generally assume that the user typo'ed a double colon. +// issue: <https://github.com/rust-lang/rust/issues/140227> +//@ check-pass +#![crate_type = "lib"] +#![expect(non_camel_case_types)] + +#[rustfmt::skip] +mod garden { + + fn f<path>() where path:to::somewhere {} // OK! + + fn g(_: impl Take<path:to::somewhere>) {} // OK! + + #[cfg(any())] fn h() where a::path:to::nowhere {} // OK! + + fn i(_: impl Take<path::<>:to::somewhere>) {} // OK! + + mod to { pub(super) trait somewhere {} } + trait Take { type path; } + +} diff --git a/tests/ui/path.rs b/tests/ui/path.rs deleted file mode 100644 index bd7b99ac01a..00000000000 --- a/tests/ui/path.rs +++ /dev/null @@ -1,7 +0,0 @@ -//@ run-pass - -mod foo { - pub fn bar(_offset: usize) { } -} - -pub fn main() { foo::bar(0); } diff --git a/tests/ui/capture1.rs b/tests/ui/resolve/fn-item-cant-capture-dynamic-env.rs index 4f8c04e3fa9..4f8c04e3fa9 100644 --- a/tests/ui/capture1.rs +++ b/tests/ui/resolve/fn-item-cant-capture-dynamic-env.rs diff --git a/tests/ui/capture1.stderr b/tests/ui/resolve/fn-item-cant-capture-dynamic-env.stderr index 8027430de52..6b3e8792011 100644 --- a/tests/ui/capture1.stderr +++ b/tests/ui/resolve/fn-item-cant-capture-dynamic-env.stderr @@ -1,5 +1,5 @@ error[E0434]: can't capture dynamic environment in a fn item - --> $DIR/capture1.rs:3:32 + --> $DIR/fn-item-cant-capture-dynamic-env.rs:3:32 | LL | fn foo() -> isize { return bar; } | ^^^ diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.rs index ae13f7c76ba..ed461af66aa 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.rs +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.rs @@ -3,7 +3,7 @@ fn let_or_guard(x: Result<Option<i32>, ()>) { match x { Ok(opt) if let Some(4) = opt || false => {} - //~^ ERROR expected expression, found `let` statement + //~^ ERROR `||` operators are not supported in let chain conditions _ => {} } } diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.stderr index 4b85fdd5050..0566d96fddc 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.stderr +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.stderr @@ -1,11 +1,4 @@ -error: expected expression, found `let` statement - --> $DIR/ast-validate-guards.rs:5:20 - | -LL | Ok(opt) if let Some(4) = opt || false => {} - | ^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions +error: `||` operators are not supported in let chain conditions --> $DIR/ast-validate-guards.rs:5:38 | LL | Ok(opt) if let Some(4) = opt || false => {} diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.feature.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.feature.stderr index 817e226bc45..141a6d255d0 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.feature.stderr +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.feature.stderr @@ -272,14 +272,7 @@ LL | if (let 0 = 0)? {} | = note: only supported directly in conditions of `if` and `while` expressions -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:121:16 - | -LL | if true || let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions +error: `||` operators are not supported in let chain conditions --> $DIR/disallowed-positions.rs:121:13 | LL | if true || let 0 = 0 {} @@ -485,14 +478,7 @@ LL | while (let 0 = 0)? {} | = note: only supported directly in conditions of `if` and `while` expressions -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:212:19 - | -LL | while true || let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions +error: `||` operators are not supported in let chain conditions --> $DIR/disallowed-positions.rs:212:16 | LL | while true || let 0 = 0 {} diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.no_feature.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.no_feature.stderr index bab50c22c03..dda09de4c53 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.no_feature.stderr +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.no_feature.stderr @@ -272,14 +272,7 @@ LL | if (let 0 = 0)? {} | = note: only supported directly in conditions of `if` and `while` expressions -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:121:16 - | -LL | if true || let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions +error: `||` operators are not supported in let chain conditions --> $DIR/disallowed-positions.rs:121:13 | LL | if true || let 0 = 0 {} @@ -485,14 +478,7 @@ LL | while (let 0 = 0)? {} | = note: only supported directly in conditions of `if` and `while` expressions -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:212:19 - | -LL | while true || let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions +error: `||` operators are not supported in let chain conditions --> $DIR/disallowed-positions.rs:212:16 | LL | while true || let 0 = 0 {} diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.nothing.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.nothing.stderr index 943956feb4e..5b53691cbf5 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.nothing.stderr +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.nothing.stderr @@ -272,14 +272,7 @@ LL | if (let 0 = 0)? {} | = note: only supported directly in conditions of `if` and `while` expressions -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:121:16 - | -LL | if true || let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions +error: `||` operators are not supported in let chain conditions --> $DIR/disallowed-positions.rs:121:13 | LL | if true || let 0 = 0 {} @@ -485,14 +478,7 @@ LL | while (let 0 = 0)? {} | = note: only supported directly in conditions of `if` and `while` expressions -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:212:19 - | -LL | while true || let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions +error: `||` operators are not supported in let chain conditions --> $DIR/disallowed-positions.rs:212:16 | LL | while true || let 0 = 0 {} diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.rs index 0b0abe6ec17..65beccf2214 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.rs +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.rs @@ -119,7 +119,7 @@ fn nested_within_if_expr() { //~^ ERROR expected expression, found `let` statement if true || let 0 = 0 {} - //~^ ERROR expected expression, found `let` statement + //~^ ERROR `||` operators are not supported in let chain conditions if (true || let 0 = 0) {} //~^ ERROR expected expression, found `let` statement if true && (true || let 0 = 0) {} @@ -210,7 +210,7 @@ fn nested_within_while_expr() { //~^ ERROR expected expression, found `let` statement while true || let 0 = 0 {} - //~^ ERROR expected expression, found `let` statement + //~^ ERROR `||` operators are not supported in let chain conditions while (true || let 0 = 0) {} //~^ ERROR expected expression, found `let` statement while true && (true || let 0 = 0) {} diff --git a/tests/ui/simd/intrinsic/generic-comparison-pass.rs b/tests/ui/simd/intrinsic/generic-comparison-pass.rs index 2ee164cdfd8..50a05eecb03 100644 --- a/tests/ui/simd/intrinsic/generic-comparison-pass.rs +++ b/tests/ui/simd/intrinsic/generic-comparison-pass.rs @@ -1,6 +1,6 @@ //@ run-pass -#![feature(repr_simd, core_intrinsics, concat_idents)] +#![feature(repr_simd, core_intrinsics, macro_metavar_expr_concat)] #![allow(non_camel_case_types)] use std::intrinsics::simd::{simd_eq, simd_ge, simd_gt, simd_le, simd_lt, simd_ne}; @@ -19,7 +19,7 @@ macro_rules! cmp { ($method: ident($lhs: expr, $rhs: expr)) => {{ let lhs = $lhs; let rhs = $rhs; - let e: u32x4 = concat_idents!(simd_, $method)($lhs, $rhs); + let e: u32x4 = ${concat(simd_, $method)}($lhs, $rhs); // assume the scalar version is correct/the behaviour we want. assert!((e.0[0] != 0) == lhs.0[0].$method(&rhs.0[0])); assert!((e.0[1] != 0) == lhs.0[1].$method(&rhs.0[1])); diff --git a/tests/ui/specialization/prefer-specializing-impl-over-default.current.stderr b/tests/ui/specialization/prefer-specializing-impl-over-default.current.stderr new file mode 100644 index 00000000000..7e3df0c83f9 --- /dev/null +++ b/tests/ui/specialization/prefer-specializing-impl-over-default.current.stderr @@ -0,0 +1,12 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/prefer-specializing-impl-over-default.rs:5:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information + = help: consider using `min_specialization` instead, which is more stable and complete + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/specialization/prefer-specializing-impl-over-default.next.stderr b/tests/ui/specialization/prefer-specializing-impl-over-default.next.stderr new file mode 100644 index 00000000000..7e3df0c83f9 --- /dev/null +++ b/tests/ui/specialization/prefer-specializing-impl-over-default.next.stderr @@ -0,0 +1,12 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/prefer-specializing-impl-over-default.rs:5:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information + = help: consider using `min_specialization` instead, which is more stable and complete + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/specialization/prefer-specializing-impl-over-default.rs b/tests/ui/specialization/prefer-specializing-impl-over-default.rs new file mode 100644 index 00000000000..af6837b30ca --- /dev/null +++ b/tests/ui/specialization/prefer-specializing-impl-over-default.rs @@ -0,0 +1,29 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-pass +#![feature(specialization)] +//~^ WARN the feature `specialization` is incomplete + +trait WithAssoc: 'static { + type Assoc; +} +impl<T: 'static> WithAssoc for (T,) { + type Assoc = (); +} + +struct GenericArray<U: WithAssoc>(U::Assoc); + +trait AbiExample { + fn example(); +} +impl<U: WithAssoc> AbiExample for GenericArray<U> { + fn example() {} +} +impl<T> AbiExample for T { + default fn example() {} +} + +fn main() { + let _ = GenericArray::<((),)>::example(); +} diff --git a/tests/ui/specialization/specialization-default-projection.stderr b/tests/ui/specialization/specialization-default-projection.current.stderr index b8b81876d81..038c379c43e 100644 --- a/tests/ui/specialization/specialization-default-projection.stderr +++ b/tests/ui/specialization/specialization-default-projection.current.stderr @@ -1,5 +1,5 @@ warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/specialization-default-projection.rs:1:12 + --> $DIR/specialization-default-projection.rs:5:12 | LL | #![feature(specialization)] | ^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | #![feature(specialization)] = note: `#[warn(incomplete_features)]` on by default error[E0308]: mismatched types - --> $DIR/specialization-default-projection.rs:21:5 + --> $DIR/specialization-default-projection.rs:25:5 | LL | fn generic<T>() -> <T as Foo>::Assoc { | ----------------- expected `<T as Foo>::Assoc` because of return type @@ -23,7 +23,7 @@ LL | () = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error[E0308]: mismatched types - --> $DIR/specialization-default-projection.rs:28:5 + --> $DIR/specialization-default-projection.rs:32:5 | LL | fn monomorphic() -> () { | -- expected `()` because of return type diff --git a/tests/ui/specialization/specialization-default-projection.next.stderr b/tests/ui/specialization/specialization-default-projection.next.stderr new file mode 100644 index 00000000000..9111f173a9c --- /dev/null +++ b/tests/ui/specialization/specialization-default-projection.next.stderr @@ -0,0 +1,43 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-default-projection.rs:5:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information + = help: consider using `min_specialization` instead, which is more stable and complete + = note: `#[warn(incomplete_features)]` on by default + +error[E0308]: mismatched types + --> $DIR/specialization-default-projection.rs:25:5 + | +LL | fn generic<T>() -> <T as Foo>::Assoc { + | ----------------- expected `<T as Foo>::Assoc` because of return type +... +LL | () + | ^^ types differ + | + = note: expected associated type `<T as Foo>::Assoc` + found unit type `()` + = help: consider constraining the associated type `<T as Foo>::Assoc` to `()` or calling a method that returns `<T as Foo>::Assoc` + = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html + +error[E0308]: mismatched types + --> $DIR/specialization-default-projection.rs:32:5 + | +LL | fn monomorphic() -> () { + | -- expected `()` because of return type +... +LL | generic::<()>() + | ^^^^^^^^^^^^^^^- help: consider using a semicolon here: `;` + | | + | types differ + | + = note: expected unit type `()` + found associated type `<() as Foo>::Assoc` + = help: consider constraining the associated type `<() as Foo>::Assoc` to `()` + = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html + +error: aborting due to 2 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/specialization/specialization-default-projection.rs b/tests/ui/specialization/specialization-default-projection.rs index 7f3ae951287..4f69ccb5974 100644 --- a/tests/ui/specialization/specialization-default-projection.rs +++ b/tests/ui/specialization/specialization-default-projection.rs @@ -1,3 +1,7 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + #![feature(specialization)] //~ WARN the feature `specialization` is incomplete // Make sure we can't project defaulted associated types diff --git a/tests/ui/specialization/specialization-default-types.stderr b/tests/ui/specialization/specialization-default-types.current.stderr index 774ac953617..67477f9a6d5 100644 --- a/tests/ui/specialization/specialization-default-types.stderr +++ b/tests/ui/specialization/specialization-default-types.current.stderr @@ -1,5 +1,5 @@ warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/specialization-default-types.rs:5:12 + --> $DIR/specialization-default-types.rs:9:12 | LL | #![feature(specialization)] | ^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | #![feature(specialization)] = note: `#[warn(incomplete_features)]` on by default error[E0308]: mismatched types - --> $DIR/specialization-default-types.rs:15:9 + --> $DIR/specialization-default-types.rs:19:9 | LL | default type Output = Box<T>; | ----------------------------- associated type is `default` and may be overridden @@ -22,7 +22,7 @@ LL | Box::new(self) found struct `Box<T>` error[E0308]: mismatched types - --> $DIR/specialization-default-types.rs:25:5 + --> $DIR/specialization-default-types.rs:29:5 | LL | fn trouble<T>(t: T) -> Box<T> { | ------ expected `Box<T>` because of return type diff --git a/tests/ui/specialization/specialization-default-types.next.stderr b/tests/ui/specialization/specialization-default-types.next.stderr new file mode 100644 index 00000000000..4f7c4765446 --- /dev/null +++ b/tests/ui/specialization/specialization-default-types.next.stderr @@ -0,0 +1,39 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-default-types.rs:9:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information + = help: consider using `min_specialization` instead, which is more stable and complete + = note: `#[warn(incomplete_features)]` on by default + +error[E0308]: mismatched types + --> $DIR/specialization-default-types.rs:19:9 + | +LL | default type Output = Box<T>; + | ----------------------------- associated type is `default` and may be overridden +LL | default fn generate(self) -> Self::Output { + | ------------ expected `<T as Example>::Output` because of return type +LL | Box::new(self) + | ^^^^^^^^^^^^^^ types differ + | + = note: expected associated type `<T as Example>::Output` + found struct `Box<T>` + +error[E0308]: mismatched types + --> $DIR/specialization-default-types.rs:29:5 + | +LL | fn trouble<T>(t: T) -> Box<T> { + | ------ expected `Box<T>` because of return type +LL | Example::generate(t) + | ^^^^^^^^^^^^^^^^^^^^ types differ + | + = note: expected struct `Box<T>` + found associated type `<T as Example>::Output` + = help: consider constraining the associated type `<T as Example>::Output` to `Box<T>` + = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html + +error: aborting due to 2 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/specialization/specialization-default-types.rs b/tests/ui/specialization/specialization-default-types.rs index 346471f11e4..77817abea12 100644 --- a/tests/ui/specialization/specialization-default-types.rs +++ b/tests/ui/specialization/specialization-default-types.rs @@ -1,3 +1,7 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + // It should not be possible to use the concrete value of a defaulted // associated type in the impl defining it -- otherwise, what happens // if it's overridden? diff --git a/tests/ui/suggestions/argument-list-from-path-sep-error-129273.fixed b/tests/ui/suggestions/argument-list-from-path-sep-error-129273.fixed deleted file mode 100644 index f5dbf0c8b6f..00000000000 --- a/tests/ui/suggestions/argument-list-from-path-sep-error-129273.fixed +++ /dev/null @@ -1,15 +0,0 @@ -//@ run-rustfix - -use std::fmt; - -struct Hello; - -impl fmt::Display for Hello { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { //~ ERROR path separator must be a double colon - write!(f, "hello") - } -} - -fn main() { - let _ = Hello; -} diff --git a/tests/ui/suggestions/argument-list-from-path-sep-error-129273.rs b/tests/ui/suggestions/argument-list-from-path-sep-error-129273.rs deleted file mode 100644 index c41880a26f6..00000000000 --- a/tests/ui/suggestions/argument-list-from-path-sep-error-129273.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@ run-rustfix - -use std::fmt; - -struct Hello; - -impl fmt::Display for Hello { - fn fmt(&self, f: &mut fmt:Formatter) -> fmt::Result { //~ ERROR path separator must be a double colon - write!(f, "hello") - } -} - -fn main() { - let _ = Hello; -} diff --git a/tests/ui/suggestions/argument-list-from-path-sep-error-129273.stderr b/tests/ui/suggestions/argument-list-from-path-sep-error-129273.stderr deleted file mode 100644 index 713b071a625..00000000000 --- a/tests/ui/suggestions/argument-list-from-path-sep-error-129273.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error: path separator must be a double colon - --> $DIR/argument-list-from-path-sep-error-129273.rs:8:30 - | -LL | fn fmt(&self, f: &mut fmt:Formatter) -> fmt::Result { - | ^ - | -help: use a double colon instead - | -LL | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - | + - -error: aborting due to 1 previous error - diff --git a/tests/ui/suggestions/struct-field-type-including-single-colon.rs b/tests/ui/suggestions/struct-field-type-including-single-colon.rs index a3111028895..482641fc7ca 100644 --- a/tests/ui/suggestions/struct-field-type-including-single-colon.rs +++ b/tests/ui/suggestions/struct-field-type-including-single-colon.rs @@ -7,14 +7,14 @@ mod foo { struct Foo { a: foo:A, - //~^ ERROR path separator must be a double colon - //~| ERROR struct `A` is private + //~^ ERROR found single colon in a struct field type path + //~| ERROR expected `,`, or `}`, found `:` } struct Bar { b: foo::bar:B, - //~^ ERROR path separator must be a double colon - //~| ERROR module `bar` is private + //~^ ERROR found single colon in a struct field type path + //~| ERROR expected `,`, or `}`, found `:` } fn main() {} diff --git a/tests/ui/suggestions/struct-field-type-including-single-colon.stderr b/tests/ui/suggestions/struct-field-type-including-single-colon.stderr index b9302b0453d..5ffc5b40849 100644 --- a/tests/ui/suggestions/struct-field-type-including-single-colon.stderr +++ b/tests/ui/suggestions/struct-field-type-including-single-colon.stderr @@ -1,51 +1,40 @@ -error: path separator must be a double colon +error: found single colon in a struct field type path --> $DIR/struct-field-type-including-single-colon.rs:9:11 | LL | a: foo:A, | ^ | -help: use a double colon instead +help: write a path separator here | LL | a: foo::A, | + -error: path separator must be a double colon +error: expected `,`, or `}`, found `:` + --> $DIR/struct-field-type-including-single-colon.rs:9:11 + | +LL | struct Foo { + | --- while parsing this struct +LL | a: foo:A, + | ^ + +error: found single colon in a struct field type path --> $DIR/struct-field-type-including-single-colon.rs:15:16 | LL | b: foo::bar:B, | ^ | -help: use a double colon instead +help: write a path separator here | LL | b: foo::bar::B, | + -error[E0603]: struct `A` is private - --> $DIR/struct-field-type-including-single-colon.rs:9:12 - | -LL | a: foo:A, - | ^ private struct - | -note: the struct `A` is defined here - --> $DIR/struct-field-type-including-single-colon.rs:2:5 - | -LL | struct A; - | ^^^^^^^^^ - -error[E0603]: module `bar` is private - --> $DIR/struct-field-type-including-single-colon.rs:15:13 +error: expected `,`, or `}`, found `:` + --> $DIR/struct-field-type-including-single-colon.rs:15:16 | +LL | struct Bar { + | --- while parsing this struct LL | b: foo::bar:B, - | ^^^ - struct `B` is not publicly re-exported - | | - | private module - | -note: the module `bar` is defined here - --> $DIR/struct-field-type-including-single-colon.rs:3:5 - | -LL | mod bar { - | ^^^^^^^ + | ^ error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0603`. diff --git a/tests/ui/syntax-extension-minor.rs b/tests/ui/syntax-extension-minor.rs index cdd572b50fc..826990a89a5 100644 --- a/tests/ui/syntax-extension-minor.rs +++ b/tests/ui/syntax-extension-minor.rs @@ -1,6 +1,7 @@ //@ run-pass #![feature(concat_idents)] +#![expect(deprecated)] // concat_idents is deprecated pub fn main() { struct Foo; diff --git a/tests/ui/traits/next-solver/coerce-depth.rs b/tests/ui/traits/next-solver/coerce-depth.rs new file mode 100644 index 00000000000..c8fc3fcab59 --- /dev/null +++ b/tests/ui/traits/next-solver/coerce-depth.rs @@ -0,0 +1,31 @@ +//@ check-pass +//@ compile-flags: -Znext-solver + +// Ensure that a stack of coerce predicates doesn't end up overflowing when they get procesed +// in *reverse* order, which may require O(N) iterations of the fulfillment loop. + +#![recursion_limit = "16"] + +fn main() { + match 0 { + 0 => None, + 1 => None, + 2 => None, + 3 => None, + 4 => None, + 5 => None, + 6 => None, + 7 => None, + 8 => None, + 9 => None, + 10 => None, + 11 => None, + 12 => None, + 13 => None, + 14 => None, + 15 => None, + 16 => None, + 17 => None, + _ => Some(1u32), + }; +} diff --git a/tests/ui/traits/next-solver/normalize/eager-norm-pre-normalizes-to.rs b/tests/ui/traits/next-solver/normalize/eager-norm-pre-normalizes-to.rs new file mode 100644 index 00000000000..ea18ac54c05 --- /dev/null +++ b/tests/ui/traits/next-solver/normalize/eager-norm-pre-normalizes-to.rs @@ -0,0 +1,44 @@ +//@ check-pass +//@ compile-flags: -Znext-solver + +// A regression test for trait-system-refactor-initiative#184. +// +// When adding nested goals we replace aliases with infer vars +// and add `AliasRelate` goals to constrain them. When doing this +// for `NormalizesTo` goals, we then first tries to prove the +// `NormalizesTo` goal and then normalized the nested aliases. + +trait Trait<T> { + type Assoc; +} +impl<T, U> Trait<U> for T { + type Assoc = (); +} + +trait Id { + type This; +} +impl<T> Id for T { + type This = T; +} +trait Relate<T> { + type Alias; +} +impl<T, U> Relate<U> for T { + type Alias = <T as Trait<<U as Id>::This>>::Assoc; +} + + +fn guide_me<T: Trait<u32>>() { + // Normalizing `<T as Relate<i32>>::Alias` relates the associated type with an unconstrained + // term. This resulted in a `NormalizesTo(<T as Trait<<U as Id>::This>>::Assoc, ?x)` goal. + // We replace `<i32 as Id>::This` with an infer var `?y`, resulting in the following goals: + // - `NormalizesTo(<T as Trait<?y>::Assoc, ?x)` + // - `AliasRelate(<i32 as Id>::This, ?y)` + // + // When proving the `NormalizesTo` goal first, we incompletely constrain `?y` to `u32`, + // causing an unexpected type mismatch. + let _: <T as Relate<i32>>::Alias; +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/normalize/normalize-allow-too-many-vars.rs b/tests/ui/traits/next-solver/normalize/normalize-allow-too-many-vars.rs index 5284220ac38..3150d9a88d0 100644 --- a/tests/ui/traits/next-solver/normalize/normalize-allow-too-many-vars.rs +++ b/tests/ui/traits/next-solver/normalize/normalize-allow-too-many-vars.rs @@ -1,4 +1,5 @@ //@ check-pass +//@ compile-flags: -Znext-solver // When canonicalizing a response in the trait solver, we bail with overflow // if there are too many non-region inference variables. Doing so in normalizes-to diff --git a/tests/ui/traits/next-solver/specialization-transmute.rs b/tests/ui/traits/next-solver/specialization-transmute.rs index 376fa22ae19..f1447cd6a9e 100644 --- a/tests/ui/traits/next-solver/specialization-transmute.rs +++ b/tests/ui/traits/next-solver/specialization-transmute.rs @@ -10,11 +10,8 @@ trait Default { impl<T> Default for T { default type Id = T; - // This will be fixed by #111994 fn intu(&self) -> &Self::Id { - //~^ ERROR type annotations needed - //~| ERROR cannot normalize `<T as Default>::Id: '_` - self //~ ERROR cannot satisfy + self //~ ERROR mismatched types } } @@ -25,6 +22,7 @@ fn transmute<T: Default<Id = U>, U: Copy>(t: T) -> U { use std::num::NonZero; fn main() { - let s = transmute::<u8, Option<NonZero<u8>>>(0); //~ ERROR cannot satisfy + let s = transmute::<u8, Option<NonZero<u8>>>(0); + //~^ ERROR type mismatch resolving `<u8 as Default>::Id == Option<NonZero<u8>>` assert_eq!(s, None); } diff --git a/tests/ui/traits/next-solver/specialization-transmute.stderr b/tests/ui/traits/next-solver/specialization-transmute.stderr index eeb101911c4..8bd290ea197 100644 --- a/tests/ui/traits/next-solver/specialization-transmute.stderr +++ b/tests/ui/traits/next-solver/specialization-transmute.stderr @@ -8,37 +8,32 @@ LL | #![feature(specialization)] = help: consider using `min_specialization` instead, which is more stable and complete = note: `#[warn(incomplete_features)]` on by default -error: cannot normalize `<T as Default>::Id: '_` - --> $DIR/specialization-transmute.rs:14:5 +error[E0308]: mismatched types + --> $DIR/specialization-transmute.rs:14:9 | LL | fn intu(&self) -> &Self::Id { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0282]: type annotations needed - --> $DIR/specialization-transmute.rs:14:23 - | -LL | fn intu(&self) -> &Self::Id { - | ^^^^^^^^^ cannot infer type for reference `&<T as Default>::Id` - -error[E0284]: type annotations needed: cannot satisfy `<T as Default>::Id normalizes-to T` - --> $DIR/specialization-transmute.rs:17:9 - | + | --------- expected `&<T as Default>::Id` because of return type LL | self - | ^^^^ cannot satisfy `<T as Default>::Id normalizes-to T` + | ^^^^ types differ + | + = note: expected reference `&<T as Default>::Id` + found reference `&T` -error[E0284]: type annotations needed: cannot satisfy `<u8 as Default>::Id normalizes-to Option<NonZero<u8>>` - --> $DIR/specialization-transmute.rs:28:13 +error[E0271]: type mismatch resolving `<u8 as Default>::Id == Option<NonZero<u8>>` + --> $DIR/specialization-transmute.rs:25:50 | LL | let s = transmute::<u8, Option<NonZero<u8>>>(0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `<u8 as Default>::Id normalizes-to Option<NonZero<u8>>` + | ------------------------------------ ^ types differ + | | + | required by a bound introduced by this call | note: required by a bound in `transmute` - --> $DIR/specialization-transmute.rs:21:25 + --> $DIR/specialization-transmute.rs:18:25 | LL | fn transmute<T: Default<Id = U>, U: Copy>(t: T) -> U { | ^^^^^^ required by this bound in `transmute` -error: aborting due to 4 previous errors; 1 warning emitted +error: aborting due to 2 previous errors; 1 warning emitted -Some errors have detailed explanations: E0282, E0284. -For more information about an error, try `rustc --explain E0282`. +Some errors have detailed explanations: E0271, E0308. +For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/traits/next-solver/specialization-unconstrained.rs b/tests/ui/traits/next-solver/specialization-unconstrained.rs index 2bbe7840110..6835c0764d6 100644 --- a/tests/ui/traits/next-solver/specialization-unconstrained.rs +++ b/tests/ui/traits/next-solver/specialization-unconstrained.rs @@ -18,5 +18,5 @@ fn test<T: Default<Id = U>, U>() {} fn main() { test::<u32, ()>(); - //~^ ERROR cannot satisfy `<u32 as Default>::Id normalizes-to ()` + //~^ ERROR type mismatch resolving `<u32 as Default>::Id == ()` } diff --git a/tests/ui/traits/next-solver/specialization-unconstrained.stderr b/tests/ui/traits/next-solver/specialization-unconstrained.stderr index e1d785b554b..1bcf5eddb5b 100644 --- a/tests/ui/traits/next-solver/specialization-unconstrained.stderr +++ b/tests/ui/traits/next-solver/specialization-unconstrained.stderr @@ -8,11 +8,11 @@ LL | #![feature(specialization)] = help: consider using `min_specialization` instead, which is more stable and complete = note: `#[warn(incomplete_features)]` on by default -error[E0284]: type annotations needed: cannot satisfy `<u32 as Default>::Id normalizes-to ()` - --> $DIR/specialization-unconstrained.rs:20:5 +error[E0271]: type mismatch resolving `<u32 as Default>::Id == ()` + --> $DIR/specialization-unconstrained.rs:20:12 | LL | test::<u32, ()>(); - | ^^^^^^^^^^^^^^^^^ cannot satisfy `<u32 as Default>::Id normalizes-to ()` + | ^^^ types differ | note: required by a bound in `test` --> $DIR/specialization-unconstrained.rs:17:20 @@ -22,4 +22,4 @@ LL | fn test<T: Default<Id = U>, U>() {} error: aborting due to 1 previous error; 1 warning emitted -For more information about this error, try `rustc --explain E0284`. +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/transmutability/char.rs b/tests/ui/transmutability/char.rs new file mode 100644 index 00000000000..55a61537329 --- /dev/null +++ b/tests/ui/transmutability/char.rs @@ -0,0 +1,41 @@ +#![feature(never_type)] +#![feature(transmutability)] + +use std::mem::{Assume, TransmuteFrom}; + +pub fn is_transmutable<Src, Dst>() +where + Dst: TransmuteFrom<Src, { Assume::SAFETY }>, +{ +} + +fn main() { + is_transmutable::<char, u32>(); + + // `char`s can be in the following ranges: + // - [0, 0xD7FF] + // - [0xE000, 10FFFF] + // + // `Char` has variants whose tags are in the top and bottom of each range. + // It also has generic variants which are out of bounds of these ranges, but + // are generic on types which may be set to `!` to "disable" them in the + // transmutability analysis. + #[repr(u32)] + enum Char<B, C, D> { + A = 0, + B = 0xD7FF, + OverB(B) = 0xD800, + UnderC(C) = 0xDFFF, + C = 0xE000, + D = 0x10FFFF, + OverD(D) = 0x110000, + } + + is_transmutable::<Char<!, !, !>, char>(); + is_transmutable::<Char<(), !, !>, char>(); + //~^ ERROR cannot be safely transmuted into `char` + is_transmutable::<Char<!, (), !>, char>(); + //~^ ERROR cannot be safely transmuted into `char` + is_transmutable::<Char<!, !, ()>, char>(); + //~^ ERROR cannot be safely transmuted into `char` +} diff --git a/tests/ui/transmutability/char.stderr b/tests/ui/transmutability/char.stderr new file mode 100644 index 00000000000..98e7ae9c0d4 --- /dev/null +++ b/tests/ui/transmutability/char.stderr @@ -0,0 +1,48 @@ +error[E0277]: `main::Char<(), !, !>` cannot be safely transmuted into `char` + --> $DIR/char.rs:35:39 + | +LL | is_transmutable::<Char<(), !, !>, char>(); + | ^^^^ at least one value of `main::Char<(), !, !>` isn't a bit-valid value of `char` + | +note: required by a bound in `is_transmutable` + --> $DIR/char.rs:8:10 + | +LL | pub fn is_transmutable<Src, Dst>() + | --------------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom<Src, { Assume::SAFETY }>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable` + +error[E0277]: `main::Char<!, (), !>` cannot be safely transmuted into `char` + --> $DIR/char.rs:37:39 + | +LL | is_transmutable::<Char<!, (), !>, char>(); + | ^^^^ at least one value of `main::Char<!, (), !>` isn't a bit-valid value of `char` + | +note: required by a bound in `is_transmutable` + --> $DIR/char.rs:8:10 + | +LL | pub fn is_transmutable<Src, Dst>() + | --------------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom<Src, { Assume::SAFETY }>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable` + +error[E0277]: `main::Char<!, !, ()>` cannot be safely transmuted into `char` + --> $DIR/char.rs:39:39 + | +LL | is_transmutable::<Char<!, !, ()>, char>(); + | ^^^^ at least one value of `main::Char<!, !, ()>` isn't a bit-valid value of `char` + | +note: required by a bound in `is_transmutable` + --> $DIR/char.rs:8:10 + | +LL | pub fn is_transmutable<Src, Dst>() + | --------------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom<Src, { Assume::SAFETY }>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/transmute/auxiliary/unnecessary-transmute-path-remap-ice-140277-trans.rs b/tests/ui/transmute/auxiliary/unnecessary-transmute-path-remap-ice-140277-trans.rs new file mode 100644 index 00000000000..0f273a6f536 --- /dev/null +++ b/tests/ui/transmute/auxiliary/unnecessary-transmute-path-remap-ice-140277-trans.rs @@ -0,0 +1,10 @@ +//@ compile-flags: --remap-path-prefix=/=/non-existent +// helper for ../unnecessary-transmute-path-remap-ice-140277.rs + +#[macro_export] +macro_rules! transmute { + ($e:expr) => {{ + let e = $e; + std::mem::transmute(e) + }}; +} diff --git a/tests/ui/transmute/unnecessary-transmutation.fixed b/tests/ui/transmute/unnecessary-transmutation.fixed new file mode 100644 index 00000000000..1a0df143cc5 --- /dev/null +++ b/tests/ui/transmute/unnecessary-transmutation.fixed @@ -0,0 +1,85 @@ +//@ run-rustfix +#![deny(unnecessary_transmutes)] +#![allow(unused_unsafe, unused_imports, unused_variables, unused_parens)] +use std::mem::transmute; + +pub fn bytes_at_home(x: u32) -> [u8; 4] { + unsafe { u32::to_ne_bytes(x) } + //~^ ERROR +} + +fn main() { + unsafe { + let x: u16 = u16::from_ne_bytes(*b"01"); + //~^ ERROR + let x: [u8; 2] = u16::to_ne_bytes(x); + //~^ ERROR + let x: u32 = u32::from_ne_bytes(*b"0123"); + //~^ ERROR + let x: [u8; 4] = u32::to_ne_bytes(x); + //~^ ERROR + let x: u64 = u64::from_ne_bytes(*b"feriscat"); + //~^ ERROR + let x: [u8; 8] = u64::to_ne_bytes(x); + //~^ ERROR + + let y: i16 = i16::from_ne_bytes(*b"01"); + //~^ ERROR + let y: [u8; 2] = i16::to_ne_bytes(y); + //~^ ERROR + let y: i32 = i32::from_ne_bytes(*b"0123"); + //~^ ERROR + let y: [u8; 4] = i32::to_ne_bytes(y); + //~^ ERROR + let y: i64 = i64::from_ne_bytes(*b"feriscat"); + //~^ ERROR + let y: [u8; 8] = i64::to_ne_bytes(y); + //~^ ERROR + + let z: f32 = f32::from_ne_bytes(*b"0123"); + //~^ ERROR + let z: [u8; 4] = f32::to_ne_bytes(z); + //~^ ERROR + let z: f64 = f64::from_ne_bytes(*b"feriscat"); + //~^ ERROR + let z: [u8; 8] = f64::to_ne_bytes(z); + //~^ ERROR + + let y: u32 = u32::from('🦀'); + //~^ ERROR + let y: char = char::from_u32_unchecked(y); + //~^ ERROR + + let x: u16 = i16::cast_unsigned(8i16); + //~^ ERROR + let x: i16 = u16::cast_signed(x); + //~^ ERROR + let x: u32 = i32::cast_unsigned(4i32); + //~^ ERROR + let x: i32 = u32::cast_signed(x); + //~^ ERROR + let x: u64 = i64::cast_unsigned(7i64); + //~^ ERROR + let x: i64 = u64::cast_signed(x); + //~^ ERROR + + let y: f32 = f32::from_bits(1u32); + //~^ ERROR + let y: u32 = f32::to_bits(y); + //~^ ERROR + let y: f64 = f64::from_bits(3u64); + //~^ ERROR + let y: u64 = f64::to_bits(2.0); + //~^ ERROR + + let z: bool = (1u8 == 1); + //~^ ERROR + let z: u8 = (z) as u8; + //~^ ERROR + + let z: bool = transmute(1i8); + // no error! + let z: i8 = (z) as i8; + //~^ ERROR + } +} diff --git a/tests/ui/transmute/unnecessary-transmutation.rs b/tests/ui/transmute/unnecessary-transmutation.rs new file mode 100644 index 00000000000..6b979263c56 --- /dev/null +++ b/tests/ui/transmute/unnecessary-transmutation.rs @@ -0,0 +1,85 @@ +//@ run-rustfix +#![deny(unnecessary_transmutes)] +#![allow(unused_unsafe, unused_imports, unused_variables, unused_parens)] +use std::mem::transmute; + +pub fn bytes_at_home(x: u32) -> [u8; 4] { + unsafe { transmute(x) } + //~^ ERROR +} + +fn main() { + unsafe { + let x: u16 = transmute(*b"01"); + //~^ ERROR + let x: [u8; 2] = transmute(x); + //~^ ERROR + let x: u32 = transmute(*b"0123"); + //~^ ERROR + let x: [u8; 4] = transmute(x); + //~^ ERROR + let x: u64 = transmute(*b"feriscat"); + //~^ ERROR + let x: [u8; 8] = transmute(x); + //~^ ERROR + + let y: i16 = transmute(*b"01"); + //~^ ERROR + let y: [u8; 2] = transmute(y); + //~^ ERROR + let y: i32 = transmute(*b"0123"); + //~^ ERROR + let y: [u8; 4] = transmute(y); + //~^ ERROR + let y: i64 = transmute(*b"feriscat"); + //~^ ERROR + let y: [u8; 8] = transmute(y); + //~^ ERROR + + let z: f32 = transmute(*b"0123"); + //~^ ERROR + let z: [u8; 4] = transmute(z); + //~^ ERROR + let z: f64 = transmute(*b"feriscat"); + //~^ ERROR + let z: [u8; 8] = transmute(z); + //~^ ERROR + + let y: u32 = transmute('🦀'); + //~^ ERROR + let y: char = transmute(y); + //~^ ERROR + + let x: u16 = transmute(8i16); + //~^ ERROR + let x: i16 = transmute(x); + //~^ ERROR + let x: u32 = transmute(4i32); + //~^ ERROR + let x: i32 = transmute(x); + //~^ ERROR + let x: u64 = transmute(7i64); + //~^ ERROR + let x: i64 = transmute(x); + //~^ ERROR + + let y: f32 = transmute(1u32); + //~^ ERROR + let y: u32 = transmute(y); + //~^ ERROR + let y: f64 = transmute(3u64); + //~^ ERROR + let y: u64 = transmute(2.0); + //~^ ERROR + + let z: bool = transmute(1u8); + //~^ ERROR + let z: u8 = transmute(z); + //~^ ERROR + + let z: bool = transmute(1i8); + // no error! + let z: i8 = transmute(z); + //~^ ERROR + } +} diff --git a/tests/ui/transmute/unnecessary-transmutation.stderr b/tests/ui/transmute/unnecessary-transmutation.stderr new file mode 100644 index 00000000000..b661aa13c98 --- /dev/null +++ b/tests/ui/transmute/unnecessary-transmutation.stderr @@ -0,0 +1,235 @@ +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:7:14 + | +LL | unsafe { transmute(x) } + | ^^^^^^^^^^^^ help: replace this with: `u32::to_ne_bytes(x)` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order +note: the lint level is defined here + --> $DIR/unnecessary-transmutation.rs:2:9 + | +LL | #![deny(unnecessary_transmutes)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:13:22 + | +LL | let x: u16 = transmute(*b"01"); + | ^^^^^^^^^^^^^^^^^ help: replace this with: `u16::from_ne_bytes(*b"01")` + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:15:26 + | +LL | let x: [u8; 2] = transmute(x); + | ^^^^^^^^^^^^ help: replace this with: `u16::to_ne_bytes(x)` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:17:22 + | +LL | let x: u32 = transmute(*b"0123"); + | ^^^^^^^^^^^^^^^^^^^ help: replace this with: `u32::from_ne_bytes(*b"0123")` + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:19:26 + | +LL | let x: [u8; 4] = transmute(x); + | ^^^^^^^^^^^^ help: replace this with: `u32::to_ne_bytes(x)` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:21:22 + | +LL | let x: u64 = transmute(*b"feriscat"); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace this with: `u64::from_ne_bytes(*b"feriscat")` + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:23:26 + | +LL | let x: [u8; 8] = transmute(x); + | ^^^^^^^^^^^^ help: replace this with: `u64::to_ne_bytes(x)` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:26:22 + | +LL | let y: i16 = transmute(*b"01"); + | ^^^^^^^^^^^^^^^^^ help: replace this with: `i16::from_ne_bytes(*b"01")` + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:28:26 + | +LL | let y: [u8; 2] = transmute(y); + | ^^^^^^^^^^^^ help: replace this with: `i16::to_ne_bytes(y)` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:30:22 + | +LL | let y: i32 = transmute(*b"0123"); + | ^^^^^^^^^^^^^^^^^^^ help: replace this with: `i32::from_ne_bytes(*b"0123")` + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:32:26 + | +LL | let y: [u8; 4] = transmute(y); + | ^^^^^^^^^^^^ help: replace this with: `i32::to_ne_bytes(y)` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:34:22 + | +LL | let y: i64 = transmute(*b"feriscat"); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace this with: `i64::from_ne_bytes(*b"feriscat")` + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:36:26 + | +LL | let y: [u8; 8] = transmute(y); + | ^^^^^^^^^^^^ help: replace this with: `i64::to_ne_bytes(y)` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:39:22 + | +LL | let z: f32 = transmute(*b"0123"); + | ^^^^^^^^^^^^^^^^^^^ help: replace this with: `f32::from_ne_bytes(*b"0123")` + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:41:26 + | +LL | let z: [u8; 4] = transmute(z); + | ^^^^^^^^^^^^ help: replace this with: `f32::to_ne_bytes(z)` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:43:22 + | +LL | let z: f64 = transmute(*b"feriscat"); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace this with: `f64::from_ne_bytes(*b"feriscat")` + | + = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:45:26 + | +LL | let z: [u8; 8] = transmute(z); + | ^^^^^^^^^^^^ help: replace this with: `f64::to_ne_bytes(z)` + | + = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:48:22 + | +LL | let y: u32 = transmute('🦀'); + | ^^^^^^^^^^^^^^^ help: replace this with: `u32::from('🦀')` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:50:23 + | +LL | let y: char = transmute(y); + | ^^^^^^^^^^^^ help: replace this with: `char::from_u32_unchecked(y)` + | + = help: consider `char::from_u32(…).unwrap()` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:53:22 + | +LL | let x: u16 = transmute(8i16); + | ^^^^^^^^^^^^^^^ help: replace this with: `i16::cast_unsigned(8i16)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:55:22 + | +LL | let x: i16 = transmute(x); + | ^^^^^^^^^^^^ help: replace this with: `u16::cast_signed(x)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:57:22 + | +LL | let x: u32 = transmute(4i32); + | ^^^^^^^^^^^^^^^ help: replace this with: `i32::cast_unsigned(4i32)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:59:22 + | +LL | let x: i32 = transmute(x); + | ^^^^^^^^^^^^ help: replace this with: `u32::cast_signed(x)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:61:22 + | +LL | let x: u64 = transmute(7i64); + | ^^^^^^^^^^^^^^^ help: replace this with: `i64::cast_unsigned(7i64)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:63:22 + | +LL | let x: i64 = transmute(x); + | ^^^^^^^^^^^^ help: replace this with: `u64::cast_signed(x)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:66:22 + | +LL | let y: f32 = transmute(1u32); + | ^^^^^^^^^^^^^^^ help: replace this with: `f32::from_bits(1u32)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:68:22 + | +LL | let y: u32 = transmute(y); + | ^^^^^^^^^^^^ help: replace this with: `f32::to_bits(y)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:70:22 + | +LL | let y: f64 = transmute(3u64); + | ^^^^^^^^^^^^^^^ help: replace this with: `f64::from_bits(3u64)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:72:22 + | +LL | let y: u64 = transmute(2.0); + | ^^^^^^^^^^^^^^ help: replace this with: `f64::to_bits(2.0)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:75:23 + | +LL | let z: bool = transmute(1u8); + | ^^^^^^^^^^^^^^ help: replace this with: `(1u8 == 1)` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:77:21 + | +LL | let z: u8 = transmute(z); + | ^^^^^^^^^^^^ help: replace this with: `(z) as u8` + +error: unnecessary transmute + --> $DIR/unnecessary-transmutation.rs:82:21 + | +LL | let z: i8 = transmute(z); + | ^^^^^^^^^^^^ help: replace this with: `(z) as i8` + +error: aborting due to 32 previous errors + diff --git a/tests/ui/transmute/unnecessary-transmute-path-remap-ice-140277.rs b/tests/ui/transmute/unnecessary-transmute-path-remap-ice-140277.rs new file mode 100644 index 00000000000..756ce7b3d50 --- /dev/null +++ b/tests/ui/transmute/unnecessary-transmute-path-remap-ice-140277.rs @@ -0,0 +1,10 @@ +//@ aux-crate: zerocopy=unnecessary-transmute-path-remap-ice-140277-trans.rs +//@ check-pass +// tests for a regression in linting for unnecessary transmutes +// where a span was inacessible for snippet procuring, +// when remap-path-prefix was set, causing a panic. + +fn bytes_at_home(x: [u8; 4]) -> u32 { + unsafe { zerocopy::transmute!(x) } +} +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.rs b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.rs index 4391bf01dc9..7a540d2a574 100644 --- a/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.rs +++ b/tests/ui/type-alias-impl-trait/issue-84660-unsoundness.rs @@ -22,7 +22,7 @@ impl<In, Out> Trait<Bar, In> for Out { type Out = Out; #[define_opaque(Bar)] fn convert(_i: In) -> Self::Out { - //[next]~^ ERROR: cannot satisfy `Bar == _` + //[next]~^ ERROR: type annotations needed: cannot satisfy `Bar == _` //[current]~^^ ERROR: item does not constrain `Bar::{opaque#0}` unreachable!(); } diff --git a/tests/ui/type-alias/type-param.rs b/tests/ui/type-alias/type-param.rs new file mode 100644 index 00000000000..f8e73518bad --- /dev/null +++ b/tests/ui/type-alias/type-param.rs @@ -0,0 +1,11 @@ +//@ run-pass +// This is a smoke test to ensure that type aliases with type parameters +// are accepted by the compiler and that the parameters are correctly +// resolved in the aliased item type. + +#![allow(dead_code)] + +type Foo<T> = extern "C" fn(T) -> bool; +type Bar<T> = fn(T) -> bool; + +fn main() {} diff --git a/tests/ui/type-param.rs b/tests/ui/type-param.rs deleted file mode 100644 index e7cf0e5446b..00000000000 --- a/tests/ui/type-param.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ run-pass - -#![allow(non_camel_case_types)] -#![allow(dead_code)] - - - -type lteq<T> = extern "C" fn(T) -> bool; - -pub fn main() { } diff --git a/tests/ui/unpretty/expanded-exhaustive.rs b/tests/ui/unpretty/expanded-exhaustive.rs index 0fb5a26c5aa..5697f615b97 100644 --- a/tests/ui/unpretty/expanded-exhaustive.rs +++ b/tests/ui/unpretty/expanded-exhaustive.rs @@ -839,6 +839,7 @@ mod types { } /// TyKind::MacCall + #[expect(deprecated)] // concat_idents is deprecated fn ty_mac_call() { let _: concat_idents!(T); let _: concat_idents![T]; diff --git a/tests/ui/unpretty/expanded-exhaustive.stdout b/tests/ui/unpretty/expanded-exhaustive.stdout index 8febd2d6d49..841edf63c91 100644 --- a/tests/ui/unpretty/expanded-exhaustive.stdout +++ b/tests/ui/unpretty/expanded-exhaustive.stdout @@ -359,6 +359,7 @@ mod expressions { + // concat_idents is deprecated @@ -674,6 +675,7 @@ mod types { /*! there is no syntax for this */ } /// TyKind::MacCall + #[expect(deprecated)] fn ty_mac_call() { let _: T; let _: T; let _: T; } /// TyKind::CVarArgs fn ty_c_var_args() { diff --git a/triagebot.toml b/triagebot.toml index 189223f2feb..422996cb200 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -585,6 +585,11 @@ trigger_files = [ "src/tools/compiletest" ] +[autolabel."A-test-infra-minicore"] +trigger_files = [ + "tests/auxiliary/minicore.rs", +] + [autolabel."A-rustc-dev-guide"] trigger_files = [ "src/doc/rustc-dev-guide", @@ -869,6 +874,10 @@ cc = ["@GuillaumeGomez"] message = "This PR modifies `run-make` tests." cc = ["@jieyouxu"] +[mentions."tests/auxiliary/minicore.rs"] +message = "This PR modifies `tests/auxiliary/minicore.rs`." +cc = ["@jieyouxu"] + [mentions."src/rustdoc-json-types"] message = """ rustdoc-json-types is a **public** (although nightly-only) API. \ |
