From e0bbeb7a005bbf593ccae60f4b0ba86f17d3df63 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Thu, 23 Jan 2025 16:45:19 -0800 Subject: [PATCH 01/57] Make cenum_impl_drop_cast a hard error This changes the `cenum_impl_drop_cast` lint to be a hard error. This lint has been deny-by-default and warning in dependencies since https://github.com/rust-lang/rust/pull/97652 about 2.5 years ago. Closes https://github.com/rust-lang/rust/issues/73333 --- compiler/rustc_hir_typeck/src/cast.rs | 11 +-- compiler/rustc_hir_typeck/src/errors.rs | 4 +- compiler/rustc_lint/src/lib.rs | 5 ++ compiler/rustc_lint_defs/src/builtin.rs | 53 -------------- .../building/enum_cast.droppy.built.after.mir | 71 ------------------- tests/mir-opt/building/enum_cast.rs | 21 ------ tests/ui/cenum_impl_drop_cast.rs | 3 - tests/ui/cenum_impl_drop_cast.stderr | 25 +------ 8 files changed, 12 insertions(+), 181 deletions(-) delete mode 100644 tests/mir-opt/building/enum_cast.droppy.built.after.mir diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 5d00ecbe9187..65021a0cd110 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -831,7 +831,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { // prim -> prim (Int(CEnum), Int(_)) => { - self.cenum_impl_drop_lint(fcx); + self.err_if_cenum_impl_drop(fcx); Ok(CastKind::EnumCast) } (Int(Char) | Int(Bool), Int(_)) => Ok(CastKind::PrimIntCast), @@ -1091,19 +1091,14 @@ impl<'a, 'tcx> CastCheck<'tcx> { } } - fn cenum_impl_drop_lint(&self, fcx: &FnCtxt<'a, 'tcx>) { + fn err_if_cenum_impl_drop(&self, fcx: &FnCtxt<'a, 'tcx>) { if let ty::Adt(d, _) = self.expr_ty.kind() && d.has_dtor(fcx.tcx) { let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty); let cast_ty = fcx.resolve_vars_if_possible(self.cast_ty); - fcx.tcx.emit_node_span_lint( - lint::builtin::CENUM_IMPL_DROP_CAST, - self.expr.hir_id, - self.span, - errors::CastEnumDrop { expr_ty, cast_ty }, - ); + fcx.dcx().emit_err(errors::CastEnumDrop { span: self.span, expr_ty, cast_ty }); } } diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 052adaa69b2b..143736072aa5 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -677,9 +677,11 @@ pub(crate) struct CannotCastToBool<'tcx> { pub help: CannotCastToBoolHelp, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag(hir_typeck_cast_enum_drop)] pub(crate) struct CastEnumDrop<'tcx> { + #[primary_span] + pub span: Span, pub expr_ty: Ty<'tcx>, pub cast_ty: Ty<'tcx>, } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 1465c2cff7bc..39716785fbfe 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -594,6 +594,11 @@ fn register_builtins(store: &mut LintStore) { for more information", ); store.register_removed("unsupported_calling_conventions", "converted into hard error"); + store.register_removed( + "cenum_impl_drop_cast", + "converted into hard error, \ + see for more information", + ); } fn register_internals(store: &mut LintStore) { diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 9fc527a6a3ab..7fbd23bfdc4b 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -27,7 +27,6 @@ declare_lint_pass! { BARE_TRAIT_OBJECTS, BINDINGS_WITH_VARIANT_NAME, BREAK_WITH_LABEL_AND_LOOP, - CENUM_IMPL_DROP_CAST, COHERENCE_LEAK_CHECK, CONFLICTING_REPR_HINTS, CONST_EVALUATABLE_UNCHECKED, @@ -2612,58 +2611,6 @@ declare_lint! { @edition Edition2024 => Warn; } -declare_lint! { - /// The `cenum_impl_drop_cast` lint detects an `as` cast of a field-less - /// `enum` that implements [`Drop`]. - /// - /// [`Drop`]: https://doc.rust-lang.org/std/ops/trait.Drop.html - /// - /// ### Example - /// - /// ```rust,compile_fail - /// # #![allow(unused)] - /// enum E { - /// A, - /// } - /// - /// impl Drop for E { - /// fn drop(&mut self) { - /// println!("Drop"); - /// } - /// } - /// - /// fn main() { - /// let e = E::A; - /// let i = e as u32; - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// Casting a field-less `enum` that does not implement [`Copy`] to an - /// integer moves the value without calling `drop`. This can result in - /// surprising behavior if it was expected that `drop` should be called. - /// Calling `drop` automatically would be inconsistent with other move - /// operations. Since neither behavior is clear or consistent, it was - /// decided that a cast of this nature will no longer be allowed. - /// - /// This is a [future-incompatible] lint to transition this to a hard error - /// in the future. See [issue #73333] for more details. - /// - /// [future-incompatible]: ../index.md#future-incompatible-lints - /// [issue #73333]: https://github.com/rust-lang/rust/issues/73333 - /// [`Copy`]: https://doc.rust-lang.org/std/marker/trait.Copy.html - pub CENUM_IMPL_DROP_CAST, - Deny, - "a C-like enum implementing Drop is cast", - @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps, - reference: "issue #73333 ", - }; -} - declare_lint! { /// The `fuzzy_provenance_casts` lint detects an `as` cast between an integer /// and a pointer. diff --git a/tests/mir-opt/building/enum_cast.droppy.built.after.mir b/tests/mir-opt/building/enum_cast.droppy.built.after.mir deleted file mode 100644 index f53c9199a494..000000000000 --- a/tests/mir-opt/building/enum_cast.droppy.built.after.mir +++ /dev/null @@ -1,71 +0,0 @@ -// MIR for `droppy` after built - -fn droppy() -> () { - let mut _0: (); - let _1: (); - let _2: Droppy; - let _4: Droppy; - let mut _5: isize; - let mut _6: u8; - let mut _7: bool; - let _8: Droppy; - scope 1 { - debug x => _2; - scope 2 { - debug y => _3; - } - scope 3 { - let _3: usize; - } - } - scope 4 { - debug z => _8; - } - - bb0: { - StorageLive(_1); - StorageLive(_2); - _2 = Droppy::C; - FakeRead(ForLet(None), _2); - StorageLive(_3); - StorageLive(_4); - _4 = move _2; - _5 = discriminant(_4); - _6 = copy _5 as u8 (IntToInt); - _7 = Le(copy _6, const 2_u8); - assume(move _7); - _3 = move _5 as usize (IntToInt); - drop(_4) -> [return: bb1, unwind: bb4]; - } - - bb1: { - StorageDead(_4); - FakeRead(ForLet(None), _3); - _1 = const (); - StorageDead(_3); - drop(_2) -> [return: bb2, unwind: bb5]; - } - - bb2: { - StorageDead(_2); - StorageDead(_1); - StorageLive(_8); - _8 = Droppy::B; - FakeRead(ForLet(None), _8); - _0 = const (); - drop(_8) -> [return: bb3, unwind: bb5]; - } - - bb3: { - StorageDead(_8); - return; - } - - bb4 (cleanup): { - drop(_2) -> [return: bb5, unwind terminate(cleanup)]; - } - - bb5 (cleanup): { - resume; - } -} diff --git a/tests/mir-opt/building/enum_cast.rs b/tests/mir-opt/building/enum_cast.rs index 7ff0cdbfe8df..4fb9a27e3093 100644 --- a/tests/mir-opt/building/enum_cast.rs +++ b/tests/mir-opt/building/enum_cast.rs @@ -41,27 +41,6 @@ fn far(far: Far) -> isize { far as isize } -// EMIT_MIR enum_cast.droppy.built.after.mir -enum Droppy { - A, - B, - C, -} - -impl Drop for Droppy { - fn drop(&mut self) {} -} - -fn droppy() { - { - let x = Droppy::C; - // remove this entire test once `cenum_impl_drop_cast` becomes a hard error - #[allow(cenum_impl_drop_cast)] - let y = x as usize; - } - let z = Droppy::B; -} - #[repr(i16)] enum SignedAroundZero { A = -2, diff --git a/tests/ui/cenum_impl_drop_cast.rs b/tests/ui/cenum_impl_drop_cast.rs index 96e3d967e2c6..f681434dd86a 100644 --- a/tests/ui/cenum_impl_drop_cast.rs +++ b/tests/ui/cenum_impl_drop_cast.rs @@ -1,5 +1,3 @@ -#![deny(cenum_impl_drop_cast)] - enum E { A = 0, } @@ -14,5 +12,4 @@ fn main() { let e = E::A; let i = e as u32; //~^ ERROR cannot cast enum `E` into integer `u32` because it implements `Drop` - //~| WARN this was previously accepted } diff --git a/tests/ui/cenum_impl_drop_cast.stderr b/tests/ui/cenum_impl_drop_cast.stderr index 541d15d021d3..35c69f4b4b78 100644 --- a/tests/ui/cenum_impl_drop_cast.stderr +++ b/tests/ui/cenum_impl_drop_cast.stderr @@ -1,31 +1,8 @@ error: cannot cast enum `E` into integer `u32` because it implements `Drop` - --> $DIR/cenum_impl_drop_cast.rs:15:13 + --> $DIR/cenum_impl_drop_cast.rs:13:13 | LL | let i = e as u32; | ^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #73333 -note: the lint level is defined here - --> $DIR/cenum_impl_drop_cast.rs:1:9 - | -LL | #![deny(cenum_impl_drop_cast)] - | ^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error -Future incompatibility report: Future breakage diagnostic: -error: cannot cast enum `E` into integer `u32` because it implements `Drop` - --> $DIR/cenum_impl_drop_cast.rs:15:13 - | -LL | let i = e as u32; - | ^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #73333 -note: the lint level is defined here - --> $DIR/cenum_impl_drop_cast.rs:1:9 - | -LL | #![deny(cenum_impl_drop_cast)] - | ^^^^^^^^^^^^^^^^^^^^ - From 56c6ffbbdabda4c23cc2bbd6473829de8125f4b9 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 28 Jan 2025 02:15:15 +0900 Subject: [PATCH 02/57] Use +secure-plt for powerpc-unknown-linux-gnu{,spe} --- .../src/spec/targets/powerpc_unknown_linux_gnu.rs | 7 ++++++- .../src/spec/targets/powerpc_unknown_linux_gnuspe.rs | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_gnu.rs index 1ae879d01bd4..1c537491a6ca 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_gnu.rs @@ -18,6 +18,11 @@ pub(crate) fn target() -> Target { pointer_width: 32, data_layout: "E-m:e-p:32:32-Fn32-i64:64-n32".into(), arch: "powerpc".into(), - options: TargetOptions { endian: Endian::Big, mcount: "_mcount".into(), ..base }, + options: TargetOptions { + endian: Endian::Big, + features: "+secure-plt".into(), + mcount: "_mcount".into(), + ..base + }, } } diff --git a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_gnuspe.rs b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_gnuspe.rs index 4a5ab375c730..4f5d9c661b08 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_gnuspe.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_gnuspe.rs @@ -21,6 +21,7 @@ pub(crate) fn target() -> Target { options: TargetOptions { abi: "spe".into(), endian: Endian::Big, + features: "+secure-plt".into(), mcount: "_mcount".into(), ..base }, From 68646e9e5fd6731106c47696dbe04a6945996197 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Wed, 29 Jan 2025 09:34:36 -0700 Subject: [PATCH 03/57] rustdoc: run css and html minifier at build instead of runtime This way, adding a bunch of comments to the JS files won't make rustdoc slower. --- src/librustdoc/Cargo.toml | 1 + src/librustdoc/build.rs | 24 +++++++++++++++---- src/librustdoc/config.rs | 4 ++-- src/librustdoc/html/render/write_shared.rs | 10 ++------ src/librustdoc/html/static_files.rs | 28 ++++++++-------------- 5 files changed, 35 insertions(+), 32 deletions(-) diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index 70749f7cb17a..d9bd11267dad 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -33,6 +33,7 @@ features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] [build-dependencies] sha2 = "0.10.8" +minifier = { version = "0.3.2", default-features = false } [dev-dependencies] expect-test = "1.4.0" diff --git a/src/librustdoc/build.rs b/src/librustdoc/build.rs index 69337fb1d250..119836257fdd 100644 --- a/src/librustdoc/build.rs +++ b/src/librustdoc/build.rs @@ -1,3 +1,6 @@ +use std::str; + +use sha2::Digest; fn main() { // generate sha256 files // this avoids having to perform hashing at runtime @@ -35,14 +38,27 @@ fn main() { for path in files { let inpath = format!("html/{path}"); println!("cargo::rerun-if-changed={inpath}"); - let bytes = std::fs::read(inpath).expect("static path exists"); - use sha2::Digest; - let bytes = sha2::Sha256::digest(bytes); - let mut digest = format!("-{bytes:x}"); + let data_bytes = std::fs::read(&inpath).expect("static path exists"); + let hash_bytes = sha2::Sha256::digest(&data_bytes); + let mut digest = format!("-{hash_bytes:x}"); digest.truncate(9); let outpath = std::path::PathBuf::from(format!("{out_dir}/{path}.sha256")); std::fs::create_dir_all(outpath.parent().expect("all file paths are in a directory")) .expect("should be able to write to out_dir"); std::fs::write(&outpath, digest.as_bytes()).expect("write to out_dir"); + let minified_path = std::path::PathBuf::from(format!("{out_dir}/{path}.min")); + if path.ends_with(".js") || path.ends_with(".css") { + let minified: String = if path.ends_with(".css") { + minifier::css::minify(str::from_utf8(&data_bytes).unwrap()) + .unwrap() + .to_string() + .into() + } else { + minifier::js::minify(str::from_utf8(&data_bytes).unwrap()).to_string().into() + }; + std::fs::write(&minified_path, minified.as_bytes()).expect("write to out_dir"); + } else { + std::fs::copy(&inpath, &minified_path).unwrap(); + } } } diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 80bc6cebd2aa..e9b0fdcc7d85 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -458,7 +458,7 @@ impl Options { let to_check = matches.opt_strs("check-theme"); if !to_check.is_empty() { let mut content = - std::str::from_utf8(static_files::STATIC_FILES.rustdoc_css.bytes).unwrap(); + std::str::from_utf8(static_files::STATIC_FILES.rustdoc_css.src_bytes).unwrap(); if let Some((_, inside)) = content.split_once("/* Begin theme: light */") { content = inside; } @@ -607,7 +607,7 @@ impl Options { let mut themes = Vec::new(); if matches.opt_present("theme") { let mut content = - std::str::from_utf8(static_files::STATIC_FILES.rustdoc_css.bytes).unwrap(); + std::str::from_utf8(static_files::STATIC_FILES.rustdoc_css.src_bytes).unwrap(); if let Some((_, inside)) = content.split_once("/* Begin theme: light */") { content = inside; } diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index fb6f3bc2c76e..57d07c05c118 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -207,14 +207,8 @@ fn write_static_files( if opt.emit.is_empty() || opt.emit.contains(&EmitType::Toolchain) { static_files::for_each(|f: &static_files::StaticFile| { let filename = static_dir.join(f.output_filename()); - let contents: &[u8]; - let contents_vec: Vec; - if opt.disable_minification { - contents = f.bytes; - } else { - contents_vec = f.minified(); - contents = &contents_vec; - }; + let contents: &[u8] = + if opt.disable_minification { f.src_bytes } else { f.minified_bytes }; fs::write(&filename, contents).map_err(|e| PathError::new(e, &filename)) })?; } diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs index 6457ac731cb7..b877a8526406 100644 --- a/src/librustdoc/html/static_files.rs +++ b/src/librustdoc/html/static_files.rs @@ -8,26 +8,18 @@ use std::{fmt, str}; pub(crate) struct StaticFile { pub(crate) filename: PathBuf, - pub(crate) bytes: &'static [u8], + pub(crate) src_bytes: &'static [u8], + pub(crate) minified_bytes: &'static [u8], } impl StaticFile { - fn new(filename: &str, bytes: &'static [u8], sha256: &'static str) -> StaticFile { - Self { filename: static_filename(filename, sha256), bytes } - } - - pub(crate) fn minified(&self) -> Vec { - let extension = match self.filename.extension() { - Some(e) => e, - None => return self.bytes.to_owned(), - }; - if extension == "css" { - minifier::css::minify(str::from_utf8(self.bytes).unwrap()).unwrap().to_string().into() - } else if extension == "js" { - minifier::js::minify(str::from_utf8(self.bytes).unwrap()).to_string().into() - } else { - self.bytes.to_owned() - } + fn new( + filename: &str, + src_bytes: &'static [u8], + minified_bytes: &'static [u8], + sha256: &'static str, + ) -> StaticFile { + Self { filename: static_filename(filename, sha256), src_bytes, minified_bytes } } pub(crate) fn output_filename(&self) -> &Path { @@ -68,7 +60,7 @@ macro_rules! static_files { // sha256 files are generated in build.rs pub(crate) static STATIC_FILES: std::sync::LazyLock = std::sync::LazyLock::new(|| StaticFiles { - $($field: StaticFile::new($file_path, include_bytes!($file_path), include_str!(concat!(env!("OUT_DIR"), "/", $file_path, ".sha256"))),)+ + $($field: StaticFile::new($file_path, include_bytes!($file_path), include_bytes!(concat!(env!("OUT_DIR"), "/", $file_path, ".min")), include_str!(concat!(env!("OUT_DIR"), "/", $file_path, ".sha256"))),)+ }); pub(crate) fn for_each(f: impl Fn(&StaticFile) -> Result<(), E>) -> Result<(), E> { From 1b8a792b96b37aaa415d620c6cb5c2fbe5f01ac4 Mon Sep 17 00:00:00 2001 From: David Venhoek Date: Sun, 26 Jan 2025 14:18:59 +0100 Subject: [PATCH 04/57] Upgrade elsa to the newest version. --- Cargo.lock | 4 ++-- compiler/rustc_data_structures/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 10889139e8d6..4e4f6e7d4d3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1095,9 +1095,9 @@ dependencies = [ [[package]] name = "elsa" -version = "1.7.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "848fe615fbb0a74d9ae68dcaa510106d32e37d9416207bbea4bd008bd89c47ed" +checksum = "2343daaeabe09879d4ea058bb4f1e63da3fc07dadc6634e01bda1b3d6a9d9d2b" dependencies = [ "stable_deref_trait", ] diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index 889a8299c18f..7cdcb8a7b66c 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" arrayvec = { version = "0.7", default-features = false } bitflags = "2.4.1" either = "1.0" -elsa = "=1.7.1" +elsa = "1.11.0" ena = "0.14.3" indexmap = { version = "2.4.0", features = ["rustc-rayon"] } jobserver_crate = { version = "0.1.28", package = "jobserver" } From e90f129adfebc7edce61b9f6ec573fc09f712ab3 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Wed, 29 Jan 2025 15:14:10 -0700 Subject: [PATCH 05/57] rustdoc: use ThinVec for generic arg parts This reduces the size of both these args, and of path segments, so should measurably help with memory use. --- src/librustdoc/clean/types.rs | 10 +++++----- src/librustdoc/html/render/search_index.rs | 2 +- src/librustdoc/json/conversions.rs | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 6e817af0d6e0..4b01eed40418 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -2254,8 +2254,8 @@ impl GenericArg { #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub(crate) enum GenericArgs { - AngleBracketed { args: Box<[GenericArg]>, constraints: ThinVec }, - Parenthesized { inputs: Box<[Type]>, output: Option> }, + AngleBracketed { args: ThinVec, constraints: ThinVec }, + Parenthesized { inputs: ThinVec, output: Option> }, } impl GenericArgs { @@ -2279,7 +2279,7 @@ impl GenericArgs { assoc: PathSegment { name: sym::Output, args: GenericArgs::AngleBracketed { - args: Vec::new().into_boxed_slice(), + args: ThinVec::new(), constraints: ThinVec::new(), }, }, @@ -2596,12 +2596,12 @@ mod size_asserts { static_assert_size!(Crate, 56); // frequently moved by-value static_assert_size!(DocFragment, 32); static_assert_size!(GenericArg, 32); - static_assert_size!(GenericArgs, 32); + static_assert_size!(GenericArgs, 24); static_assert_size!(GenericParamDef, 40); static_assert_size!(Generics, 16); static_assert_size!(Item, 48); static_assert_size!(ItemKind, 48); - static_assert_size!(PathSegment, 40); + static_assert_size!(PathSegment, 32); static_assert_size!(Type, 32); // tidy-alphabetical-end } diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index e4a9a2b512e5..8ac8b3a1f843 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -821,7 +821,7 @@ pub(crate) fn get_function_type_for_search( .map(|name| clean::PathSegment { name: *name, args: clean::GenericArgs::AngleBracketed { - args: Vec::new().into_boxed_slice(), + args: ThinVec::new(), constraints: ThinVec::new(), }, }) diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index afe81937495d..fcee29609797 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -231,11 +231,11 @@ impl FromClean for GenericArgs { use clean::GenericArgs::*; match args { AngleBracketed { args, constraints } => GenericArgs::AngleBracketed { - args: args.into_vec().into_json(renderer), + args: args.into_json(renderer), constraints: constraints.into_json(renderer), }, Parenthesized { inputs, output } => GenericArgs::Parenthesized { - inputs: inputs.into_vec().into_json(renderer), + inputs: inputs.into_json(renderer), output: output.map(|a| (*a).into_json(renderer)), }, } From 3814ec5b16c3ad7f1a37099b3a89644defee4fdf Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Wed, 29 Jan 2025 15:23:09 -0700 Subject: [PATCH 06/57] Collect directly into ThinVec --- src/librustdoc/clean/mod.rs | 10 ++++------ src/librustdoc/clean/utils.rs | 6 +++--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 7853e311a040..2c66e4569968 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -516,8 +516,7 @@ fn projection_to_path_segment<'tcx>( ty.map_bound(|ty| &ty.args[generics.parent_count..]), false, def_id, - ) - .into(), + ), constraints: Default::default(), }, } @@ -2202,8 +2201,7 @@ pub(crate) fn clean_middle_ty<'tcx>( alias_ty.map_bound(|ty| ty.args.as_slice()), true, def_id, - ) - .into(), + ), constraints: Default::default(), }, }, @@ -2521,7 +2519,7 @@ fn clean_generic_args<'tcx>( ) -> GenericArgs { // FIXME(return_type_notation): Fix RTN parens rendering if let Some((inputs, output)) = generic_args.paren_sugar_inputs_output() { - let inputs = inputs.iter().map(|x| clean_ty(x, cx)).collect::>().into(); + let inputs = inputs.iter().map(|x| clean_ty(x, cx)).collect::>().into(); let output = match output.kind { hir::TyKind::Tup(&[]) => None, _ => Some(Box::new(clean_ty(output, cx))), @@ -2542,7 +2540,7 @@ fn clean_generic_args<'tcx>( } hir::GenericArg::Infer(_inf) => GenericArg::Infer, }) - .collect::>() + .collect::>() .into(); let constraints = generic_args .constraints diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 80dc6b7250cc..b0928aa276df 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -81,11 +81,11 @@ pub(crate) fn clean_middle_generic_args<'tcx>( args: ty::Binder<'tcx, &'tcx [ty::GenericArg<'tcx>]>, mut has_self: bool, owner: DefId, -) -> Vec { +) -> ThinVec { let (args, bound_vars) = (args.skip_binder(), args.bound_vars()); if args.is_empty() { // Fast path which avoids executing the query `generics_of`. - return Vec::new(); + return ThinVec::new(); } // If the container is a trait object type, the arguments won't contain the self type but the @@ -144,7 +144,7 @@ pub(crate) fn clean_middle_generic_args<'tcx>( }; let offset = if has_self { 1 } else { 0 }; - let mut clean_args = Vec::with_capacity(args.len().saturating_sub(offset)); + let mut clean_args = ThinVec::with_capacity(args.len().saturating_sub(offset)); clean_args.extend(args.iter().enumerate().rev().filter_map(clean_arg)); clean_args.reverse(); clean_args From a7ce15d361ca3d774981e35c3c03d8dcccf0ed88 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 30 Jan 2025 12:37:40 +0000 Subject: [PATCH 07/57] Don't allow negative unsigned literals --- compiler/rustc_mir_build/src/thir/constant.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index 49db522cf0ee..98edd3fea47e 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -55,7 +55,11 @@ pub(crate) fn lit_to_const<'tcx>( let bytes = data as &[u8]; ty::ValTree::from_raw_bytes(tcx, bytes) } - (ast::LitKind::Int(n, _), ty::Uint(_)) | (ast::LitKind::Int(n, _), ty::Int(_)) => { + (ast::LitKind::Int(n, _), ty::Uint(_)) if !neg => { + let scalar_int = trunc(n.get()); + ty::ValTree::from_scalar_int(scalar_int) + } + (ast::LitKind::Int(n, _), ty::Int(_)) => { let scalar_int = trunc(if neg { (n.get() as i128).overflowing_neg().0 as u128 } else { n.get() }); ty::ValTree::from_scalar_int(scalar_int) From 0cd51863ff1d92a904bec6fc854fbf201fb52bdd Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 30 Jan 2025 14:34:50 +0000 Subject: [PATCH 08/57] Avoid calling the layout_of query in lit_to_const --- .../src/builder/expr/as_constant.rs | 21 +++++++------- compiler/rustc_mir_build/src/thir/constant.rs | 28 ++++++++++--------- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs index e4e452aff755..84c9297e6589 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs @@ -105,13 +105,12 @@ fn lit_to_mir_constant<'tcx>(tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx> return Const::Ty(Ty::new_error(tcx, guar), ty::Const::new_error(tcx, guar)); } - let trunc = |n| { - let width = match tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty)) { - Ok(layout) => layout.size, - Err(_) => { - tcx.dcx().bug(format!("couldn't compute width of literal: {:?}", lit_input.lit)) - } - }; + let trunc = |n, width: ty::UintTy| { + let width = width + .normalize(tcx.data_layout.pointer_size.bits().try_into().unwrap()) + .bit_width() + .unwrap(); + let width = Size::from_bits(width); trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits()); let result = width.truncate(n); trace!("trunc result: {}", result); @@ -145,9 +144,11 @@ fn lit_to_mir_constant<'tcx>(tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx> (ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => { ConstValue::Scalar(Scalar::from_uint(*n, Size::from_bytes(1))) } - (ast::LitKind::Int(n, _), ty::Uint(_)) | (ast::LitKind::Int(n, _), ty::Int(_)) => { - trunc(if neg { (n.get() as i128).overflowing_neg().0 as u128 } else { n.get() }) - } + (ast::LitKind::Int(n, _), ty::Uint(ui)) if !neg => trunc(n.get(), *ui), + (ast::LitKind::Int(n, _), ty::Int(i)) => trunc( + if neg { (n.get() as i128).overflowing_neg().0 as u128 } else { n.get() }, + i.to_unsigned(), + ), (ast::LitKind::Float(n, _), ty::Float(fty)) => { parse_float_into_constval(*n, *fty, neg).unwrap() } diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index 98edd3fea47e..f303053390c4 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -1,4 +1,5 @@ -use rustc_ast as ast; +use rustc_abi::Size; +use rustc_ast::{self as ast}; use rustc_hir::LangItem; use rustc_middle::bug; use rustc_middle::mir::interpret::LitToConstInput; @@ -17,13 +18,12 @@ pub(crate) fn lit_to_const<'tcx>( return ty::Const::new_error(tcx, guar); } - let trunc = |n| { - let width = match tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty)) { - Ok(layout) => layout.size, - Err(_) => { - tcx.dcx().bug(format!("couldn't compute width of literal: {:?}", lit_input.lit)) - } - }; + let trunc = |n, width: ty::UintTy| { + let width = width + .normalize(tcx.data_layout.pointer_size.bits().try_into().unwrap()) + .bit_width() + .unwrap(); + let width = Size::from_bits(width); trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits()); let result = width.truncate(n); trace!("trunc result: {}", result); @@ -55,13 +55,15 @@ pub(crate) fn lit_to_const<'tcx>( let bytes = data as &[u8]; ty::ValTree::from_raw_bytes(tcx, bytes) } - (ast::LitKind::Int(n, _), ty::Uint(_)) if !neg => { - let scalar_int = trunc(n.get()); + (ast::LitKind::Int(n, _), ty::Uint(ui)) if !neg => { + let scalar_int = trunc(n.get(), *ui); ty::ValTree::from_scalar_int(scalar_int) } - (ast::LitKind::Int(n, _), ty::Int(_)) => { - let scalar_int = - trunc(if neg { (n.get() as i128).overflowing_neg().0 as u128 } else { n.get() }); + (ast::LitKind::Int(n, _), ty::Int(i)) => { + let scalar_int = trunc( + if neg { (n.get() as i128).overflowing_neg().0 as u128 } else { n.get() }, + i.to_unsigned(), + ); ty::ValTree::from_scalar_int(scalar_int) } (ast::LitKind::Bool(b), ty::Bool) => ty::ValTree::from_scalar_int((*b).into()), From 832fcfb64fdf47714b181bc29f2d93cb5a3383f4 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 31 Jan 2025 16:29:09 +1100 Subject: [PATCH 09/57] Introduce `DIBuilderBox`, an owning pointer to `DIBuilder` --- .../src/debuginfo/metadata.rs | 4 +- .../rustc_codegen_llvm/src/debuginfo/mod.rs | 16 ++---- .../rustc_codegen_llvm/src/debuginfo/utils.rs | 2 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 51 +++++++++++++++++-- 4 files changed, 53 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 3a0c7f007bdd..56ae9607adff 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -931,7 +931,7 @@ pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>( unsafe { let compile_unit_file = llvm::LLVMRustDIBuilderCreateFile( - debug_context.builder, + debug_context.builder.as_ref(), name_in_debuginfo.as_c_char_ptr(), name_in_debuginfo.len(), work_dir.as_c_char_ptr(), @@ -944,7 +944,7 @@ pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>( ); let unit_metadata = llvm::LLVMRustDIBuilderCreateCompileUnit( - debug_context.builder, + debug_context.builder.as_ref(), dwarf_const::DW_LANG_Rust, compile_unit_file, producer.as_c_char_ptr(), diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index b1ce52667bd6..57549eb9f8d4 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -34,7 +34,7 @@ use crate::builder::Builder; use crate::common::{AsCCharPtr, CodegenCx}; use crate::llvm; use crate::llvm::debuginfo::{ - DIArray, DIBuilder, DIFile, DIFlags, DILexicalBlock, DILocation, DISPFlags, DIScope, DIType, + DIArray, DIBuilderBox, DIFile, DIFlags, DILexicalBlock, DILocation, DISPFlags, DIScope, DIType, DIVariable, }; use crate::value::Value; @@ -61,7 +61,7 @@ const DW_TAG_arg_variable: c_uint = 0x101; /// A context object for maintaining all state needed by the debuginfo module. pub(crate) struct CodegenUnitDebugContext<'ll, 'tcx> { llmod: &'ll llvm::Module, - builder: &'ll mut DIBuilder<'ll>, + builder: DIBuilderBox<'ll>, created_files: RefCell, &'ll DIFile>>, type_map: metadata::TypeMap<'ll, 'tcx>, @@ -69,18 +69,10 @@ pub(crate) struct CodegenUnitDebugContext<'ll, 'tcx> { recursion_marker_type: OnceCell<&'ll DIType>, } -impl Drop for CodegenUnitDebugContext<'_, '_> { - fn drop(&mut self) { - unsafe { - llvm::LLVMRustDIBuilderDispose(&mut *(self.builder as *mut _)); - } - } -} - impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { pub(crate) fn new(llmod: &'ll llvm::Module) -> Self { debug!("CodegenUnitDebugContext::new"); - let builder = unsafe { llvm::LLVMRustDIBuilderCreate(llmod) }; + let builder = DIBuilderBox::new(llmod); // DIBuilder inherits context from the module, so we'd better use the same one CodegenUnitDebugContext { llmod, @@ -93,7 +85,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { } pub(crate) fn finalize(&self, sess: &Session) { - unsafe { llvm::LLVMRustDIBuilderFinalize(self.builder) }; + unsafe { llvm::LLVMRustDIBuilderFinalize(self.builder.as_ref()) }; match sess.target.debuginfo_kind { DebuginfoKind::Dwarf | DebuginfoKind::DwarfDsym => { diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs index 6e8412934778..cc1d504b4301 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs @@ -41,7 +41,7 @@ pub(crate) fn debug_context<'a, 'll, 'tcx>( #[inline] #[allow(non_snake_case)] pub(crate) fn DIB<'a, 'll>(cx: &'a CodegenCx<'ll, '_>) -> &'a DIBuilder<'ll> { - cx.dbg_cx.as_ref().unwrap().builder + cx.dbg_cx.as_ref().unwrap().builder.as_ref() } pub(crate) fn get_namespace_for_item<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId) -> &'ll DIScope { diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index cc7c5231aca5..c3752f36f506 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -789,12 +789,50 @@ pub type DiagnosticHandlerTy = unsafe extern "C" fn(&DiagnosticInfo, *mut c_void pub type InlineAsmDiagHandlerTy = unsafe extern "C" fn(&SMDiagnostic, *const c_void, c_uint); pub mod debuginfo { + use std::ptr; + use bitflags::bitflags; use super::{InvariantOpaque, Metadata}; + use crate::llvm::{self, Module}; + /// Opaque target type for references to an LLVM debuginfo builder. + /// + /// `&'_ DIBuilder<'ll>` corresponds to `LLVMDIBuilderRef`, which is the + /// LLVM-C wrapper for `DIBuilder *`. + /// + /// Debuginfo builders are created and destroyed during codegen, so the + /// builder reference typically has a shorter lifetime than the LLVM + /// session (`'ll`) that it participates in. #[repr(C)] - pub struct DIBuilder<'a>(InvariantOpaque<'a>); + pub struct DIBuilder<'ll>(InvariantOpaque<'ll>); + + /// Owning pointer to a `DIBuilder<'ll>` that will dispose of the builder + /// when dropped. Use `.as_ref()` to get the underlying `&DIBuilder` + /// needed for debuginfo FFI calls. + pub(crate) struct DIBuilderBox<'ll> { + raw: ptr::NonNull>, + } + + impl<'ll> DIBuilderBox<'ll> { + pub(crate) fn new(llmod: &'ll Module) -> Self { + let raw = unsafe { llvm::LLVMCreateDIBuilder(llmod) }; + let raw = ptr::NonNull::new(raw).unwrap(); + Self { raw } + } + + pub(crate) fn as_ref(&self) -> &DIBuilder<'ll> { + // SAFETY: This is an owning pointer, so `&DIBuilder` is valid + // for as long as `&self` is. + unsafe { self.raw.as_ref() } + } + } + + impl<'ll> Drop for DIBuilderBox<'ll> { + fn drop(&mut self) { + unsafe { llvm::LLVMDisposeDIBuilder(self.raw) }; + } + } pub type DIDescriptor = Metadata; pub type DILocation = Metadata; @@ -1672,6 +1710,13 @@ unsafe extern "C" { ) -> &'a Value; } +// FFI bindings for `DIBuilder` functions in the LLVM-C API. +// Try to keep these in the same order as in `llvm/include/llvm-c/DebugInfo.h`. +unsafe extern "C" { + pub(crate) fn LLVMCreateDIBuilder<'ll>(M: &'ll Module) -> *mut DIBuilder<'ll>; + pub(crate) fn LLVMDisposeDIBuilder<'ll>(Builder: ptr::NonNull>); +} + #[link(name = "llvm-wrapper", kind = "static")] unsafe extern "C" { pub fn LLVMRustInstallErrorHandlers(); @@ -1939,10 +1984,6 @@ unsafe extern "C" { ValueLen: size_t, ); - pub fn LLVMRustDIBuilderCreate(M: &Module) -> &mut DIBuilder<'_>; - - pub fn LLVMRustDIBuilderDispose<'a>(Builder: &'a mut DIBuilder<'a>); - pub fn LLVMRustDIBuilderFinalize(Builder: &DIBuilder<'_>); pub fn LLVMRustDIBuilderCreateCompileUnit<'a>( From cd2af2dd9a82b564fd74f7d2b7876c120812f6ba Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 1 Feb 2025 13:38:12 +1100 Subject: [PATCH 10/57] Use `LLVMDIBuilderFinalize` --- compiler/rustc_codegen_llvm/src/debuginfo/mod.rs | 2 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 4 ++-- compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 4 ---- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 57549eb9f8d4..cb84bede8933 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -85,7 +85,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { } pub(crate) fn finalize(&self, sess: &Session) { - unsafe { llvm::LLVMRustDIBuilderFinalize(self.builder.as_ref()) }; + unsafe { llvm::LLVMDIBuilderFinalize(self.builder.as_ref()) }; match sess.target.debuginfo_kind { DebuginfoKind::Dwarf | DebuginfoKind::DwarfDsym => { diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index c3752f36f506..e6ffead845dd 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1715,6 +1715,8 @@ unsafe extern "C" { unsafe extern "C" { pub(crate) fn LLVMCreateDIBuilder<'ll>(M: &'ll Module) -> *mut DIBuilder<'ll>; pub(crate) fn LLVMDisposeDIBuilder<'ll>(Builder: ptr::NonNull>); + + pub(crate) fn LLVMDIBuilderFinalize<'ll>(Builder: &DIBuilder<'ll>); } #[link(name = "llvm-wrapper", kind = "static")] @@ -1984,8 +1986,6 @@ unsafe extern "C" { ValueLen: size_t, ); - pub fn LLVMRustDIBuilderFinalize(Builder: &DIBuilder<'_>); - pub fn LLVMRustDIBuilderCreateCompileUnit<'a>( Builder: &DIBuilder<'a>, Lang: c_uint, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 7ff316ba83a6..6c5bfc1ac781 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1003,10 +1003,6 @@ extern "C" void LLVMRustDIBuilderDispose(LLVMDIBuilderRef Builder) { delete unwrap(Builder); } -extern "C" void LLVMRustDIBuilderFinalize(LLVMDIBuilderRef Builder) { - unwrap(Builder)->finalize(); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateCompileUnit( LLVMDIBuilderRef Builder, unsigned Lang, LLVMMetadataRef FileRef, const char *Producer, size_t ProducerLen, bool isOptimized, From 878ab125a107d8f8036cc41a2db6abcb68996226 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 1 Feb 2025 13:43:30 +1100 Subject: [PATCH 11/57] Use `LLVMDIBuilderCreateNameSpace` --- .../src/debuginfo/namespace.rs | 8 +++--- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 27 ++++++++++--------- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 8 ------ 3 files changed, 18 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/namespace.rs b/compiler/rustc_codegen_llvm/src/debuginfo/namespace.rs index 33d9bc238903..b4d639368b00 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/namespace.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/namespace.rs @@ -5,7 +5,7 @@ use rustc_hir::def_id::DefId; use rustc_middle::ty::{self, Instance}; use super::utils::{DIB, debug_context}; -use crate::common::{AsCCharPtr, CodegenCx}; +use crate::common::CodegenCx; use crate::llvm; use crate::llvm::debuginfo::DIScope; @@ -33,12 +33,12 @@ pub(crate) fn item_namespace<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId) -> &'l }; let scope = unsafe { - llvm::LLVMRustDIBuilderCreateNameSpace( + llvm::LLVMDIBuilderCreateNameSpace( DIB(cx), parent_scope, - namespace_name_string.as_c_char_ptr(), + namespace_name_string.as_ptr(), namespace_name_string.len(), - false, // ExportSymbols (only relevant for C++ anonymous namespaces) + llvm::False, // ExportSymbols (only relevant for C++ anonymous namespaces) ) }; diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index e6ffead845dd..d7324779ea8c 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -5,17 +5,19 @@ use std::fmt::Debug; use std::marker::PhantomData; use std::ptr; -use libc::{c_char, c_int, c_uint, c_ulonglong, c_void, size_t}; +use bitflags::bitflags; +use libc::{c_char, c_int, c_uchar, c_uint, c_ulonglong, c_void, size_t}; use rustc_macros::TryFromU32; use rustc_target::spec::SymbolVisibility; use super::RustString; use super::debuginfo::{ DIArray, DIBasicType, DIBuilder, DICompositeType, DIDerivedType, DIDescriptor, DIEnumerator, - DIFile, DIFlags, DIGlobalVariableExpression, DILexicalBlock, DILocation, DINameSpace, - DISPFlags, DIScope, DISubprogram, DISubrange, DITemplateTypeParameter, DIType, DIVariable, - DebugEmissionKind, DebugNameTableKind, + DIFile, DIFlags, DIGlobalVariableExpression, DILexicalBlock, DILocation, DISPFlags, DIScope, + DISubprogram, DISubrange, DITemplateTypeParameter, DIType, DIVariable, DebugEmissionKind, + DebugNameTableKind, }; +use crate::llvm; /// In the LLVM-C API, boolean values are passed as `typedef int LLVMBool`, /// which has a different ABI from Rust or C++ `bool`. @@ -952,7 +954,6 @@ pub mod debuginfo { } } -use bitflags::bitflags; // These values **must** match with LLVMRustAllocKindFlags bitflags! { #[repr(transparent)] @@ -1717,6 +1718,14 @@ unsafe extern "C" { pub(crate) fn LLVMDisposeDIBuilder<'ll>(Builder: ptr::NonNull>); pub(crate) fn LLVMDIBuilderFinalize<'ll>(Builder: &DIBuilder<'ll>); + + pub(crate) fn LLVMDIBuilderCreateNameSpace<'ll>( + Builder: &DIBuilder<'ll>, + ParentScope: Option<&'ll Metadata>, + Name: *const c_uchar, + NameLen: size_t, + ExportSymbols: llvm::Bool, + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2286,14 +2295,6 @@ unsafe extern "C" { Ty: &'a DIType, ) -> &'a DITemplateTypeParameter; - pub fn LLVMRustDIBuilderCreateNameSpace<'a>( - Builder: &DIBuilder<'a>, - Scope: Option<&'a DIScope>, - Name: *const c_char, - NameLen: size_t, - ExportSymbols: bool, - ) -> &'a DINameSpace; - pub fn LLVMRustDICompositeTypeReplaceArrays<'a>( Builder: &DIBuilder<'a>, CompositeType: &'a DIType, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 6c5bfc1ac781..2b561dc35347 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1321,14 +1321,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateTemplateTypeParameter( unwrapDI(Ty), IsDefault)); } -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateNameSpace(LLVMDIBuilderRef Builder, - LLVMMetadataRef Scope, const char *Name, - size_t NameLen, bool ExportSymbols) { - return wrap(unwrap(Builder)->createNameSpace( - unwrapDI(Scope), StringRef(Name, NameLen), ExportSymbols)); -} - extern "C" void LLVMRustDICompositeTypeReplaceArrays( LLVMDIBuilderRef Builder, LLVMMetadataRef CompositeTy, LLVMMetadataRef Elements, LLVMMetadataRef Params) { From 70d41bc7110ed05c8befb65403b028e1fbaf2c5a Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 1 Feb 2025 13:50:01 +1100 Subject: [PATCH 12/57] Use `LLVMDIBuilderCreateLexicalBlock` --- .../src/debuginfo/create_scope_map.rs | 2 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 16 ++++++++-------- compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 8 -------- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs index 11eb9651af6c..f52991b36979 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs @@ -127,7 +127,7 @@ fn make_mir_scope<'ll, 'tcx>( }) } None => unsafe { - llvm::LLVMRustDIBuilderCreateLexicalBlock( + llvm::LLVMDIBuilderCreateLexicalBlock( DIB(cx), parent_scope.dbg_scope, file_metadata, diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index d7324779ea8c..ed2ea9701863 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1726,6 +1726,14 @@ unsafe extern "C" { NameLen: size_t, ExportSymbols: llvm::Bool, ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateLexicalBlock<'ll>( + Builder: &DIBuilder<'ll>, + Scope: &'ll Metadata, + File: &'ll Metadata, + Line: c_uint, + Column: c_uint, + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2157,14 +2165,6 @@ unsafe extern "C" { Type: &'a DIType, ) -> &'a DIDerivedType; - pub fn LLVMRustDIBuilderCreateLexicalBlock<'a>( - Builder: &DIBuilder<'a>, - Scope: &'a DIScope, - File: &'a DIFile, - Line: c_uint, - Col: c_uint, - ) -> &'a DILexicalBlock; - pub fn LLVMRustDIBuilderCreateLexicalBlockFile<'a>( Builder: &DIBuilder<'a>, Scope: &'a DIScope, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 2b561dc35347..0c3fc6c46710 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1179,14 +1179,6 @@ LLVMRustDIBuilderCreateQualifiedType(LLVMDIBuilderRef Builder, unsigned Tag, unwrap(Builder)->createQualifiedType(Tag, unwrapDI(Type))); } -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateLexicalBlock(LLVMDIBuilderRef Builder, - LLVMMetadataRef Scope, LLVMMetadataRef File, - unsigned Line, unsigned Col) { - return wrap(unwrap(Builder)->createLexicalBlock( - unwrapDI(Scope), unwrapDI(File), Line, Col)); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateLexicalBlockFile( LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, LLVMMetadataRef File) { return wrap(unwrap(Builder)->createLexicalBlockFile( From 949b4673ceebd767cc0b138392b420a050dc6ce0 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 1 Feb 2025 13:55:44 +1100 Subject: [PATCH 13/57] Use `LLVMDIBuilderCreateLexicalBlockFile` --- .../src/debuginfo/metadata.rs | 9 ++++++++- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 18 +++++++++--------- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 6 ------ 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 56ae9607adff..f497ba95661d 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -1641,7 +1641,14 @@ pub(crate) fn extend_scope_to_file<'ll>( file: &SourceFile, ) -> &'ll DILexicalBlock { let file_metadata = file_metadata(cx, file); - unsafe { llvm::LLVMRustDIBuilderCreateLexicalBlockFile(DIB(cx), scope_metadata, file_metadata) } + unsafe { + llvm::LLVMDIBuilderCreateLexicalBlockFile( + DIB(cx), + scope_metadata, + file_metadata, + /* Discriminator (default) */ 0u32, + ) + } } fn tuple_field_name(field_index: usize) -> Cow<'static, str> { diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index ed2ea9701863..f7713e9a726e 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -13,9 +13,8 @@ use rustc_target::spec::SymbolVisibility; use super::RustString; use super::debuginfo::{ DIArray, DIBasicType, DIBuilder, DICompositeType, DIDerivedType, DIDescriptor, DIEnumerator, - DIFile, DIFlags, DIGlobalVariableExpression, DILexicalBlock, DILocation, DISPFlags, DIScope, - DISubprogram, DISubrange, DITemplateTypeParameter, DIType, DIVariable, DebugEmissionKind, - DebugNameTableKind, + DIFile, DIFlags, DIGlobalVariableExpression, DILocation, DISPFlags, DIScope, DISubprogram, + DISubrange, DITemplateTypeParameter, DIType, DIVariable, DebugEmissionKind, DebugNameTableKind, }; use crate::llvm; @@ -1734,6 +1733,13 @@ unsafe extern "C" { Line: c_uint, Column: c_uint, ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateLexicalBlockFile<'ll>( + Builder: &DIBuilder<'ll>, + Scope: &'ll Metadata, + File: &'ll Metadata, + Discriminator: c_uint, // (optional "DWARF path discriminator"; default is 0) + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2165,12 +2171,6 @@ unsafe extern "C" { Type: &'a DIType, ) -> &'a DIDerivedType; - pub fn LLVMRustDIBuilderCreateLexicalBlockFile<'a>( - Builder: &DIBuilder<'a>, - Scope: &'a DIScope, - File: &'a DIFile, - ) -> &'a DILexicalBlock; - pub fn LLVMRustDIBuilderCreateStaticVariable<'a>( Builder: &DIBuilder<'a>, Context: Option<&'a DIScope>, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 0c3fc6c46710..6805ecfed032 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1179,12 +1179,6 @@ LLVMRustDIBuilderCreateQualifiedType(LLVMDIBuilderRef Builder, unsigned Tag, unwrap(Builder)->createQualifiedType(Tag, unwrapDI(Type))); } -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateLexicalBlockFile( - LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, LLVMMetadataRef File) { - return wrap(unwrap(Builder)->createLexicalBlockFile( - unwrapDI(Scope), unwrapDI(File))); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticVariable( LLVMDIBuilderRef Builder, LLVMMetadataRef Context, const char *Name, size_t NameLen, const char *LinkageName, size_t LinkageNameLen, From 8ddd9c38f6165d40ca1ab82e1d2bf9890a047c3a Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 1 Feb 2025 14:00:20 +1100 Subject: [PATCH 14/57] Use `LLVMDIBuilderCreateDebugLocation` The LLVM-C binding takes an explicit context, whereas our binding obtained the context from the scope argument. --- compiler/rustc_codegen_llvm/src/debuginfo/mod.rs | 2 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 14 ++++++++------ compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 10 ---------- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index cb84bede8933..496178c6b1d9 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -574,7 +574,7 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { (line, col) }; - unsafe { llvm::LLVMRustDIBuilderCreateDebugLocation(line, col, scope, inlined_at) } + unsafe { llvm::LLVMDIBuilderCreateDebugLocation(self.llcx, line, col, scope, inlined_at) } } fn create_vtable_debuginfo( diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index f7713e9a726e..95896916adc7 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1740,6 +1740,14 @@ unsafe extern "C" { File: &'ll Metadata, Discriminator: c_uint, // (optional "DWARF path discriminator"; default is 0) ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateDebugLocation<'ll>( + Ctx: &'ll Context, + Line: c_uint, + Column: c_uint, + Scope: &'ll Metadata, + InlinedAt: Option<&'ll Metadata>, + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2302,12 +2310,6 @@ unsafe extern "C" { Params: Option<&'a DIArray>, ); - pub fn LLVMRustDIBuilderCreateDebugLocation<'a>( - Line: c_uint, - Column: c_uint, - Scope: &'a DIScope, - InlinedAt: Option<&'a DILocation>, - ) -> &'a DILocation; pub fn LLVMRustDILocationCloneWithBaseDiscriminator<'a>( Location: &'a DILocation, BD: c_uint, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 6805ecfed032..76ad7fc89091 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1315,16 +1315,6 @@ extern "C" void LLVMRustDICompositeTypeReplaceArrays( DINodeArray(unwrap(Params))); } -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateDebugLocation(unsigned Line, unsigned Column, - LLVMMetadataRef ScopeRef, - LLVMMetadataRef InlinedAt) { - MDNode *Scope = unwrapDIPtr(ScopeRef); - DILocation *Loc = DILocation::get(Scope->getContext(), Line, Column, Scope, - unwrapDIPtr(InlinedAt)); - return wrap(Loc); -} - extern "C" LLVMMetadataRef LLVMRustDILocationCloneWithBaseDiscriminator(LLVMMetadataRef Location, unsigned BD) { From 5413d2bd6fd23103a8f44b306cdc6da1e109b6a2 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 8 Dec 2024 00:10:39 +1100 Subject: [PATCH 15/57] Add FIXME for auditing optional parameters passed to DIBuilder --- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 95896916adc7..222e69df3358 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1712,6 +1712,10 @@ unsafe extern "C" { // FFI bindings for `DIBuilder` functions in the LLVM-C API. // Try to keep these in the same order as in `llvm/include/llvm-c/DebugInfo.h`. +// +// FIXME(#134001): Audit all `Option` parameters, especially in lists, to check +// that they really are nullable on the C/C++ side. LLVM doesn't appear to +// actually document which ones are nullable. unsafe extern "C" { pub(crate) fn LLVMCreateDIBuilder<'ll>(M: &'ll Module) -> *mut DIBuilder<'ll>; pub(crate) fn LLVMDisposeDIBuilder<'ll>(Builder: ptr::NonNull>); From c3f2930edc22407455b303612f46a684f23622b1 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 12 Dec 2024 20:41:01 +1100 Subject: [PATCH 16/57] Explain why (some) pointer/length strings are `*const c_uchar` --- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 222e69df3358..5c53a419a6cc 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1,3 +1,15 @@ +//! Bindings to the LLVM-C API (`LLVM*`), and to our own `extern "C"` wrapper +//! functions around the unstable LLVM C++ API (`LLVMRust*`). +//! +//! ## Passing pointer/length strings as `*const c_uchar` +//! +//! Normally it's a good idea for Rust-side bindings to match the corresponding +//! C-side function declarations as closely as possible. But when passing `&str` +//! or `&[u8]` data as a pointer/length pair, it's more convenient to declare +//! the Rust-side pointer as `*const c_uchar` instead of `*const c_char`. +//! Both pointer types have the same ABI, and using `*const c_uchar` avoids +//! the need for an extra cast from `*const u8` on the Rust side. + #![allow(non_camel_case_types)] #![allow(non_upper_case_globals)] From d440ea0c8ef1af75cc034f119a3b62cbbabf4ad3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= <39484203+jieyouxu@users.noreply.github.com> Date: Wed, 15 Jan 2025 16:44:45 +0800 Subject: [PATCH 17/57] tests: reformat `split-debuginfo`'s `Makefile` To make it easier to tell the conditional branching. --- tests/run-make/split-debuginfo/Makefile | 718 ++++++++++++------------ 1 file changed, 359 insertions(+), 359 deletions(-) diff --git a/tests/run-make/split-debuginfo/Makefile b/tests/run-make/split-debuginfo/Makefile index 5f463ffe8cde..53d3470afb5e 100644 --- a/tests/run-make/split-debuginfo/Makefile +++ b/tests/run-make/split-debuginfo/Makefile @@ -6,366 +6,366 @@ include ../tools.mk all: off packed unpacked ifeq ($(UNAME),Darwin) -# If disabled, don't run `dsymutil`. -off: - rm -rf $(TMPDIR)/*.dSYM - $(RUSTC) foo.rs -g -C split-debuginfo=off - [ ! -d $(TMPDIR)/foo.dSYM ] + # If disabled, don't run `dsymutil`. + off: + rm -rf $(TMPDIR)/*.dSYM + $(RUSTC) foo.rs -g -C split-debuginfo=off + [ ! -d $(TMPDIR)/foo.dSYM ] -# Packed by default, but only if debuginfo is requested -packed: - rm -rf $(TMPDIR)/*.dSYM - $(RUSTC) foo.rs - [ ! -d $(TMPDIR)/foo.dSYM ] - rm -rf $(TMPDIR)/*.dSYM - $(RUSTC) foo.rs -g - [ -d $(TMPDIR)/foo.dSYM ] - rm -rf $(TMPDIR)/*.dSYM - $(RUSTC) foo.rs -g -C split-debuginfo=packed - [ -d $(TMPDIR)/foo.dSYM ] - rm -rf $(TMPDIR)/*.dSYM + # Packed by default, but only if debuginfo is requested + packed: + rm -rf $(TMPDIR)/*.dSYM + $(RUSTC) foo.rs + [ ! -d $(TMPDIR)/foo.dSYM ] + rm -rf $(TMPDIR)/*.dSYM + $(RUSTC) foo.rs -g + [ -d $(TMPDIR)/foo.dSYM ] + rm -rf $(TMPDIR)/*.dSYM + $(RUSTC) foo.rs -g -C split-debuginfo=packed + [ -d $(TMPDIR)/foo.dSYM ] + rm -rf $(TMPDIR)/*.dSYM -# Object files are preserved with unpacked and `dsymutil` isn't run -unpacked: - $(RUSTC) foo.rs -g -C split-debuginfo=unpacked - ls $(TMPDIR)/*.o - [ ! -d $(TMPDIR)/foo.dSYM ] + # Object files are preserved with unpacked and `dsymutil` isn't run + unpacked: + $(RUSTC) foo.rs -g -C split-debuginfo=unpacked + ls $(TMPDIR)/*.o + [ ! -d $(TMPDIR)/foo.dSYM ] else -ifdef IS_WINDOWS -# Windows only supports packed debuginfo - nothing to test. -off: -packed: -unpacked: -else -# Some non-Windows, non-Darwin platforms are not stable, and some are. -ifeq ($(UNAME),Linux) - UNSTABLEOPTS := -else - UNSTABLEOPTS := -Zunstable-options -endif - -# - Debuginfo in `.o` files -# - `.o` deleted -# - `.dwo` never created -# - `.dwp` never created -off: - $(RUSTC) foo.rs -g -C $(UNSTABLEOPTS) split-debuginfo=off - [ ! -f $(TMPDIR)/*.dwp ] - [ ! -f $(TMPDIR)/*.dwo ] - $(RUSTC) foo.rs -g - [ ! -f $(TMPDIR)/*.dwp ] - [ ! -f $(TMPDIR)/*.dwo ] - -packed: packed-split packed-single packed-lto packed-remapped packed-crosscrate - -# - Debuginfo in `.dwo` files -# - `.o` deleted -# - `.dwo` deleted -# - `.dwp` present -packed-split: - $(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=packed -Zsplit-dwarf-kind=split - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/foo.dwp - rm $(TMPDIR)/$(call BIN,foo) - -# - Debuginfo in `.o` files -# - `.o` deleted -# - `.dwo` never created -# - `.dwp` present -packed-single: - $(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=packed -Zsplit-dwarf-kind=single - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/foo.dwp - rm $(TMPDIR)/$(call BIN,foo) - -packed-lto: packed-lto-split packed-lto-single - -# - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated -# - `.o` never created -# - `.dwo` never created -# - `.dwp` never created -packed-lto-split: - $(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=packed -Zsplit-dwarf-kind=split \ - --crate-type=rlib -Clinker-plugin-lto - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/libbaz.rlib - -# - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated -# - `.o` never created -# - `.dwo` never created -# - `.dwp` never created -packed-lto-single: - $(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=packed -Zsplit-dwarf-kind=single \ - --crate-type=rlib -Clinker-plugin-lto - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/libbaz.rlib - -packed-remapped: packed-remapped-split packed-remapped-single packed-remapped-scope packed-remapped-wrong-scope - -# - Debuginfo in `.dwo` files -# - `.o` and binary refer to remapped `.dwo` paths which do not exist -# - `.o` deleted -# - `.dwo` deleted -# - `.dwp` present -packed-remapped-split: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ - -Z split-dwarf-kind=split --remap-path-prefix $(TMPDIR)=/a foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/foo.dwp - rm $(TMPDIR)/$(call BIN,foo) - -# - Debuginfo in `.o` files -# - `.o` and binary refer to remapped `.o` paths which do not exist -# - `.o` deleted -# - `.dwo` never created -# - `.dwp` present -packed-remapped-single: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/foo.dwp - rm $(TMPDIR)/$(call BIN,foo) - -# - Debuginfo in `.o` files -# - `.o` and binary refer to remapped `.o` paths which do not exist -# - `.o` deleted -# - `.dwo` never created -# - `.dwp` present -packed-remapped-scope: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ - -Z remap-path-scope=debuginfo foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/foo.dwp - rm $(TMPDIR)/$(call BIN,foo) - -# - Debuginfo in `.o` files -# - `.o` and binary refer to remapped `.o` paths which do not exist -# - `.o` deleted -# - `.dwo` never created -# - `.dwp` present -packed-remapped-wrong-scope: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ - -Z remap-path-scope=macro foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (grep $(TMPDIR)) || exit 1 - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/foo.dwp - rm $(TMPDIR)/$(call BIN,foo) - -packed-crosscrate: packed-crosscrate-split packed-crosscrate-single - -# - Debuginfo in `.dwo` files -# - (bar) `.rlib` file created, contains `.dwo` -# - (bar) `.o` deleted -# - (bar) `.dwo` deleted -# - (bar) `.dwp` never created -# - (main) `.o` deleted -# - (main) `.dwo` deleted -# - (main) `.dwp` present -packed-crosscrate-split: - $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=packed \ - -Zsplit-dwarf-kind=split -C debuginfo=2 -g bar.rs - ls $(TMPDIR)/*.rlib - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ - -C split-debuginfo=packed -Zsplit-dwarf-kind=split -C debuginfo=2 -g main.rs - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/main.dwp - rm $(TMPDIR)/$(call BIN,main) - -# - Debuginfo in `.o` files -# - (bar) `.rlib` file created, contains `.o` -# - (bar) `.o` deleted -# - (bar) `.dwo` never created -# - (bar) `.dwp` never created -# - (main) `.o` deleted -# - (main) `.dwo` never created -# - (main) `.dwp` present -packed-crosscrate-single: - $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=packed \ - -Zsplit-dwarf-kind=single -C debuginfo=2 -g bar.rs - ls $(TMPDIR)/*.rlib - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ - -C split-debuginfo=packed -Zsplit-dwarf-kind=single -C debuginfo=2 -g main.rs - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/main.dwp - rm $(TMPDIR)/$(call BIN,main) - -unpacked: unpacked-split unpacked-single unpacked-lto unpacked-remapped unpacked-crosscrate - -# - Debuginfo in `.dwo` files -# - `.o` deleted -# - `.dwo` present -# - `.dwp` never created -unpacked-split: - $(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=unpacked -Zsplit-dwarf-kind=split - ls $(TMPDIR)/*.o && exit 1 || exit 0 - rm $(TMPDIR)/*.dwo - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,foo) - -# - Debuginfo in `.o` files -# - `.o` present -# - `.dwo` never created -# - `.dwp` never created -unpacked-single: - $(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=unpacked -Zsplit-dwarf-kind=single - ls $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,foo) - -unpacked-lto: unpacked-lto-split unpacked-lto-single - -# - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated -# - `.o` present (bitcode) -# - `.dwo` never created -# - `.dwp` never created -unpacked-lto-split: - $(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=unpacked -Zsplit-dwarf-kind=split \ - --crate-type=rlib -Clinker-plugin-lto - rm $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/libbaz.rlib - -# - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated -# - `.o` present (bitcode) -# - `.dwo` never created -# - `.dwp` never created -unpacked-lto-single: - $(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=unpacked -Zsplit-dwarf-kind=single \ - --crate-type=rlib -Clinker-plugin-lto - rm $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/libbaz.rlib - -unpacked-remapped: unpacked-remapped-split unpacked-remapped-single unpacked-remapped-scope unpacked-remapped-wrong-scope - -# - Debuginfo in `.dwo` files -# - `.o` and binary refer to remapped `.dwo` paths which do not exist -# - `.o` deleted -# - `.dwo` present -# - `.dwp` never created -unpacked-remapped-split: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ - -Z split-dwarf-kind=split --remap-path-prefix $(TMPDIR)=/a foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 - ls $(TMPDIR)/*.o && exit 1 || exit 0 - rm $(TMPDIR)/*.dwo - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,foo) - -# - Debuginfo in `.o` files -# - `.o` and binary refer to remapped `.o` paths which do not exist -# - `.o` present -# - `.dwo` never created -# - `.dwp` never created -unpacked-remapped-single: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 - rm $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,foo) - -# - Debuginfo in `.o` files -# - `.o` and binary refer to remapped `.o` paths which do not exist -# - `.o` present -# - `.dwo` never created -# - `.dwp` never created -unpacked-remapped-scope: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ - -Z remap-path-scope=debuginfo foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 - rm $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,foo) - -# - Debuginfo in `.o` files -# - `.o` and binary refer to remapped `.o` paths which do not exist -# - `.o` present -# - `.dwo` never created -# - `.dwp` never created -unpacked-remapped-wrong-scope: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ - -Z remap-path-scope=macro foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (grep $(TMPDIR)) || exit 1 - rm $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,foo) - -unpacked-crosscrate: unpacked-crosscrate-split unpacked-crosscrate-single - -# - Debuginfo in `.dwo` files -# - (bar) `.rlib` file created, contains `.dwo` -# - (bar) `.o` deleted -# - (bar) `.dwo` present -# - (bar) `.dwp` never created -# - (main) `.o` deleted -# - (main) `.dwo` present -# - (main) `.dwp` never created -unpacked-crosscrate-split: - $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=unpacked \ - -Zsplit-dwarf-kind=split -C debuginfo=2 -g bar.rs - ls $(TMPDIR)/*.rlib - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ - -C split-debuginfo=unpacked -Zsplit-dwarf-kind=split -C debuginfo=2 -g main.rs - ls $(TMPDIR)/*.o && exit 1 || exit 0 - rm $(TMPDIR)/*.dwo - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,main) - -# - Debuginfo in `.o` files -# - (bar) `.rlib` file created, contains `.o` -# - (bar) `.o` present -# - (bar) `.dwo` never created -# - (bar) `.dwp` never created -# - (main) `.o` present -# - (main) `.dwo` never created -# - (main) `.dwp` never created -unpacked-crosscrate-single: - $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=unpacked \ - -Zsplit-dwarf-kind=single -C debuginfo=2 -g bar.rs - ls $(TMPDIR)/*.rlib - ls $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ - -C split-debuginfo=unpacked -Zsplit-dwarf-kind=single -C debuginfo=2 -g main.rs - ls $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,main) -endif + ifdef IS_WINDOWS + # Windows only supports packed debuginfo - nothing to test. + off: + packed: + unpacked: + else + # Some non-Windows, non-Darwin platforms are not stable, and some are. + ifeq ($(UNAME),Linux) + UNSTABLEOPTS := + else + UNSTABLEOPTS := -Zunstable-options + endif + + # - Debuginfo in `.o` files + # - `.o` deleted + # - `.dwo` never created + # - `.dwp` never created + off: + $(RUSTC) foo.rs -g -C $(UNSTABLEOPTS) split-debuginfo=off + [ ! -f $(TMPDIR)/*.dwp ] + [ ! -f $(TMPDIR)/*.dwo ] + $(RUSTC) foo.rs -g + [ ! -f $(TMPDIR)/*.dwp ] + [ ! -f $(TMPDIR)/*.dwo ] + + packed: packed-split packed-single packed-lto packed-remapped packed-crosscrate + + # - Debuginfo in `.dwo` files + # - `.o` deleted + # - `.dwo` deleted + # - `.dwp` present + packed-split: + $(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=packed -Zsplit-dwarf-kind=split + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + rm $(TMPDIR)/foo.dwp + rm $(TMPDIR)/$(call BIN,foo) + + # - Debuginfo in `.o` files + # - `.o` deleted + # - `.dwo` never created + # - `.dwp` present + packed-single: + $(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=packed -Zsplit-dwarf-kind=single + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + rm $(TMPDIR)/foo.dwp + rm $(TMPDIR)/$(call BIN,foo) + + packed-lto: packed-lto-split packed-lto-single + + # - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated + # - `.o` never created + # - `.dwo` never created + # - `.dwp` never created + packed-lto-split: + $(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=packed -Zsplit-dwarf-kind=split \ + --crate-type=rlib -Clinker-plugin-lto + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/libbaz.rlib + + # - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated + # - `.o` never created + # - `.dwo` never created + # - `.dwp` never created + packed-lto-single: + $(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=packed -Zsplit-dwarf-kind=single \ + --crate-type=rlib -Clinker-plugin-lto + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/libbaz.rlib + + packed-remapped: packed-remapped-split packed-remapped-single packed-remapped-scope packed-remapped-wrong-scope + + # - Debuginfo in `.dwo` files + # - `.o` and binary refer to remapped `.dwo` paths which do not exist + # - `.o` deleted + # - `.dwo` deleted + # - `.dwp` present + packed-remapped-split: + $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ + -Z split-dwarf-kind=split --remap-path-prefix $(TMPDIR)=/a foo.rs -g + objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + rm $(TMPDIR)/foo.dwp + rm $(TMPDIR)/$(call BIN,foo) + + # - Debuginfo in `.o` files + # - `.o` and binary refer to remapped `.o` paths which do not exist + # - `.o` deleted + # - `.dwo` never created + # - `.dwp` present + packed-remapped-single: + $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ + -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a foo.rs -g + objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + rm $(TMPDIR)/foo.dwp + rm $(TMPDIR)/$(call BIN,foo) + + # - Debuginfo in `.o` files + # - `.o` and binary refer to remapped `.o` paths which do not exist + # - `.o` deleted + # - `.dwo` never created + # - `.dwp` present + packed-remapped-scope: + $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ + -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ + -Z remap-path-scope=debuginfo foo.rs -g + objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + rm $(TMPDIR)/foo.dwp + rm $(TMPDIR)/$(call BIN,foo) + + # - Debuginfo in `.o` files + # - `.o` and binary refer to remapped `.o` paths which do not exist + # - `.o` deleted + # - `.dwo` never created + # - `.dwp` present + packed-remapped-wrong-scope: + $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ + -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ + -Z remap-path-scope=macro foo.rs -g + objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (grep $(TMPDIR)) || exit 1 + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + rm $(TMPDIR)/foo.dwp + rm $(TMPDIR)/$(call BIN,foo) + + packed-crosscrate: packed-crosscrate-split packed-crosscrate-single + + # - Debuginfo in `.dwo` files + # - (bar) `.rlib` file created, contains `.dwo` + # - (bar) `.o` deleted + # - (bar) `.dwo` deleted + # - (bar) `.dwp` never created + # - (main) `.o` deleted + # - (main) `.dwo` deleted + # - (main) `.dwp` present + packed-crosscrate-split: + $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=packed \ + -Zsplit-dwarf-kind=split -C debuginfo=2 -g bar.rs + ls $(TMPDIR)/*.rlib + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ + -C split-debuginfo=packed -Zsplit-dwarf-kind=split -C debuginfo=2 -g main.rs + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + rm $(TMPDIR)/main.dwp + rm $(TMPDIR)/$(call BIN,main) + + # - Debuginfo in `.o` files + # - (bar) `.rlib` file created, contains `.o` + # - (bar) `.o` deleted + # - (bar) `.dwo` never created + # - (bar) `.dwp` never created + # - (main) `.o` deleted + # - (main) `.dwo` never created + # - (main) `.dwp` present + packed-crosscrate-single: + $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=packed \ + -Zsplit-dwarf-kind=single -C debuginfo=2 -g bar.rs + ls $(TMPDIR)/*.rlib + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ + -C split-debuginfo=packed -Zsplit-dwarf-kind=single -C debuginfo=2 -g main.rs + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + rm $(TMPDIR)/main.dwp + rm $(TMPDIR)/$(call BIN,main) + + unpacked: unpacked-split unpacked-single unpacked-lto unpacked-remapped unpacked-crosscrate + + # - Debuginfo in `.dwo` files + # - `.o` deleted + # - `.dwo` present + # - `.dwp` never created + unpacked-split: + $(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=unpacked -Zsplit-dwarf-kind=split + ls $(TMPDIR)/*.o && exit 1 || exit 0 + rm $(TMPDIR)/*.dwo + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/$(call BIN,foo) + + # - Debuginfo in `.o` files + # - `.o` present + # - `.dwo` never created + # - `.dwp` never created + unpacked-single: + $(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=unpacked -Zsplit-dwarf-kind=single + ls $(TMPDIR)/*.o + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/$(call BIN,foo) + + unpacked-lto: unpacked-lto-split unpacked-lto-single + + # - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated + # - `.o` present (bitcode) + # - `.dwo` never created + # - `.dwp` never created + unpacked-lto-split: + $(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=unpacked -Zsplit-dwarf-kind=split \ + --crate-type=rlib -Clinker-plugin-lto + rm $(TMPDIR)/*.o + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/libbaz.rlib + + # - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated + # - `.o` present (bitcode) + # - `.dwo` never created + # - `.dwp` never created + unpacked-lto-single: + $(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=unpacked -Zsplit-dwarf-kind=single \ + --crate-type=rlib -Clinker-plugin-lto + rm $(TMPDIR)/*.o + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/libbaz.rlib + + unpacked-remapped: unpacked-remapped-split unpacked-remapped-single unpacked-remapped-scope unpacked-remapped-wrong-scope + + # - Debuginfo in `.dwo` files + # - `.o` and binary refer to remapped `.dwo` paths which do not exist + # - `.o` deleted + # - `.dwo` present + # - `.dwp` never created + unpacked-remapped-split: + $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ + -Z split-dwarf-kind=split --remap-path-prefix $(TMPDIR)=/a foo.rs -g + objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 + ls $(TMPDIR)/*.o && exit 1 || exit 0 + rm $(TMPDIR)/*.dwo + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/$(call BIN,foo) + + # - Debuginfo in `.o` files + # - `.o` and binary refer to remapped `.o` paths which do not exist + # - `.o` present + # - `.dwo` never created + # - `.dwp` never created + unpacked-remapped-single: + $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ + -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a foo.rs -g + objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 + rm $(TMPDIR)/*.o + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/$(call BIN,foo) + + # - Debuginfo in `.o` files + # - `.o` and binary refer to remapped `.o` paths which do not exist + # - `.o` present + # - `.dwo` never created + # - `.dwp` never created + unpacked-remapped-scope: + $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ + -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ + -Z remap-path-scope=debuginfo foo.rs -g + objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 + rm $(TMPDIR)/*.o + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/$(call BIN,foo) + + # - Debuginfo in `.o` files + # - `.o` and binary refer to remapped `.o` paths which do not exist + # - `.o` present + # - `.dwo` never created + # - `.dwp` never created + unpacked-remapped-wrong-scope: + $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ + -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ + -Z remap-path-scope=macro foo.rs -g + objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (grep $(TMPDIR)) || exit 1 + rm $(TMPDIR)/*.o + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/$(call BIN,foo) + + unpacked-crosscrate: unpacked-crosscrate-split unpacked-crosscrate-single + + # - Debuginfo in `.dwo` files + # - (bar) `.rlib` file created, contains `.dwo` + # - (bar) `.o` deleted + # - (bar) `.dwo` present + # - (bar) `.dwp` never created + # - (main) `.o` deleted + # - (main) `.dwo` present + # - (main) `.dwp` never created + unpacked-crosscrate-split: + $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=unpacked \ + -Zsplit-dwarf-kind=split -C debuginfo=2 -g bar.rs + ls $(TMPDIR)/*.rlib + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ + -C split-debuginfo=unpacked -Zsplit-dwarf-kind=split -C debuginfo=2 -g main.rs + ls $(TMPDIR)/*.o && exit 1 || exit 0 + rm $(TMPDIR)/*.dwo + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/$(call BIN,main) + + # - Debuginfo in `.o` files + # - (bar) `.rlib` file created, contains `.o` + # - (bar) `.o` present + # - (bar) `.dwo` never created + # - (bar) `.dwp` never created + # - (main) `.o` present + # - (main) `.dwo` never created + # - (main) `.dwp` never created + unpacked-crosscrate-single: + $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=unpacked \ + -Zsplit-dwarf-kind=single -C debuginfo=2 -g bar.rs + ls $(TMPDIR)/*.rlib + ls $(TMPDIR)/*.o + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ + -C split-debuginfo=unpacked -Zsplit-dwarf-kind=single -C debuginfo=2 -g main.rs + ls $(TMPDIR)/*.o + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/$(call BIN,main) + endif endif From 97ae7a0b8a1167649620a7df0dcfa670a3db5e3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= <39484203+jieyouxu@users.noreply.github.com> Date: Wed, 15 Jan 2025 16:50:43 +0800 Subject: [PATCH 18/57] tests: use an illustrative else-if branch in `split-debuginfo` Makefile This does not work with GNU Make 3.80, but this is just to make it easier to follow during review. --- tests/run-make/split-debuginfo/Makefile | 669 ++++++++++++------------ 1 file changed, 333 insertions(+), 336 deletions(-) diff --git a/tests/run-make/split-debuginfo/Makefile b/tests/run-make/split-debuginfo/Makefile index 53d3470afb5e..4259f6e1dd01 100644 --- a/tests/run-make/split-debuginfo/Makefile +++ b/tests/run-make/split-debuginfo/Makefile @@ -30,342 +30,339 @@ ifeq ($(UNAME),Darwin) $(RUSTC) foo.rs -g -C split-debuginfo=unpacked ls $(TMPDIR)/*.o [ ! -d $(TMPDIR)/foo.dSYM ] -else - ifdef IS_WINDOWS - # Windows only supports packed debuginfo - nothing to test. - off: - packed: - unpacked: +else ifdef IS_WINDOWS + # Windows only supports packed debuginfo - nothing to test. + off: + packed: + unpacked: +else # Some non-Windows, non-Darwin platforms are not stable, and some are. + ifeq ($(UNAME),Linux) + UNSTABLEOPTS := else - # Some non-Windows, non-Darwin platforms are not stable, and some are. - ifeq ($(UNAME),Linux) - UNSTABLEOPTS := - else - UNSTABLEOPTS := -Zunstable-options - endif - - # - Debuginfo in `.o` files - # - `.o` deleted - # - `.dwo` never created - # - `.dwp` never created - off: - $(RUSTC) foo.rs -g -C $(UNSTABLEOPTS) split-debuginfo=off - [ ! -f $(TMPDIR)/*.dwp ] - [ ! -f $(TMPDIR)/*.dwo ] - $(RUSTC) foo.rs -g - [ ! -f $(TMPDIR)/*.dwp ] - [ ! -f $(TMPDIR)/*.dwo ] - - packed: packed-split packed-single packed-lto packed-remapped packed-crosscrate - - # - Debuginfo in `.dwo` files - # - `.o` deleted - # - `.dwo` deleted - # - `.dwp` present - packed-split: - $(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=packed -Zsplit-dwarf-kind=split - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/foo.dwp - rm $(TMPDIR)/$(call BIN,foo) - - # - Debuginfo in `.o` files - # - `.o` deleted - # - `.dwo` never created - # - `.dwp` present - packed-single: - $(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=packed -Zsplit-dwarf-kind=single - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/foo.dwp - rm $(TMPDIR)/$(call BIN,foo) - - packed-lto: packed-lto-split packed-lto-single - - # - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated - # - `.o` never created - # - `.dwo` never created - # - `.dwp` never created - packed-lto-split: - $(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=packed -Zsplit-dwarf-kind=split \ - --crate-type=rlib -Clinker-plugin-lto - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/libbaz.rlib - - # - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated - # - `.o` never created - # - `.dwo` never created - # - `.dwp` never created - packed-lto-single: - $(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=packed -Zsplit-dwarf-kind=single \ - --crate-type=rlib -Clinker-plugin-lto - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/libbaz.rlib - - packed-remapped: packed-remapped-split packed-remapped-single packed-remapped-scope packed-remapped-wrong-scope - - # - Debuginfo in `.dwo` files - # - `.o` and binary refer to remapped `.dwo` paths which do not exist - # - `.o` deleted - # - `.dwo` deleted - # - `.dwp` present - packed-remapped-split: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ - -Z split-dwarf-kind=split --remap-path-prefix $(TMPDIR)=/a foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/foo.dwp - rm $(TMPDIR)/$(call BIN,foo) - - # - Debuginfo in `.o` files - # - `.o` and binary refer to remapped `.o` paths which do not exist - # - `.o` deleted - # - `.dwo` never created - # - `.dwp` present - packed-remapped-single: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/foo.dwp - rm $(TMPDIR)/$(call BIN,foo) - - # - Debuginfo in `.o` files - # - `.o` and binary refer to remapped `.o` paths which do not exist - # - `.o` deleted - # - `.dwo` never created - # - `.dwp` present - packed-remapped-scope: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ - -Z remap-path-scope=debuginfo foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/foo.dwp - rm $(TMPDIR)/$(call BIN,foo) - - # - Debuginfo in `.o` files - # - `.o` and binary refer to remapped `.o` paths which do not exist - # - `.o` deleted - # - `.dwo` never created - # - `.dwp` present - packed-remapped-wrong-scope: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ - -Z remap-path-scope=macro foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (grep $(TMPDIR)) || exit 1 - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/foo.dwp - rm $(TMPDIR)/$(call BIN,foo) - - packed-crosscrate: packed-crosscrate-split packed-crosscrate-single - - # - Debuginfo in `.dwo` files - # - (bar) `.rlib` file created, contains `.dwo` - # - (bar) `.o` deleted - # - (bar) `.dwo` deleted - # - (bar) `.dwp` never created - # - (main) `.o` deleted - # - (main) `.dwo` deleted - # - (main) `.dwp` present - packed-crosscrate-split: - $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=packed \ - -Zsplit-dwarf-kind=split -C debuginfo=2 -g bar.rs - ls $(TMPDIR)/*.rlib - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ - -C split-debuginfo=packed -Zsplit-dwarf-kind=split -C debuginfo=2 -g main.rs - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/main.dwp - rm $(TMPDIR)/$(call BIN,main) - - # - Debuginfo in `.o` files - # - (bar) `.rlib` file created, contains `.o` - # - (bar) `.o` deleted - # - (bar) `.dwo` never created - # - (bar) `.dwp` never created - # - (main) `.o` deleted - # - (main) `.dwo` never created - # - (main) `.dwp` present - packed-crosscrate-single: - $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=packed \ - -Zsplit-dwarf-kind=single -C debuginfo=2 -g bar.rs - ls $(TMPDIR)/*.rlib - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ - -C split-debuginfo=packed -Zsplit-dwarf-kind=single -C debuginfo=2 -g main.rs - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/main.dwp - rm $(TMPDIR)/$(call BIN,main) - - unpacked: unpacked-split unpacked-single unpacked-lto unpacked-remapped unpacked-crosscrate - - # - Debuginfo in `.dwo` files - # - `.o` deleted - # - `.dwo` present - # - `.dwp` never created - unpacked-split: - $(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=unpacked -Zsplit-dwarf-kind=split - ls $(TMPDIR)/*.o && exit 1 || exit 0 - rm $(TMPDIR)/*.dwo - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,foo) - - # - Debuginfo in `.o` files - # - `.o` present - # - `.dwo` never created - # - `.dwp` never created - unpacked-single: - $(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=unpacked -Zsplit-dwarf-kind=single - ls $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,foo) - - unpacked-lto: unpacked-lto-split unpacked-lto-single - - # - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated - # - `.o` present (bitcode) - # - `.dwo` never created - # - `.dwp` never created - unpacked-lto-split: - $(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=unpacked -Zsplit-dwarf-kind=split \ - --crate-type=rlib -Clinker-plugin-lto - rm $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/libbaz.rlib - - # - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated - # - `.o` present (bitcode) - # - `.dwo` never created - # - `.dwp` never created - unpacked-lto-single: - $(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=unpacked -Zsplit-dwarf-kind=single \ - --crate-type=rlib -Clinker-plugin-lto - rm $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/libbaz.rlib - - unpacked-remapped: unpacked-remapped-split unpacked-remapped-single unpacked-remapped-scope unpacked-remapped-wrong-scope - - # - Debuginfo in `.dwo` files - # - `.o` and binary refer to remapped `.dwo` paths which do not exist - # - `.o` deleted - # - `.dwo` present - # - `.dwp` never created - unpacked-remapped-split: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ - -Z split-dwarf-kind=split --remap-path-prefix $(TMPDIR)=/a foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 - ls $(TMPDIR)/*.o && exit 1 || exit 0 - rm $(TMPDIR)/*.dwo - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,foo) - - # - Debuginfo in `.o` files - # - `.o` and binary refer to remapped `.o` paths which do not exist - # - `.o` present - # - `.dwo` never created - # - `.dwp` never created - unpacked-remapped-single: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 - rm $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,foo) - - # - Debuginfo in `.o` files - # - `.o` and binary refer to remapped `.o` paths which do not exist - # - `.o` present - # - `.dwo` never created - # - `.dwp` never created - unpacked-remapped-scope: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ - -Z remap-path-scope=debuginfo foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 - rm $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,foo) - - # - Debuginfo in `.o` files - # - `.o` and binary refer to remapped `.o` paths which do not exist - # - `.o` present - # - `.dwo` never created - # - `.dwp` never created - unpacked-remapped-wrong-scope: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ - -Z remap-path-scope=macro foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (grep $(TMPDIR)) || exit 1 - rm $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,foo) - - unpacked-crosscrate: unpacked-crosscrate-split unpacked-crosscrate-single - - # - Debuginfo in `.dwo` files - # - (bar) `.rlib` file created, contains `.dwo` - # - (bar) `.o` deleted - # - (bar) `.dwo` present - # - (bar) `.dwp` never created - # - (main) `.o` deleted - # - (main) `.dwo` present - # - (main) `.dwp` never created - unpacked-crosscrate-split: - $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=unpacked \ - -Zsplit-dwarf-kind=split -C debuginfo=2 -g bar.rs - ls $(TMPDIR)/*.rlib - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ - -C split-debuginfo=unpacked -Zsplit-dwarf-kind=split -C debuginfo=2 -g main.rs - ls $(TMPDIR)/*.o && exit 1 || exit 0 - rm $(TMPDIR)/*.dwo - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,main) - - # - Debuginfo in `.o` files - # - (bar) `.rlib` file created, contains `.o` - # - (bar) `.o` present - # - (bar) `.dwo` never created - # - (bar) `.dwp` never created - # - (main) `.o` present - # - (main) `.dwo` never created - # - (main) `.dwp` never created - unpacked-crosscrate-single: - $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=unpacked \ - -Zsplit-dwarf-kind=single -C debuginfo=2 -g bar.rs - ls $(TMPDIR)/*.rlib - ls $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ - -C split-debuginfo=unpacked -Zsplit-dwarf-kind=single -C debuginfo=2 -g main.rs - ls $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,main) + UNSTABLEOPTS := -Zunstable-options endif + + # - Debuginfo in `.o` files + # - `.o` deleted + # - `.dwo` never created + # - `.dwp` never created + off: + $(RUSTC) foo.rs -g -C $(UNSTABLEOPTS) split-debuginfo=off + [ ! -f $(TMPDIR)/*.dwp ] + [ ! -f $(TMPDIR)/*.dwo ] + $(RUSTC) foo.rs -g + [ ! -f $(TMPDIR)/*.dwp ] + [ ! -f $(TMPDIR)/*.dwo ] + + packed: packed-split packed-single packed-lto packed-remapped packed-crosscrate + + # - Debuginfo in `.dwo` files + # - `.o` deleted + # - `.dwo` deleted + # - `.dwp` present + packed-split: + $(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=packed -Zsplit-dwarf-kind=split + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + rm $(TMPDIR)/foo.dwp + rm $(TMPDIR)/$(call BIN,foo) + + # - Debuginfo in `.o` files + # - `.o` deleted + # - `.dwo` never created + # - `.dwp` present + packed-single: + $(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=packed -Zsplit-dwarf-kind=single + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + rm $(TMPDIR)/foo.dwp + rm $(TMPDIR)/$(call BIN,foo) + + packed-lto: packed-lto-split packed-lto-single + + # - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated + # - `.o` never created + # - `.dwo` never created + # - `.dwp` never created + packed-lto-split: + $(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=packed -Zsplit-dwarf-kind=split \ + --crate-type=rlib -Clinker-plugin-lto + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/libbaz.rlib + + # - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated + # - `.o` never created + # - `.dwo` never created + # - `.dwp` never created + packed-lto-single: + $(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=packed -Zsplit-dwarf-kind=single \ + --crate-type=rlib -Clinker-plugin-lto + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/libbaz.rlib + + packed-remapped: packed-remapped-split packed-remapped-single packed-remapped-scope packed-remapped-wrong-scope + + # - Debuginfo in `.dwo` files + # - `.o` and binary refer to remapped `.dwo` paths which do not exist + # - `.o` deleted + # - `.dwo` deleted + # - `.dwp` present + packed-remapped-split: + $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ + -Z split-dwarf-kind=split --remap-path-prefix $(TMPDIR)=/a foo.rs -g + objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + rm $(TMPDIR)/foo.dwp + rm $(TMPDIR)/$(call BIN,foo) + + # - Debuginfo in `.o` files + # - `.o` and binary refer to remapped `.o` paths which do not exist + # - `.o` deleted + # - `.dwo` never created + # - `.dwp` present + packed-remapped-single: + $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ + -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a foo.rs -g + objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + rm $(TMPDIR)/foo.dwp + rm $(TMPDIR)/$(call BIN,foo) + + # - Debuginfo in `.o` files + # - `.o` and binary refer to remapped `.o` paths which do not exist + # - `.o` deleted + # - `.dwo` never created + # - `.dwp` present + packed-remapped-scope: + $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ + -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ + -Z remap-path-scope=debuginfo foo.rs -g + objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + rm $(TMPDIR)/foo.dwp + rm $(TMPDIR)/$(call BIN,foo) + + # - Debuginfo in `.o` files + # - `.o` and binary refer to remapped `.o` paths which do not exist + # - `.o` deleted + # - `.dwo` never created + # - `.dwp` present + packed-remapped-wrong-scope: + $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ + -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ + -Z remap-path-scope=macro foo.rs -g + objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (grep $(TMPDIR)) || exit 1 + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + rm $(TMPDIR)/foo.dwp + rm $(TMPDIR)/$(call BIN,foo) + + packed-crosscrate: packed-crosscrate-split packed-crosscrate-single + + # - Debuginfo in `.dwo` files + # - (bar) `.rlib` file created, contains `.dwo` + # - (bar) `.o` deleted + # - (bar) `.dwo` deleted + # - (bar) `.dwp` never created + # - (main) `.o` deleted + # - (main) `.dwo` deleted + # - (main) `.dwp` present + packed-crosscrate-split: + $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=packed \ + -Zsplit-dwarf-kind=split -C debuginfo=2 -g bar.rs + ls $(TMPDIR)/*.rlib + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ + -C split-debuginfo=packed -Zsplit-dwarf-kind=split -C debuginfo=2 -g main.rs + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + rm $(TMPDIR)/main.dwp + rm $(TMPDIR)/$(call BIN,main) + + # - Debuginfo in `.o` files + # - (bar) `.rlib` file created, contains `.o` + # - (bar) `.o` deleted + # - (bar) `.dwo` never created + # - (bar) `.dwp` never created + # - (main) `.o` deleted + # - (main) `.dwo` never created + # - (main) `.dwp` present + packed-crosscrate-single: + $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=packed \ + -Zsplit-dwarf-kind=single -C debuginfo=2 -g bar.rs + ls $(TMPDIR)/*.rlib + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ + -C split-debuginfo=packed -Zsplit-dwarf-kind=single -C debuginfo=2 -g main.rs + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + rm $(TMPDIR)/main.dwp + rm $(TMPDIR)/$(call BIN,main) + + unpacked: unpacked-split unpacked-single unpacked-lto unpacked-remapped unpacked-crosscrate + + # - Debuginfo in `.dwo` files + # - `.o` deleted + # - `.dwo` present + # - `.dwp` never created + unpacked-split: + $(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=unpacked -Zsplit-dwarf-kind=split + ls $(TMPDIR)/*.o && exit 1 || exit 0 + rm $(TMPDIR)/*.dwo + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/$(call BIN,foo) + + # - Debuginfo in `.o` files + # - `.o` present + # - `.dwo` never created + # - `.dwp` never created + unpacked-single: + $(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=unpacked -Zsplit-dwarf-kind=single + ls $(TMPDIR)/*.o + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/$(call BIN,foo) + + unpacked-lto: unpacked-lto-split unpacked-lto-single + + # - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated + # - `.o` present (bitcode) + # - `.dwo` never created + # - `.dwp` never created + unpacked-lto-split: + $(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=unpacked -Zsplit-dwarf-kind=split \ + --crate-type=rlib -Clinker-plugin-lto + rm $(TMPDIR)/*.o + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/libbaz.rlib + + # - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated + # - `.o` present (bitcode) + # - `.dwo` never created + # - `.dwp` never created + unpacked-lto-single: + $(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=unpacked -Zsplit-dwarf-kind=single \ + --crate-type=rlib -Clinker-plugin-lto + rm $(TMPDIR)/*.o + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/libbaz.rlib + + unpacked-remapped: unpacked-remapped-split unpacked-remapped-single unpacked-remapped-scope unpacked-remapped-wrong-scope + + # - Debuginfo in `.dwo` files + # - `.o` and binary refer to remapped `.dwo` paths which do not exist + # - `.o` deleted + # - `.dwo` present + # - `.dwp` never created + unpacked-remapped-split: + $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ + -Z split-dwarf-kind=split --remap-path-prefix $(TMPDIR)=/a foo.rs -g + objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 + ls $(TMPDIR)/*.o && exit 1 || exit 0 + rm $(TMPDIR)/*.dwo + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/$(call BIN,foo) + + # - Debuginfo in `.o` files + # - `.o` and binary refer to remapped `.o` paths which do not exist + # - `.o` present + # - `.dwo` never created + # - `.dwp` never created + unpacked-remapped-single: + $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ + -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a foo.rs -g + objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 + rm $(TMPDIR)/*.o + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/$(call BIN,foo) + + # - Debuginfo in `.o` files + # - `.o` and binary refer to remapped `.o` paths which do not exist + # - `.o` present + # - `.dwo` never created + # - `.dwp` never created + unpacked-remapped-scope: + $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ + -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ + -Z remap-path-scope=debuginfo foo.rs -g + objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 + rm $(TMPDIR)/*.o + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/$(call BIN,foo) + + # - Debuginfo in `.o` files + # - `.o` and binary refer to remapped `.o` paths which do not exist + # - `.o` present + # - `.dwo` never created + # - `.dwp` never created + unpacked-remapped-wrong-scope: + $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ + -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ + -Z remap-path-scope=macro foo.rs -g + objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (grep $(TMPDIR)) || exit 1 + rm $(TMPDIR)/*.o + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/$(call BIN,foo) + + unpacked-crosscrate: unpacked-crosscrate-split unpacked-crosscrate-single + + # - Debuginfo in `.dwo` files + # - (bar) `.rlib` file created, contains `.dwo` + # - (bar) `.o` deleted + # - (bar) `.dwo` present + # - (bar) `.dwp` never created + # - (main) `.o` deleted + # - (main) `.dwo` present + # - (main) `.dwp` never created + unpacked-crosscrate-split: + $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=unpacked \ + -Zsplit-dwarf-kind=split -C debuginfo=2 -g bar.rs + ls $(TMPDIR)/*.rlib + ls $(TMPDIR)/*.o && exit 1 || exit 0 + ls $(TMPDIR)/*.dwo + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ + -C split-debuginfo=unpacked -Zsplit-dwarf-kind=split -C debuginfo=2 -g main.rs + ls $(TMPDIR)/*.o && exit 1 || exit 0 + rm $(TMPDIR)/*.dwo + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/$(call BIN,main) + + # - Debuginfo in `.o` files + # - (bar) `.rlib` file created, contains `.o` + # - (bar) `.o` present + # - (bar) `.dwo` never created + # - (bar) `.dwp` never created + # - (main) `.o` present + # - (main) `.dwo` never created + # - (main) `.dwp` never created + unpacked-crosscrate-single: + $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=unpacked \ + -Zsplit-dwarf-kind=single -C debuginfo=2 -g bar.rs + ls $(TMPDIR)/*.rlib + ls $(TMPDIR)/*.o + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ + -C split-debuginfo=unpacked -Zsplit-dwarf-kind=single -C debuginfo=2 -g main.rs + ls $(TMPDIR)/*.o + ls $(TMPDIR)/*.dwo && exit 1 || exit 0 + ls $(TMPDIR)/*.dwp && exit 1 || exit 0 + rm $(TMPDIR)/$(call BIN,main) endif From 95ba76b3c5d78622a6fa5b9d654e5624c4cfb463 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= <39484203+jieyouxu@users.noreply.github.com> Date: Wed, 15 Jan 2025 17:25:18 +0800 Subject: [PATCH 19/57] tests: remove redundant `-C debuginfo=2` when `-g` is already specified `-g` is an alias for `-C debuginfo=2`. --- tests/run-make/split-debuginfo/Makefile | 48 ++++++++++++------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/tests/run-make/split-debuginfo/Makefile b/tests/run-make/split-debuginfo/Makefile index 4259f6e1dd01..15952ddee80f 100644 --- a/tests/run-make/split-debuginfo/Makefile +++ b/tests/run-make/split-debuginfo/Makefile @@ -112,8 +112,8 @@ else # Some non-Windows, non-Darwin platforms are not stable, and some are. # - `.dwo` deleted # - `.dwp` present packed-remapped-split: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ - -Z split-dwarf-kind=split --remap-path-prefix $(TMPDIR)=/a foo.rs -g + $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -g \ + -Z split-dwarf-kind=split --remap-path-prefix $(TMPDIR)=/a foo.rs objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 ls $(TMPDIR)/*.o && exit 1 || exit 0 ls $(TMPDIR)/*.dwo && exit 1 || exit 0 @@ -126,8 +126,8 @@ else # Some non-Windows, non-Darwin platforms are not stable, and some are. # - `.dwo` never created # - `.dwp` present packed-remapped-single: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a foo.rs -g + $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -g \ + -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a foo.rs objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 ls $(TMPDIR)/*.o && exit 1 || exit 0 ls $(TMPDIR)/*.dwo && exit 1 || exit 0 @@ -140,9 +140,9 @@ else # Some non-Windows, non-Darwin platforms are not stable, and some are. # - `.dwo` never created # - `.dwp` present packed-remapped-scope: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ + $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -g \ -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ - -Z remap-path-scope=debuginfo foo.rs -g + -Z remap-path-scope=debuginfo foo.rs objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 ls $(TMPDIR)/*.o && exit 1 || exit 0 ls $(TMPDIR)/*.dwo && exit 1 || exit 0 @@ -155,9 +155,9 @@ else # Some non-Windows, non-Darwin platforms are not stable, and some are. # - `.dwo` never created # - `.dwp` present packed-remapped-wrong-scope: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ + $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -g \ -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ - -Z remap-path-scope=macro foo.rs -g + -Z remap-path-scope=macro foo.rs objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (grep $(TMPDIR)) || exit 1 ls $(TMPDIR)/*.o && exit 1 || exit 0 ls $(TMPDIR)/*.dwo && exit 1 || exit 0 @@ -176,13 +176,13 @@ else # Some non-Windows, non-Darwin platforms are not stable, and some are. # - (main) `.dwp` present packed-crosscrate-split: $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=packed \ - -Zsplit-dwarf-kind=split -C debuginfo=2 -g bar.rs + -Zsplit-dwarf-kind=split -g bar.rs ls $(TMPDIR)/*.rlib ls $(TMPDIR)/*.o && exit 1 || exit 0 ls $(TMPDIR)/*.dwo && exit 1 || exit 0 ls $(TMPDIR)/*.dwp && exit 1 || exit 0 $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ - -C split-debuginfo=packed -Zsplit-dwarf-kind=split -C debuginfo=2 -g main.rs + -C split-debuginfo=packed -Zsplit-dwarf-kind=split -g main.rs ls $(TMPDIR)/*.o && exit 1 || exit 0 ls $(TMPDIR)/*.dwo && exit 1 || exit 0 rm $(TMPDIR)/main.dwp @@ -198,13 +198,13 @@ else # Some non-Windows, non-Darwin platforms are not stable, and some are. # - (main) `.dwp` present packed-crosscrate-single: $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=packed \ - -Zsplit-dwarf-kind=single -C debuginfo=2 -g bar.rs + -Zsplit-dwarf-kind=single -g bar.rs ls $(TMPDIR)/*.rlib ls $(TMPDIR)/*.o && exit 1 || exit 0 ls $(TMPDIR)/*.dwo && exit 1 || exit 0 ls $(TMPDIR)/*.dwp && exit 1 || exit 0 $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ - -C split-debuginfo=packed -Zsplit-dwarf-kind=single -C debuginfo=2 -g main.rs + -C split-debuginfo=packed -Zsplit-dwarf-kind=single -g main.rs ls $(TMPDIR)/*.o && exit 1 || exit 0 ls $(TMPDIR)/*.dwo && exit 1 || exit 0 rm $(TMPDIR)/main.dwp @@ -268,8 +268,8 @@ else # Some non-Windows, non-Darwin platforms are not stable, and some are. # - `.dwo` present # - `.dwp` never created unpacked-remapped-split: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ - -Z split-dwarf-kind=split --remap-path-prefix $(TMPDIR)=/a foo.rs -g + $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -g \ + -Z split-dwarf-kind=split --remap-path-prefix $(TMPDIR)=/a foo.rs objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 ls $(TMPDIR)/*.o && exit 1 || exit 0 rm $(TMPDIR)/*.dwo @@ -282,8 +282,8 @@ else # Some non-Windows, non-Darwin platforms are not stable, and some are. # - `.dwo` never created # - `.dwp` never created unpacked-remapped-single: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a foo.rs -g + $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -g \ + -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a foo.rs objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 rm $(TMPDIR)/*.o ls $(TMPDIR)/*.dwo && exit 1 || exit 0 @@ -296,9 +296,9 @@ else # Some non-Windows, non-Darwin platforms are not stable, and some are. # - `.dwo` never created # - `.dwp` never created unpacked-remapped-scope: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ + $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -g \ -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ - -Z remap-path-scope=debuginfo foo.rs -g + -Z remap-path-scope=debuginfo foo.rs objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 rm $(TMPDIR)/*.o ls $(TMPDIR)/*.dwo && exit 1 || exit 0 @@ -311,9 +311,9 @@ else # Some non-Windows, non-Darwin platforms are not stable, and some are. # - `.dwo` never created # - `.dwp` never created unpacked-remapped-wrong-scope: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ + $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -g \ -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ - -Z remap-path-scope=macro foo.rs -g + -Z remap-path-scope=macro foo.rs objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (grep $(TMPDIR)) || exit 1 rm $(TMPDIR)/*.o ls $(TMPDIR)/*.dwo && exit 1 || exit 0 @@ -332,13 +332,13 @@ else # Some non-Windows, non-Darwin platforms are not stable, and some are. # - (main) `.dwp` never created unpacked-crosscrate-split: $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=unpacked \ - -Zsplit-dwarf-kind=split -C debuginfo=2 -g bar.rs + -Zsplit-dwarf-kind=split -g bar.rs ls $(TMPDIR)/*.rlib ls $(TMPDIR)/*.o && exit 1 || exit 0 ls $(TMPDIR)/*.dwo ls $(TMPDIR)/*.dwp && exit 1 || exit 0 $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ - -C split-debuginfo=unpacked -Zsplit-dwarf-kind=split -C debuginfo=2 -g main.rs + -C split-debuginfo=unpacked -Zsplit-dwarf-kind=split -g main.rs ls $(TMPDIR)/*.o && exit 1 || exit 0 rm $(TMPDIR)/*.dwo ls $(TMPDIR)/*.dwp && exit 1 || exit 0 @@ -354,13 +354,13 @@ else # Some non-Windows, non-Darwin platforms are not stable, and some are. # - (main) `.dwp` never created unpacked-crosscrate-single: $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=unpacked \ - -Zsplit-dwarf-kind=single -C debuginfo=2 -g bar.rs + -Zsplit-dwarf-kind=single -g bar.rs ls $(TMPDIR)/*.rlib ls $(TMPDIR)/*.o ls $(TMPDIR)/*.dwo && exit 1 || exit 0 ls $(TMPDIR)/*.dwp && exit 1 || exit 0 $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ - -C split-debuginfo=unpacked -Zsplit-dwarf-kind=single -C debuginfo=2 -g main.rs + -C split-debuginfo=unpacked -Zsplit-dwarf-kind=single -g main.rs ls $(TMPDIR)/*.o ls $(TMPDIR)/*.dwo && exit 1 || exit 0 ls $(TMPDIR)/*.dwp && exit 1 || exit 0 From b06066341b72ecb5568809c28f8a441c1875aa27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= <39484203+jieyouxu@users.noreply.github.com> Date: Wed, 15 Jan 2025 23:16:10 +0800 Subject: [PATCH 20/57] tests: fix `-Zunstable-options` bug in `split-debuginfo` --- tests/run-make/split-debuginfo/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/run-make/split-debuginfo/Makefile b/tests/run-make/split-debuginfo/Makefile index 15952ddee80f..fcc80cb7fd80 100644 --- a/tests/run-make/split-debuginfo/Makefile +++ b/tests/run-make/split-debuginfo/Makefile @@ -47,7 +47,7 @@ else # Some non-Windows, non-Darwin platforms are not stable, and some are. # - `.dwo` never created # - `.dwp` never created off: - $(RUSTC) foo.rs -g -C $(UNSTABLEOPTS) split-debuginfo=off + $(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=off [ ! -f $(TMPDIR)/*.dwp ] [ ! -f $(TMPDIR)/*.dwo ] $(RUSTC) foo.rs -g From 16e5ea4ec43814abdec220ec834845b47b390a0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= <39484203+jieyouxu@users.noreply.github.com> Date: Thu, 16 Jan 2025 14:37:50 +0800 Subject: [PATCH 21/57] tests: fix incorrect comment for wrong path remap scope --- tests/run-make/split-debuginfo/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/run-make/split-debuginfo/Makefile b/tests/run-make/split-debuginfo/Makefile index fcc80cb7fd80..8a7461727d18 100644 --- a/tests/run-make/split-debuginfo/Makefile +++ b/tests/run-make/split-debuginfo/Makefile @@ -150,7 +150,7 @@ else # Some non-Windows, non-Darwin platforms are not stable, and some are. rm $(TMPDIR)/$(call BIN,foo) # - Debuginfo in `.o` files - # - `.o` and binary refer to remapped `.o` paths which do not exist + # - `.o` and binary refer to un-remapped `.o` because the remap path scope is for macros. # - `.o` deleted # - `.dwo` never created # - `.dwp` present From 223e0f1ce1dc7e06041f7226a661a653c0ddb3df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= <39484203+jieyouxu@users.noreply.github.com> Date: Thu, 16 Jan 2025 15:23:53 +0800 Subject: [PATCH 22/57] tests: fix incorrect comment --- tests/run-make/split-debuginfo/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/run-make/split-debuginfo/Makefile b/tests/run-make/split-debuginfo/Makefile index 8a7461727d18..abb1fab71413 100644 --- a/tests/run-make/split-debuginfo/Makefile +++ b/tests/run-make/split-debuginfo/Makefile @@ -237,7 +237,7 @@ else # Some non-Windows, non-Darwin platforms are not stable, and some are. unpacked-lto: unpacked-lto-split unpacked-lto-single # - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated - # - `.o` present (bitcode) + # - `.o` not present # - `.dwo` never created # - `.dwp` never created unpacked-lto-split: From 68b6f92a56f36cb891b84c918eb70d3acbffd10f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= <39484203+jieyouxu@users.noreply.github.com> Date: Thu, 16 Jan 2025 03:14:34 +0800 Subject: [PATCH 23/57] run-make-support: add `shallow_find_directories` helper --- src/tools/run-make-support/src/fs.rs | 4 +--- src/tools/run-make-support/src/lib.rs | 4 ++-- .../run-make-support/src/path_helpers.rs | 19 +++++++++++++++++++ 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/tools/run-make-support/src/fs.rs b/src/tools/run-make-support/src/fs.rs index 7ebe4a9ca13b..ceb4ebbaca55 100644 --- a/src/tools/run-make-support/src/fs.rs +++ b/src/tools/run-make-support/src/fs.rs @@ -238,9 +238,7 @@ pub fn set_permissions>(path: P, perm: std::fs::Permissions) { )); } -/// A function which prints all file names in the directory `dir` similarly to Unix's `ls`. -/// Useful for debugging. -/// Usage: `eprintln!("{:#?}", shallow_find_dir_entries(some_dir));` +/// List directory entries immediately under the given `dir`. #[track_caller] pub fn shallow_find_dir_entries>(dir: P) -> Vec { let paths = read_dir(dir); diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index a8c9bec57fde..7e63ab3159ac 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -94,8 +94,8 @@ pub use artifact_names::{ /// Path-related helpers. pub use path_helpers::{ - cwd, filename_contains, filename_not_in_denylist, has_extension, has_prefix, has_suffix, - not_contains, path, shallow_find_files, build_root, source_root, + build_root, cwd, filename_contains, filename_not_in_denylist, has_extension, has_prefix, + has_suffix, not_contains, path, shallow_find_directories, shallow_find_files, source_root, }; /// Helpers for scoped test execution where certain properties are attempted to be maintained. diff --git a/src/tools/run-make-support/src/path_helpers.rs b/src/tools/run-make-support/src/path_helpers.rs index 1c59f2feea4c..b766e50e523b 100644 --- a/src/tools/run-make-support/src/path_helpers.rs +++ b/src/tools/run-make-support/src/path_helpers.rs @@ -59,6 +59,25 @@ pub fn shallow_find_files, F: Fn(&PathBuf) -> bool>( matching_files } +/// Browse the directory `path` non-recursively and return all directories which respect the +/// parameters outlined by `closure`. +#[track_caller] +pub fn shallow_find_directories, F: Fn(&PathBuf) -> bool>( + path: P, + filter: F, +) -> Vec { + let mut matching_files = Vec::new(); + for entry in rfs::read_dir(path) { + let entry = entry.expect("failed to read directory entry."); + let path = entry.path(); + + if path.is_dir() && filter(&path) { + matching_files.push(path); + } + } + matching_files +} + /// Returns true if the filename at `path` does not contain `expected`. pub fn not_contains>(path: P, expected: &str) -> bool { !path.as_ref().file_name().is_some_and(|name| name.to_str().unwrap().contains(expected)) From 1e87d00e11c261f80d581c87f0ed7ffbb373d46a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= <39484203+jieyouxu@users.noreply.github.com> Date: Thu, 16 Jan 2025 14:18:44 +0800 Subject: [PATCH 24/57] run-make-support: add some stable `rustc` flag helpers --- .../run-make-support/src/external_deps/rustc.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/tools/run-make-support/src/external_deps/rustc.rs b/src/tools/run-make-support/src/external_deps/rustc.rs index 8d99924a2d17..33dc57cbc077 100644 --- a/src/tools/run-make-support/src/external_deps/rustc.rs +++ b/src/tools/run-make-support/src/external_deps/rustc.rs @@ -338,6 +338,18 @@ impl Rustc { self } + /// Specify `-C debuginfo=...`. + pub fn debuginfo(&mut self, level: &str) -> &mut Self { + self.cmd.arg(format!("-Cdebuginfo={level}")); + self + } + + /// Specify `-C split-debuginfo={packed,unpacked,off}`. + pub fn split_debuginfo(&mut self, split_kind: &str) -> &mut Self { + self.cmd.arg(format!("-Csplit-debuginfo={split_kind}")); + self + } + /// Pass the `--verbose` flag. pub fn verbose(&mut self) -> &mut Self { self.cmd.arg("--verbose"); From 58c4b3ce4ae067c054bcb57a6890701eb827ef62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= <39484203+jieyouxu@users.noreply.github.com> Date: Wed, 15 Jan 2025 19:23:07 +0800 Subject: [PATCH 25/57] tests: port `split-debuginfo` to rmake.rs Co-authored-by: Oneirical --- .../tidy/src/allowed_run_make_makefiles.txt | 1 - tests/run-make/split-debuginfo/Makefile | 368 ---- tests/run-make/split-debuginfo/rmake.rs | 1618 +++++++++++++++++ 3 files changed, 1618 insertions(+), 369 deletions(-) delete mode 100644 tests/run-make/split-debuginfo/Makefile create mode 100644 tests/run-make/split-debuginfo/rmake.rs diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index 45b40b17ea37..e69de29bb2d1 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -1 +0,0 @@ -run-make/split-debuginfo/Makefile diff --git a/tests/run-make/split-debuginfo/Makefile b/tests/run-make/split-debuginfo/Makefile deleted file mode 100644 index abb1fab71413..000000000000 --- a/tests/run-make/split-debuginfo/Makefile +++ /dev/null @@ -1,368 +0,0 @@ -# ignore-cross-compile -# ignore-riscv64 On this platform only `-Csplit-debuginfo=off` is supported, see #120518 - -include ../tools.mk - -all: off packed unpacked - -ifeq ($(UNAME),Darwin) - # If disabled, don't run `dsymutil`. - off: - rm -rf $(TMPDIR)/*.dSYM - $(RUSTC) foo.rs -g -C split-debuginfo=off - [ ! -d $(TMPDIR)/foo.dSYM ] - - # Packed by default, but only if debuginfo is requested - packed: - rm -rf $(TMPDIR)/*.dSYM - $(RUSTC) foo.rs - [ ! -d $(TMPDIR)/foo.dSYM ] - rm -rf $(TMPDIR)/*.dSYM - $(RUSTC) foo.rs -g - [ -d $(TMPDIR)/foo.dSYM ] - rm -rf $(TMPDIR)/*.dSYM - $(RUSTC) foo.rs -g -C split-debuginfo=packed - [ -d $(TMPDIR)/foo.dSYM ] - rm -rf $(TMPDIR)/*.dSYM - - # Object files are preserved with unpacked and `dsymutil` isn't run - unpacked: - $(RUSTC) foo.rs -g -C split-debuginfo=unpacked - ls $(TMPDIR)/*.o - [ ! -d $(TMPDIR)/foo.dSYM ] -else ifdef IS_WINDOWS - # Windows only supports packed debuginfo - nothing to test. - off: - packed: - unpacked: -else # Some non-Windows, non-Darwin platforms are not stable, and some are. - ifeq ($(UNAME),Linux) - UNSTABLEOPTS := - else - UNSTABLEOPTS := -Zunstable-options - endif - - # - Debuginfo in `.o` files - # - `.o` deleted - # - `.dwo` never created - # - `.dwp` never created - off: - $(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=off - [ ! -f $(TMPDIR)/*.dwp ] - [ ! -f $(TMPDIR)/*.dwo ] - $(RUSTC) foo.rs -g - [ ! -f $(TMPDIR)/*.dwp ] - [ ! -f $(TMPDIR)/*.dwo ] - - packed: packed-split packed-single packed-lto packed-remapped packed-crosscrate - - # - Debuginfo in `.dwo` files - # - `.o` deleted - # - `.dwo` deleted - # - `.dwp` present - packed-split: - $(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=packed -Zsplit-dwarf-kind=split - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/foo.dwp - rm $(TMPDIR)/$(call BIN,foo) - - # - Debuginfo in `.o` files - # - `.o` deleted - # - `.dwo` never created - # - `.dwp` present - packed-single: - $(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=packed -Zsplit-dwarf-kind=single - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/foo.dwp - rm $(TMPDIR)/$(call BIN,foo) - - packed-lto: packed-lto-split packed-lto-single - - # - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated - # - `.o` never created - # - `.dwo` never created - # - `.dwp` never created - packed-lto-split: - $(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=packed -Zsplit-dwarf-kind=split \ - --crate-type=rlib -Clinker-plugin-lto - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/libbaz.rlib - - # - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated - # - `.o` never created - # - `.dwo` never created - # - `.dwp` never created - packed-lto-single: - $(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=packed -Zsplit-dwarf-kind=single \ - --crate-type=rlib -Clinker-plugin-lto - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/libbaz.rlib - - packed-remapped: packed-remapped-split packed-remapped-single packed-remapped-scope packed-remapped-wrong-scope - - # - Debuginfo in `.dwo` files - # - `.o` and binary refer to remapped `.dwo` paths which do not exist - # - `.o` deleted - # - `.dwo` deleted - # - `.dwp` present - packed-remapped-split: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -g \ - -Z split-dwarf-kind=split --remap-path-prefix $(TMPDIR)=/a foo.rs - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/foo.dwp - rm $(TMPDIR)/$(call BIN,foo) - - # - Debuginfo in `.o` files - # - `.o` and binary refer to remapped `.o` paths which do not exist - # - `.o` deleted - # - `.dwo` never created - # - `.dwp` present - packed-remapped-single: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -g \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a foo.rs - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/foo.dwp - rm $(TMPDIR)/$(call BIN,foo) - - # - Debuginfo in `.o` files - # - `.o` and binary refer to remapped `.o` paths which do not exist - # - `.o` deleted - # - `.dwo` never created - # - `.dwp` present - packed-remapped-scope: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -g \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ - -Z remap-path-scope=debuginfo foo.rs - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/foo.dwp - rm $(TMPDIR)/$(call BIN,foo) - - # - Debuginfo in `.o` files - # - `.o` and binary refer to un-remapped `.o` because the remap path scope is for macros. - # - `.o` deleted - # - `.dwo` never created - # - `.dwp` present - packed-remapped-wrong-scope: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -g \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ - -Z remap-path-scope=macro foo.rs - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (grep $(TMPDIR)) || exit 1 - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/foo.dwp - rm $(TMPDIR)/$(call BIN,foo) - - packed-crosscrate: packed-crosscrate-split packed-crosscrate-single - - # - Debuginfo in `.dwo` files - # - (bar) `.rlib` file created, contains `.dwo` - # - (bar) `.o` deleted - # - (bar) `.dwo` deleted - # - (bar) `.dwp` never created - # - (main) `.o` deleted - # - (main) `.dwo` deleted - # - (main) `.dwp` present - packed-crosscrate-split: - $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=packed \ - -Zsplit-dwarf-kind=split -g bar.rs - ls $(TMPDIR)/*.rlib - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ - -C split-debuginfo=packed -Zsplit-dwarf-kind=split -g main.rs - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/main.dwp - rm $(TMPDIR)/$(call BIN,main) - - # - Debuginfo in `.o` files - # - (bar) `.rlib` file created, contains `.o` - # - (bar) `.o` deleted - # - (bar) `.dwo` never created - # - (bar) `.dwp` never created - # - (main) `.o` deleted - # - (main) `.dwo` never created - # - (main) `.dwp` present - packed-crosscrate-single: - $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=packed \ - -Zsplit-dwarf-kind=single -g bar.rs - ls $(TMPDIR)/*.rlib - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ - -C split-debuginfo=packed -Zsplit-dwarf-kind=single -g main.rs - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/main.dwp - rm $(TMPDIR)/$(call BIN,main) - - unpacked: unpacked-split unpacked-single unpacked-lto unpacked-remapped unpacked-crosscrate - - # - Debuginfo in `.dwo` files - # - `.o` deleted - # - `.dwo` present - # - `.dwp` never created - unpacked-split: - $(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=unpacked -Zsplit-dwarf-kind=split - ls $(TMPDIR)/*.o && exit 1 || exit 0 - rm $(TMPDIR)/*.dwo - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,foo) - - # - Debuginfo in `.o` files - # - `.o` present - # - `.dwo` never created - # - `.dwp` never created - unpacked-single: - $(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=unpacked -Zsplit-dwarf-kind=single - ls $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,foo) - - unpacked-lto: unpacked-lto-split unpacked-lto-single - - # - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated - # - `.o` not present - # - `.dwo` never created - # - `.dwp` never created - unpacked-lto-split: - $(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=unpacked -Zsplit-dwarf-kind=split \ - --crate-type=rlib -Clinker-plugin-lto - rm $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/libbaz.rlib - - # - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated - # - `.o` present (bitcode) - # - `.dwo` never created - # - `.dwp` never created - unpacked-lto-single: - $(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=unpacked -Zsplit-dwarf-kind=single \ - --crate-type=rlib -Clinker-plugin-lto - rm $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/libbaz.rlib - - unpacked-remapped: unpacked-remapped-split unpacked-remapped-single unpacked-remapped-scope unpacked-remapped-wrong-scope - - # - Debuginfo in `.dwo` files - # - `.o` and binary refer to remapped `.dwo` paths which do not exist - # - `.o` deleted - # - `.dwo` present - # - `.dwp` never created - unpacked-remapped-split: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -g \ - -Z split-dwarf-kind=split --remap-path-prefix $(TMPDIR)=/a foo.rs - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 - ls $(TMPDIR)/*.o && exit 1 || exit 0 - rm $(TMPDIR)/*.dwo - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,foo) - - # - Debuginfo in `.o` files - # - `.o` and binary refer to remapped `.o` paths which do not exist - # - `.o` present - # - `.dwo` never created - # - `.dwp` never created - unpacked-remapped-single: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -g \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a foo.rs - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 - rm $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,foo) - - # - Debuginfo in `.o` files - # - `.o` and binary refer to remapped `.o` paths which do not exist - # - `.o` present - # - `.dwo` never created - # - `.dwp` never created - unpacked-remapped-scope: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -g \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ - -Z remap-path-scope=debuginfo foo.rs - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 - rm $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,foo) - - # - Debuginfo in `.o` files - # - `.o` and binary refer to remapped `.o` paths which do not exist - # - `.o` present - # - `.dwo` never created - # - `.dwp` never created - unpacked-remapped-wrong-scope: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -g \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ - -Z remap-path-scope=macro foo.rs - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (grep $(TMPDIR)) || exit 1 - rm $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,foo) - - unpacked-crosscrate: unpacked-crosscrate-split unpacked-crosscrate-single - - # - Debuginfo in `.dwo` files - # - (bar) `.rlib` file created, contains `.dwo` - # - (bar) `.o` deleted - # - (bar) `.dwo` present - # - (bar) `.dwp` never created - # - (main) `.o` deleted - # - (main) `.dwo` present - # - (main) `.dwp` never created - unpacked-crosscrate-split: - $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=unpacked \ - -Zsplit-dwarf-kind=split -g bar.rs - ls $(TMPDIR)/*.rlib - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ - -C split-debuginfo=unpacked -Zsplit-dwarf-kind=split -g main.rs - ls $(TMPDIR)/*.o && exit 1 || exit 0 - rm $(TMPDIR)/*.dwo - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,main) - - # - Debuginfo in `.o` files - # - (bar) `.rlib` file created, contains `.o` - # - (bar) `.o` present - # - (bar) `.dwo` never created - # - (bar) `.dwp` never created - # - (main) `.o` present - # - (main) `.dwo` never created - # - (main) `.dwp` never created - unpacked-crosscrate-single: - $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=unpacked \ - -Zsplit-dwarf-kind=single -g bar.rs - ls $(TMPDIR)/*.rlib - ls $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ - -C split-debuginfo=unpacked -Zsplit-dwarf-kind=single -g main.rs - ls $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,main) -endif diff --git a/tests/run-make/split-debuginfo/rmake.rs b/tests/run-make/split-debuginfo/rmake.rs new file mode 100644 index 000000000000..530a5d119f13 --- /dev/null +++ b/tests/run-make/split-debuginfo/rmake.rs @@ -0,0 +1,1618 @@ +// ignore-tidy-linelength +//! Basic smoke tests for behavior of `-C split-debuginfo` and the combined behavior when used in +//! conjunction with other flags such as: +//! +//! - `--remap-path-prefix`: see +//! . +//! - `-Z remap-path-scope`: see +//! - +//! - +//! - RFC #3127 trim-paths: +//! - `-Z split-dwarf-kind`: see . +//! - `-Clinker-plugin-lto`: see . +//! +//! # Test implementation remark +//! +//! - The pattern match on enum variants are intentional, because I find that they are very +//! revealing with respect to the kind of test coverage that we have and don't have. +//! +//! # Known limitations +//! +//! - The linux test coverage of cross-interactions between `-C split-debuginfo` and other flags are +//! significantly higher than the lack of such coverage for Windows and Darwin. +//! - windows-gnu is not tested at all, see the `FIXME(#135531)`s below. +//! - This test for the most part merely checks for existence/absence of certain artifacts, it does +//! not sanity check if the debuginfo artifacts are actually usable or contains the expected +//! amount/quality of debuginfo, especially on windows-msvc and darwin. +//! - FIXME(#111540): this test has insufficient coverage in relation to trim-paths RFC, see also +//! the comment . The +//! basic `llvm-dwarfdump` textual output inspection here is very fragile. The original `Makefile` +//! version used `objdump` (not to be confused with `llvm-objdump`) but inspected the wrong line +//! because it was looking at `DW_AT_GNU_dwo_name` when it should've been looking at +//! `DW_AT_comp_dir`. +//! - This test does not have good coverage for what values of `-Csplit-debuginfo` are stable vs +//! non-stable for the various targets, i.e. which values *should* be gated behind +//! `-Zunstable-options` for a given target. The `Makefile` version yolo'd a `-Zunstable-options` +//! for non-windows + non-linux + non-darwin, but had a misplaced interpolation which suggested to +//! me that that conditional `-Zunstable-options` never actually materialized. +//! +//! # Additional references +//! +//! - Apple `.dSYM` debug symbol bundles: . +//! - LLVM `dsymutil`: . + +// NOTE: this is a host test +//@ ignore-cross-compile + +// NOTE: this seems to be a host test, and testing on host `riscv64-gc-unknown-linux-gnu` reveals +// that this test is failing because of [MC: "error: A dwo section may not contain relocations" when +// building with fission + RISCV64 #56642](https://github.com/llvm/llvm-project/issues/56642). This +// test is ignored for now to unblock efforts to bring riscv64 targets to be exercised in CI, cf. +// [Enable riscv64gc-gnu testing #126641](https://github.com/rust-lang/rust/pull/126641). +//@ ignore-riscv64 (https://github.com/llvm/llvm-project/issues/56642) + +// FIXME(#135531): the `Makefile` version practically didn't test `-C split-debuginfo` on Windows +// at all, and lumped windows-msvc and windows-gnu together at that. +//@ ignore-windows-gnu + +#![deny(warnings)] + +use std::collections::BTreeSet; + +use run_make_support::rustc::Rustc; +use run_make_support::{ + cwd, has_extension, is_darwin, is_msvc, is_windows, llvm_dwarfdump, run_in_tmpdir, rustc, + shallow_find_directories, shallow_find_files, uname, +}; + +/// `-C debuginfo`. See . +#[derive(Debug, PartialEq, Copy, Clone)] +enum DebuginfoLevel { + /// `-C debuginfo=0` or `-C debuginfo=none` aka no debuginfo at all, default. + None, + /// `-C debuginfo=2` aka full debuginfo, aliased via `-g`. + Full, + /// The cli flag is not explicitly provided; default. + Unspecified, +} + +impl DebuginfoLevel { + fn cli_value(&self) -> &'static str { + // `-Cdebuginfo=...` + match self { + DebuginfoLevel::None => "none", + DebuginfoLevel::Full => "2", + DebuginfoLevel::Unspecified => unreachable!(), + } + } +} + +/// `-C split-debuginfo`. See +/// . +/// +/// Note that all three options are supported on Linux and Apple platforms, packed is supported on +/// Windows-MSVC, and all other platforms support off. Attempting to use an unsupported option +/// requires using the nightly channel with the `-Z unstable-options` flag. +#[derive(Debug, PartialEq, Copy, Clone)] +enum SplitDebuginfo { + /// `-C split-debuginfo=off`. Default for platforms with ELF binaries and windows-gnu (not + /// Windows MSVC and not macOS). Typically DWARF debug information can be found in the final + /// artifact in sections of the executable. + /// + /// - Not supported on Windows MSVC. + /// - On macOS this options prevents the final execution of `dsymutil` to generate debuginfo. + Off, + /// `-C split-debuginfo=unpacked`. Debug information will be found in separate files for each + /// compilation unit (object file). + /// + /// - Not supported on Windows MSVC. + /// - On macOS this means the original object files will contain debug information. + /// - On other Unix platforms this means that `*.dwo` files will contain debug information. + Unpacked, + /// `-C split-debuginfo=packed`. Default for Windows MSVC and macOS. "Packed" here means that + /// all the debug information is packed into a separate file from the main executable. + /// + /// - On Windows MSVC this is a `*.pdb` file. + /// - On macOS this is a `*.dSYM` folder. + /// - On other platforms this is a `*.dwp` file. + Packed, + /// The cli flag is not explicitly provided; uses platform default. + Unspecified, +} + +impl SplitDebuginfo { + fn cli_value(&self) -> &'static str { + // `-Csplit-debuginfo=...` + match self { + SplitDebuginfo::Off => "off", + SplitDebuginfo::Unpacked => "unpacked", + SplitDebuginfo::Packed => "packed", + SplitDebuginfo::Unspecified => unreachable!(), + } + } +} + +/// `-Z split-dwarf-kind` +#[derive(Debug, PartialEq, Copy, Clone)] +enum SplitDwarfKind { + /// `-Zsplit-dwarf-kind=split` + Split, + /// `-Zsplit-dwarf-kind=single` + Single, + Unspecified, +} + +impl SplitDwarfKind { + fn cli_value(&self) -> &'static str { + // `-Zsplit-dwarf-kind=...` + match self { + SplitDwarfKind::Split => "split", + SplitDwarfKind::Single => "single", + SplitDwarfKind::Unspecified => unreachable!(), + } + } +} + +/// `-C linker-plugin-lto` +#[derive(Debug, PartialEq, Copy, Clone)] +enum LinkerPluginLto { + /// Pass `-C linker-plugin-lto`. + Yes, + /// Don't pass `-C linker-plugin-lto`. + Unspecified, +} + +/// `--remap-path-prefix` or not. +#[derive(Debug, Clone)] +enum RemapPathPrefix { + /// `--remap-path-prefix=$prefix=$remapped_prefix`. + Yes { remapped_prefix: &'static str }, + /// Don't pass `--remap-path-prefix`. + Unspecified, +} + +/// `-Zremap-path-scope`. See +/// . +#[derive(Debug, Clone)] +enum RemapPathScope { + /// Comma-separated list of remap scopes: `macro`, `diagnostics`, `debuginfo`, `object`, `all`. + Yes(&'static str), + Unspecified, +} + +/// Whether to pass `-Zunstable-options`. +#[derive(Debug, PartialEq, Copy, Clone)] +enum UnstableOptions { + Yes, + Unspecified, +} + +#[track_caller] +fn cwd_filenames() -> BTreeSet { + let files = shallow_find_files(cwd(), |path| { + // Fiilter out source files + !has_extension(path, "rs") + }); + files.iter().map(|p| p.file_name().unwrap().to_os_string().into_string().unwrap()).collect() +} + +#[track_caller] +fn cwd_dwo_filenames() -> BTreeSet { + let files = shallow_find_files(cwd(), |path| has_extension(path, "dwo")); + files.iter().map(|p| p.file_name().unwrap().to_os_string().into_string().unwrap()).collect() +} + +#[track_caller] +fn cwd_object_filenames() -> BTreeSet { + let files = shallow_find_files(cwd(), |path| has_extension(path, "o")); + files.iter().map(|p| p.file_name().unwrap().to_os_string().into_string().unwrap()).collect() +} + +#[must_use] +struct FileAssertions<'expected> { + expected_files: BTreeSet<&'expected str>, +} + +impl<'expected> FileAssertions<'expected> { + #[track_caller] + fn assert_on(self, found_files: &BTreeSet) { + let found_files: BTreeSet<_> = found_files.iter().map(|f| f.as_str()).collect(); + assert!( + found_files.is_superset(&self.expected_files), + "expected {:?} to exist, but only found {:?}", + self.expected_files, + found_files + ); + + let unexpected_files: BTreeSet<_> = + found_files.difference(&self.expected_files).copied().collect(); + assert!(unexpected_files.is_empty(), "found unexpected files: {:?}", unexpected_files); + } +} + +/// Windows MSVC only supports packed debuginfo. +mod windows_msvc_tests { + use super::*; + + pub(crate) fn split_debuginfo(split_kind: SplitDebuginfo, level: DebuginfoLevel) { + // NOTE: `-C debuginfo` and other flags are not exercised here on Windows MSVC. + run_in_tmpdir(|| { + println!("checking: split_kind={:?} + level={:?}", split_kind, level); + match (split_kind, level) { + (SplitDebuginfo::Off, _) => { + rustc() + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .run_fail() + .assert_stderr_contains( + "error: `-Csplit-debuginfo=off` is unstable on this platform", + ); + } + (SplitDebuginfo::Unpacked, _) => { + rustc() + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .run_fail() + .assert_stderr_contains( + "error: `-Csplit-debuginfo=unpacked` is unstable on this platform", + ); + } + (SplitDebuginfo::Packed, _) => { + rustc().input("foo.rs").split_debuginfo(split_kind.cli_value()).run(); + + let found_files = cwd_filenames(); + FileAssertions { expected_files: BTreeSet::from(["foo.exe", "foo.pdb"]) } + .assert_on(&found_files); + } + (split_kind, level) => { + panic!( + "split_kind={:?} + level={:?} is not handled on Windows MSVC", + split_kind, level + ) + } + } + }); + } +} + +mod darwin_tests { + use super::*; + + pub(crate) fn split_debuginfo(split_kind: SplitDebuginfo, level: DebuginfoLevel) { + run_in_tmpdir(|| { + println!("checking: split_kind={:?} + level={:?}", split_kind, level); + + let dsym_directories = + || shallow_find_directories(cwd(), |path| has_extension(path, "dSYM")); + + match (split_kind, level) { + (_, DebuginfoLevel::Unspecified) => { + rustc().input("foo.rs").run(); + let directories = + shallow_find_directories(cwd(), |path| has_extension(path, "dSYM")); + assert!( + directories.is_empty(), + "expected no `*.dSYM` folder to be generated when `-Cdebuginfo` is not specified" + ); + } + (_, DebuginfoLevel::None) => { + rustc().input("foo.rs").debuginfo(level.cli_value()).run(); + let directories = dsym_directories(); + assert!( + directories.is_empty(), + "expected no `*.dSYM` folder to be generated when `-Cdebuginfo=none`" + ); + } + (SplitDebuginfo::Off, _) => { + rustc() + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .run(); + let directories = dsym_directories(); + assert!( + directories.is_empty(), + "expected no `*.dSYM` folder to be generated since we expect `-Csplit-debuginfo=off` to inhibit final debuginfo generation on macOS" + ); + } + (SplitDebuginfo::Unpacked, _) => { + rustc().input("foo.rs").split_debuginfo(split_kind.cli_value()).run(); + let directories = dsym_directories(); + assert!( + directories.is_empty(), + "expected no `*.dSYM` folder to be generated since we expect on macOS the object files to contain debuginfo instead" + ); + } + (SplitDebuginfo::Packed, DebuginfoLevel::Full) => { + rustc() + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .run(); + let directories = shallow_find_directories(cwd(), |path| { + path.file_name().unwrap() == "foo.dSYM" + }); + assert_eq!(directories.len(), 1, "failed to find `foo.dSYM`"); + } + (SplitDebuginfo::Unspecified, DebuginfoLevel::Full) => { + rustc().input("foo.rs").debuginfo(level.cli_value()).run(); + let directories = shallow_find_directories(cwd(), |path| { + path.file_name().unwrap() == "foo.dSYM" + }); + assert_eq!(directories.len(), 1, "failed to find `foo.dSYM`"); + } + } + }); + } +} + +mod shared_linux_other_tests { + use std::path::PathBuf; + + use super::*; + + fn rustc(unstable_options: UnstableOptions) -> Rustc { + if unstable_options == UnstableOptions::Yes { + let mut rustc = run_make_support::rustc(); + rustc.arg("-Zunstable-options"); + rustc + } else { + run_make_support::rustc() + } + } + + #[derive(PartialEq)] + pub(crate) enum CrossCrateTest { + Yes, + No, + } + + pub(crate) fn split_debuginfo( + cross_crate_test: CrossCrateTest, + unstable_options: UnstableOptions, + split_kind: SplitDebuginfo, + level: DebuginfoLevel, + split_dwarf_kind: SplitDwarfKind, + lto: LinkerPluginLto, + remap_path_prefix: RemapPathPrefix, + remap_path_scope: RemapPathScope, + ) { + run_in_tmpdir(|| { + println!( + "checking: unstable_options={:?} + split_kind={:?} + level={:?} + split_dwarf_kind={:?} + lto={:?} + remap_path_prefix={:?} + remap_path_scope={:?}", + unstable_options, + split_kind, + level, + split_dwarf_kind, + lto, + remap_path_prefix, + remap_path_scope + ); + + match cross_crate_test { + CrossCrateTest::Yes => cross_crate_split_debuginfo( + unstable_options, + split_kind, + level, + split_dwarf_kind, + lto, + remap_path_prefix, + remap_path_scope, + ), + CrossCrateTest::No => simple_split_debuginfo( + unstable_options, + split_kind, + level, + split_dwarf_kind, + lto, + remap_path_prefix, + remap_path_scope, + ), + } + }); + } + + fn cross_crate_split_debuginfo( + unstable_options: UnstableOptions, + split_kind: SplitDebuginfo, + level: DebuginfoLevel, + split_dwarf_kind: SplitDwarfKind, + lto: LinkerPluginLto, + remap_path_prefix: RemapPathPrefix, + remap_path_scope: RemapPathScope, + ) { + match (split_kind, level, split_dwarf_kind, lto, remap_path_prefix, remap_path_scope) { + // packed-crosscrate-split + // - Debuginfo in `.dwo` files + // - (bar) `.rlib` file created, contains `.dwo` + // - (bar) `.o` deleted + // - (bar) `.dwo` deleted + // - (bar) `.dwp` never created + // - (main) `.o` deleted + // - (main) `.dwo` deleted + // - (main) `.dwp` present + ( + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("bar.rs") + .crate_type("lib") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .run(); + + let found_files = cwd_filenames(); + FileAssertions { expected_files: BTreeSet::from(["libbar.rlib"]) } + .assert_on(&found_files); + + rustc(unstable_options) + .extern_("bar", "libbar.rlib") + .input("main.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .run(); + + let found_files = cwd_filenames(); + FileAssertions { + expected_files: BTreeSet::from(["libbar.rlib", "main", "main.dwp"]), + } + .assert_on(&found_files); + } + + // packed-crosscrate-single + // - Debuginfo in `.o` files + // - (bar) `.rlib` file created, contains `.o` + // - (bar) `.o` deleted + // - (bar) `.dwo` never created + // - (bar) `.dwp` never created + // - (main) `.o` deleted + // - (main) `.dwo` never created + // - (main) `.dwp` present + ( + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("bar.rs") + .crate_type("lib") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .run(); + + let found_files = cwd_filenames(); + FileAssertions { expected_files: BTreeSet::from(["libbar.rlib"]) } + .assert_on(&found_files); + + rustc(unstable_options) + .extern_("bar", "libbar.rlib") + .input("main.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .run(); + + let found_files = cwd_filenames(); + FileAssertions { + expected_files: BTreeSet::from(["libbar.rlib", "main", "main.dwp"]), + } + .assert_on(&found_files); + } + + // unpacked-crosscrate-split + // - Debuginfo in `.dwo` files + // - (bar) `.rlib` file created, contains `.dwo` + // - (bar) `.o` deleted + // - (bar) `.dwo` present + // - (bar) `.dwp` never created + // - (main) `.o` deleted + // - (main) `.dwo` present + // - (main) `.dwp` never created + ( + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("bar.rs") + .crate_type("lib") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .run(); + + let bar_found_files = cwd_filenames(); + + let bar_dwo_files = cwd_dwo_filenames(); + assert_eq!(bar_dwo_files.len(), 1); + + let mut bar_expected_files = BTreeSet::new(); + bar_expected_files.extend(bar_dwo_files); + bar_expected_files.insert("libbar.rlib".to_string()); + + FileAssertions { + expected_files: bar_expected_files.iter().map(String::as_ref).collect(), + } + .assert_on(&bar_found_files); + + rustc(unstable_options) + .extern_("bar", "libbar.rlib") + .input("main.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .run(); + + let overall_found_files = cwd_filenames(); + + let overall_dwo_files = cwd_dwo_filenames(); + assert_eq!(overall_dwo_files.len(), 2); + + let mut overall_expected_files = BTreeSet::new(); + overall_expected_files.extend(overall_dwo_files); + overall_expected_files.insert("main".to_string()); + overall_expected_files.insert("libbar.rlib".to_string()); + + FileAssertions { + expected_files: overall_expected_files.iter().map(String::as_ref).collect(), + } + .assert_on(&overall_found_files); + } + + // unpacked-crosscrate-single + // - Debuginfo in `.o` files + // - (bar) `.rlib` file created, contains `.o` + // - (bar) `.o` present + // - (bar) `.dwo` never created + // - (bar) `.dwp` never created + // - (main) `.o` present + // - (main) `.dwo` never created + // - (main) `.dwp` never created + ( + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("bar.rs") + .crate_type("lib") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .run(); + + let bar_found_files = cwd_filenames(); + + let bar_object_files = cwd_object_filenames(); + assert_eq!(bar_object_files.len(), 1); + + let mut bar_expected_files = BTreeSet::new(); + bar_expected_files.extend(bar_object_files); + bar_expected_files.insert("libbar.rlib".to_string()); + + FileAssertions { + expected_files: bar_expected_files.iter().map(String::as_ref).collect(), + } + .assert_on(&bar_found_files); + + rustc(unstable_options) + .extern_("bar", "libbar.rlib") + .input("main.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .run(); + + let overall_found_files = cwd_filenames(); + + let overall_object_files = cwd_object_filenames(); + assert_eq!(overall_object_files.len(), 2); + + let mut overall_expected_files = BTreeSet::new(); + overall_expected_files.extend(overall_object_files); + overall_expected_files.insert("main".to_string()); + overall_expected_files.insert("libbar.rlib".to_string()); + + FileAssertions { + expected_files: overall_expected_files.iter().map(String::as_ref).collect(), + } + .assert_on(&overall_found_files); + } + + _ => {} + } + } + + fn simple_split_debuginfo( + unstable_options: UnstableOptions, + split_kind: SplitDebuginfo, + level: DebuginfoLevel, + split_dwarf_kind: SplitDwarfKind, + lto: LinkerPluginLto, + remap_path_prefix: RemapPathPrefix, + remap_path_scope: RemapPathScope, + ) { + match (split_kind, level, split_dwarf_kind, lto, remap_path_prefix, remap_path_scope) { + // off (unspecified): + // - Debuginfo in `.o` files + // - `.o` deleted + // - `.dwo` never created + // - `.dwp` never created + ( + SplitDebuginfo::Unspecified, + DebuginfoLevel::Full, + SplitDwarfKind::Unspecified, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options).input("foo.rs").debuginfo(level.cli_value()).run(); + let found_files = cwd_filenames(); + FileAssertions { expected_files: BTreeSet::from(["foo"]) }.assert_on(&found_files); + } + + // off: + // - Debuginfo in `.o` files + // - `.o` deleted + // - `.dwo` never created + // - `.dwp` never created + ( + SplitDebuginfo::Off, + DebuginfoLevel::Full, + SplitDwarfKind::Unspecified, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .run(); + let found_files = cwd_filenames(); + FileAssertions { expected_files: BTreeSet::from(["foo"]) }.assert_on(&found_files); + } + + // packed-split: + // - Debuginfo in `.dwo` files + // - `.o` deleted + // - `.dwo` deleted + // - `.dwp` present + ( + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .run(); + let found_files = cwd_filenames(); + FileAssertions { expected_files: BTreeSet::from(["foo", "foo.dwp"]) } + .assert_on(&found_files); + } + + // packed-single: + // - Debuginfo in `.o` files + // - `.o` deleted + // - `.dwo` never created + // - `.dwp` present + ( + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .run(); + let found_files = cwd_filenames(); + FileAssertions { expected_files: BTreeSet::from(["foo", "foo.dwp"]) } + .assert_on(&found_files); + } + + // packed-lto-split:: + // - `rmeta` file added to `rlib`, no object files are generated and thus no + // debuginfo is generated. + // - `.o` never created + // - `.dwo` never created + // - `.dwp` never created + ( + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Yes, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("baz.rs") + .crate_type("rlib") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .arg("-Clinker-plugin-lto") + .run(); + let found_files = cwd_filenames(); + FileAssertions { expected_files: BTreeSet::from(["libbaz.rlib"]) } + .assert_on(&found_files); + } + + // packed-lto-single: + // - `rmeta` file added to `rlib`, no object files are generated and thus no + // debuginfo is generated + // - `.o` never created + // - `.dwo` never created + // - `.dwp` never created + ( + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Yes, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("baz.rs") + .crate_type("rlib") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .arg("-Clinker-plugin-lto") + .run(); + let found_files = cwd_filenames(); + FileAssertions { expected_files: BTreeSet::from(["libbaz.rlib"]) } + .assert_on(&found_files); + } + + // packed-remapped-split: + // - Debuginfo in `.dwo` files + // - `.o` and binary refer to remapped `.dwo` paths which do not exist + // - `.o` deleted + // - `.dwo` deleted + // - `.dwp` present + ( + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix }, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .remap_path_prefix(cwd(), remapped_prefix) + .run(); + let found_files = cwd_filenames(); + FileAssertions { expected_files: BTreeSet::from(["foo", "foo.dwp"]) } + .assert_on(&found_files); + + check_path_remap(cwd(), RemapExpectation::Remapped); + } + + // packed-remapped-single: + // - `.o` and binary refer to remapped `.o` paths which do not exist + // - `.o` deleted + // - `.dwo` never created + // - `.dwp` present + ( + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix }, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .remap_path_prefix(cwd(), remapped_prefix) + .run(); + let found_files = cwd_filenames(); + FileAssertions { expected_files: BTreeSet::from(["foo", "foo.dwp"]) } + .assert_on(&found_files); + + check_path_remap(cwd(), RemapExpectation::Remapped); + } + + // packed-remapped-scope: + // - Debuginfo in `.o` files + // - `.o` and binary refer to remapped `.o` paths which do not exist + // - `.o` deleted + // - `.dwo` never created + // - `.dwp` present + ( + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix }, + RemapPathScope::Yes(scope @ "debuginfo"), + ) => { + rustc(unstable_options) + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .remap_path_prefix(cwd(), remapped_prefix) + .arg(format!("-Zremap-path-scope={scope}")) + .run(); + let found_files = cwd_filenames(); + FileAssertions { expected_files: BTreeSet::from(["foo", "foo.dwp"]) } + .assert_on(&found_files); + + check_path_remap(cwd(), RemapExpectation::Remapped); + } + + // packed-remapped-wrong-scope: + // - `.o` and binary refer to un-remapped `.o` paths because remap path scope is + // macro. + // - `.o` deleted + // - `.dwo` never created + // - `.dwp` present + ( + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix }, + RemapPathScope::Yes(scope @ "macro"), + ) => { + rustc(unstable_options) + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .remap_path_prefix(cwd(), remapped_prefix) + .arg(format!("-Zremap-path-scope={scope}")) + .run(); + let found_files = cwd_filenames(); + FileAssertions { expected_files: BTreeSet::from(["foo", "foo.dwp"]) } + .assert_on(&found_files); + + check_path_remap(cwd(), RemapExpectation::NoRemap); + } + + // unpacked-split + // - Debuginfo in `.dwo` files + // - `.o` deleted + // - `.dwo` present + // - `.dwp` never created + ( + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .run(); + let found_files = cwd_filenames(); + + let dwo_files = cwd_dwo_filenames(); + assert_eq!(dwo_files.len(), 1); + + let mut expected_files = BTreeSet::new(); + expected_files.extend(dwo_files); + expected_files.insert("foo".to_string()); + + FileAssertions { + expected_files: expected_files.iter().map(String::as_str).collect(), + } + .assert_on(&found_files); + } + + // unpacked-single + // - Debuginfo in `.o` files + // - `.o` present + // - `.dwo` never created + // - `.dwp` never created + ( + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .run(); + let found_files = cwd_filenames(); + + let object_files = cwd_object_filenames(); + assert_eq!(object_files.len(), 1); + + let mut expected_files = BTreeSet::new(); + expected_files.extend(object_files); + expected_files.insert("foo".to_string()); + + FileAssertions { + expected_files: expected_files.iter().map(String::as_str).collect(), + } + .assert_on(&found_files); + } + + // unpacked-lto-split + // - `rmeta` file added to `rlib`, no object files are generated and thus no debuginfo + // is generated + // - `.o` not present + // - `.dwo` never created + // - `.dwp` never created + ( + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Yes, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("baz.rs") + .crate_type("rlib") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .arg("-Clinker-plugin-lto") + .run(); + + let found_files = cwd_filenames(); + + FileAssertions { expected_files: BTreeSet::from(["libbaz.rlib"]) } + .assert_on(&found_files); + } + + // unpacked-lto-single + // - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated + // - `.o` present (bitcode) + // - `.dwo` never created + // - `.dwp` never created + ( + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Yes, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("baz.rs") + .crate_type("rlib") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .arg("-Clinker-plugin-lto") + .run(); + + let found_files = cwd_filenames(); + + let object_files = cwd_object_filenames(); + assert_eq!(object_files.len(), 1); + + let mut expected_files = BTreeSet::new(); + expected_files.extend(object_files); + expected_files.insert("libbaz.rlib".to_string()); + + FileAssertions { + expected_files: expected_files.iter().map(String::as_ref).collect(), + } + .assert_on(&found_files); + } + + // unpacked-remapped-split + // - Debuginfo in `.dwo` files + // - `.o` and binary refer to remapped `.dwo` paths which do not exist + // - `.o` deleted + // - `.dwo` present + // - `.dwp` never created + ( + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix }, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .remap_path_prefix(cwd(), remapped_prefix) + .run(); + + let found_files = cwd_filenames(); + + let dwo_files = cwd_dwo_filenames(); + assert_eq!(dwo_files.len(), 1); + + let mut expected_files = BTreeSet::new(); + expected_files.extend(dwo_files); + expected_files.insert("foo".to_string()); + + FileAssertions { + expected_files: expected_files.iter().map(String::as_ref).collect(), + } + .assert_on(&found_files); + + check_path_remap(cwd(), RemapExpectation::Remapped); + } + + // unpacked-remapped-single + // - Debuginfo in `.o` files + // - `.o` and binary refer to remapped `.o` paths which do not exist + // - `.o` present + // - `.dwo` never created + // - `.dwp` never created + ( + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix }, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .remap_path_prefix(cwd(), remapped_prefix) + .run(); + + let found_files = cwd_filenames(); + + let object_files = cwd_object_filenames(); + assert_eq!(object_files.len(), 1); + + let mut expected_files = BTreeSet::new(); + expected_files.extend(object_files); + expected_files.insert("foo".to_string()); + + FileAssertions { + expected_files: expected_files.iter().map(String::as_ref).collect(), + } + .assert_on(&found_files); + + check_path_remap(cwd(), RemapExpectation::Remapped); + } + + // unpacked-remapped-scope + // - Debuginfo in `.o` files + // - `.o` and binary refer to remapped `.o` paths which do not exist + // - `.o` present + // - `.dwo` never created + // - `.dwp` never created + ( + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix }, + RemapPathScope::Yes(scope @ "debuginfo"), + ) => { + rustc(unstable_options) + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .remap_path_prefix(cwd(), remapped_prefix) + .arg(format!("-Zremap-path-scope={scope}")) + .run(); + + let found_files = cwd_filenames(); + + let object_files = cwd_object_filenames(); + assert_eq!(object_files.len(), 1); + + let mut expected_files = BTreeSet::new(); + expected_files.extend(object_files); + expected_files.insert("foo".to_string()); + + FileAssertions { + expected_files: expected_files.iter().map(String::as_ref).collect(), + } + .assert_on(&found_files); + + check_path_remap(cwd(), RemapExpectation::Remapped); + } + + // unpacked-remapped-wrong-scope + // - Debuginfo in `.o` files + // - `.o` and binary refer to un-remapped `.o` paths because remap path scope is macro + // - `.o` present + // - `.dwo` never created + // - `.dwp` never created + ( + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix }, + RemapPathScope::Yes(scope @ "macro"), + ) => { + rustc(unstable_options) + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .remap_path_prefix(cwd(), remapped_prefix) + .arg(format!("-Zremap-path-scope={scope}")) + .run(); + + let found_files = cwd_filenames(); + + let object_files = cwd_object_filenames(); + assert_eq!(object_files.len(), 1); + + let mut expected_files = BTreeSet::new(); + expected_files.extend(object_files); + expected_files.insert("foo".to_string()); + + FileAssertions { + expected_files: expected_files.iter().map(String::as_ref).collect(), + } + .assert_on(&found_files); + + check_path_remap(cwd(), RemapExpectation::NoRemap); + } + + (split_kind, level, split_dwarf_kind, lto, remap_path_prefix, remap_path_scope) => { + panic!( + "split_kind={:?} + level={:?} + split_dwarf_kind={:?} + lto={:?} + remap_path_prefix={:?} + remap_path_scope={:?} is not handled on linux/other", + split_kind, level, split_dwarf_kind, lto, remap_path_prefix, remap_path_scope + ) + } + } + } + + #[derive(PartialEq)] + enum RemapExpectation { + Remapped, + NoRemap, + } + + #[track_caller] + fn check_path_remap(cwd_path: PathBuf, remap_expectation: RemapExpectation) { + let cwd_path = cwd_path.to_str().unwrap(); + let output = llvm_dwarfdump().input("foo").arg("--debug-info").run().stdout_utf8(); + let output_lines: Vec<_> = output.lines().collect(); + + // Look for `DW_AT_comp_dir` and `DW_AT_GNU_dwo_name` via `llvm-dwarfdump`. Note: space + // between uses tabs. + // + // ```text + // 0x0000000b: DW_TAG_compile_unit + // DW_AT_stmt_list (0x00000000) + // DW_AT_comp_dir ("/__MY_REMAPPED_PATH") # this could be e.g. /home/repos/rust/ if not remapped + // DW_AT_GNU_dwo_name ("foo.foo.fc848df41df7a00d-cgu.0.rcgu.dwo") + // ``` + // + // FIXME: this is very fragile because the output format can be load-bearing, but doing this + // via `object` + `gimli` is rather difficult. + let mut window = output_lines.windows(2); + while let Some([first_ln, second_ln]) = window.next() { + let first_ln = first_ln.trim(); + let second_ln = second_ln.trim(); + + if !second_ln.starts_with("DW_AT_GNU_dwo_name") { + continue; + } + + let Some((comp_dir_attr_name, comp_dir_attr_val)) = first_ln.split_once("\t") else { + continue; + }; + + println!("comp_dir_attr_name: `{}`", comp_dir_attr_name); + println!("cwd_path_string: `{}`", cwd_path); + + if comp_dir_attr_name != "DW_AT_comp_dir" { + continue; + } + + println!("comp_dir_attr_val: `{}`", comp_dir_attr_val); + + // Possibly `("/__MY_REMAPPED_PATH")` or `($cwd_path_string)`. + // + // FIXME: this check is insufficiently precise, it should probably also match on suffix + // (`.o` vs `.dwo` reference). But also, string matching is just very fragile. + let comp_dir_attr_val = comp_dir_attr_val.trim(); + + match remap_expectation { + RemapExpectation::Remapped => { + assert!( + !comp_dir_attr_val.contains(&cwd_path), + "unexpected non-remapped path found in `DW_AT_comp_dir`: {}", + comp_dir_attr_val + ); + } + RemapExpectation::NoRemap => { + assert!( + comp_dir_attr_val.contains(&cwd_path), + "failed to find un-remapped path in `DW_AT_comp_dir`: {}", + comp_dir_attr_val + ); + } + } + } + } +} + +fn main() { + // ENHANCEMENT: we are only checking that split-debuginfo is splitting is some way, shape or + // form, we do not sanity check the actual debuginfo artifacts on non-Linux! It may be possible + // to also sanity check the debuginfo artifacts, but I did not want to do that during the port + // to rmake.rs initially. + + // ENHANCEMENT: the Linux checks have significantly more coverage for interaction between `-C + // split-debuginfo` and other flags compared to windows-msvc or windows-gnu or darwin or some + // other non-linux targets. It would be cool if their test coverage could be improved. + + // NOTE: these combinations are not exhaustive, because while porting to rmake.rs initially I + // tried to preserve the existing test behavior closely. Notably, no attempt was made to + // exhaustively cover all cases in the 6-fold Cartesian product of `{,-Csplit=debuginfo=...}` x + // `{,-Cdebuginfo=...}` x `{,--remap-path-prefix}` x `{,-Zremap-path-scope=...}` x + // `{,-Zsplit-dwarf-kind=...}` x `{,-Clinker-plugin-lto}`. If you really want to, you can + // identify which combination isn't exercised with a 6-layers nested for loop iterating through + // each of the cli flag enum variants. + + if is_msvc() { + // FIXME: the windows-msvc test coverage is sparse at best. + + windows_msvc_tests::split_debuginfo(SplitDebuginfo::Off, DebuginfoLevel::Unspecified); + windows_msvc_tests::split_debuginfo(SplitDebuginfo::Unpacked, DebuginfoLevel::Unspecified); + windows_msvc_tests::split_debuginfo(SplitDebuginfo::Packed, DebuginfoLevel::Unspecified); + + windows_msvc_tests::split_debuginfo(SplitDebuginfo::Off, DebuginfoLevel::None); + windows_msvc_tests::split_debuginfo(SplitDebuginfo::Unpacked, DebuginfoLevel::None); + windows_msvc_tests::split_debuginfo(SplitDebuginfo::Packed, DebuginfoLevel::None); + + windows_msvc_tests::split_debuginfo(SplitDebuginfo::Off, DebuginfoLevel::Full); + windows_msvc_tests::split_debuginfo(SplitDebuginfo::Unpacked, DebuginfoLevel::Full); + windows_msvc_tests::split_debuginfo(SplitDebuginfo::Packed, DebuginfoLevel::Full); + } else if is_windows() { + // FIXME(#135531): the `Makefile` version didn't test windows at all. I don't know about the + // intended behavior on windows-gnu to expand test coverage while porting this to rmake.rs, + // but the test coverage here really should be expanded since some windows-gnu targets are + // Tier 1. + } else if is_darwin() { + // FIXME: the darwin test coverage is sparse at best. + + // Expect no `.dSYM` generation if debuginfo is not requested (special case). + darwin_tests::split_debuginfo(SplitDebuginfo::Unspecified, DebuginfoLevel::Unspecified); + + darwin_tests::split_debuginfo(SplitDebuginfo::Off, DebuginfoLevel::Unspecified); + darwin_tests::split_debuginfo(SplitDebuginfo::Unpacked, DebuginfoLevel::Unspecified); + darwin_tests::split_debuginfo(SplitDebuginfo::Packed, DebuginfoLevel::Unspecified); + + darwin_tests::split_debuginfo(SplitDebuginfo::Off, DebuginfoLevel::None); + darwin_tests::split_debuginfo(SplitDebuginfo::Unpacked, DebuginfoLevel::None); + darwin_tests::split_debuginfo(SplitDebuginfo::Packed, DebuginfoLevel::None); + + darwin_tests::split_debuginfo(SplitDebuginfo::Off, DebuginfoLevel::Full); + darwin_tests::split_debuginfo(SplitDebuginfo::Unpacked, DebuginfoLevel::Full); + darwin_tests::split_debuginfo(SplitDebuginfo::Packed, DebuginfoLevel::Full); + } else { + // Unix as well as the non-linux + non-windows + non-darwin targets. + + // FIXME: this `uname` check is very funny, it really should be refined (e.g. llvm bug + // for riscv64 targets). + + // NOTE: some options are not stable on non-linux + non-windows + non-darwin targets... + let unstable_options = + if uname() == "Linux" { UnstableOptions::Unspecified } else { UnstableOptions::Yes }; + + // FIXME: we should add a test with scope `split-debuginfo,split-debuginfo-path` that greps + // the entire `.dwp` file for remapped paths (i.e. without going through objdump or + // readelf). See . + + use shared_linux_other_tests::CrossCrateTest; + + // unspecified `-Csplit-debuginfo` + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Unspecified, + DebuginfoLevel::Full, + SplitDwarfKind::Unspecified, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ); + + // off + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Off, + DebuginfoLevel::Full, + SplitDwarfKind::Unspecified, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ); + + // packed-split + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ); + + // packed-single + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ); + + // packed-lto-split + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Yes, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ); + + // packed-lto-single + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Yes, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ); + + // FIXME: the remapping tests probably need to be reworked, see + // . + + // packed-remapped-split + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix: "/__MY_REMAPPED_PATH__" }, + RemapPathScope::Unspecified, + ); + + // packed-remapped-single + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix: "/__MY_REMAPPED_PATH__" }, + RemapPathScope::Unspecified, + ); + + // packed-remapped-scope + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix: "/__MY_REMAPPED_PATH__" }, + RemapPathScope::Yes("debuginfo"), + ); + + // packed-remapped-wrong-scope + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix: "/__MY_REMAPPED_PATH__" }, + RemapPathScope::Yes("macro"), + ); + + // packed-crosscrate-split + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::Yes, + unstable_options, + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ); + + // packed-crosscrate-single + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::Yes, + unstable_options, + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ); + + // unpacked-split + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ); + + // unpacked-single + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ); + + // unpacked-lto-split + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Yes, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ); + + // unpacked-lto-single + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Yes, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ); + + // unpacked-remapped-split + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix: "/__MY_REMAPPED_PATH__" }, + RemapPathScope::Unspecified, + ); + + // unpacked-remapped-single + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix: "/__MY_REMAPPED_PATH__" }, + RemapPathScope::Unspecified, + ); + + // unpacked-remapped-scope + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix: "/__MY_REMAPPED_PATH__" }, + RemapPathScope::Yes("debuginfo"), + ); + + // unpacked-remapped-wrong-scope + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix: "/__MY_REMAPPED_PATH__" }, + RemapPathScope::Yes("macro"), + ); + + // unpacked-crosscrate-split + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::Yes, + unstable_options, + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ); + + // unpacked-crosscrate-single + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::Yes, + unstable_options, + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ); + } +} From 510de59dbd470d9f62a570eb46bfe4bbb10f0988 Mon Sep 17 00:00:00 2001 From: Alona Enraght-Moony Date: Sat, 1 Feb 2025 19:57:59 +0000 Subject: [PATCH 26/57] rustdoc-json-types: Document that crate name isn't package name. --- src/rustdoc-json-types/lib.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index e67e17a73cb7..a92f3ded7746 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -61,6 +61,13 @@ pub struct Crate { #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct ExternalCrate { /// The name of the crate. + /// + /// Note: This is the [*crate* name][crate-name], which may not be the same as the + /// [*package* name][package-name]. For example, for , + /// this field will be `regex_syntax` (which uses an `_`, not a `-`). + /// + /// [crate-name]: https://doc.rust-lang.org/stable/cargo/reference/cargo-targets.html#the-name-field + /// [package-name]: https://doc.rust-lang.org/stable/cargo/reference/manifest.html#the-name-field pub name: String, /// The root URL at which the crate's documentation lives. pub html_root_url: Option, From f5d5210d26d29faad12c6aa7ef55aefa8d9f6ce9 Mon Sep 17 00:00:00 2001 From: Alona Enraght-Moony Date: Sat, 1 Feb 2025 21:55:50 +0000 Subject: [PATCH 27/57] rustdoc-book: Clean up section on `--output-format` --- src/doc/rustdoc/src/unstable-features.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index 7eb1b8df2333..3abd538d372c 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -330,7 +330,7 @@ the source. ## `--show-type-layout`: add a section to each type's docs describing its memory layout -* Tracking issue: [#113248](https://github.com/rust-lang/rust/issues/113248) + * Tracking issue: [#113248](https://github.com/rust-lang/rust/issues/113248) Using this flag looks like this: @@ -526,9 +526,10 @@ use `-o -`. ### json + * Tracking Issue: [#76578](https://github.com/rust-lang/rust/issues/76578) + `--output-format json` emits documentation in the experimental -[JSON format](https://doc.rust-lang.org/nightly/nightly-rustc/rustdoc_json_types/). `--output-format html` has no effect, -and is also accepted on stable toolchains. +[JSON format](https://doc.rust-lang.org/nightly/nightly-rustc/rustdoc_json_types/). JSON Output for toolchain crates (`std`, `alloc`, `core`, `test`, and `proc_macro`) is available via the `rust-docs-json` rustup component. @@ -546,11 +547,11 @@ information. ### doctest + * Tracking issue: [#134529](https://github.com/rust-lang/rust/issues/134529) + `--output-format doctest` emits JSON on stdout which gives you information about doctests in the provided crate. -Tracking issue: [#134529](https://github.com/rust-lang/rust/issues/134529) - You can use this option like this: ```bash @@ -606,6 +607,11 @@ The generated output (formatted) will look like this: * `doctest_code` is the code modified by rustdoc that will be run. If there is a fatal syntax error, this field will not be present. * `name` is the name generated by rustdoc which represents this doctest. +### html + +`--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) From 1ed804ddb9c9c018aeb386669f81e42a21721d90 Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Sun, 19 Jan 2025 22:23:35 +0530 Subject: [PATCH 28/57] uefi: process: Add support for command environment variables Set environment variables before launching the process and restore the prior variables after the program exists. This is the same implementation as the one used by UEFI Shell Execute [0]. [0]: https://github.com/tianocore/edk2/blob/2d2642f4832ebc45cb7d5ba9430b933d953b94f2/ShellPkg/Application/Shell/ShellProtocol.c#L1700 Signed-off-by: Ayush Singh --- library/std/src/sys/pal/uefi/process.rs | 65 +++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/library/std/src/sys/pal/uefi/process.rs b/library/std/src/sys/pal/uefi/process.rs index 3077a72eac66..0757f1cb490d 100644 --- a/library/std/src/sys/pal/uefi/process.rs +++ b/library/std/src/sys/pal/uefi/process.rs @@ -1,6 +1,7 @@ use r_efi::protocols::simple_text_output; use super::helpers; +use crate::collections::BTreeMap; pub use crate::ffi::OsString as EnvKey; use crate::ffi::{OsStr, OsString}; use crate::num::{NonZero, NonZeroI32}; @@ -21,6 +22,7 @@ pub struct Command { args: Vec, stdout: Option, stderr: Option, + env: CommandEnv, } // passed back to std::process with the pipes connected to the child, if any @@ -40,7 +42,13 @@ pub enum Stdio { impl Command { pub fn new(program: &OsStr) -> Command { - Command { prog: program.to_os_string(), args: Vec::new(), stdout: None, stderr: None } + Command { + prog: program.to_os_string(), + args: Vec::new(), + stdout: None, + stderr: None, + env: Default::default(), + } } pub fn arg(&mut self, arg: &OsStr) { @@ -48,7 +56,7 @@ impl Command { } pub fn env_mut(&mut self) -> &mut CommandEnv { - panic!("unsupported") + &mut self.env } pub fn cwd(&mut self, _dir: &OsStr) { @@ -76,7 +84,7 @@ impl Command { } pub fn get_envs(&self) -> CommandEnvs<'_> { - panic!("unsupported") + self.env.iter() } pub fn get_current_dir(&self) -> Option<&Path> { @@ -140,8 +148,30 @@ impl Command { cmd.stderr_inherit() }; + let env = env_changes(&self.env); + + // Set any new vars + if let Some(e) = &env { + for (k, (_, v)) in e { + match v { + Some(v) => crate::env::set_var(k, v), + None => crate::env::remove_var(k), + } + } + } + let stat = cmd.start_image()?; + // Rollback any env changes + if let Some(e) = env { + for (k, (v, _)) in e { + match v { + Some(v) => crate::env::set_var(k, v), + None => crate::env::remove_var(k), + } + } + } + let stdout = cmd.stdout()?; let stderr = cmd.stderr()?; @@ -725,3 +755,32 @@ mod uefi_command_internal { res.into_boxed_slice() } } + +/// Create a map of environment variable changes. Allows efficient setting and rolling back of +/// enviroment variable changes. +/// +/// Entry: (Old Value, New Value) +fn env_changes(env: &CommandEnv) -> Option, Option)>> { + if env.is_unchanged() { + return None; + } + + let mut result = BTreeMap::, Option)>::new(); + + // Check if we want to clear all prior variables + if env.does_clear() { + for (k, v) in crate::env::vars_os() { + result.insert(k.into(), (Some(v), None)); + } + } + + for (k, v) in env.iter() { + let v: Option = v.map(Into::into); + result + .entry(k.into()) + .and_modify(|cur| *cur = (cur.0.clone(), v.clone())) + .or_insert((crate::env::var_os(k), v)); + } + + Some(result) +} From 4f834264ab8d0cb95ac7972532661c84a154667e Mon Sep 17 00:00:00 2001 From: joboet Date: Sun, 2 Feb 2025 18:53:52 +0100 Subject: [PATCH 29/57] std: move network code into `sys` As per #117276, this PR moves `sys_common::net` and the `sys::pal::net` into the newly created `sys::net` module. In order to support #135141, I've moved all the current network code into a separate `connection` module, future functions like `hostname` can live in separate modules. I'll probably do a follow-up PR and clean up some of the actual code, this is mostly just a reorganization. --- library/std/src/net/socket_addr.rs | 3 +- library/std/src/net/tcp.rs | 3 +- library/std/src/net/udp.rs | 3 +- library/std/src/os/fd/net.rs | 4 +- library/std/src/os/hermit/io/net.rs | 2 +- library/std/src/os/solid/io.rs | 4 +- library/std/src/os/windows/io/raw.rs | 8 +- library/std/src/sys/mod.rs | 1 + .../{pal/sgx/net.rs => net/connection/sgx.rs} | 2 +- .../net.rs => sys/net/connection/socket.rs} | 30 +- .../connection/socket/hermit.rs} | 13 +- .../net.rs => net/connection/socket/solid.rs} | 24 +- .../net/connection/socket}/tests.rs | 0 .../net.rs => net/connection/socket/unix.rs} | 10 +- .../connection/socket/wasip2.rs} | 2 +- .../connection/socket/windows.rs} | 21 +- .../net.rs => net/connection/unsupported.rs} | 0 .../wasi/net.rs => net/connection/wasip1.rs} | 5 +- .../xous/net => net/connection/xous}/dns.rs | 0 .../xous/net => net/connection/xous}/mod.rs | 0 .../connection/xous}/tcplistener.rs | 0 .../net => net/connection/xous}/tcpstream.rs | 0 .../xous/net => net/connection/xous}/udp.rs | 0 library/std/src/sys/net/mod.rs | 36 ++ library/std/src/sys/pal/hermit/mod.rs | 1 - library/std/src/sys/pal/sgx/mod.rs | 1 - library/std/src/sys/pal/solid/error.rs | 3 +- library/std/src/sys/pal/solid/mod.rs | 3 +- library/std/src/sys/pal/teeos/mod.rs | 1 - library/std/src/sys/pal/teeos/net.rs | 369 ------------ library/std/src/sys/pal/uefi/mod.rs | 2 - library/std/src/sys/pal/unix/l4re.rs | 564 ------------------ library/std/src/sys/pal/unix/mod.rs | 6 - library/std/src/sys/pal/unsupported/mod.rs | 1 - library/std/src/sys/pal/wasi/mod.rs | 4 +- library/std/src/sys/pal/wasip2/mod.rs | 1 - library/std/src/sys/pal/wasm/mod.rs | 2 - library/std/src/sys/pal/windows/mod.rs | 3 +- library/std/src/sys/pal/xous/mod.rs | 1 - library/std/src/sys/pal/zkvm/mod.rs | 2 - library/std/src/sys_common/mod.rs | 14 - 41 files changed, 113 insertions(+), 1036 deletions(-) rename library/std/src/sys/{pal/sgx/net.rs => net/connection/sgx.rs} (99%) rename library/std/src/{sys_common/net.rs => sys/net/connection/socket.rs} (97%) rename library/std/src/sys/{pal/hermit/net.rs => net/connection/socket/hermit.rs} (98%) rename library/std/src/sys/{pal/solid/net.rs => net/connection/socket/solid.rs} (95%) rename library/std/src/{sys_common/net => sys/net/connection/socket}/tests.rs (100%) rename library/std/src/sys/{pal/unix/net.rs => net/connection/socket/unix.rs} (99%) rename library/std/src/sys/{pal/wasip2/net.rs => net/connection/socket/wasip2.rs} (99%) rename library/std/src/sys/{pal/windows/net.rs => net/connection/socket/windows.rs} (95%) rename library/std/src/sys/{pal/unsupported/net.rs => net/connection/unsupported.rs} (100%) rename library/std/src/sys/{pal/wasi/net.rs => net/connection/wasip1.rs} (99%) rename library/std/src/sys/{pal/xous/net => net/connection/xous}/dns.rs (100%) rename library/std/src/sys/{pal/xous/net => net/connection/xous}/mod.rs (100%) rename library/std/src/sys/{pal/xous/net => net/connection/xous}/tcplistener.rs (100%) rename library/std/src/sys/{pal/xous/net => net/connection/xous}/tcpstream.rs (100%) rename library/std/src/sys/{pal/xous/net => net/connection/xous}/udp.rs (100%) create mode 100644 library/std/src/sys/net/mod.rs delete mode 100644 library/std/src/sys/pal/teeos/net.rs delete mode 100644 library/std/src/sys/pal/unix/l4re.rs diff --git a/library/std/src/net/socket_addr.rs b/library/std/src/net/socket_addr.rs index ba9c948a2e96..e8355cc31d7a 100644 --- a/library/std/src/net/socket_addr.rs +++ b/library/std/src/net/socket_addr.rs @@ -6,8 +6,7 @@ mod tests; pub use core::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -use crate::sys::net::netc as c; -use crate::sys_common::net::LookupHost; +use crate::sys::net::{LookupHost, netc as c}; use crate::sys_common::{FromInner, IntoInner}; use crate::{io, iter, mem, option, slice, vec}; diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index 67a0f7e439d5..9b68f872955c 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -15,7 +15,8 @@ use crate::io::prelude::*; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::iter::FusedIterator; use crate::net::{Shutdown, SocketAddr, ToSocketAddrs}; -use crate::sys_common::{AsInner, FromInner, IntoInner, net as net_imp}; +use crate::sys::net as net_imp; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; /// A TCP stream between a local and a remote socket. diff --git a/library/std/src/net/udp.rs b/library/std/src/net/udp.rs index 674c5fb7d6ea..3eb798ad34aa 100644 --- a/library/std/src/net/udp.rs +++ b/library/std/src/net/udp.rs @@ -12,7 +12,8 @@ mod tests; use crate::fmt; use crate::io::{self, ErrorKind}; use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs}; -use crate::sys_common::{AsInner, FromInner, IntoInner, net as net_imp}; +use crate::sys::net as net_imp; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; /// A UDP socket. diff --git a/library/std/src/os/fd/net.rs b/library/std/src/os/fd/net.rs index 843f45f7f5f9..34479ca0e190 100644 --- a/library/std/src/os/fd/net.rs +++ b/library/std/src/os/fd/net.rs @@ -1,6 +1,6 @@ use crate::os::fd::owned::OwnedFd; use crate::os::fd::raw::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; -use crate::sys_common::{self, AsInner, FromInner, IntoInner}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::{net, sys}; macro_rules! impl_as_raw_fd { @@ -24,7 +24,7 @@ macro_rules! impl_from_raw_fd { unsafe fn from_raw_fd(fd: RawFd) -> net::$t { unsafe { let socket = sys::net::Socket::from_inner(FromInner::from_inner(OwnedFd::from_raw_fd(fd))); - net::$t::from_inner(sys_common::net::$t::from_inner(socket)) + net::$t::from_inner(sys::net::$t::from_inner(socket)) } } } diff --git a/library/std/src/os/hermit/io/net.rs b/library/std/src/os/hermit/io/net.rs index 7a774345b231..233bc885fc73 100644 --- a/library/std/src/os/hermit/io/net.rs +++ b/library/std/src/os/hermit/io/net.rs @@ -23,7 +23,7 @@ macro_rules! impl_from_raw_fd { unsafe fn from_raw_fd(fd: RawFd) -> net::$t { unsafe { let socket = sys::net::Socket::from_inner(FromInner::from_inner(OwnedFd::from_raw_fd(fd))); - net::$t::from_inner(sys_common::net::$t::from_inner(socket)) + net::$t::from_inner(sys::net::$t::from_inner(socket)) } } } diff --git a/library/std/src/os/solid/io.rs b/library/std/src/os/solid/io.rs index b8601b533fe0..b8c3440542d0 100644 --- a/library/std/src/os/solid/io.rs +++ b/library/std/src/os/solid/io.rs @@ -48,7 +48,7 @@ use crate::marker::PhantomData; use crate::mem::ManuallyDrop; -use crate::sys_common::{self, AsInner, FromInner, IntoInner}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::{fmt, net, sys}; /// Raw file descriptors. @@ -387,7 +387,7 @@ macro_rules! impl_from_raw_fd { #[inline] unsafe fn from_raw_fd(fd: RawFd) -> net::$t { let socket = unsafe { sys::net::Socket::from_raw_fd(fd) }; - net::$t::from_inner(sys_common::net::$t::from_inner(socket)) + net::$t::from_inner(sys::net::$t::from_inner(socket)) } } )*}; diff --git a/library/std/src/os/windows/io/raw.rs b/library/std/src/os/windows/io/raw.rs index 6658248d574c..c0517fab9506 100644 --- a/library/std/src/os/windows/io/raw.rs +++ b/library/std/src/os/windows/io/raw.rs @@ -6,7 +6,7 @@ use crate::os::windows::io::{AsHandle, AsSocket}; use crate::os::windows::io::{OwnedHandle, OwnedSocket}; use crate::os::windows::raw; -use crate::sys_common::{self, AsInner, FromInner, IntoInner}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::{fs, io, net, ptr, sys}; /// Raw HANDLEs. @@ -262,7 +262,7 @@ impl FromRawSocket for net::TcpStream { unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpStream { unsafe { let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock)); - net::TcpStream::from_inner(sys_common::net::TcpStream::from_inner(sock)) + net::TcpStream::from_inner(sys::net::TcpStream::from_inner(sock)) } } } @@ -272,7 +272,7 @@ impl FromRawSocket for net::TcpListener { unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpListener { unsafe { let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock)); - net::TcpListener::from_inner(sys_common::net::TcpListener::from_inner(sock)) + net::TcpListener::from_inner(sys::net::TcpListener::from_inner(sock)) } } } @@ -282,7 +282,7 @@ impl FromRawSocket for net::UdpSocket { unsafe fn from_raw_socket(sock: RawSocket) -> net::UdpSocket { unsafe { let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock)); - net::UdpSocket::from_inner(sys_common::net::UdpSocket::from_inner(sock)) + net::UdpSocket::from_inner(sys::net::UdpSocket::from_inner(sock)) } } } diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index f17dd47deced..39a0bc6e337c 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 backtrace; pub mod cmath; pub mod exit_guard; +pub mod net; pub mod os_str; pub mod path; pub mod random; diff --git a/library/std/src/sys/pal/sgx/net.rs b/library/std/src/sys/net/connection/sgx.rs similarity index 99% rename from library/std/src/sys/pal/sgx/net.rs rename to library/std/src/sys/net/connection/sgx.rs index c966886d1634..b390a5eac5f7 100644 --- a/library/std/src/sys/pal/sgx/net.rs +++ b/library/std/src/sys/net/connection/sgx.rs @@ -1,7 +1,7 @@ -use super::abi::usercalls; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs}; use crate::sync::Arc; +use crate::sys::abi::usercalls; use crate::sys::fd::FileDesc; use crate::sys::{AsInner, FromInner, IntoInner, TryIntoInner, sgx_ineffective, unsupported}; use crate::time::Duration; diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys/net/connection/socket.rs similarity index 97% rename from library/std/src/sys_common/net.rs rename to library/std/src/sys/net/connection/socket.rs index 74306978d228..6fe3430b53f7 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys/net/connection/socket.rs @@ -5,11 +5,31 @@ use crate::ffi::{c_int, c_void}; use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; use crate::sys::common::small_c_string::run_with_cstr; -use crate::sys::net::{Socket, cvt, cvt_gai, cvt_r, init, netc as c, wrlen_t}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; use crate::{cmp, fmt, mem, ptr}; +cfg_if::cfg_if! { + if #[cfg(target_os = "hermit")] { + mod hermit; + pub use hermit::*; + } else if #[cfg(target_os = "solid_asp3")] { + mod solid; + pub use solid::*; + } else if #[cfg(target_family = "unix")] { + mod unix; + pub use unix::*; + } else if #[cfg(all(target_os = "wasi", target_env = "p2"))] { + mod wasip2; + pub use wasip2::*; + } else if #[cfg(target_os = "windows")] { + mod windows; + pub use windows::*; + } +} + +use netc as c; + cfg_if::cfg_if! { if #[cfg(any( target_os = "dragonfly", @@ -24,11 +44,11 @@ cfg_if::cfg_if! { target_os = "nuttx", target_vendor = "apple", ))] { - use crate::sys::net::netc::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; - use crate::sys::net::netc::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP; + use c::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; + use c::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP; } else { - use crate::sys::net::netc::IPV6_ADD_MEMBERSHIP; - use crate::sys::net::netc::IPV6_DROP_MEMBERSHIP; + use c::IPV6_ADD_MEMBERSHIP; + use c::IPV6_DROP_MEMBERSHIP; } } diff --git a/library/std/src/sys/pal/hermit/net.rs b/library/std/src/sys/net/connection/socket/hermit.rs similarity index 98% rename from library/std/src/sys/pal/hermit/net.rs rename to library/std/src/sys/net/connection/socket/hermit.rs index 4e12374203e3..42179dcc9156 100644 --- a/library/std/src/sys/pal/hermit/net.rs +++ b/library/std/src/sys/net/connection/socket/hermit.rs @@ -2,21 +2,20 @@ use core::ffi::c_int; -use super::fd::FileDesc; +pub(crate) use hermit_abi as netc; + use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr}; use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, RawFd}; +use crate::sys::fd::FileDesc; +use crate::sys::net::{getsockopt, setsockopt, sockaddr_to_addr}; use crate::sys::time::Instant; -use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; +pub use crate::sys::{cvt, cvt_r}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; use crate::{cmp, mem}; -#[allow(unused_extern_crates)] -pub extern crate hermit_abi as netc; - -pub use crate::sys::{cvt, cvt_r}; - +#[expect(non_camel_case_types)] pub type wrlen_t = usize; pub fn cvt_gai(err: i32) -> io::Result<()> { diff --git a/library/std/src/sys/pal/solid/net.rs b/library/std/src/sys/net/connection/socket/solid.rs similarity index 95% rename from library/std/src/sys/pal/solid/net.rs rename to library/std/src/sys/net/connection/socket/solid.rs index 5f6436807e27..f85ecbb883ee 100644 --- a/library/std/src/sys/pal/solid/net.rs +++ b/library/std/src/sys/net/connection/socket/solid.rs @@ -1,24 +1,23 @@ use libc::{c_int, c_void, size_t}; use self::netc::{MSG_PEEK, sockaddr, socklen_t}; -use super::abi; use crate::ffi::CStr; use crate::io::{self, BorrowedBuf, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr}; use crate::os::solid::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd}; -use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; +use crate::sys::abi; +use crate::sys::net::{getsockopt, setsockopt, sockaddr_to_addr}; use crate::sys_common::{FromInner, IntoInner}; use crate::time::Duration; use crate::{cmp, mem, ptr, str}; pub mod netc { - pub use super::super::abi::sockets::*; + pub use crate::sys::abi::sockets::*; } +#[expect(non_camel_case_types)] pub type wrlen_t = size_t; -const READ_LIMIT: usize = libc::ssize_t::MAX as usize; - const fn max_iov() -> usize { // Judging by the source code, it's unlimited, but specify a lower // value just in case. @@ -78,7 +77,7 @@ fn last_error() -> io::Error { io::Error::from_raw_os_error(unsafe { netc::SOLID_NET_GetLastError() }) } -pub(super) fn error_name(er: abi::ER) -> Option<&'static str> { +pub fn error_name(er: abi::ER) -> Option<&'static str> { unsafe { CStr::from_ptr(netc::strerror(er)) }.to_str().ok() } @@ -87,7 +86,7 @@ pub fn is_interrupted(er: abi::ER) -> bool { er == netc::SOLID_NET_ERR_BASE - libc::EINTR } -pub(super) fn decode_error_kind(er: abi::ER) -> ErrorKind { +pub fn decode_error_kind(er: abi::ER) -> ErrorKind { let errno = netc::SOLID_NET_ERR_BASE - er; match errno as libc::c_int { libc::ECONNREFUSED => ErrorKind::ConnectionRefused, @@ -268,17 +267,6 @@ impl Socket { self.recv_from_with_flags(buf, MSG_PEEK) } - pub fn write(&self, buf: &[u8]) -> io::Result { - let ret = cvt(unsafe { - netc::write( - self.as_raw_fd(), - buf.as_ptr() as *const c_void, - cmp::min(buf.len(), READ_LIMIT), - ) - })?; - Ok(ret as usize) - } - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { let ret = cvt(unsafe { netc::writev( diff --git a/library/std/src/sys_common/net/tests.rs b/library/std/src/sys/net/connection/socket/tests.rs similarity index 100% rename from library/std/src/sys_common/net/tests.rs rename to library/std/src/sys/net/connection/socket/tests.rs diff --git a/library/std/src/sys/pal/unix/net.rs b/library/std/src/sys/net/connection/socket/unix.rs similarity index 99% rename from library/std/src/sys/pal/unix/net.rs rename to library/std/src/sys/net/connection/socket/unix.rs index d73b9fd5eb88..da6316055273 100644 --- a/library/std/src/sys/pal/unix/net.rs +++ b/library/std/src/sys/net/connection/socket/unix.rs @@ -5,8 +5,8 @@ use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr}; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::sys::fd::FileDesc; -use crate::sys::pal::unix::IsMinusOne; -use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; +use crate::sys::net::{getsockopt, setsockopt, sockaddr_to_addr}; +use crate::sys::pal::IsMinusOne; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::{Duration, Instant}; use crate::{cmp, mem}; @@ -19,11 +19,11 @@ cfg_if::cfg_if! { } } +pub(crate) use libc as netc; + pub use crate::sys::{cvt, cvt_r}; -#[allow(unused_extern_crates)] -pub extern crate libc as netc; - +#[expect(non_camel_case_types)] pub type wrlen_t = size_t; pub struct Socket(FileDesc); diff --git a/library/std/src/sys/pal/wasip2/net.rs b/library/std/src/sys/net/connection/socket/wasip2.rs similarity index 99% rename from library/std/src/sys/pal/wasip2/net.rs rename to library/std/src/sys/net/connection/socket/wasip2.rs index f009a51821f3..9d1c05a473e4 100644 --- a/library/std/src/sys/pal/wasip2/net.rs +++ b/library/std/src/sys/net/connection/socket/wasip2.rs @@ -6,8 +6,8 @@ use crate::ffi::CStr; use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr}; use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; +use crate::sys::net::{getsockopt, setsockopt, sockaddr_to_addr}; use crate::sys::unsupported; -use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::{Duration, Instant}; use crate::{cmp, mem, str}; diff --git a/library/std/src/sys/pal/windows/net.rs b/library/std/src/sys/net/connection/socket/windows.rs similarity index 95% rename from library/std/src/sys/pal/windows/net.rs rename to library/std/src/sys/net/connection/socket/windows.rs index a92853c642c0..80cf37eaf058 100644 --- a/library/std/src/sys/pal/windows/net.rs +++ b/library/std/src/sys/net/connection/socket/windows.rs @@ -9,7 +9,7 @@ use crate::os::windows::io::{ }; use crate::sync::OnceLock; use crate::sys::c; -use crate::sys_common::{AsInner, FromInner, IntoInner, net}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; use crate::{cmp, mem, ptr, sys}; @@ -110,6 +110,7 @@ pub mod netc { } } +#[expect(missing_debug_implementations)] pub struct Socket(OwnedSocket); static WSA_CLEANUP: OnceLock i32> = OnceLock::new(); @@ -400,12 +401,12 @@ impl Socket { let error = unsafe { c::WSAGetLastError() }; if error == c::WSAESHUTDOWN { - Ok((0, net::sockaddr_to_addr(&storage, addrlen as usize)?)) + Ok((0, super::sockaddr_to_addr(&storage, addrlen as usize)?)) } else { Err(io::Error::from_raw_os_error(error)) } } - _ => Ok((result as usize, net::sockaddr_to_addr(&storage, addrlen as usize)?)), + _ => Ok((result as usize, super::sockaddr_to_addr(&storage, addrlen as usize)?)), } } @@ -450,11 +451,11 @@ impl Socket { } None => 0, }; - net::setsockopt(self, c::SOL_SOCKET, kind, timeout) + super::setsockopt(self, c::SOL_SOCKET, kind, timeout) } pub fn timeout(&self, kind: c_int) -> io::Result> { - let raw: u32 = net::getsockopt(self, c::SOL_SOCKET, kind)?; + let raw: u32 = super::getsockopt(self, c::SOL_SOCKET, kind)?; if raw == 0 { Ok(None) } else { @@ -487,26 +488,26 @@ impl Socket { l_linger: linger.unwrap_or_default().as_secs() as c_ushort, }; - net::setsockopt(self, c::SOL_SOCKET, c::SO_LINGER, linger) + super::setsockopt(self, c::SOL_SOCKET, c::SO_LINGER, linger) } pub fn linger(&self) -> io::Result> { - let val: c::LINGER = net::getsockopt(self, c::SOL_SOCKET, c::SO_LINGER)?; + let val: c::LINGER = super::getsockopt(self, c::SOL_SOCKET, c::SO_LINGER)?; Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) } pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - net::setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BOOL) + super::setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BOOL) } pub fn nodelay(&self) -> io::Result { - let raw: c::BOOL = net::getsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY)?; + let raw: c::BOOL = super::getsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY)?; Ok(raw != 0) } pub fn take_error(&self) -> io::Result> { - let raw: c_int = net::getsockopt(self, c::SOL_SOCKET, c::SO_ERROR)?; + let raw: c_int = super::getsockopt(self, c::SOL_SOCKET, c::SO_ERROR)?; if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } } diff --git a/library/std/src/sys/pal/unsupported/net.rs b/library/std/src/sys/net/connection/unsupported.rs similarity index 100% rename from library/std/src/sys/pal/unsupported/net.rs rename to library/std/src/sys/net/connection/unsupported.rs diff --git a/library/std/src/sys/pal/wasi/net.rs b/library/std/src/sys/net/connection/wasip1.rs similarity index 99% rename from library/std/src/sys/pal/wasi/net.rs rename to library/std/src/sys/net/connection/wasip1.rs index a64867998281..27e3a528af49 100644 --- a/library/std/src/sys/pal/wasi/net.rs +++ b/library/std/src/sys/net/connection/wasip1.rs @@ -1,12 +1,11 @@ #![forbid(unsafe_op_in_unsafe_fn)] -use super::err2io; -use super::fd::WasiFd; use crate::fmt; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; -use crate::sys::unsupported; +use crate::sys::fd::WasiFd; +use crate::sys::{err2io, unsupported}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; diff --git a/library/std/src/sys/pal/xous/net/dns.rs b/library/std/src/sys/net/connection/xous/dns.rs similarity index 100% rename from library/std/src/sys/pal/xous/net/dns.rs rename to library/std/src/sys/net/connection/xous/dns.rs diff --git a/library/std/src/sys/pal/xous/net/mod.rs b/library/std/src/sys/net/connection/xous/mod.rs similarity index 100% rename from library/std/src/sys/pal/xous/net/mod.rs rename to library/std/src/sys/net/connection/xous/mod.rs diff --git a/library/std/src/sys/pal/xous/net/tcplistener.rs b/library/std/src/sys/net/connection/xous/tcplistener.rs similarity index 100% rename from library/std/src/sys/pal/xous/net/tcplistener.rs rename to library/std/src/sys/net/connection/xous/tcplistener.rs diff --git a/library/std/src/sys/pal/xous/net/tcpstream.rs b/library/std/src/sys/net/connection/xous/tcpstream.rs similarity index 100% rename from library/std/src/sys/pal/xous/net/tcpstream.rs rename to library/std/src/sys/net/connection/xous/tcpstream.rs diff --git a/library/std/src/sys/pal/xous/net/udp.rs b/library/std/src/sys/net/connection/xous/udp.rs similarity index 100% rename from library/std/src/sys/pal/xous/net/udp.rs rename to library/std/src/sys/net/connection/xous/udp.rs diff --git a/library/std/src/sys/net/mod.rs b/library/std/src/sys/net/mod.rs new file mode 100644 index 000000000000..5aa197fbc0df --- /dev/null +++ b/library/std/src/sys/net/mod.rs @@ -0,0 +1,36 @@ +cfg_if::cfg_if! { + if #[cfg(any( + all(target_family = "unix", not(target_os = "l4re")), + target_os = "windows", + target_os = "hermit", + all(target_os = "wasi", target_env = "p2"), + target_os = "solid_asp3", + ))] { + mod connection { + mod socket; + pub use socket::*; + } + } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + mod connection { + mod sgx; + pub use sgx::*; + } + } else if #[cfg(all(target_os = "wasi", target_env = "p1"))] { + mod connection { + mod wasip1; + pub use wasip1::*; + } + } else if #[cfg(target_os = "xous")] { + mod connection { + mod xous; + pub use xous::*; + } + } else { + mod connection { + mod unsupported; + pub use unsupported::*; + } + } +} + +pub use connection::*; diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index d833c9d632c6..032007aa4dca 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -24,7 +24,6 @@ pub mod fd; pub mod fs; pub mod futex; pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs index ce8a2fed4bc6..0f5935d0c718 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -17,7 +17,6 @@ pub mod fs; #[path = "../unsupported/io.rs"] pub mod io; mod libunwind_integration; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/solid/error.rs b/library/std/src/sys/pal/solid/error.rs index e092497856d4..b399463c0c28 100644 --- a/library/std/src/sys/pal/solid/error.rs +++ b/library/std/src/sys/pal/solid/error.rs @@ -1,6 +1,7 @@ pub use self::itron::error::{ItronError as SolidError, expect_success}; -use super::{abi, itron, net}; +use super::{abi, itron}; use crate::io::ErrorKind; +use crate::sys::net; /// Describe the specified SOLID error code. Returns `None` if it's an /// undefined error code. diff --git a/library/std/src/sys/pal/solid/mod.rs b/library/std/src/sys/pal/solid/mod.rs index d41042be5184..caf848a4e9b0 100644 --- a/library/std/src/sys/pal/solid/mod.rs +++ b/library/std/src/sys/pal/solid/mod.rs @@ -24,7 +24,6 @@ pub mod env; pub(crate) mod error; pub mod fs; pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; @@ -51,7 +50,7 @@ pub fn unsupported_err() -> crate::io::Error { #[inline] pub fn is_interrupted(code: i32) -> bool { - net::is_interrupted(code) + crate::sys::net::is_interrupted(code) } pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind { diff --git a/library/std/src/sys/pal/teeos/mod.rs b/library/std/src/sys/pal/teeos/mod.rs index a9900f55b192..a9904e66664e 100644 --- a/library/std/src/sys/pal/teeos/mod.rs +++ b/library/std/src/sys/pal/teeos/mod.rs @@ -15,7 +15,6 @@ pub mod env; pub mod fs; #[path = "../unsupported/io.rs"] pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/teeos/net.rs b/library/std/src/sys/pal/teeos/net.rs deleted file mode 100644 index fed95205027a..000000000000 --- a/library/std/src/sys/pal/teeos/net.rs +++ /dev/null @@ -1,369 +0,0 @@ -use crate::fmt; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; -use crate::sys::unsupported; -use crate::time::Duration; - -pub struct TcpStream(!); - -impl TcpStream { - pub fn connect(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() - } - - pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { - unsupported() - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn read_timeout(&self) -> io::Result> { - self.0 - } - - pub fn write_timeout(&self) -> io::Result> { - self.0 - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn read(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn read_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> { - self.0 - } - - pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { - self.0 - } - - pub fn is_read_vectored(&self) -> bool { - self.0 - } - - pub fn write(&self, _: &[u8]) -> io::Result { - self.0 - } - - pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { - self.0 - } - - pub fn is_write_vectored(&self) -> bool { - self.0 - } - - pub fn peer_addr(&self) -> io::Result { - self.0 - } - - pub fn socket_addr(&self) -> io::Result { - self.0 - } - - pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - self.0 - } - - pub fn duplicate(&self) -> io::Result { - self.0 - } - - pub fn set_linger(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn linger(&self) -> io::Result> { - self.0 - } - - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn nodelay(&self) -> io::Result { - self.0 - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn ttl(&self) -> io::Result { - self.0 - } - - pub fn take_error(&self) -> io::Result> { - self.0 - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - self.0 - } -} - -impl fmt::Debug for TcpStream { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -pub struct TcpListener(!); - -impl TcpListener { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() - } - - pub fn socket_addr(&self) -> io::Result { - self.0 - } - - pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - self.0 - } - - pub fn duplicate(&self) -> io::Result { - self.0 - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn ttl(&self) -> io::Result { - self.0 - } - - pub fn set_only_v6(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn only_v6(&self) -> io::Result { - self.0 - } - - pub fn take_error(&self) -> io::Result> { - self.0 - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - self.0 - } -} - -impl fmt::Debug for TcpListener { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -pub struct UdpSocket(!); - -impl UdpSocket { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() - } - - pub fn peer_addr(&self) -> io::Result { - self.0 - } - - pub fn socket_addr(&self) -> io::Result { - self.0 - } - - pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.0 - } - - pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.0 - } - - pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { - self.0 - } - - pub fn duplicate(&self) -> io::Result { - self.0 - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn read_timeout(&self) -> io::Result> { - self.0 - } - - pub fn write_timeout(&self) -> io::Result> { - self.0 - } - - pub fn set_broadcast(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn broadcast(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn multicast_loop_v4(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn multicast_ttl_v4(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn multicast_loop_v6(&self) -> io::Result { - self.0 - } - - pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - self.0 - } - - pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - self.0 - } - - pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - self.0 - } - - pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - self.0 - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn ttl(&self) -> io::Result { - self.0 - } - - pub fn take_error(&self) -> io::Result> { - self.0 - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn recv(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn send(&self, _: &[u8]) -> io::Result { - self.0 - } - - pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { - self.0 - } -} - -impl fmt::Debug for UdpSocket { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -pub struct LookupHost(!); - -impl LookupHost { - pub fn port(&self) -> u16 { - self.0 - } -} - -impl Iterator for LookupHost { - type Item = SocketAddr; - fn next(&mut self) -> Option { - self.0 - } -} - -impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(_v: &str) -> io::Result { - unsupported() - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(_v: (&'a str, u16)) -> io::Result { - unsupported() - } -} - -#[allow(nonstandard_style)] -pub mod netc { - pub const AF_INET: u8 = 0; - pub const AF_INET6: u8 = 1; - pub type sa_family_t = u8; - - #[derive(Copy, Clone)] - pub struct in_addr { - pub s_addr: u32, - } - - #[derive(Copy, Clone)] - pub struct sockaddr_in { - pub sin_family: sa_family_t, - pub sin_port: u16, - pub sin_addr: in_addr, - } - - #[derive(Copy, Clone)] - pub struct in6_addr { - pub s6_addr: [u8; 16], - } - - #[derive(Copy, Clone)] - pub struct sockaddr_in6 { - pub sin6_family: sa_family_t, - pub sin6_port: u16, - pub sin6_addr: in6_addr, - pub sin6_flowinfo: u32, - pub sin6_scope_id: u32, - } -} - -pub type Socket = UdpSocket; diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs index 111bed7a7eb6..07025a304bfd 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -19,8 +19,6 @@ pub mod fs; pub mod helpers; #[path = "../unsupported/io.rs"] pub mod io; -#[path = "../unsupported/net.rs"] -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/unix/l4re.rs b/library/std/src/sys/pal/unix/l4re.rs deleted file mode 100644 index 37dd370c5146..000000000000 --- a/library/std/src/sys/pal/unix/l4re.rs +++ /dev/null @@ -1,564 +0,0 @@ -macro_rules! unimpl { - () => { - return Err(io::const_error!( - io::ErrorKind::Unsupported, - "No networking available on L4Re.", - )); - }; -} - -pub mod net { - #![allow(warnings)] - use crate::fmt; - use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; - use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; - use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; - use crate::sys::fd::FileDesc; - use crate::sys_common::{AsInner, FromInner, IntoInner}; - use crate::time::Duration; - - #[allow(unused_extern_crates)] - pub extern crate libc as netc; - - pub struct Socket(FileDesc); - impl Socket { - pub fn new(_: &SocketAddr, _: libc::c_int) -> io::Result { - unimpl!(); - } - - pub fn new_raw(_: libc::c_int, _: libc::c_int) -> io::Result { - unimpl!(); - } - - pub fn new_pair(_: libc::c_int, _: libc::c_int) -> io::Result<(Socket, Socket)> { - unimpl!(); - } - - pub fn connect_timeout(&self, _: &SocketAddr, _: Duration) -> io::Result<()> { - unimpl!(); - } - - pub fn accept( - &self, - _: *mut libc::sockaddr, - _: *mut libc::socklen_t, - ) -> io::Result { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result { - unimpl!(); - } - - pub fn read(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn read_buf(&self, _: BorrowedCursor<'_>) -> io::Result<()> { - unimpl!(); - } - - pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { - unimpl!(); - } - - pub fn is_read_vectored(&self) -> bool { - false - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unimpl!(); - } - - pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unimpl!(); - } - - pub fn write(&self, _: &[u8]) -> io::Result { - unimpl!(); - } - - pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { - unimpl!(); - } - - pub fn is_write_vectored(&self) -> bool { - false - } - - pub fn set_timeout(&self, _: Option, _: libc::c_int) -> io::Result<()> { - unimpl!(); - } - - pub fn timeout(&self, _: libc::c_int) -> io::Result> { - unimpl!(); - } - - pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - unimpl!(); - } - - pub fn set_linger(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn linger(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn nodelay(&self) -> io::Result { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result> { - unimpl!(); - } - - // This is used by sys_common code to abstract over Windows and Unix. - pub fn as_raw(&self) -> RawFd { - self.as_raw_fd() - } - } - - impl AsInner for Socket { - #[inline] - fn as_inner(&self) -> &FileDesc { - &self.0 - } - } - - impl FromInner for Socket { - fn from_inner(file_desc: FileDesc) -> Socket { - Socket(file_desc) - } - } - - impl IntoInner for Socket { - fn into_inner(self) -> FileDesc { - self.0 - } - } - - impl AsFd for Socket { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } - } - - impl AsRawFd for Socket { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } - } - - impl IntoRawFd for Socket { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw_fd() - } - } - - impl FromRawFd for Socket { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - Self(FromRawFd::from_raw_fd(raw_fd)) - } - } - - pub struct TcpStream { - inner: Socket, - } - - impl TcpStream { - pub fn connect(_: io::Result<&SocketAddr>) -> io::Result { - unimpl!(); - } - - pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { - unimpl!(); - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn read_timeout(&self) -> io::Result> { - unimpl!(); - } - - pub fn write_timeout(&self) -> io::Result> { - unimpl!(); - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn read(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn read_buf(&self, _: BorrowedCursor<'_>) -> io::Result<()> { - unimpl!(); - } - - pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { - unimpl!(); - } - - pub fn is_read_vectored(&self) -> bool { - false - } - - pub fn write(&self, _: &[u8]) -> io::Result { - unimpl!(); - } - - pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { - unimpl!(); - } - - pub fn is_write_vectored(&self) -> bool { - false - } - - pub fn peer_addr(&self) -> io::Result { - unimpl!(); - } - - pub fn socket_addr(&self) -> io::Result { - unimpl!(); - } - - pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result { - unimpl!(); - } - - pub fn set_linger(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn linger(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn nodelay(&self) -> io::Result { - unimpl!(); - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn ttl(&self) -> io::Result { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - } - - impl FromInner for TcpStream { - fn from_inner(socket: Socket) -> TcpStream { - TcpStream { inner: socket } - } - } - - impl fmt::Debug for TcpStream { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "No networking support available on L4Re") - } - } - - pub struct TcpListener { - inner: Socket, - } - - impl TcpListener { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unimpl!(); - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } - - pub fn socket_addr(&self) -> io::Result { - unimpl!(); - } - - pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result { - unimpl!(); - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn ttl(&self) -> io::Result { - unimpl!(); - } - - pub fn set_only_v6(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn only_v6(&self) -> io::Result { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - } - - impl FromInner for TcpListener { - fn from_inner(socket: Socket) -> TcpListener { - TcpListener { inner: socket } - } - } - - impl fmt::Debug for TcpListener { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "No networking support available on L4Re.") - } - } - - pub struct UdpSocket { - inner: Socket, - } - - impl UdpSocket { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unimpl!(); - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } - - pub fn peer_addr(&self) -> io::Result { - unimpl!(); - } - - pub fn socket_addr(&self) -> io::Result { - unimpl!(); - } - - pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unimpl!(); - } - - pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unimpl!(); - } - - pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result { - unimpl!(); - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn read_timeout(&self) -> io::Result> { - unimpl!(); - } - - pub fn write_timeout(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_broadcast(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn broadcast(&self) -> io::Result { - unimpl!(); - } - - pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn multicast_loop_v4(&self) -> io::Result { - unimpl!(); - } - - pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn multicast_ttl_v4(&self) -> io::Result { - unimpl!(); - } - - pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn multicast_loop_v6(&self) -> io::Result { - unimpl!(); - } - - pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - unimpl!(); - } - - pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - unimpl!(); - } - - pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn ttl(&self) -> io::Result { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn recv(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn send(&self, _: &[u8]) -> io::Result { - unimpl!(); - } - - pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { - unimpl!(); - } - } - - impl FromInner for UdpSocket { - fn from_inner(socket: Socket) -> UdpSocket { - UdpSocket { inner: socket } - } - } - - impl fmt::Debug for UdpSocket { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "No networking support on L4Re available.") - } - } - - pub struct LookupHost { - original: *mut libc::addrinfo, - cur: *mut libc::addrinfo, - } - - impl Iterator for LookupHost { - type Item = SocketAddr; - fn next(&mut self) -> Option { - None - } - } - - impl LookupHost { - pub fn port(&self) -> u16 { - 0 // unimplemented - } - } - - unsafe impl Sync for LookupHost {} - unsafe impl Send for LookupHost {} - - impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(_v: &str) -> io::Result { - unimpl!(); - } - } - - impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(_v: (&'a str, u16)) -> io::Result { - unimpl!(); - } - } -} diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index 3cc1cae8d000..6862399b9425 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -14,14 +14,8 @@ pub mod futex; pub mod io; #[cfg(any(target_os = "linux", target_os = "android"))] pub mod kernel_copy; -#[cfg(target_os = "l4re")] -mod l4re; #[cfg(target_os = "linux")] pub mod linux; -#[cfg(not(target_os = "l4re"))] -pub mod net; -#[cfg(target_os = "l4re")] -pub use self::l4re::net; pub mod os; pub mod pipe; pub mod process; diff --git a/library/std/src/sys/pal/unsupported/mod.rs b/library/std/src/sys/pal/unsupported/mod.rs index 01d516f7568b..4941dd4918c8 100644 --- a/library/std/src/sys/pal/unsupported/mod.rs +++ b/library/std/src/sys/pal/unsupported/mod.rs @@ -4,7 +4,6 @@ pub mod args; pub mod env; pub mod fs; pub mod io; -pub mod net; pub mod os; pub mod pipe; pub mod process; diff --git a/library/std/src/sys/pal/wasi/mod.rs b/library/std/src/sys/pal/wasi/mod.rs index 361802d101df..312ad28ab51c 100644 --- a/library/std/src/sys/pal/wasi/mod.rs +++ b/library/std/src/sys/pal/wasi/mod.rs @@ -22,7 +22,6 @@ pub mod fs; pub mod futex; pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; @@ -45,5 +44,4 @@ mod helpers; // import conflict rules. If we glob export `helpers` and `common` together, // then the compiler complains about conflicts. -use helpers::err2io; -pub use helpers::{abort_internal, decode_error_kind, is_interrupted}; +pub(crate) use helpers::{abort_internal, decode_error_kind, err2io, is_interrupted}; diff --git a/library/std/src/sys/pal/wasip2/mod.rs b/library/std/src/sys/pal/wasip2/mod.rs index 320712fdcc9f..234e946d3b8c 100644 --- a/library/std/src/sys/pal/wasip2/mod.rs +++ b/library/std/src/sys/pal/wasip2/mod.rs @@ -20,7 +20,6 @@ pub mod futex; #[path = "../wasi/io.rs"] pub mod io; -pub mod net; #[path = "../wasi/os.rs"] pub mod os; #[path = "../unsupported/pipe.rs"] diff --git a/library/std/src/sys/pal/wasm/mod.rs b/library/std/src/sys/pal/wasm/mod.rs index 41fe019f1102..1280f3532001 100644 --- a/library/std/src/sys/pal/wasm/mod.rs +++ b/library/std/src/sys/pal/wasm/mod.rs @@ -23,8 +23,6 @@ pub mod env; pub mod fs; #[path = "../unsupported/io.rs"] pub mod io; -#[path = "../unsupported/net.rs"] -pub mod net; #[path = "../unsupported/os.rs"] pub mod os; #[path = "../unsupported/pipe.rs"] diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 4282dbb54934..f9aa049ca9ae 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -22,7 +22,6 @@ pub mod fs; pub mod futex; pub mod handle; pub mod io; -pub mod net; pub mod os; pub mod pipe; pub mod process; @@ -63,7 +62,7 @@ pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) { // SAFETY: must be called only once during runtime cleanup. // NOTE: this is not guaranteed to run, for example when the program aborts. pub unsafe fn cleanup() { - net::cleanup(); + crate::sys::net::cleanup(); } #[inline] diff --git a/library/std/src/sys/pal/xous/mod.rs b/library/std/src/sys/pal/xous/mod.rs index a64cd0685600..8ba2b6e2f20f 100644 --- a/library/std/src/sys/pal/xous/mod.rs +++ b/library/std/src/sys/pal/xous/mod.rs @@ -7,7 +7,6 @@ pub mod env; pub mod fs; #[path = "../unsupported/io.rs"] pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/zkvm/mod.rs b/library/std/src/sys/pal/zkvm/mod.rs index 6ea057720296..9e9ae8610703 100644 --- a/library/std/src/sys/pal/zkvm/mod.rs +++ b/library/std/src/sys/pal/zkvm/mod.rs @@ -18,8 +18,6 @@ pub mod env; pub mod fs; #[path = "../unsupported/io.rs"] pub mod io; -#[path = "../unsupported/net.rs"] -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs index 4f7a131f6bb9..959fbd4ca0a1 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -26,20 +26,6 @@ pub mod process; pub mod wstr; pub mod wtf8; -cfg_if::cfg_if! { - if #[cfg(any( - all(unix, not(target_os = "l4re")), - windows, - target_os = "hermit", - target_os = "solid_asp3", - all(target_os = "wasi", target_env = "p2") - ))] { - pub mod net; - } else { - pub use crate::sys::net; - } -} - // common error constructors /// A trait for viewing representations from std types From 8a3cb1351183e963da96ae6bbb8dcce478be9f7b Mon Sep 17 00:00:00 2001 From: joboet Date: Sun, 2 Feb 2025 19:53:40 +0100 Subject: [PATCH 30/57] bless UI test involving private name --- tests/ui/inference/issue-72616.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/inference/issue-72616.stderr b/tests/ui/inference/issue-72616.stderr index a26f9a1ff561..3848fcf61d90 100644 --- a/tests/ui/inference/issue-72616.stderr +++ b/tests/ui/inference/issue-72616.stderr @@ -26,7 +26,7 @@ LL | if String::from("a") == "a".try_into().unwrap() {} | ^^^^^^^^ | = note: multiple `impl`s satisfying `_: TryFrom<&str>` found in the following crates: `core`, `std`: - - impl TryFrom<&str> for std::sys_common::net::LookupHost; + - impl TryFrom<&str> for std::sys::net::connection::socket::LookupHost; - impl TryFrom for T where U: Into; = note: required for `&str` to implement `TryInto<_>` From 2ea95f867012e91fa742e60b7b6424abf86c05cf Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Wed, 29 Jan 2025 13:11:17 -0700 Subject: [PATCH 31/57] rustdoc: clean up a bunch of ts-expected-error declarations in main This mostly consists of handling potentially-null input and adding more global functions to the list of globals. --- src/librustdoc/html/static/js/main.js | 264 ++++++++++----------- src/librustdoc/html/static/js/rustdoc.d.ts | 22 ++ src/librustdoc/html/static/js/search.js | 2 - src/librustdoc/html/static/js/storage.js | 16 +- 4 files changed, 156 insertions(+), 148 deletions(-) diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index ccf4002bb300..4698375f6ccb 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -196,15 +196,13 @@ function switchDisplayedElement(elemToDisplay) { removeClass(el, "hidden"); const mainHeading = elemToDisplay.querySelector(".main-heading"); - // @ts-expect-error - if (mainHeading && searchState.rustdocToolbar) { - // @ts-expect-error - if (searchState.rustdocToolbar.parentElement) { - // @ts-expect-error - searchState.rustdocToolbar.parentElement.removeChild(searchState.rustdocToolbar); + if (mainHeading && window.searchState.rustdocToolbar) { + if (window.searchState.rustdocToolbar.parentElement) { + window.searchState.rustdocToolbar.parentElement.removeChild( + window.searchState.rustdocToolbar, + ); } - // @ts-expect-error - mainHeading.appendChild(searchState.rustdocToolbar); + mainHeading.appendChild(window.searchState.rustdocToolbar); } } @@ -212,7 +210,12 @@ function browserSupportsHistoryApi() { return window.history && typeof window.history.pushState === "function"; } -// @ts-expect-error +/** + * Download CSS from the web without making it the active stylesheet. + * We use this in the settings popover so that you don't get FOUC when switching. + * + * @param {string} cssUrl + */ function preLoadCss(cssUrl) { // https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload const link = document.createElement("link"); @@ -225,7 +228,11 @@ function preLoadCss(cssUrl) { (function() { const isHelpPage = window.location.pathname.endsWith("/help.html"); - // @ts-expect-error + /** + * Run a JavaScript file asynchronously. + * @param {string} url + * @param {function(): any} errorCallback + */ function loadScript(url, errorCallback) { const script = document.createElement("script"); script.src = url; @@ -235,13 +242,12 @@ function preLoadCss(cssUrl) { document.head.append(script); } - if (getSettingsButton()) { - // @ts-expect-error - getSettingsButton().onclick = event => { + const settingsButton = getSettingsButton(); + if (settingsButton) { + settingsButton.onclick = event => { if (event.ctrlKey || event.altKey || event.metaKey) { return; } - // @ts-expect-error window.hideAllModals(false); addClass(getSettingsButton(), "rotate"); event.preventDefault(); @@ -470,7 +476,6 @@ function preLoadCss(cssUrl) { } return onEachLazy(implElem.parentElement.parentElement.querySelectorAll( `[id^="${assocId}"]`), - // @ts-expect-error item => { const numbered = /^(.+?)-([0-9]+)$/.exec(item.id); if (item.id === assocId || (numbered && numbered[1] === assocId)) { @@ -522,7 +527,6 @@ function preLoadCss(cssUrl) { ev.preventDefault(); // @ts-expect-error searchState.defocus(); - // @ts-expect-error window.hideAllModals(true); // true = reset focus for tooltips } @@ -687,7 +691,6 @@ function preLoadCss(cssUrl) { // // By the way, this is only used by and useful for traits implemented automatically // (like "Send" and "Sync"). - // @ts-expect-error onEachLazy(synthetic_implementors.getElementsByClassName("impl"), el => { const aliases = el.getAttribute("data-aliases"); if (!aliases) { @@ -740,7 +743,6 @@ function preLoadCss(cssUrl) { code.innerHTML = struct[TEXT_IDX]; addClass(code, "code-header"); - // @ts-expect-error onEachLazy(code.getElementsByTagName("a"), elem => { const href = elem.getAttribute("href"); @@ -886,7 +888,6 @@ function preLoadCss(cssUrl) { const template = document.createElement("template"); template.innerHTML = text; - // @ts-expect-error onEachLazy(template.content.querySelectorAll("a"), elem => { const href = elem.getAttribute("href"); @@ -894,7 +895,6 @@ function preLoadCss(cssUrl) { elem.setAttribute("href", window.rootPath + href); } }); - // @ts-expect-error onEachLazy(template.content.querySelectorAll("[id]"), el => { let i = 0; if (idMap.has(el.id)) { @@ -912,7 +912,6 @@ function preLoadCss(cssUrl) { const oldHref = `#${el.id}`; const newHref = `#${el.id}-${i}`; el.id = `${el.id}-${i}`; - // @ts-expect-error onEachLazy(template.content.querySelectorAll("a[href]"), link => { if (link.getAttribute("href") === oldHref) { link.href = newHref; @@ -933,7 +932,6 @@ function preLoadCss(cssUrl) { // @ts-expect-error sidebarTraitList.append(li); } else { - // @ts-expect-error onEachLazy(templateAssocItems, item => { let block = hasClass(item, "associatedtype") ? associatedTypes : ( hasClass(item, "associatedconstant") ? associatedConstants : ( @@ -1040,7 +1038,6 @@ function preLoadCss(cssUrl) { function expandAllDocs() { const innerToggle = document.getElementById(toggleAllDocsId); removeClass(innerToggle, "will-expand"); - // @ts-expect-error onEachLazy(document.getElementsByClassName("toggle"), e => { if (!hasClass(e, "type-contents-toggle") && !hasClass(e, "more-examples-toggle")) { e.open = true; @@ -1053,7 +1050,6 @@ function preLoadCss(cssUrl) { function collapseAllDocs() { const innerToggle = document.getElementById(toggleAllDocsId); addClass(innerToggle, "will-expand"); - // @ts-expect-error onEachLazy(document.getElementsByClassName("toggle"), e => { if (e.parentNode.id !== "implementations-list" || (!hasClass(e, "implementors-toggle") && @@ -1092,7 +1088,6 @@ function preLoadCss(cssUrl) { function setImplementorsTogglesOpen(id, open) { const list = document.getElementById(id); if (list !== null) { - // @ts-expect-error onEachLazy(list.getElementsByClassName("implementors-toggle"), e => { e.open = open; }); @@ -1104,7 +1099,6 @@ function preLoadCss(cssUrl) { setImplementorsTogglesOpen("blanket-implementations-list", false); } - // @ts-expect-error onEachLazy(document.getElementsByClassName("toggle"), e => { if (!hideLargeItemContents && hasClass(e, "type-contents-toggle")) { e.open = true; @@ -1124,7 +1118,6 @@ function preLoadCss(cssUrl) { } onEachLazy(document.querySelectorAll( ":not(.scraped-example) > .example-wrap > pre:not(.example-line-numbers)", - // @ts-expect-error ), x => { const parent = x.parentNode; const line_numbers = parent.querySelectorAll(".example-line-numbers"); @@ -1145,7 +1138,6 @@ function preLoadCss(cssUrl) { // @ts-expect-error window.rustdoc_remove_line_numbers_from_examples = () => { - // @ts-expect-error onEachLazy(document.querySelectorAll(".example-wrap > .example-line-numbers"), x => { x.parentNode.removeChild(x); }); @@ -1157,16 +1149,13 @@ function preLoadCss(cssUrl) { } function showSidebar() { - // @ts-expect-error window.hideAllModals(false); const sidebar = document.getElementsByClassName("sidebar")[0]; - // @ts-expect-error addClass(sidebar, "shown"); } function hideSidebar() { const sidebar = document.getElementsByClassName("sidebar")[0]; - // @ts-expect-error removeClass(sidebar, "shown"); } @@ -1193,7 +1182,6 @@ function preLoadCss(cssUrl) { mainElem.addEventListener("click", hideSidebar); } - // @ts-expect-error onEachLazy(document.querySelectorAll("a[href^='#']"), el => { // For clicks on internal links ( tags with a hash property), we expand the section we're // jumping to *before* jumping there. We can't do this in onHashChange, because it changes @@ -1204,7 +1192,6 @@ function preLoadCss(cssUrl) { }); }); - // @ts-expect-error onEachLazy(document.querySelectorAll(".toggle > summary:not(.hideme)"), el => { // @ts-expect-error el.addEventListener("click", e => { @@ -1241,7 +1228,6 @@ function preLoadCss(cssUrl) { clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT); return; } - // @ts-expect-error window.hideAllModals(false); const wrapper = document.createElement("div"); if (notable_ty) { @@ -1422,7 +1408,6 @@ function preLoadCss(cssUrl) { } } - // @ts-expect-error onEachLazy(document.getElementsByClassName("tooltip"), e => { e.onclick = () => { e.TOOLTIP_FORCE_VISIBLE = e.TOOLTIP_FORCE_VISIBLE ? false : true; @@ -1527,7 +1512,6 @@ function preLoadCss(cssUrl) { // @ts-expect-error !getSettingsButton().contains(event.relatedTarget) ) { - // @ts-expect-error window.hidePopoverMenus(); } } @@ -1626,10 +1610,8 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm * * Pass "true" to reset focus for tooltip popovers. */ - // @ts-expect-error window.hideAllModals = switchFocus => { hideSidebar(); - // @ts-expect-error window.hidePopoverMenus(); hideTooltip(switchFocus); }; @@ -1637,9 +1619,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm /** * Hide all the popover menus. */ - // @ts-expect-error window.hidePopoverMenus = () => { - // @ts-expect-error onEachLazy(document.querySelectorAll("rustdoc-toolbar .popover"), elem => { elem.style.display = "none"; }); @@ -1708,7 +1688,6 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm if (shouldShowHelp) { showHelp(); } else { - // @ts-expect-error window.hidePopoverMenus(); } }); @@ -1780,30 +1759,42 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm }); } - // Pointer capture. - // - // Resizing is a single-pointer gesture. Any secondary pointer is ignored - // @ts-expect-error + /** + * Pointer capture. + * + * Resizing is a single-pointer gesture. Any secondary pointer is ignored + * + * @type {null|number} + */ let currentPointerId = null; - // "Desired" sidebar size. - // - // This is stashed here for window resizing. If the sidebar gets - // shrunk to maintain BODY_MIN, and then the user grows the window again, - // it gets the sidebar to restore its size. - // @ts-expect-error + /** + * "Desired" sidebar size. + * + * This is stashed here for window resizing. If the sidebar gets + * shrunk to maintain BODY_MIN, and then the user grows the window again, + * it gets the sidebar to restore its size. + * + * @type {null|number} + */ let desiredSidebarSize = null; - // Sidebar resize debouncer. - // - // The sidebar itself is resized instantly, but the body HTML can be too - // big for that, causing reflow jank. To reduce this, we queue up a separate - // animation frame and throttle it. + /** + * Sidebar resize debouncer. + * + * The sidebar itself is resized instantly, but the body HTML can be too + * big for that, causing reflow jank. To reduce this, we queue up a separate + * animation frame and throttle it. + * + * @type {false|ReturnType} + */ let pendingSidebarResizingFrame = false; - // If this page has no sidebar at all, bail out. + /** @type {HTMLElement|null} */ const resizer = document.querySelector(".sidebar-resizer"); + /** @type {HTMLElement|null} */ const sidebar = document.querySelector(".sidebar"); + // If this page has no sidebar at all, bail out. if (!resizer || !sidebar) { return; } @@ -1820,11 +1811,9 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // from settings.js, which uses a separate function. It's done here because // the minimum sidebar size is rather uncomfortable, and it must pass // through that size when using the shrink-to-nothing gesture. - function hideSidebar() { + const hideSidebar = function() { if (isSrcPage) { - // @ts-expect-error window.rustdocCloseSourceSidebar(); - // @ts-expect-error updateLocalStorage("src-sidebar-width", null); // [RUSTDOCIMPL] CSS variable fast path // @@ -1837,22 +1826,17 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // // So, to clear it, we need to clear all three. document.documentElement.style.removeProperty("--src-sidebar-width"); - // @ts-expect-error sidebar.style.removeProperty("--src-sidebar-width"); - // @ts-expect-error resizer.style.removeProperty("--src-sidebar-width"); } else { addClass(document.documentElement, "hide-sidebar"); updateLocalStorage("hide-sidebar", "true"); - // @ts-expect-error updateLocalStorage("desktop-sidebar-width", null); document.documentElement.style.removeProperty("--desktop-sidebar-width"); - // @ts-expect-error sidebar.style.removeProperty("--desktop-sidebar-width"); - // @ts-expect-error resizer.style.removeProperty("--desktop-sidebar-width"); } - } + }; // Call this function to show the sidebar from the resize handle. // On docs pages, this can only happen if the user has grabbed the resize @@ -1860,15 +1844,14 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // the visible range without releasing it. You can, however, grab the // resize handle on a source page with the sidebar closed, because it // remains visible all the time on there. - function showSidebar() { + const showSidebar = function() { if (isSrcPage) { - // @ts-expect-error window.rustdocShowSourceSidebar(); } else { removeClass(document.documentElement, "hide-sidebar"); updateLocalStorage("hide-sidebar", "false"); } - } + }; /** * Call this to set the correct CSS variable and setting. @@ -1876,44 +1859,40 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm * * @param {number} size - CSS px width of the sidebar. */ - function changeSidebarSize(size) { + const changeSidebarSize = function(size) { if (isSrcPage) { - // @ts-expect-error - updateLocalStorage("src-sidebar-width", size); + updateLocalStorage("src-sidebar-width", size.toString()); // [RUSTDOCIMPL] CSS variable fast path // // While this property is set on the HTML element at load time, // because the sidebar isn't actually loaded yet, // we scope this update to the sidebar to avoid hitting a slow // path in WebKit. - // @ts-expect-error sidebar.style.setProperty("--src-sidebar-width", size + "px"); - // @ts-expect-error resizer.style.setProperty("--src-sidebar-width", size + "px"); } else { - // @ts-expect-error - updateLocalStorage("desktop-sidebar-width", size); - // @ts-expect-error + updateLocalStorage("desktop-sidebar-width", size.toString()); sidebar.style.setProperty("--desktop-sidebar-width", size + "px"); - // @ts-expect-error resizer.style.setProperty("--desktop-sidebar-width", size + "px"); } - } + }; // Check if the sidebar is hidden. Since src pages and doc pages have // different settings, this function has to check that. - function isSidebarHidden() { + const isSidebarHidden = function() { return isSrcPage ? !hasClass(document.documentElement, "src-sidebar-expanded") : hasClass(document.documentElement, "hide-sidebar"); - } + }; - // Respond to the resize handle event. - // This function enforces size constraints, and implements the - // shrink-to-nothing gesture based on thresholds defined above. - // @ts-expect-error - function resize(e) { - // @ts-expect-error + /** + * Respond to the resize handle event. + * This function enforces size constraints, and implements the + * shrink-to-nothing gesture based on thresholds defined above. + * + * @param {PointerEvent} e + */ + const resize = function(e) { if (currentPointerId === null || currentPointerId !== e.pointerId) { return; } @@ -1931,97 +1910,83 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm changeSidebarSize(constrainedPos); desiredSidebarSize = constrainedPos; if (pendingSidebarResizingFrame !== false) { - // @ts-expect-error clearTimeout(pendingSidebarResizingFrame); } - // @ts-expect-error pendingSidebarResizingFrame = setTimeout(() => { - // @ts-expect-error if (currentPointerId === null || pendingSidebarResizingFrame === false) { return; } pendingSidebarResizingFrame = false; document.documentElement.style.setProperty( "--resizing-sidebar-width", - // @ts-expect-error desiredSidebarSize + "px", ); }, 100); } - } + }; // Respond to the window resize event. window.addEventListener("resize", () => { if (window.innerWidth < RUSTDOC_MOBILE_BREAKPOINT) { return; } stopResize(); - // @ts-expect-error - if (desiredSidebarSize >= (window.innerWidth - BODY_MIN)) { + if (desiredSidebarSize !== null && desiredSidebarSize >= (window.innerWidth - BODY_MIN)) { changeSidebarSize(window.innerWidth - BODY_MIN); - // @ts-expect-error } else if (desiredSidebarSize !== null && desiredSidebarSize > SIDEBAR_MIN) { - // @ts-expect-error changeSidebarSize(desiredSidebarSize); } }); - // @ts-expect-error - function stopResize(e) { - // @ts-expect-error + + /** + * @param {PointerEvent=} e + */ + const stopResize = function(e) { if (currentPointerId === null) { return; } if (e) { e.preventDefault(); } - // @ts-expect-error desiredSidebarSize = sidebar.getBoundingClientRect().width; - // @ts-expect-error removeClass(resizer, "active"); window.removeEventListener("pointermove", resize, false); window.removeEventListener("pointerup", stopResize, false); removeClass(document.documentElement, "sidebar-resizing"); document.documentElement.style.removeProperty( "--resizing-sidebar-width"); - // @ts-expect-error if (resizer.releasePointerCapture) { - // @ts-expect-error resizer.releasePointerCapture(currentPointerId); currentPointerId = null; } - } - // @ts-expect-error - function initResize(e) { - // @ts-expect-error + }; + + /** + * @param {PointerEvent} e + */ + const initResize = function(e) { if (currentPointerId !== null || e.altKey || e.ctrlKey || e.metaKey || e.button !== 0) { return; } - // @ts-expect-error if (resizer.setPointerCapture) { - // @ts-expect-error resizer.setPointerCapture(e.pointerId); - // @ts-expect-error if (!resizer.hasPointerCapture(e.pointerId)) { // unable to capture pointer; something else has it // on iOS, this usually means you long-clicked a link instead - // @ts-expect-error resizer.releasePointerCapture(e.pointerId); return; } currentPointerId = e.pointerId; } - // @ts-expect-error window.hideAllModals(false); e.preventDefault(); window.addEventListener("pointermove", resize, false); window.addEventListener("pointercancel", stopResize, false); window.addEventListener("pointerup", stopResize, false); - // @ts-expect-error addClass(resizer, "active"); addClass(document.documentElement, "sidebar-resizing"); - // @ts-expect-error const pos = e.clientX - sidebar.offsetLeft - 3; document.documentElement.style.setProperty( "--resizing-sidebar-width", pos + "px"); desiredSidebarSize = null; - } + }; resizer.addEventListener("pointerdown", initResize, false); }()); @@ -2029,8 +1994,13 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // and the copy buttons on the code examples. (function() { // Common functions to copy buttons. - // @ts-expect-error + /** + * @param {string|null} content + */ function copyContentToClipboard(content) { + if (content === null) { + return; + } const el = document.createElement("textarea"); el.value = content; el.setAttribute("readonly", ""); @@ -2044,15 +2014,17 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm document.body.removeChild(el); } - // @ts-expect-error + /** + * @param {HTMLElement & {reset_button_timeout?: ReturnType}} button + */ function copyButtonAnimation(button) { button.classList.add("clicked"); if (button.reset_button_timeout !== undefined) { - window.clearTimeout(button.reset_button_timeout); + clearTimeout(button.reset_button_timeout); } - button.reset_button_timeout = window.setTimeout(() => { + button.reset_button_timeout = setTimeout(() => { button.reset_button_timeout = undefined; button.classList.remove("clicked"); }, 1000); @@ -2067,9 +2039,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // Most page titles are ' in - Rust', except // modules (which don't have the first part) and keywords/primitives // (which don't have a module path) - // @ts-expect-error - const title = document.querySelector("title").textContent.replace(" - Rust", ""); - const [item, module] = title.split(" in "); + const [item, module] = document.title.split(" in "); const path = [item]; if (module !== undefined) { path.unshift(module); @@ -2079,8 +2049,10 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm copyButtonAnimation(but); }; - // Copy buttons on code examples. - // @ts-expect-error + /** + * Copy buttons on code examples. + * @param {HTMLElement|null} codeElem + */ function copyCode(codeElem) { if (!codeElem) { // Should never happen, but the world is a dark and dangerous place. @@ -2089,23 +2061,34 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm copyContentToClipboard(codeElem.textContent); } - // @ts-expect-error + /** + * @param {UIEvent} event + * @returns {HTMLElement|null} + */ function getExampleWrap(event) { - let elem = event.target; - while (!hasClass(elem, "example-wrap")) { - if (elem === document.body || - elem.tagName === "A" || - elem.tagName === "BUTTON" || - hasClass(elem, "docblock") - ) { - return null; + const target = event.target; + if (target instanceof HTMLElement) { + /** @type {HTMLElement|null} */ + let elem = target; + while (elem !== null && !hasClass(elem, "example-wrap")) { + if (elem === document.body || + elem.tagName === "A" || + elem.tagName === "BUTTON" || + hasClass(elem, "docblock") + ) { + return null; + } + elem = elem.parentElement; } - elem = elem.parentElement; + return elem; + } else { + return null; } - return elem; } - // @ts-expect-error + /** + * @param {UIEvent} event + */ function addCopyButton(event) { const elem = getExampleWrap(event); if (elem === null) { @@ -2132,15 +2115,17 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm }); parent.appendChild(copyButton); - if (!elem.parentElement.classList.contains("scraped-example")) { + if (!elem.parentElement || !elem.parentElement.classList.contains("scraped-example") || + !window.updateScrapedExample) { return; } const scrapedWrapped = elem.parentElement; - // @ts-expect-error window.updateScrapedExample(scrapedWrapped, parent); } - // @ts-expect-error + /** + * @param {UIEvent} event + */ function showHideCodeExampleButtons(event) { const elem = getExampleWrap(event); if (elem === null) { @@ -2159,7 +2144,6 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm buttons.classList.toggle("keep-visible"); } - // @ts-expect-error onEachLazy(document.querySelectorAll(".docblock .example-wrap"), elem => { elem.addEventListener("mouseover", addCopyButton); elem.addEventListener("click", showHideCodeExampleButtons); diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts index 18a3e22113b8..acea7828e86a 100644 --- a/src/librustdoc/html/static/js/rustdoc.d.ts +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -20,6 +20,28 @@ declare global { * As a multi-page application, we know this never changes once set. */ currentCrate: string|null; + /** + * Hide popovers, tooltips, or the mobile sidebar. + */ + hideAllModals: function(boolean), + /** + * Hide popovers, but leave other modals alone. + */ + hidePopoverMenus: function(), + /** + * Hide the source page sidebar. If it's already closed, + * or if this is a docs page, this function does nothing. + */ + rustdocCloseSourceSidebar: function(), + /** + * Show the source page sidebar. If it's already opened, + * or if this is a docs page, this function does nothing. + */ + rustdocShowSourceSidebar: function(), + /** + * Set up event listeners for a scraped source example. + */ + updateScrapedExample?: function(HTMLElement, HTMLElement), } interface HTMLElement { /** Used by the popover tooltip code. */ diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 1ad32721e068..f25f37bab59c 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -4732,10 +4732,8 @@ function printTab(nb) { // Corrections only kick in on type-based searches. const correctionsElem = document.getElementsByClassName("search-corrections"); if (isTypeSearch) { - // @ts-expect-error removeClass(correctionsElem[0], "hidden"); } else { - // @ts-expect-error addClass(correctionsElem[0], "hidden"); } } else if (nb !== 0) { diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js index 10369e77320f..3042373fb096 100644 --- a/src/librustdoc/html/static/js/storage.js +++ b/src/librustdoc/html/static/js/storage.js @@ -59,7 +59,7 @@ function hasClass(elem, className) { * Add a class to a DOM Element. If `elem` is null, * does nothing. This function is idempotent. * - * @param {HTMLElement|null} elem + * @param {Element|null} elem * @param {string} className */ function addClass(elem, className) { @@ -72,7 +72,7 @@ function addClass(elem, className) { * Remove a class from a DOM Element. If `elem` is null, * does nothing. This function is idempotent. * - * @param {HTMLElement|null} elem + * @param {Element|null} elem * @param {string} className */ // eslint-disable-next-line no-unused-vars @@ -85,7 +85,7 @@ function removeClass(elem, className) { /** * Run a callback for every element of an Array. * @param {Array} arr - The array to iterate over - * @param {function(?): boolean|undefined} func - The callback + * @param {function(?): boolean|void} func - The callback */ function onEach(arr, func) { for (const elem of arr) { @@ -103,7 +103,7 @@ function onEach(arr, func) { * https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection * https://developer.mozilla.org/en-US/docs/Web/API/NodeList * @param {NodeList|HTMLCollection} lazyArray - An array to iterate over - * @param {function(?): boolean} func - The callback + * @param {function(?): boolean|void} func - The callback */ // eslint-disable-next-line no-unused-vars function onEachLazy(lazyArray, func) { @@ -119,11 +119,15 @@ function onEachLazy(lazyArray, func) { * If localStorage is disabled, this function does nothing. * * @param {string} name - * @param {string} value + * @param {string|null} value */ function updateLocalStorage(name, value) { try { - window.localStorage.setItem("rustdoc-" + name, value); + if (value === null) { + window.localStorage.removeItem("rustdoc-" + name); + } else { + window.localStorage.setItem("rustdoc-" + name, value); + } } catch (e) { // localStorage is not accessible, do nothing } From 6b016d7e592798610f6313891cbc3ba81906f894 Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Mon, 3 Feb 2025 19:18:07 +0000 Subject: [PATCH 32/57] Mark `std::fmt::from_fn` as `#[must_use]` --- library/core/src/fmt/builders.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs index 1862be0e86c5..665b05b12ec0 100644 --- a/library/core/src/fmt/builders.rs +++ b/library/core/src/fmt/builders.rs @@ -1228,6 +1228,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// assert_eq!(format!("{:?}", wrapped), "'a'"); /// ``` #[unstable(feature = "debug_closure_helpers", issue = "117729")] +#[must_use = "returns a type implementing Debug and Display, which do not have any effects unless they are used"] pub fn from_fn) -> fmt::Result>(f: F) -> FromFn { FromFn(f) } From bcb8565f301b579dee60fffe87d5a329cc69fefa Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Mon, 2 Dec 2024 20:35:13 +0000 Subject: [PATCH 33/57] Contracts core intrinsics. These are hooks to: 1. control whether contract checks are run 2. allow 3rd party tools to intercept and reintepret the results of running contracts. --- compiler/rustc_borrowck/src/type_check/mod.rs | 1 + compiler/rustc_codegen_cranelift/src/base.rs | 9 ++++++ compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 4 +++ .../src/check_consts/check.rs | 6 +++- .../rustc_const_eval/src/interpret/machine.rs | 10 ++++++ .../src/interpret/operator.rs | 1 + compiler/rustc_feature/src/builtin_attrs.rs | 1 + compiler/rustc_feature/src/unstable.rs | 2 ++ .../rustc_hir_analysis/src/check/intrinsic.rs | 20 ++++++++++++ compiler/rustc_middle/src/mir/pretty.rs | 1 + compiler/rustc_middle/src/mir/syntax.rs | 3 ++ compiler/rustc_middle/src/mir/tcx.rs | 3 +- .../src/move_paths/builder.rs | 6 +++- compiler/rustc_mir_transform/src/gvn.rs | 1 + .../src/known_panics_lint.rs | 1 + .../src/lower_intrinsics.rs | 11 +++++++ .../rustc_mir_transform/src/promote_consts.rs | 1 + compiler/rustc_mir_transform/src/validate.rs | 5 ++- compiler/rustc_session/src/config/cfg.rs | 7 ++++ compiler/rustc_session/src/options.rs | 2 ++ compiler/rustc_session/src/session.rs | 4 +++ .../rustc_smir/src/rustc_smir/convert/mir.rs | 1 + compiler/rustc_span/src/symbol.rs | 4 +++ compiler/stable_mir/src/mir/body.rs | 5 ++- library/core/src/intrinsics/mod.rs | 32 +++++++++++++++++++ .../clippy_utils/src/qualify_min_const_fn.rs | 2 +- src/tools/miri/src/machine.rs | 5 +++ tests/ui/contracts/contract-intrinsics.rs | 23 +++++++++++++ .../feature-gate-cfg-contract-checks.rs | 5 +++ .../feature-gate-cfg-contract-checks.stderr | 13 ++++++++ 30 files changed, 183 insertions(+), 6 deletions(-) create mode 100644 tests/ui/contracts/contract-intrinsics.rs create mode 100644 tests/ui/feature-gates/feature-gate-cfg-contract-checks.rs create mode 100644 tests/ui/feature-gates/feature-gate-cfg-contract-checks.stderr diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 92492bfdb8d3..b727616ebceb 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1650,6 +1650,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ConstraintCategory::SizedBound, ); } + &Rvalue::NullaryOp(NullOp::ContractChecks, _) => {} &Rvalue::NullaryOp(NullOp::UbChecks, _) => {} Rvalue::ShallowInitBox(operand, ty) => { diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 7a40d236b928..de2ce1768fa1 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -874,6 +874,15 @@ fn codegen_stmt<'tcx>( lval.write_cvalue(fx, val); return; } + NullOp::ContractChecks => { + let val = fx.tcx.sess.contract_checks(); + let val = CValue::by_val( + fx.bcx.ins().iconst(types::I8, i64::try_from(val).unwrap()), + fx.layout_of(fx.tcx.types.bool), + ); + lval.write_cvalue(fx, val); + return; + } }; let val = CValue::by_val( fx.bcx.ins().iconst(fx.pointer_type, i64::try_from(val).unwrap()), diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 85de3238b3e7..27cb7883b9a6 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -741,6 +741,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let val = bx.tcx().sess.ub_checks(); bx.cx().const_bool(val) } + mir::NullOp::ContractChecks => { + let val = bx.tcx().sess.contract_checks(); + bx.cx().const_bool(val) + } }; let tcx = self.cx.tcx(); OperandRef { diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index e8052a3c83a1..d0ce027ec2b7 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -675,7 +675,11 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { Rvalue::Cast(_, _, _) => {} Rvalue::NullaryOp( - NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbChecks, + NullOp::SizeOf + | NullOp::AlignOf + | NullOp::OffsetOf(_) + | NullOp::UbChecks + | NullOp::ContractChecks, _, ) => {} Rvalue::ShallowInitBox(_, _) => {} diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 8f6b15b8df01..1a799f5dea5b 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -293,6 +293,9 @@ pub trait Machine<'tcx>: Sized { /// Determines the result of a `NullaryOp::UbChecks` invocation. fn ub_checks(_ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool>; + /// Determines the result of a `NullaryOp::ContractChecks` invocation. + fn contract_checks(_ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool>; + /// Called when the interpreter encounters a `StatementKind::ConstEvalCounter` instruction. /// You can use this to detect long or endlessly running programs. #[inline] @@ -679,6 +682,13 @@ pub macro compile_time_machine(<$tcx: lifetime>) { interp_ok(true) } + #[inline(always)] + fn contract_checks(_ecx: &InterpCx<$tcx, Self>) -> InterpResult<$tcx, bool> { + // We can't look at `tcx.sess` here as that can differ across crates, which can lead to + // unsound differences in evaluating the same constant at different instantiation sites. + interp_ok(true) + } + #[inline(always)] fn adjust_global_allocation<'b>( _ecx: &InterpCx<$tcx, Self>, diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index 5fa632fc57aa..899670aeb62d 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -537,6 +537,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ImmTy::from_uint(val, usize_layout()) } UbChecks => ImmTy::from_bool(M::ub_checks(self)?, *self.tcx), + ContractChecks => ImmTy::from_bool(M::contract_checks(self)?, *self.tcx), }) } } diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index e0543977e98d..67eb96e4d56a 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -19,6 +19,7 @@ const GATED_CFGS: &[GatedCfg] = &[ // (name in cfg, feature, function to check if the feature is enabled) (sym::overflow_checks, sym::cfg_overflow_checks, Features::cfg_overflow_checks), (sym::ub_checks, sym::cfg_ub_checks, Features::cfg_ub_checks), + (sym::contract_checks, sym::cfg_contract_checks, Features::cfg_contract_checks), (sym::target_thread_local, sym::cfg_target_thread_local, Features::cfg_target_thread_local), ( sym::target_has_atomic_equal_alignment, diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 1a216ebf117c..08a5e22db3a5 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -403,6 +403,8 @@ declare_features! ( (unstable, c_variadic, "1.34.0", Some(44930)), /// Allows the use of `#[cfg()]`. (unstable, cfg_boolean_literals, "1.83.0", Some(131204)), + /// Allows the use of `#[cfg(contract_checks)` to check if contract checks are enabled. + (unstable, cfg_contract_checks, "CURRENT_RUSTC_VERSION", Some(133866)), /// Allows the use of `#[cfg(overflow_checks)` to check if integer overflow behaviour. (unstable, cfg_overflow_checks, "1.71.0", Some(111466)), /// Provides the relocation model information as cfg entry diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index cf3d48973042..6c0cebccefd9 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -132,6 +132,9 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) - | sym::aggregate_raw_ptr | sym::ptr_metadata | sym::ub_checks + | sym::contract_checks + | sym::contract_check_requires + | sym::contract_check_ensures | sym::fadd_algebraic | sym::fsub_algebraic | sym::fmul_algebraic @@ -219,6 +222,18 @@ pub fn check_intrinsic_type( } }; (n_tps, 0, 0, inputs, output, hir::Safety::Unsafe) + } else if intrinsic_name == sym::contract_check_ensures { + // contract_check_ensures::<'a, Ret, C>(&'a Ret, C) -> bool + // where C: impl Fn(&'a Ret) -> bool, + // + // so: two type params, one lifetime param, 0 const params, two inputs, returns boolean + + let p = generics.param_at(0, tcx); + let r = ty::Region::new_early_param(tcx, p.to_early_bound_region_data()); + let ref_ret = Ty::new_imm_ref(tcx, r, param(1)); + // let br = ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BrAnon }; + // let ref_ret = Ty::new_imm_ref(tcx, ty::Region::new_bound(tcx, ty::INNERMOST, br), param(0)); + (2, 1, 0, vec![ref_ret, param(2)], tcx.types.bool, hir::Safety::Safe) } else { let safety = intrinsic_operation_unsafety(tcx, intrinsic_id); let (n_tps, n_cts, inputs, output) = match intrinsic_name { @@ -610,6 +625,11 @@ pub fn check_intrinsic_type( sym::box_new => (1, 0, vec![param(0)], Ty::new_box(tcx, param(0))), + // contract_checks() -> bool + sym::contract_checks => (0, 0, Vec::new(), tcx.types.bool), + // contract_check_requires::(C) -> bool, where C: impl Fn() -> bool + sym::contract_check_requires => (1, 0, vec![param(0)], tcx.types.bool), + sym::simd_eq | sym::simd_ne | sym::simd_lt diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 09d7e60e1994..f53b4f3def25 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1103,6 +1103,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { NullOp::AlignOf => write!(fmt, "AlignOf({t})"), NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({t}, {fields:?})"), NullOp::UbChecks => write!(fmt, "UbChecks()"), + NullOp::ContractChecks => write!(fmt, "ContractChecks()"), } } ThreadLocalRef(did) => ty::tls::with(|tcx| { diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 2da25f480c65..9cec8d832dd1 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1591,6 +1591,9 @@ pub enum NullOp<'tcx> { /// Returns whether we should perform some UB-checking at runtime. /// See the `ub_checks` intrinsic docs for details. UbChecks, + /// Returns whether we should perform contract-checking at runtime. + /// See the `contract_checks` intrinsic docs for details. + ContractChecks, } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index 49449426fa40..af23c8b2ea76 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -230,7 +230,8 @@ impl<'tcx> Rvalue<'tcx> { Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => { tcx.types.usize } - Rvalue::NullaryOp(NullOp::UbChecks, _) => tcx.types.bool, + Rvalue::NullaryOp(NullOp::ContractChecks, _) + | Rvalue::NullaryOp(NullOp::UbChecks, _) => tcx.types.bool, Rvalue::Aggregate(ref ak, ref ops) => match **ak { AggregateKind::Array(ty) => Ty::new_array(tcx, ty, ops.len() as u64), AggregateKind::Tuple => { diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 6e00e427a46c..b6c259aa4e0a 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -417,7 +417,11 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { | Rvalue::Discriminant(..) | Rvalue::Len(..) | Rvalue::NullaryOp( - NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..) | NullOp::UbChecks, + NullOp::SizeOf + | NullOp::AlignOf + | NullOp::OffsetOf(..) + | NullOp::UbChecks + | NullOp::ContractChecks, _, ) => {} } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index c261e25100d3..d2ffd26f0a06 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -545,6 +545,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { .offset_of_subfield(self.typing_env(), layout, fields.iter()) .bytes(), NullOp::UbChecks => return None, + NullOp::ContractChecks => return None, }; let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap(); let imm = ImmTy::from_uint(val, usize_layout); diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index 2864cc0b9fe0..e43254ba089e 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -629,6 +629,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { .offset_of_subfield(self.typing_env, op_layout, fields.iter()) .bytes(), NullOp::UbChecks => return None, + NullOp::ContractChecks => return None, }; ImmTy::from_scalar(Scalar::from_target_usize(val, self), layout).into() } diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index 9a9f66ed4fd7..9c21bcfc0d26 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -34,6 +34,17 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { }); terminator.kind = TerminatorKind::Goto { target }; } + sym::contract_checks => { + let target = target.unwrap(); + block.statements.push(Statement { + source_info: terminator.source_info, + kind: StatementKind::Assign(Box::new(( + *destination, + Rvalue::NullaryOp(NullOp::ContractChecks, tcx.types.bool), + ))), + }); + terminator.kind = TerminatorKind::Goto { target }; + } sym::forget => { let target = target.unwrap(); block.statements.push(Statement { diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 9101c9bfc9ae..4dbbcae1756b 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -457,6 +457,7 @@ impl<'tcx> Validator<'_, 'tcx> { NullOp::AlignOf => {} NullOp::OffsetOf(_) => {} NullOp::UbChecks => {} + NullOp::ContractChecks => {} }, Rvalue::ShallowInitBox(_, _) => return Err(Unpromotable), diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index e282eaf761c1..b7a3770fc6b1 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1379,7 +1379,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { Rvalue::Repeat(_, _) | Rvalue::ThreadLocalRef(_) | Rvalue::RawPtr(_, _) - | Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::UbChecks, _) + | Rvalue::NullaryOp( + NullOp::SizeOf | NullOp::AlignOf | NullOp::UbChecks | NullOp::ContractChecks, + _, + ) | Rvalue::Discriminant(_) => {} Rvalue::WrapUnsafeBinder(op, ty) => { diff --git a/compiler/rustc_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs index d586f913335e..52920e0372e2 100644 --- a/compiler/rustc_session/src/config/cfg.rs +++ b/compiler/rustc_session/src/config/cfg.rs @@ -119,6 +119,7 @@ pub(crate) fn disallow_cfgs(sess: &Session, user_cfgs: &Cfg) { (sym::overflow_checks, None) => disallow(cfg, "-C overflow-checks"), (sym::debug_assertions, None) => disallow(cfg, "-C debug-assertions"), (sym::ub_checks, None) => disallow(cfg, "-Z ub-checks"), + (sym::contract_checks, None) => disallow(cfg, "-Z contract-checks"), (sym::sanitize, None | Some(_)) => disallow(cfg, "-Z sanitizer"), ( sym::sanitizer_cfi_generalize_pointers | sym::sanitizer_cfi_normalize_integers, @@ -300,6 +301,11 @@ pub(crate) fn default_configuration(sess: &Session) -> Cfg { if sess.is_nightly_build() && sess.opts.unstable_opts.emscripten_wasm_eh { ins_none!(sym::emscripten_wasm_eh); } + + if sess.contract_checks() { + ins_none!(sym::contract_checks); + } + ret } @@ -464,6 +470,7 @@ impl CheckCfg { ins!(sym::target_thread_local, no_values); ins!(sym::ub_checks, no_values); + ins!(sym::contract_checks, no_values); ins!(sym::unix, no_values); ins!(sym::windows, no_values); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 4ed5499d32c6..35819f896c5b 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2114,6 +2114,8 @@ options! { "the backend to use"), combine_cgu: bool = (false, parse_bool, [TRACKED], "combine CGUs into a single one"), + contract_checks: Option = (None, parse_opt_bool, [TRACKED], + "emit runtime checks for contract pre- and post-conditions (default: no)"), coverage_options: CoverageOptions = (CoverageOptions::default(), parse_coverage_options, [TRACKED], "control details of coverage instrumentation"), crate_attr: Vec = (Vec::new(), parse_string_push, [TRACKED], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 2b79081a26e7..c9bb42bdfa1e 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -709,6 +709,10 @@ impl Session { self.opts.unstable_opts.ub_checks.unwrap_or(self.opts.debug_assertions) } + pub fn contract_checks(&self) -> bool { + self.opts.unstable_opts.contract_checks.unwrap_or(false) + } + pub fn relocation_model(&self) -> RelocModel { self.opts.cg.relocation_model.unwrap_or(self.target.relocation_model) } diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index 4a0420cc6031..bdd6e16a7c17 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -291,6 +291,7 @@ impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> { indices.iter().map(|idx| idx.stable(tables)).collect(), ), UbChecks => stable_mir::mir::NullOp::UbChecks, + ContractChecks => stable_mir::mir::NullOp::ContractChecks, } } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index b23cc9099115..3138efdfd9b0 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -566,6 +566,7 @@ symbols! { cfg_attr, cfg_attr_multi, cfg_boolean_literals, + cfg_contract_checks, cfg_doctest, cfg_emscripten_wasm_eh, cfg_eval, @@ -675,6 +676,9 @@ symbols! { const_ty_placeholder: "", constant, constructor, + contract_check_ensures, + contract_check_requires, + contract_checks, convert_identity, copy, copy_closures, diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index eec6cd8d49ba..a6406e9db8e3 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -608,7 +608,8 @@ impl Rvalue { Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => { Ok(Ty::usize_ty()) } - Rvalue::NullaryOp(NullOp::UbChecks, _) => Ok(Ty::bool_ty()), + Rvalue::NullaryOp(NullOp::ContractChecks, _) + | Rvalue::NullaryOp(NullOp::UbChecks, _) => Ok(Ty::bool_ty()), Rvalue::Aggregate(ak, ops) => match *ak { AggregateKind::Array(ty) => Ty::try_new_array(ty, ops.len() as u64), AggregateKind::Tuple => Ok(Ty::new_tuple( @@ -1007,6 +1008,8 @@ pub enum NullOp { OffsetOf(Vec<(VariantIdx, FieldIdx)>), /// cfg!(ub_checks), but at codegen time UbChecks, + /// cfg!(contract_checks), but at codegen time + ContractChecks, } impl Operand { diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index bf07632d9928..beea0996775a 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -4044,6 +4044,38 @@ pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) // Runtime NOP } +/// Returns whether we should perform contract-checking at runtime. +/// +/// This is meant to be similar to the ub_checks intrinsic, in terms +/// of not prematurely commiting at compile-time to whether contract +/// checking is turned on, so that we can specify contracts in libstd +/// and let an end user opt into turning them on. +#[cfg(not(bootstrap))] +#[rustc_const_unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)] +#[unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)] +#[inline(always)] +#[rustc_intrinsic] +pub const fn contract_checks() -> bool { + // FIXME: should this be `false` or `cfg!(contract_checks)`? + + // cfg!(contract_checks) + false +} + +#[cfg(not(bootstrap))] +#[unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)] +#[rustc_intrinsic] +pub fn contract_check_requires bool>(c: C) -> bool { + c() +} + +#[cfg(not(bootstrap))] +#[unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)] +#[rustc_intrinsic] +pub fn contract_check_ensures<'a, Ret, C: FnOnce(&'a Ret) -> bool>(ret: &'a Ret, c: C) -> bool { + c(ret) +} + /// The intrinsic will return the size stored in that vtable. /// /// # Safety diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 0aaef91e48a6..5a3a3d0cedc4 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -179,7 +179,7 @@ fn check_rvalue<'tcx>( )) } }, - Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbChecks, _) + Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbChecks | NullOp::ContractChecks, _) | Rvalue::ShallowInitBox(_, _) => Ok(()), Rvalue::UnaryOp(_, operand) => { let ty = operand.ty(body, tcx); diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 4735db48e81f..6bd1076a8a84 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -1150,6 +1150,11 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { interp_ok(ecx.tcx.sess.ub_checks()) } + #[inline(always)] + fn contract_checks(ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool> { + interp_ok(ecx.tcx.sess.contract_checks()) + } + #[inline(always)] fn thread_local_static_pointer( ecx: &mut MiriInterpCx<'tcx>, diff --git a/tests/ui/contracts/contract-intrinsics.rs b/tests/ui/contracts/contract-intrinsics.rs new file mode 100644 index 000000000000..6e3565baf7ac --- /dev/null +++ b/tests/ui/contracts/contract-intrinsics.rs @@ -0,0 +1,23 @@ +//@ run-pass +//@ revisions: yes no none +//@ [yes] compile-flags: -Zcontract-checks=yes +//@ [no] compile-flags: -Zcontract-checks=no +#![feature(cfg_contract_checks, rustc_contracts, core_intrinsics)] + +fn main() { + #[cfg(none)] // default: disabled + assert_eq!(core::intrinsics::contract_checks(), false); + + #[cfg(yes)] // explicitly enabled + assert_eq!(core::intrinsics::contract_checks(), true); + + #[cfg(no)] // explicitly disabled + assert_eq!(core::intrinsics::contract_checks(), false); + + assert_eq!(core::intrinsics::contract_check_requires(|| true), true); + assert_eq!(core::intrinsics::contract_check_requires(|| false), false); + + let doubles_to_two = { let old = 2; move |ret| ret + ret == old }; + assert_eq!(core::intrinsics::contract_check_ensures(&1, doubles_to_two), true); + assert_eq!(core::intrinsics::contract_check_ensures(&2, doubles_to_two), false); +} diff --git a/tests/ui/feature-gates/feature-gate-cfg-contract-checks.rs b/tests/ui/feature-gates/feature-gate-cfg-contract-checks.rs new file mode 100644 index 000000000000..cd9bf12b5e77 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-cfg-contract-checks.rs @@ -0,0 +1,5 @@ +#![crate_type = "lib"] + +pub fn contract_checks_are_enabled() -> bool { + cfg!(contract_checks) //~ ERROR `cfg(contract_checks)` is experimental +} diff --git a/tests/ui/feature-gates/feature-gate-cfg-contract-checks.stderr b/tests/ui/feature-gates/feature-gate-cfg-contract-checks.stderr new file mode 100644 index 000000000000..af4e605e5709 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-cfg-contract-checks.stderr @@ -0,0 +1,13 @@ +error[E0658]: `cfg(contract_checks)` is experimental and subject to change + --> $DIR/feature-gate-cfg-contract-checks.rs:4:10 + | +LL | cfg!(contract_checks) + | ^^^^^^^^^^^^^^^ + | + = note: see issue #133866 for more information + = help: add `#![feature(cfg_contract_checks)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. From 777def87d58ee067e1df2e94a99fc099bcb15189 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Mon, 2 Dec 2024 21:16:35 +0000 Subject: [PATCH 34/57] contracts: added lang items that act as hooks for rustc-injected code to invoke. see test for an example of the kind of injected code that is anticipated here. --- compiler/rustc_hir/src/lang_items.rs | 4 ++ compiler/rustc_span/src/symbol.rs | 1 + library/core/src/contracts.rs | 33 ++++++++++++++++ library/core/src/lib.rs | 5 +++ tests/ui/contracts/contract-lang-items.rs | 47 +++++++++++++++++++++++ 5 files changed, 90 insertions(+) create mode 100644 library/core/src/contracts.rs create mode 100644 tests/ui/contracts/contract-lang-items.rs diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index d9759580e8fd..75898cbec14b 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -418,6 +418,10 @@ language_item_table! { String, sym::String, string, Target::Struct, GenericRequirement::None; CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None; + + // Experimental lang items for implementing contract pre- and post-condition checking. + ContractBuildCheckEnsures, sym::contract_build_check_ensures, contract_build_check_ensures_fn, Target::Fn, GenericRequirement::None; + ContractCheckRequires, sym::contract_check_requires, contract_check_requires_fn, Target::Fn, GenericRequirement::None; } pub enum GenericRequirement { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 3138efdfd9b0..df424b3312a0 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -676,6 +676,7 @@ symbols! { const_ty_placeholder: "", constant, constructor, + contract_build_check_ensures, contract_check_ensures, contract_check_requires, contract_checks, diff --git a/library/core/src/contracts.rs b/library/core/src/contracts.rs new file mode 100644 index 000000000000..c13565d59d58 --- /dev/null +++ b/library/core/src/contracts.rs @@ -0,0 +1,33 @@ +//! Unstable module containing the unstable contracts lang items and attribute macros. + +/// Emitted by rustc as a desugaring of `#[requires(PRED)] fn foo(x: X) { ... }` +/// into: `fn foo(x: X) { check_requires(|| PRED) ... }` +#[cfg(not(bootstrap))] +#[unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)] +#[lang = "contract_check_requires"] +#[track_caller] +pub fn check_requires bool>(c: C) { + if core::intrinsics::contract_checks() { + assert!(core::intrinsics::contract_check_requires(c), "failed requires check"); + } +} + +/// Emitted by rustc as a desugaring of `#[ensures(PRED)] fn foo() -> R { ... [return R;] ... }` +/// into: `fn foo() { let _check = build_check_ensures(|ret| PRED) ... [return _check(R);] ... }` +/// (including the implicit return of the tail expression, if any). +#[cfg(not(bootstrap))] +#[unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)] +#[lang = "contract_build_check_ensures"] +#[track_caller] +pub fn build_check_ensures(c: C) -> impl (FnOnce(Ret) -> Ret) + Copy +where + C: for<'a> FnOnce(&'a Ret) -> bool + Copy + 'static, +{ + #[track_caller] + move |ret| { + if core::intrinsics::contract_checks() { + assert!(core::intrinsics::contract_check_ensures(&ret, c), "failed ensures check"); + } + ret + } +} diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index c18e0405f729..fd283fe92fa3 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -114,6 +114,7 @@ #![feature(bstr)] #![feature(bstr_internals)] #![feature(const_carrying_mul_add)] +#![feature(closure_track_caller)] #![feature(const_eval_select)] #![feature(core_intrinsics)] #![feature(coverage_attribute)] @@ -246,6 +247,10 @@ pub mod autodiff { pub use crate::macros::builtin::autodiff; } +#[cfg(not(bootstrap))] +#[unstable(feature = "rustc_contracts", issue = "none")] +pub mod contracts; + #[unstable(feature = "cfg_match", issue = "115585")] pub use crate::macros::cfg_match; diff --git a/tests/ui/contracts/contract-lang-items.rs b/tests/ui/contracts/contract-lang-items.rs new file mode 100644 index 000000000000..1dbf71722fd7 --- /dev/null +++ b/tests/ui/contracts/contract-lang-items.rs @@ -0,0 +1,47 @@ +//@ revisions: unchk_pass unchk_fail_pre unchk_fail_post chk_pass chk_fail_pre chk_fail_post +// +//@ [unchk_pass] run-pass +//@ [unchk_fail_pre] run-pass +//@ [unchk_fail_post] run-pass +//@ [chk_pass] run-pass +// +//@ [chk_fail_pre] run-fail +//@ [chk_fail_post] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_pre] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_post] compile-flags: -Zcontract-checks=no +// +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes + +#![feature(rustc_contracts)] + +fn foo(x: Baz) -> i32 { + core::contracts::check_requires(|| x.baz > 0); + + let injected_checker = { + core::contracts::build_check_ensures(|ret| *ret > 100) + }; + + let ret = x.baz + 50; + injected_checker(ret) +} + +struct Baz { baz: i32 } + + +const BAZ_PASS_PRE_POST: Baz = Baz { baz: 100 }; +#[cfg(any(unchk_fail_post, chk_fail_post))] +const BAZ_FAIL_POST: Baz = Baz { baz: 10 }; +#[cfg(any(unchk_fail_pre, chk_fail_pre))] +const BAZ_FAIL_PRE: Baz = Baz { baz: -10 }; + +fn main() { + assert_eq!(foo(BAZ_PASS_PRE_POST), 150); + #[cfg(any(unchk_fail_pre, chk_fail_pre))] + foo(BAZ_FAIL_PRE); + #[cfg(any(unchk_fail_post, chk_fail_post))] + foo(BAZ_FAIL_POST); +} From 38eff16d0aa029706a0b5845961f9b5ccddfd999 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Wed, 8 Jan 2025 16:38:25 -0800 Subject: [PATCH 35/57] Express contracts as part of function header and lower it to the contract lang items includes post-developed commit: do not suggest internal-only keywords as corrections to parse failures. includes post-developed commit: removed tabs that creeped in into rustfmt tool source code. includes post-developed commit, placating rustfmt self dogfooding. includes post-developed commit: add backquotes to prevent markdown checking from trying to treat an attr as a markdown hyperlink/ includes post-developed commit: fix lowering to keep contracts from being erroneously inherited by nested bodies (like closures). Rebase Conflicts: - compiler/rustc_parse/src/parser/diagnostics.rs - compiler/rustc_parse/src/parser/item.rs - compiler/rustc_span/src/hygiene.rs Remove contracts keywords from diagnostic messages --- compiler/rustc_ast/src/ast.rs | 9 +- compiler/rustc_ast/src/mut_visit.rs | 19 ++- compiler/rustc_ast/src/visit.rs | 17 ++- compiler/rustc_ast_lowering/src/expr.rs | 15 ++- compiler/rustc_ast_lowering/src/item.rs | 112 +++++++++++++++++- compiler/rustc_ast_lowering/src/lib.rs | 20 ++++ .../rustc_ast_passes/src/ast_validation.rs | 2 +- .../rustc_ast_pretty/src/pprust/state/item.rs | 21 +++- .../src/alloc_error_handler.rs | 1 + .../src/deriving/generic/mod.rs | 1 + .../src/global_allocator.rs | 1 + .../rustc_builtin_macros/src/test_harness.rs | 1 + compiler/rustc_hir/src/hir.rs | 2 + compiler/rustc_hir_typeck/src/expr.rs | 2 + compiler/rustc_parse/src/parser/generics.rs | 22 ++++ compiler/rustc_parse/src/parser/item.rs | 13 +- compiler/rustc_parse/src/parser/token_type.rs | 8 ++ compiler/rustc_resolve/src/def_collector.rs | 5 +- compiler/rustc_resolve/src/late.rs | 6 +- compiler/rustc_span/src/hygiene.rs | 3 + compiler/rustc_span/src/symbol.rs | 2 + library/core/src/lib.rs | 2 +- .../clippy/clippy_utils/src/ast_utils/mod.rs | 20 ++++ .../contracts/contract-ast-extensions-nest.rs | 44 +++++++ .../contracts/contract-ast-extensions-tail.rs | 42 +++++++ ...g-ensures-is-not-inherited-when-nesting.rs | 15 +++ ...-requires-is-not-inherited-when-nesting.rs | 17 +++ 27 files changed, 405 insertions(+), 17 deletions(-) create mode 100644 tests/ui/contracts/contract-ast-extensions-nest.rs create mode 100644 tests/ui/contracts/contract-ast-extensions-tail.rs create mode 100644 tests/ui/contracts/contracts-lowering-ensures-is-not-inherited-when-nesting.rs create mode 100644 tests/ui/contracts/contracts-lowering-requires-is-not-inherited-when-nesting.rs diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index b6f331d316cc..08621c1c56a1 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3348,11 +3348,18 @@ pub struct Impl { pub items: ThinVec>, } +#[derive(Clone, Encodable, Decodable, Debug, Default)] +pub struct FnContract { + pub requires: Option>, + pub ensures: Option>, +} + #[derive(Clone, Encodable, Decodable, Debug)] pub struct Fn { pub defaultness: Defaultness, pub generics: Generics, pub sig: FnSig, + pub contract: Option>, pub body: Option>, } @@ -3650,7 +3657,7 @@ mod size_asserts { static_assert_size!(Block, 32); static_assert_size!(Expr, 72); static_assert_size!(ExprKind, 40); - static_assert_size!(Fn, 160); + static_assert_size!(Fn, 168); static_assert_size!(ForeignItem, 88); static_assert_size!(ForeignItemKind, 16); static_assert_size!(GenericArg, 24); diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 7caf7c4c3568..70616fe87691 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -143,6 +143,10 @@ pub trait MutVisitor: Sized { walk_flat_map_assoc_item(self, i, ctxt) } + fn visit_contract(&mut self, c: &mut P) { + walk_contract(self, c); + } + fn visit_fn_decl(&mut self, d: &mut P) { walk_fn_decl(self, d); } @@ -958,13 +962,16 @@ fn walk_fn(vis: &mut T, kind: FnKind<'_>) { _ctxt, _ident, _vis, - Fn { defaultness, generics, body, sig: FnSig { header, decl, span } }, + Fn { defaultness, generics, contract, body, sig: FnSig { header, decl, span } }, ) => { // Identifier and visibility are visited as a part of the item. visit_defaultness(vis, defaultness); vis.visit_fn_header(header); vis.visit_generics(generics); vis.visit_fn_decl(decl); + if let Some(contract) = contract { + vis.visit_contract(contract); + } if let Some(body) = body { vis.visit_block(body); } @@ -979,6 +986,16 @@ fn walk_fn(vis: &mut T, kind: FnKind<'_>) { } } +fn walk_contract(vis: &mut T, contract: &mut P) { + let FnContract { requires, ensures } = contract.deref_mut(); + if let Some(pred) = requires { + vis.visit_expr(pred); + } + if let Some(pred) = ensures { + vis.visit_expr(pred); + } +} + fn walk_fn_decl(vis: &mut T, decl: &mut P) { let FnDecl { inputs, output } = decl.deref_mut(); inputs.flat_map_in_place(|param| vis.flat_map_param(param)); diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 232fd546de9a..714b074f930c 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -188,6 +188,9 @@ pub trait Visitor<'ast>: Sized { fn visit_closure_binder(&mut self, b: &'ast ClosureBinder) -> Self::Result { walk_closure_binder(self, b) } + fn visit_contract(&mut self, c: &'ast FnContract) -> Self::Result { + walk_contract(self, c) + } fn visit_where_predicate(&mut self, p: &'ast WherePredicate) -> Self::Result { walk_where_predicate(self, p) } @@ -800,6 +803,17 @@ pub fn walk_closure_binder<'a, V: Visitor<'a>>( V::Result::output() } +pub fn walk_contract<'a, V: Visitor<'a>>(visitor: &mut V, c: &'a FnContract) -> V::Result { + let FnContract { requires, ensures } = c; + if let Some(pred) = requires { + visitor.visit_expr(pred); + } + if let Some(pred) = ensures { + visitor.visit_expr(pred); + } + V::Result::output() +} + pub fn walk_where_predicate<'a, V: Visitor<'a>>( visitor: &mut V, predicate: &'a WherePredicate, @@ -862,12 +876,13 @@ pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>) -> V::Resu _ctxt, _ident, _vis, - Fn { defaultness: _, sig: FnSig { header, decl, span: _ }, generics, body }, + Fn { defaultness: _, sig: FnSig { header, decl, span: _ }, generics, contract, body }, ) => { // Identifier and visibility are visited as a part of the item. try_visit!(visitor.visit_fn_header(header)); try_visit!(visitor.visit_generics(generics)); try_visit!(visitor.visit_fn_decl(decl)); + visit_opt!(visitor, visit_contract, contract); visit_opt!(visitor, visit_block, body); } FnKind::Closure(binder, coroutine_kind, decl, body) => { diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 1267281f73eb..19433047595a 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -314,7 +314,20 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ExprKind::Continue(self.lower_jump_destination(e.id, *opt_label)) } ExprKind::Ret(e) => { - let e = e.as_ref().map(|x| self.lower_expr(x)); + let mut e = e.as_ref().map(|x| self.lower_expr(x)); + if let Some(Some((span, fresh_ident))) = self + .contract + .as_ref() + .map(|c| c.ensures.as_ref().map(|e| (e.expr.span, e.fresh_ident))) + { + let checker_fn = self.expr_ident(span, fresh_ident.0, fresh_ident.2); + let args = if let Some(e) = e { + std::slice::from_ref(e) + } else { + std::slice::from_ref(self.expr_unit(span)) + }; + e = Some(self.expr_call(span, checker_fn, args)); + } hir::ExprKind::Ret(e) } ExprKind::Yeet(sub_expr) => self.lower_expr_yeet(e.span, sub_expr.as_deref()), diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 74870d741504..4679ccdddbbe 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -207,9 +207,42 @@ impl<'hir> LoweringContext<'_, 'hir> { sig: FnSig { decl, header, span: fn_sig_span }, generics, body, + contract, .. }) => { self.with_new_scopes(*fn_sig_span, |this| { + assert!(this.contract.is_none()); + if let Some(contract) = contract { + let requires = contract.requires.clone(); + let ensures = contract.ensures.clone(); + let ensures = if let Some(ens) = ensures { + // FIXME: this needs to be a fresh (or illegal) identifier to prevent + // accidental capture of a parameter or global variable. + let checker_ident: Ident = + Ident::from_str_and_span("__ensures_checker", ens.span); + let (checker_pat, checker_hir_id) = this.pat_ident_binding_mode_mut( + ens.span, + checker_ident, + hir::BindingMode::NONE, + ); + + Some(crate::FnContractLoweringEnsures { + expr: ens, + fresh_ident: (checker_ident, checker_pat, checker_hir_id), + }) + } else { + None + }; + + // Note: `with_new_scopes` will reinstall the outer + // item's contract (if any) after its callback finishes. + this.contract.replace(crate::FnContractLoweringInfo { + span, + requires, + ensures, + }); + } + // Note: we don't need to change the return type from `T` to // `impl Future` here because lower_body // only cares about the input argument patterns in the function @@ -1054,10 +1087,81 @@ impl<'hir> LoweringContext<'_, 'hir> { body: impl FnOnce(&mut Self) -> hir::Expr<'hir>, ) -> hir::BodyId { self.lower_body(|this| { - ( - this.arena.alloc_from_iter(decl.inputs.iter().map(|x| this.lower_param(x))), - body(this), - ) + let params = + this.arena.alloc_from_iter(decl.inputs.iter().map(|x| this.lower_param(x))); + let result = body(this); + + let opt_contract = this.contract.take(); + + // { body } + // ==> + // { rustc_contract_requires(PRECOND); { body } } + let result: hir::Expr<'hir> = if let Some(contract) = opt_contract { + let lit_unit = |this: &mut LoweringContext<'_, 'hir>| { + this.expr(contract.span, hir::ExprKind::Tup(&[])) + }; + + let precond: hir::Stmt<'hir> = if let Some(req) = contract.requires { + let lowered_req = this.lower_expr_mut(&req); + let precond = this.expr_call_lang_item_fn_mut( + req.span, + hir::LangItem::ContractCheckRequires, + &*arena_vec![this; lowered_req], + ); + this.stmt_expr(req.span, precond) + } else { + let u = lit_unit(this); + this.stmt_expr(contract.span, u) + }; + + let (postcond_checker, result) = if let Some(ens) = contract.ensures { + let crate::FnContractLoweringEnsures { expr: ens, fresh_ident } = ens; + let lowered_ens: hir::Expr<'hir> = this.lower_expr_mut(&ens); + let postcond_checker = this.expr_call_lang_item_fn( + ens.span, + hir::LangItem::ContractBuildCheckEnsures, + &*arena_vec![this; lowered_ens], + ); + let checker_binding_pat = fresh_ident.1; + ( + this.stmt_let_pat( + None, + ens.span, + Some(postcond_checker), + this.arena.alloc(checker_binding_pat), + hir::LocalSource::Contract, + ), + { + let checker_fn = + this.expr_ident(ens.span, fresh_ident.0, fresh_ident.2); + let span = this.mark_span_with_reason( + DesugaringKind::Contract, + ens.span, + None, + ); + this.expr_call_mut( + span, + checker_fn, + std::slice::from_ref(this.arena.alloc(result)), + ) + }, + ) + } else { + let u = lit_unit(this); + (this.stmt_expr(contract.span, u), result) + }; + + let block = this.block_all( + contract.span, + arena_vec![this; precond, postcond_checker], + Some(this.arena.alloc(result)), + ); + this.expr_block(block) + } else { + result + }; + + (params, result) }) } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 893da9308553..2715b3d62152 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -86,6 +86,19 @@ mod path; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } +#[derive(Debug, Clone)] +struct FnContractLoweringInfo<'hir> { + pub span: Span, + pub requires: Option>, + pub ensures: Option>, +} + +#[derive(Debug, Clone)] +struct FnContractLoweringEnsures<'hir> { + expr: ast::ptr::P, + fresh_ident: (Ident, hir::Pat<'hir>, HirId), +} + struct LoweringContext<'a, 'hir> { tcx: TyCtxt<'hir>, resolver: &'a mut ResolverAstLowering, @@ -100,6 +113,8 @@ struct LoweringContext<'a, 'hir> { /// Collect items that were created by lowering the current owner. children: Vec<(LocalDefId, hir::MaybeOwner<'hir>)>, + contract: Option>, + coroutine_kind: Option, /// When inside an `async` context, this is the `HirId` of the @@ -148,6 +163,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { bodies: Vec::new(), attrs: SortedMap::default(), children: Vec::default(), + contract: None, current_hir_id_owner: hir::CRATE_OWNER_ID, item_local_id_counter: hir::ItemLocalId::ZERO, ident_and_label_to_local_id: Default::default(), @@ -834,12 +850,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let was_in_loop_condition = self.is_in_loop_condition; self.is_in_loop_condition = false; + let old_contract = self.contract.take(); + let catch_scope = self.catch_scope.take(); let loop_scope = self.loop_scope.take(); let ret = f(self); self.catch_scope = catch_scope; self.loop_scope = loop_scope; + self.contract = old_contract; + self.is_in_loop_condition = was_in_loop_condition; self.current_item = current_item; diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index ea1f4a6559ac..0049c5b4823c 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -917,7 +917,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { walk_list!(self, visit_attribute, &item.attrs); return; // Avoid visiting again. } - ItemKind::Fn(func @ box Fn { defaultness, generics: _, sig, body }) => { + ItemKind::Fn(func @ box Fn { defaultness, generics: _, sig, contract: _, body }) => { self.check_defaultness(item.span, *defaultness); let is_intrinsic = diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 4cfcaa95233d..c10b5ad34e10 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -650,13 +650,17 @@ impl<'a> State<'a> { attrs: &[ast::Attribute], func: &ast::Fn, ) { - let ast::Fn { defaultness, generics, sig, body } = func; + let ast::Fn { defaultness, generics, sig, contract, body } = func; if body.is_some() { self.head(""); } self.print_visibility(vis); self.print_defaultness(*defaultness); self.print_fn(&sig.decl, sig.header, Some(name), generics); + if let Some(contract) = &contract { + self.nbsp(); + self.print_contract(contract); + } if let Some(body) = body { self.nbsp(); self.print_block_with_attrs(body, attrs); @@ -665,6 +669,21 @@ impl<'a> State<'a> { } } + fn print_contract(&mut self, contract: &ast::FnContract) { + if let Some(pred) = &contract.requires { + self.word("rustc_requires"); + self.popen(); + self.print_expr(pred, FixupContext::default()); + self.pclose(); + } + if let Some(pred) = &contract.ensures { + self.word("rustc_ensures"); + self.popen(); + self.print_expr(pred, FixupContext::default()); + self.pclose(); + } + } + pub(crate) fn print_fn( &mut self, decl: &ast::FnDecl, diff --git a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs index d2b4e1ca824f..cffc49786013 100644 --- a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs +++ b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs @@ -85,6 +85,7 @@ fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span defaultness: ast::Defaultness::Final, sig, generics: Generics::default(), + contract: None, body, })); diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 755a733286ce..0631c5a80fc5 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -1034,6 +1034,7 @@ impl<'a> MethodDef<'a> { defaultness, sig, generics: fn_generics, + contract: None, body: Some(body_block), })), tokens: None, diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index 8388e9dcafb8..8fdbbf8e704a 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -81,6 +81,7 @@ impl AllocFnFactory<'_, '_> { defaultness: ast::Defaultness::Final, sig, generics: Generics::default(), + contract: None, body, })); let item = self.cx.item( diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 31b068bd33da..472e16e62d5b 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -344,6 +344,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P { defaultness, sig, generics: ast::Generics::default(), + contract: None, body: Some(main_body), })); diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index af2f86b67e00..8bc09f631cf1 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2598,6 +2598,8 @@ pub enum LocalSource { /// A desugared `expr = expr`, where the LHS is a tuple, struct, array or underscore expression. /// The span is that of the `=` sign. AssignDesugar(Span), + /// A contract `#[ensures(..)]` attribute injects a let binding for the check that runs at point of return. + Contract, } /// Hints at the original code for a `match _ { .. }`. diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index a519e177fbcc..f7bc21cc5260 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -269,6 +269,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // diverging expression (e.g. it arose from desugaring of `try { return }`), // we skip issuing a warning because it is autogenerated code. ExprKind::Call(..) if expr.span.is_desugaring(DesugaringKind::TryBlock) => {} + // Likewise, do not lint unreachable code injected via contracts desugaring. + ExprKind::Call(..) if expr.span.is_desugaring(DesugaringKind::Contract) => {} ExprKind::Call(callee, _) => self.warn_if_unreachable(expr.hir_id, callee.span, "call"), ExprKind::MethodCall(segment, ..) => { self.warn_if_unreachable(expr.hir_id, segment.ident.span, "call") diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index b1b84b0b7011..d5ac8d1588d7 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -297,6 +297,28 @@ impl<'a> Parser<'a> { }) } + /// Parses a rustc-internal fn contract + /// (`rustc_contract_requires(WWW) rustc_contract_ensures(ZZZ)`) + pub(super) fn parse_contract( + &mut self, + ) -> PResult<'a, Option>> { + let requires = if self.eat_keyword_noexpect(exp!(RustcContractRequires).kw) { + Some(self.parse_expr()?) + } else { + None + }; + let ensures = if self.eat_keyword_noexpect(exp!(RustcContractEnsures).kw) { + Some(self.parse_expr()?) + } else { + None + }; + if requires.is_none() && ensures.is_none() { + Ok(None) + } else { + Ok(Some(rustc_ast::ptr::P(ast::FnContract { requires, ensures }))) + } + } + /// Parses an optional where-clause. /// /// ```ignore (only-for-syntax-highlight) diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index f3e56be9f6e8..dbdc31f06a29 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -213,9 +213,12 @@ impl<'a> Parser<'a> { self.parse_use_item()? } else if self.check_fn_front_matter(check_pub, case) { // FUNCTION ITEM - let (ident, sig, generics, body) = + let (ident, sig, generics, contract, body) = self.parse_fn(attrs, fn_parse_mode, lo, vis, case)?; - (ident, ItemKind::Fn(Box::new(Fn { defaultness: def_(), sig, generics, body }))) + ( + ident, + ItemKind::Fn(Box::new(Fn { defaultness: def_(), sig, generics, contract, body })), + ) } else if self.eat_keyword(exp!(Extern)) { if self.eat_keyword(exp!(Crate)) { // EXTERN CRATE @@ -2372,7 +2375,7 @@ impl<'a> Parser<'a> { sig_lo: Span, vis: &Visibility, case: Case, - ) -> PResult<'a, (Ident, FnSig, Generics, Option>)> { + ) -> PResult<'a, (Ident, FnSig, Generics, Option>, Option>)> { let fn_span = self.token.span; let header = self.parse_fn_front_matter(vis, case)?; // `const ... fn` let ident = self.parse_ident()?; // `foo` @@ -2398,6 +2401,8 @@ impl<'a> Parser<'a> { // inside `parse_fn_body()`. let fn_params_end = self.prev_token.span.shrink_to_hi(); + let contract = self.parse_contract()?; + generics.where_clause = self.parse_where_clause()?; // `where T: Ord` // `fn_params_end` is needed only when it's followed by a where clause. @@ -2409,7 +2414,7 @@ impl<'a> Parser<'a> { let body = self.parse_fn_body(attrs, &ident, &mut sig_hi, fn_parse_mode.req_body, fn_params_end)?; let fn_sig_span = sig_lo.to(sig_hi); - Ok((ident, FnSig { header, decl, span: fn_sig_span }, generics, body)) + Ok((ident, FnSig { header, decl, span: fn_sig_span }, generics, contract, body)) } /// Provide diagnostics when function body is not found diff --git a/compiler/rustc_parse/src/parser/token_type.rs b/compiler/rustc_parse/src/parser/token_type.rs index 73f3ac001c8a..50f03e72f82d 100644 --- a/compiler/rustc_parse/src/parser/token_type.rs +++ b/compiler/rustc_parse/src/parser/token_type.rs @@ -108,6 +108,8 @@ pub enum TokenType { KwRef, KwReturn, KwReuse, + KwRustcContractEnsures, + KwRustcContractRequires, KwSafe, KwSelfUpper, KwStatic, @@ -242,6 +244,8 @@ impl TokenType { KwRef, KwReturn, KwReuse, + KwRustcContractEnsures, + KwRustcContractRequires, KwSafe, KwSelfUpper, KwStatic, @@ -314,6 +318,8 @@ impl TokenType { TokenType::KwRef => Some(kw::Ref), TokenType::KwReturn => Some(kw::Return), TokenType::KwReuse => Some(kw::Reuse), + TokenType::KwRustcContractEnsures => Some(kw::RustcContractEnsures), + TokenType::KwRustcContractRequires => Some(kw::RustcContractRequires), TokenType::KwSafe => Some(kw::Safe), TokenType::KwSelfUpper => Some(kw::SelfUpper), TokenType::KwStatic => Some(kw::Static), @@ -544,6 +550,8 @@ macro_rules! exp { (Ref) => { exp!(@kw, Ref, KwRef) }; (Return) => { exp!(@kw, Return, KwReturn) }; (Reuse) => { exp!(@kw, Reuse, KwReuse) }; + (RustcContractEnsures) => { exp!(@kw, RustcContractEnsures, KwRustcContractEnsures) }; + (RustcContractRequires) => { exp!(@kw, RustcContractRequires, KwRustcContractRequires) }; (Safe) => { exp!(@kw, Safe, KwSafe) }; (SelfUpper) => { exp!(@kw, SelfUpper, KwSelfUpper) }; (Static) => { exp!(@kw, Static, KwStatic) }; diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 16c0a345f879..db607dbb4196 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -174,10 +174,13 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { _ctxt, _ident, _vis, - Fn { sig: FnSig { header, decl, span: _ }, generics, body, .. }, + Fn { sig: FnSig { header, decl, span: _ }, generics, contract, body, .. }, ) if let Some(coroutine_kind) = header.coroutine_kind => { self.visit_fn_header(header); self.visit_generics(generics); + if let Some(contract) = contract { + self.visit_contract(contract); + } // For async functions, we need to create their inner defs inside of a // closure to match their desugared representation. Besides that, diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 4842cbd556c3..e37e7e98ee7a 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1019,7 +1019,7 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r // Create a label rib for the function. this.with_label_rib(RibKind::FnOrCoroutine, |this| { match fn_kind { - FnKind::Fn(_, _, _, Fn { sig, generics, body, .. }) => { + FnKind::Fn(_, _, _, Fn { sig, generics, contract, body, .. }) => { this.visit_generics(generics); let declaration = &sig.decl; @@ -1046,6 +1046,10 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r }, ); + if let Some(contract) = contract { + this.visit_contract(contract); + } + if let Some(body) = body { // Ignore errors in function bodies if this is rustdoc // Be sure not to set this until the function signature has been resolved. diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index a5826137181d..dbbbb5077cb5 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -1163,6 +1163,8 @@ pub enum DesugaringKind { WhileLoop, /// `async Fn()` bound modifier BoundModifier, + /// Calls to contract checks (`#[requires]` to precond, `#[ensures]` to postcond) + Contract, } impl DesugaringKind { @@ -1179,6 +1181,7 @@ impl DesugaringKind { DesugaringKind::ForLoop => "`for` loop", DesugaringKind::WhileLoop => "`while` loop", DesugaringKind::BoundModifier => "trait bound modifier", + DesugaringKind::Contract => "contract check", } } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index df424b3312a0..1c15ca7d11aa 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -118,6 +118,8 @@ symbols! { MacroRules: "macro_rules", Raw: "raw", Reuse: "reuse", + RustcContractEnsures: "rustc_contract_ensures", + RustcContractRequires: "rustc_contract_requires", Safe: "safe", Union: "union", Yeet: "yeet", diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index fd283fe92fa3..a31cca5d425c 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -113,8 +113,8 @@ #![feature(bigint_helper_methods)] #![feature(bstr)] #![feature(bstr_internals)] -#![feature(const_carrying_mul_add)] #![feature(closure_track_caller)] +#![feature(const_carrying_mul_add)] #![feature(const_eval_select)] #![feature(core_intrinsics)] #![feature(coverage_attribute)] diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index 2eb09bac8d88..798f4575c2e1 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -362,18 +362,21 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { defaultness: ld, sig: lf, generics: lg, + contract: lc, body: lb, }), Fn(box ast::Fn { defaultness: rd, sig: rf, generics: rg, + contract: rc, body: rb, }), ) => { eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) + && eq_opt_fn_contract(lc, rc) && both(lb.as_ref(), rb.as_ref(), |l, r| eq_block(l, r)) }, (Mod(lu, lmk), Mod(ru, rmk)) => { @@ -497,18 +500,21 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { defaultness: ld, sig: lf, generics: lg, + contract: lc, body: lb, }), Fn(box ast::Fn { defaultness: rd, sig: rf, generics: rg, + contract: rc, body: rb, }), ) => { eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) + && eq_opt_fn_contract(lc, rc) && both(lb.as_ref(), rb.as_ref(), |l, r| eq_block(l, r)) }, ( @@ -559,18 +565,21 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { defaultness: ld, sig: lf, generics: lg, + contract: lc, body: lb, }), Fn(box ast::Fn { defaultness: rd, sig: rf, generics: rg, + contract: rc, body: rb, }), ) => { eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) + && eq_opt_fn_contract(lc, rc) && both(lb.as_ref(), rb.as_ref(), |l, r| eq_block(l, r)) }, ( @@ -653,6 +662,17 @@ pub fn eq_fn_header(l: &FnHeader, r: &FnHeader) -> bool { && eq_ext(&l.ext, &r.ext) } +pub fn eq_opt_fn_contract(l: &Option>, r: &Option>) -> bool { + match (l, r) { + (Some(l), Some(r)) => { + eq_expr_opt(l.requires.as_ref(), r.requires.as_ref()) + && eq_expr_opt(l.ensures.as_ref(), r.ensures.as_ref()) + } + (None, None) => true, + (Some(_), None) | (None, Some(_)) => false, + } +} + pub fn eq_generics(l: &Generics, r: &Generics) -> bool { over(&l.params, &r.params, eq_generic_param) && over(&l.where_clause.predicates, &r.where_clause.predicates, |l, r| { diff --git a/tests/ui/contracts/contract-ast-extensions-nest.rs b/tests/ui/contracts/contract-ast-extensions-nest.rs new file mode 100644 index 000000000000..ed137c4a9867 --- /dev/null +++ b/tests/ui/contracts/contract-ast-extensions-nest.rs @@ -0,0 +1,44 @@ +//@ revisions: unchk_pass unchk_fail_pre unchk_fail_post chk_pass chk_fail_pre chk_fail_post +// +//@ [unchk_pass] run-pass +//@ [unchk_fail_pre] run-pass +//@ [unchk_fail_post] run-pass +//@ [chk_pass] run-pass +// +//@ [chk_fail_pre] run-fail +//@ [chk_fail_post] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_pre] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_post] compile-flags: -Zcontract-checks=no +// +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes + +#![feature(rustc_contracts)] + +fn nest(x: Baz) -> i32 + rustc_contract_requires(|| x.baz > 0) + rustc_contract_ensures(|ret| *ret > 100) +{ + loop { + return x.baz + 50; + } +} + +struct Baz { baz: i32 } + +const BAZ_PASS_PRE_POST: Baz = Baz { baz: 100 }; +#[cfg(any(unchk_fail_post, chk_fail_post))] +const BAZ_FAIL_POST: Baz = Baz { baz: 10 }; +#[cfg(any(unchk_fail_pre, chk_fail_pre))] +const BAZ_FAIL_PRE: Baz = Baz { baz: -10 }; + +fn main() { + assert_eq!(nest(BAZ_PASS_PRE_POST), 150); + #[cfg(any(unchk_fail_pre, chk_fail_pre))] + nest(BAZ_FAIL_PRE); + #[cfg(any(unchk_fail_post, chk_fail_post))] + nest(BAZ_FAIL_POST); +} diff --git a/tests/ui/contracts/contract-ast-extensions-tail.rs b/tests/ui/contracts/contract-ast-extensions-tail.rs new file mode 100644 index 000000000000..b501c3faaed6 --- /dev/null +++ b/tests/ui/contracts/contract-ast-extensions-tail.rs @@ -0,0 +1,42 @@ +//@ revisions: unchk_pass unchk_fail_pre unchk_fail_post chk_pass chk_fail_pre chk_fail_post +// +//@ [unchk_pass] run-pass +//@ [unchk_fail_pre] run-pass +//@ [unchk_fail_post] run-pass +//@ [chk_pass] run-pass +// +//@ [chk_fail_pre] run-fail +//@ [chk_fail_post] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_pre] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_post] compile-flags: -Zcontract-checks=no +// +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes + +#![feature(rustc_contracts)] + +fn tail(x: Baz) -> i32 + rustc_contract_requires(|| x.baz > 0) + rustc_contract_ensures(|ret| *ret > 100) +{ + x.baz + 50 +} + +struct Baz { baz: i32 } + +const BAZ_PASS_PRE_POST: Baz = Baz { baz: 100 }; +#[cfg(any(unchk_fail_post, chk_fail_post))] +const BAZ_FAIL_POST: Baz = Baz { baz: 10 }; +#[cfg(any(unchk_fail_pre, chk_fail_pre))] +const BAZ_FAIL_PRE: Baz = Baz { baz: -10 }; + +fn main() { + assert_eq!(tail(BAZ_PASS_PRE_POST), 150); + #[cfg(any(unchk_fail_pre, chk_fail_pre))] + tail(BAZ_FAIL_PRE); + #[cfg(any(unchk_fail_post, chk_fail_post))] + tail(BAZ_FAIL_POST); +} diff --git a/tests/ui/contracts/contracts-lowering-ensures-is-not-inherited-when-nesting.rs b/tests/ui/contracts/contracts-lowering-ensures-is-not-inherited-when-nesting.rs new file mode 100644 index 000000000000..069f26e6796b --- /dev/null +++ b/tests/ui/contracts/contracts-lowering-ensures-is-not-inherited-when-nesting.rs @@ -0,0 +1,15 @@ +//@ run-pass +//@ compile-flags: -Zcontract-checks=yes +#![feature(rustc_contracts)] + +fn outer() -> i32 + rustc_contract_ensures(|ret| *ret > 0) +{ + let inner_closure = || -> i32 { 0 }; + inner_closure(); + 10 +} + +fn main() { + outer(); +} diff --git a/tests/ui/contracts/contracts-lowering-requires-is-not-inherited-when-nesting.rs b/tests/ui/contracts/contracts-lowering-requires-is-not-inherited-when-nesting.rs new file mode 100644 index 000000000000..fbee8277a968 --- /dev/null +++ b/tests/ui/contracts/contracts-lowering-requires-is-not-inherited-when-nesting.rs @@ -0,0 +1,17 @@ +//@ run-pass +//@ compile-flags: -Zcontract-checks=yes +#![feature(rustc_contracts)] + +struct Outer { outer: std::cell::Cell } + +fn outer(x: Outer) + rustc_contract_requires(|| x.outer.get() > 0) +{ + let inner_closure = || { }; + x.outer.set(0); + inner_closure(); +} + +fn main() { + outer(Outer { outer: 1.into() }); +} From 4636dd9347573e81056f88189ac52ee1e8ccc2f7 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Mon, 3 Feb 2025 13:01:13 -0800 Subject: [PATCH 36/57] Add tests for nested macro_rules edition behavior This adds tests to check the behavior of how nested macro_rules definitions work across edition boundaries. This covers a change in behavior due to https://github.com/rust-lang/rust/pull/133274. See https://github.com/rust-lang/rust/issues/135669 --- .../auxiliary/nested_macro_rules_dep_2021.rs | 23 +++++++++++ .../auxiliary/nested_macro_rules_dep_2024.rs | 23 +++++++++++ .../nested-macro-rules-edition.e2021.stderr | 27 +++++++++++++ .../ui/editions/nested-macro-rules-edition.rs | 39 +++++++++++++++++++ 4 files changed, 112 insertions(+) create mode 100644 tests/ui/editions/auxiliary/nested_macro_rules_dep_2021.rs create mode 100644 tests/ui/editions/auxiliary/nested_macro_rules_dep_2024.rs create mode 100644 tests/ui/editions/nested-macro-rules-edition.e2021.stderr create mode 100644 tests/ui/editions/nested-macro-rules-edition.rs diff --git a/tests/ui/editions/auxiliary/nested_macro_rules_dep_2021.rs b/tests/ui/editions/auxiliary/nested_macro_rules_dep_2021.rs new file mode 100644 index 000000000000..36d3e712dc0e --- /dev/null +++ b/tests/ui/editions/auxiliary/nested_macro_rules_dep_2021.rs @@ -0,0 +1,23 @@ +//@ edition: 2021 + +#[macro_export] +macro_rules! make_macro_with_input { + ($i:ident) => { + macro_rules! macro_inner_input { + () => { + pub fn $i() {} + }; + } + }; +} + +#[macro_export] +macro_rules! make_macro { + () => { + macro_rules! macro_inner { + () => { + pub fn gen() {} + }; + } + }; +} diff --git a/tests/ui/editions/auxiliary/nested_macro_rules_dep_2024.rs b/tests/ui/editions/auxiliary/nested_macro_rules_dep_2024.rs new file mode 100644 index 000000000000..4012398fe662 --- /dev/null +++ b/tests/ui/editions/auxiliary/nested_macro_rules_dep_2024.rs @@ -0,0 +1,23 @@ +//@ edition: 2024 + +#[macro_export] +macro_rules! make_macro_with_input { + ($i:ident) => { + macro_rules! macro_inner_input { + () => { + pub fn $i() {} + }; + } + }; +} + +#[macro_export] +macro_rules! make_macro { + () => { + macro_rules! macro_inner { + () => { + pub fn gen() {} + }; + } + }; +} diff --git a/tests/ui/editions/nested-macro-rules-edition.e2021.stderr b/tests/ui/editions/nested-macro-rules-edition.e2021.stderr new file mode 100644 index 000000000000..eac80262364b --- /dev/null +++ b/tests/ui/editions/nested-macro-rules-edition.e2021.stderr @@ -0,0 +1,27 @@ +error: expected identifier, found reserved keyword `gen` + --> $DIR/nested-macro-rules-edition.rs:30:5 + | +LL | macro_inner_input!{} + | ^^^^^^^^^^^^^^^^^^^^ expected identifier, found reserved keyword + | + = note: this error originates in the macro `macro_inner_input` (in Nightly builds, run with -Z macro-backtrace for more info) +help: escape `gen` to use it as an identifier + | +LL | nested_macro_rules_dep::make_macro_with_input!{r#gen} + | ++ + +error: expected identifier, found reserved keyword `gen` + --> $DIR/nested-macro-rules-edition.rs:35:5 + | +LL | macro_inner!{} + | ^^^^^^^^^^^^^^ expected identifier, found reserved keyword + | + = note: this error originates in the macro `macro_inner` (in Nightly builds, run with -Z macro-backtrace for more info) +help: escape `gen` to use it as an identifier + --> $DIR/auxiliary/nested_macro_rules_dep_2024.rs:19:24 + | +LL | pub fn r#gen() {} + | ++ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/editions/nested-macro-rules-edition.rs b/tests/ui/editions/nested-macro-rules-edition.rs new file mode 100644 index 000000000000..28d568f77501 --- /dev/null +++ b/tests/ui/editions/nested-macro-rules-edition.rs @@ -0,0 +1,39 @@ +// This checks the behavior of how nested macro_rules definitions are handled +// with regards to edition spans. Prior to https://github.com/rust-lang/rust/pull/133274, +// the compiler would compile the inner macro with the edition of the local crate. +// Afterwards, it uses the edition where the macro was *defined*. +// +// Unfortunately macro_rules compiler discards the edition of any *input* that +// was used to generate the macro. This is possibly not the behavior that we +// want. If we want to keep with the philosophy that code should follow the +// edition rules of the crate where it is written, then presumably we would +// want the input tokens to retain the edition of where they were written. +// +// See https://github.com/rust-lang/rust/issues/135669 for more. +// +// This has two revisions, one where local=2021 and the dep=2024. The other +// revision is vice-versa. + +//@ revisions: e2021 e2024 +//@[e2021] edition:2021 +//@[e2024] edition:2024 +//@[e2021] aux-crate: nested_macro_rules_dep=nested_macro_rules_dep_2024.rs +//@[e2024] aux-crate: nested_macro_rules_dep=nested_macro_rules_dep_2021.rs +//@[e2024] check-pass + +mod with_input { + // If we change the macro_rules input behavior, then this should pass when + // local edition is 2021 because `gen` is written in a context with 2021 + // behavior. For local edition 2024, the reverse would be true and this + // should fail. + nested_macro_rules_dep::make_macro_with_input!{gen} + macro_inner_input!{} + //[e2021]~^ ERROR found reserved keyword +} +mod no_input { + nested_macro_rules_dep::make_macro!{} + macro_inner!{} + //[e2021]~^ ERROR found reserved keyword +} + +fn main() {} From ae7eff0be5c4abae63c06851af14cfdf7fec3981 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 3 Dec 2024 02:52:29 +0000 Subject: [PATCH 37/57] Desugars contract into the internal AST extensions Check ensures on early return due to Try / Yeet Expand these two expressions to include a call to contract checking --- compiler/rustc_ast_lowering/src/expr.rs | 48 +++-- compiler/rustc_ast_lowering/src/item.rs | 123 ++++++------- .../rustc_builtin_macros/src/contracts.rs | 172 ++++++++++++++++++ compiler/rustc_builtin_macros/src/lib.rs | 5 + compiler/rustc_span/src/symbol.rs | 2 + library/core/src/contracts.rs | 5 + library/core/src/macros/mod.rs | 26 +++ .../ui/contracts/contract-attributes-nest.rs | 44 +++++ .../ui/contracts/contract-attributes-tail.rs | 42 +++++ .../contracts-ensures-early-fn-exit.rs | 48 +++++ ...s-ensures-is-not-inherited-when-nesting.rs | 14 ++ ...-requires-is-not-inherited-when-nesting.rs | 16 ++ 12 files changed, 457 insertions(+), 88 deletions(-) create mode 100644 compiler/rustc_builtin_macros/src/contracts.rs create mode 100644 tests/ui/contracts/contract-attributes-nest.rs create mode 100644 tests/ui/contracts/contract-attributes-tail.rs create mode 100644 tests/ui/contracts/contracts-ensures-early-fn-exit.rs create mode 100644 tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.rs create mode 100644 tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.rs diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 19433047595a..8125da361b81 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -314,21 +314,8 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ExprKind::Continue(self.lower_jump_destination(e.id, *opt_label)) } ExprKind::Ret(e) => { - let mut e = e.as_ref().map(|x| self.lower_expr(x)); - if let Some(Some((span, fresh_ident))) = self - .contract - .as_ref() - .map(|c| c.ensures.as_ref().map(|e| (e.expr.span, e.fresh_ident))) - { - let checker_fn = self.expr_ident(span, fresh_ident.0, fresh_ident.2); - let args = if let Some(e) = e { - std::slice::from_ref(e) - } else { - std::slice::from_ref(self.expr_unit(span)) - }; - e = Some(self.expr_call(span, checker_fn, args)); - } - hir::ExprKind::Ret(e) + let expr = e.as_ref().map(|x| self.lower_expr(x)); + self.checked_return(expr) } ExprKind::Yeet(sub_expr) => self.lower_expr_yeet(e.span, sub_expr.as_deref()), ExprKind::Become(sub_expr) => { @@ -395,6 +382,32 @@ impl<'hir> LoweringContext<'_, 'hir> { }) } + /// Create an `ExprKind::Ret` that is preceded by a call to check contract ensures clause. + fn checked_return(&mut self, opt_expr: Option<&'hir hir::Expr<'hir>>) -> hir::ExprKind<'hir> { + let checked_ret = if let Some(Some((span, fresh_ident))) = + self.contract.as_ref().map(|c| c.ensures.as_ref().map(|e| (e.expr.span, e.fresh_ident))) + { + let expr = opt_expr.unwrap_or_else(|| self.expr_unit(span)); + Some(self.inject_ensures_check(expr, span, fresh_ident.0, fresh_ident.2)) + } else { + opt_expr + }; + hir::ExprKind::Ret(checked_ret) + } + + /// Wraps an expression with a call to the ensures check before it gets returned. + pub(crate) fn inject_ensures_check( + &mut self, + expr: &'hir hir::Expr<'hir>, + span: Span, + check_ident: Ident, + check_hir_id: HirId, + ) -> &'hir hir::Expr<'hir> { + let checker_fn = self.expr_ident(span, check_ident, check_hir_id); + let span = self.mark_span_with_reason(DesugaringKind::Contract, span, None); + self.expr_call(span, checker_fn, std::slice::from_ref(expr)) + } + pub(crate) fn lower_const_block(&mut self, c: &AnonConst) -> hir::ConstBlock { self.with_new_scopes(c.value.span, |this| { let def_id = this.local_def_id(c.id); @@ -1983,7 +1996,8 @@ impl<'hir> LoweringContext<'_, 'hir> { ), )) } else { - self.arena.alloc(self.expr(try_span, hir::ExprKind::Ret(Some(from_residual_expr)))) + let ret_expr = self.checked_return(Some(from_residual_expr)); + self.arena.alloc(self.expr(try_span, ret_expr)) }; self.lower_attrs(ret_expr.hir_id, &attrs); @@ -2032,7 +2046,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let target_id = Ok(catch_id); hir::ExprKind::Break(hir::Destination { label: None, target_id }, Some(from_yeet_expr)) } else { - hir::ExprKind::Ret(Some(from_yeet_expr)) + self.checked_return(Some(from_yeet_expr)) } } diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 4679ccdddbbe..c96110fee619 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -215,7 +215,7 @@ impl<'hir> LoweringContext<'_, 'hir> { if let Some(contract) = contract { let requires = contract.requires.clone(); let ensures = contract.ensures.clone(); - let ensures = if let Some(ens) = ensures { + let ensures = ensures.map(|ens| { // FIXME: this needs to be a fresh (or illegal) identifier to prevent // accidental capture of a parameter or global variable. let checker_ident: Ident = @@ -226,13 +226,11 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::BindingMode::NONE, ); - Some(crate::FnContractLoweringEnsures { + crate::FnContractLoweringEnsures { expr: ens, fresh_ident: (checker_ident, checker_pat, checker_hir_id), - }) - } else { - None - }; + } + }); // Note: `with_new_scopes` will reinstall the outer // item's contract (if any) after its callback finishes. @@ -1095,73 +1093,56 @@ impl<'hir> LoweringContext<'_, 'hir> { // { body } // ==> - // { rustc_contract_requires(PRECOND); { body } } - let result: hir::Expr<'hir> = if let Some(contract) = opt_contract { - let lit_unit = |this: &mut LoweringContext<'_, 'hir>| { - this.expr(contract.span, hir::ExprKind::Tup(&[])) - }; - - let precond: hir::Stmt<'hir> = if let Some(req) = contract.requires { - let lowered_req = this.lower_expr_mut(&req); - let precond = this.expr_call_lang_item_fn_mut( - req.span, - hir::LangItem::ContractCheckRequires, - &*arena_vec![this; lowered_req], - ); - this.stmt_expr(req.span, precond) - } else { - let u = lit_unit(this); - this.stmt_expr(contract.span, u) - }; - - let (postcond_checker, result) = if let Some(ens) = contract.ensures { - let crate::FnContractLoweringEnsures { expr: ens, fresh_ident } = ens; - let lowered_ens: hir::Expr<'hir> = this.lower_expr_mut(&ens); - let postcond_checker = this.expr_call_lang_item_fn( - ens.span, - hir::LangItem::ContractBuildCheckEnsures, - &*arena_vec![this; lowered_ens], - ); - let checker_binding_pat = fresh_ident.1; - ( - this.stmt_let_pat( - None, - ens.span, - Some(postcond_checker), - this.arena.alloc(checker_binding_pat), - hir::LocalSource::Contract, - ), - { - let checker_fn = - this.expr_ident(ens.span, fresh_ident.0, fresh_ident.2); - let span = this.mark_span_with_reason( - DesugaringKind::Contract, - ens.span, - None, - ); - this.expr_call_mut( - span, - checker_fn, - std::slice::from_ref(this.arena.alloc(result)), - ) - }, - ) - } else { - let u = lit_unit(this); - (this.stmt_expr(contract.span, u), result) - }; - - let block = this.block_all( - contract.span, - arena_vec![this; precond, postcond_checker], - Some(this.arena.alloc(result)), - ); - this.expr_block(block) - } else { - result + // { contract_requires(PRECOND); { body } } + let Some(contract) = opt_contract else { return (params, result) }; + let result_ref = this.arena.alloc(result); + let lit_unit = |this: &mut LoweringContext<'_, 'hir>| { + this.expr(contract.span, hir::ExprKind::Tup(&[])) }; - (params, result) + let precond: hir::Stmt<'hir> = if let Some(req) = contract.requires { + let lowered_req = this.lower_expr_mut(&req); + let precond = this.expr_call_lang_item_fn_mut( + req.span, + hir::LangItem::ContractCheckRequires, + &*arena_vec![this; lowered_req], + ); + this.stmt_expr(req.span, precond) + } else { + let u = lit_unit(this); + this.stmt_expr(contract.span, u) + }; + + let (postcond_checker, result) = if let Some(ens) = contract.ensures { + let crate::FnContractLoweringEnsures { expr: ens, fresh_ident } = ens; + let lowered_ens: hir::Expr<'hir> = this.lower_expr_mut(&ens); + let postcond_checker = this.expr_call_lang_item_fn( + ens.span, + hir::LangItem::ContractBuildCheckEnsures, + &*arena_vec![this; lowered_ens], + ); + let checker_binding_pat = fresh_ident.1; + ( + this.stmt_let_pat( + None, + ens.span, + Some(postcond_checker), + this.arena.alloc(checker_binding_pat), + hir::LocalSource::Contract, + ), + this.inject_ensures_check(result_ref, ens.span, fresh_ident.0, fresh_ident.2), + ) + } else { + let u = lit_unit(this); + (this.stmt_expr(contract.span, u), &*result_ref) + }; + + let block = this.block_all( + contract.span, + arena_vec![this; precond, postcond_checker], + Some(result), + ); + (params, this.expr_block(block)) }) } diff --git a/compiler/rustc_builtin_macros/src/contracts.rs b/compiler/rustc_builtin_macros/src/contracts.rs new file mode 100644 index 000000000000..d269287b555b --- /dev/null +++ b/compiler/rustc_builtin_macros/src/contracts.rs @@ -0,0 +1,172 @@ +#![allow(unused_imports, unused_variables)] + +use rustc_ast::token; +use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree}; +use rustc_errors::ErrorGuaranteed; +use rustc_expand::base::{AttrProcMacro, ExtCtxt}; +use rustc_span::Span; +use rustc_span::symbol::{Ident, Symbol, kw, sym}; + +pub(crate) struct ExpandRequires; + +pub(crate) struct ExpandEnsures; + +impl AttrProcMacro for ExpandRequires { + fn expand<'cx>( + &self, + ecx: &'cx mut ExtCtxt<'_>, + span: Span, + annotation: TokenStream, + annotated: TokenStream, + ) -> Result { + expand_requires_tts(ecx, span, annotation, annotated) + } +} + +impl AttrProcMacro for ExpandEnsures { + fn expand<'cx>( + &self, + ecx: &'cx mut ExtCtxt<'_>, + span: Span, + annotation: TokenStream, + annotated: TokenStream, + ) -> Result { + expand_ensures_tts(ecx, span, annotation, annotated) + } +} + +fn expand_injecting_circa_where_clause( + _ecx: &mut ExtCtxt<'_>, + attr_span: Span, + annotated: TokenStream, + inject: impl FnOnce(&mut Vec) -> Result<(), ErrorGuaranteed>, +) -> Result { + let mut new_tts = Vec::with_capacity(annotated.len()); + let mut cursor = annotated.into_trees(); + + // Find the `fn name(x:X,...)` and inject the AST contract forms right after + // the formal parameters (and return type if any). + while let Some(tt) = cursor.next_ref() { + new_tts.push(tt.clone()); + if let TokenTree::Token(tok, _) = tt + && tok.is_ident_named(kw::Fn) + { + break; + } + } + + // Found the `fn` keyword, now find the formal parameters. + // + // FIXME: can this fail if you have parentheticals in a generics list, like `fn foo Y>` ? + while let Some(tt) = cursor.next_ref() { + new_tts.push(tt.clone()); + + if let TokenTree::Delimited(_, _, token::Delimiter::Parenthesis, _) = tt { + break; + } + if let TokenTree::Token(token::Token { kind: token::TokenKind::Semi, .. }, _) = tt { + panic!("contract attribute applied to fn without parameter list."); + } + } + + // There *might* be a return type declaration (and figuring out where that ends would require + // parsing an arbitrary type expression, e.g. `-> Foo` + // + // Instead of trying to figure that out, scan ahead and look for the first occurence of a + // `where`, a `{ ... }`, or a `;`. + // + // FIXME: this might still fall into a trap for something like `-> Ctor`. I + // *think* such cases must be under a Delimited (e.g. `[T; { N }]` or have the braced form + // prefixed by e.g. `const`, so we should still be able to filter them out without having to + // parse the type expression itself. But rather than try to fix things with hacks like that, + // time might be better spent extending the attribute expander to suport tt-annotation atop + // ast-annotated, which would be an elegant way to sidestep all of this. + let mut opt_next_tt = cursor.next_ref(); + while let Some(next_tt) = opt_next_tt { + if let TokenTree::Token(tok, _) = next_tt + && tok.is_ident_named(kw::Where) + { + break; + } + if let TokenTree::Delimited(_, _, token::Delimiter::Brace, _) = next_tt { + break; + } + if let TokenTree::Token(token::Token { kind: token::TokenKind::Semi, .. }, _) = next_tt { + break; + } + + // for anything else, transcribe the tt and keep looking. + new_tts.push(next_tt.clone()); + opt_next_tt = cursor.next_ref(); + continue; + } + + // At this point, we've transcribed everything from the `fn` through the formal parameter list + // and return type declaration, (if any), but `tt` itself has *not* been transcribed. + // + // Now inject the AST contract form. + // + // FIXME: this kind of manual token tree munging does not have significant precedent among + // rustc builtin macros, probably because most builtin macros use direct AST manipulation to + // accomplish similar goals. But since our attributes need to take arbitrary expressions, and + // our attribute infrastructure does not yet support mixing a token-tree annotation with an AST + // annotated, we end up doing token tree manipulation. + inject(&mut new_tts)?; + + // Above we injected the internal AST requires/ensures contruct. Now copy over all the other + // token trees. + if let Some(tt) = opt_next_tt { + new_tts.push(tt.clone()); + } + while let Some(tt) = cursor.next_ref() { + new_tts.push(tt.clone()); + } + + Ok(TokenStream::new(new_tts)) +} + +fn expand_requires_tts( + _ecx: &mut ExtCtxt<'_>, + attr_span: Span, + annotation: TokenStream, + annotated: TokenStream, +) -> Result { + expand_injecting_circa_where_clause(_ecx, attr_span, annotated, |new_tts| { + new_tts.push(TokenTree::Token( + token::Token::from_ast_ident(Ident::new(kw::RustcContractRequires, attr_span)), + Spacing::Joint, + )); + new_tts.push(TokenTree::Token( + token::Token::new(token::TokenKind::OrOr, attr_span), + Spacing::Alone, + )); + new_tts.push(TokenTree::Delimited( + DelimSpan::from_single(attr_span), + DelimSpacing::new(Spacing::JointHidden, Spacing::JointHidden), + token::Delimiter::Parenthesis, + annotation, + )); + Ok(()) + }) +} + +fn expand_ensures_tts( + _ecx: &mut ExtCtxt<'_>, + attr_span: Span, + annotation: TokenStream, + annotated: TokenStream, +) -> Result { + expand_injecting_circa_where_clause(_ecx, attr_span, annotated, |new_tts| { + new_tts.push(TokenTree::Token( + token::Token::from_ast_ident(Ident::new(kw::RustcContractEnsures, attr_span)), + Spacing::Joint, + )); + new_tts.push(TokenTree::Delimited( + DelimSpan::from_single(attr_span), + DelimSpacing::new(Spacing::JointHidden, Spacing::JointHidden), + token::Delimiter::Parenthesis, + annotation, + )); + Ok(()) + }) +} diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 6987ae95980a..ca16583a45de 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -55,6 +55,7 @@ mod trace_macros; pub mod asm; pub mod cmdline_attrs; +pub mod contracts; pub mod proc_macro_harness; pub mod standard_library_imports; pub mod test_harness; @@ -137,4 +138,8 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { let client = proc_macro::bridge::client::Client::expand1(proc_macro::quote); register(sym::quote, SyntaxExtensionKind::Bang(Box::new(BangProcMacro { client }))); + let requires = SyntaxExtensionKind::Attr(Box::new(contracts::ExpandRequires)); + register(sym::contracts_requires, requires); + let ensures = SyntaxExtensionKind::Attr(Box::new(contracts::ExpandEnsures)); + register(sym::contracts_ensures, ensures); } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 1c15ca7d11aa..43bc69e6e7cf 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -682,6 +682,8 @@ symbols! { contract_check_ensures, contract_check_requires, contract_checks, + contracts_ensures, + contracts_requires, convert_identity, copy, copy_closures, diff --git a/library/core/src/contracts.rs b/library/core/src/contracts.rs index c13565d59d58..3efd2df0a383 100644 --- a/library/core/src/contracts.rs +++ b/library/core/src/contracts.rs @@ -1,5 +1,10 @@ //! Unstable module containing the unstable contracts lang items and attribute macros. +#[cfg(not(bootstrap))] +pub use crate::macros::builtin::contracts_ensures as ensures; +#[cfg(not(bootstrap))] +pub use crate::macros::builtin::contracts_requires as requires; + /// Emitted by rustc as a desugaring of `#[requires(PRED)] fn foo(x: X) { ... }` /// into: `fn foo(x: X) { check_requires(|| PRED) ... }` #[cfg(not(bootstrap))] diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 3669051ce82d..1a5d2973dfc3 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1777,6 +1777,32 @@ pub(crate) mod builtin { /* compiler built-in */ } + /// Attribute macro applied to a function to give it a post-condition. + /// + /// The attribute carries an argument token-tree which is + /// eventually parsed as a unary closure expression that is + /// invoked on a reference to the return value. + #[cfg(not(bootstrap))] + #[unstable(feature = "rustc_contracts", issue = "none")] + #[allow_internal_unstable(core_intrinsics)] + #[rustc_builtin_macro] + pub macro contracts_ensures($item:item) { + /* compiler built-in */ + } + + /// Attribute macro applied to a function to give it a precondition. + /// + /// The attribute carries an argument token-tree which is + /// eventually parsed as an boolean expression with access to the + /// function's formal parameters + #[cfg(not(bootstrap))] + #[unstable(feature = "rustc_contracts", issue = "none")] + #[allow_internal_unstable(core_intrinsics)] + #[rustc_builtin_macro] + pub macro contracts_requires($item:item) { + /* compiler built-in */ + } + /// Attribute macro applied to a function to register it as a handler for allocation failure. /// /// See also [`std::alloc::handle_alloc_error`](../../../std/alloc/fn.handle_alloc_error.html). diff --git a/tests/ui/contracts/contract-attributes-nest.rs b/tests/ui/contracts/contract-attributes-nest.rs new file mode 100644 index 000000000000..1cda21f10d7e --- /dev/null +++ b/tests/ui/contracts/contract-attributes-nest.rs @@ -0,0 +1,44 @@ +//@ revisions: unchk_pass unchk_fail_pre unchk_fail_post chk_pass chk_fail_pre chk_fail_post +// +//@ [unchk_pass] run-pass +//@ [unchk_fail_pre] run-pass +//@ [unchk_fail_post] run-pass +//@ [chk_pass] run-pass +// +//@ [chk_fail_pre] run-fail +//@ [chk_fail_post] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_pre] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_post] compile-flags: -Zcontract-checks=no +// +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes + +#![feature(rustc_contracts)] + +#[core::contracts::requires(x.baz > 0)] +#[core::contracts::ensures(|ret| *ret > 100)] +fn nest(x: Baz) -> i32 +{ + loop { + return x.baz + 50; + } +} + +struct Baz { baz: i32 } + +const BAZ_PASS_PRE_POST: Baz = Baz { baz: 100 }; +#[cfg(any(unchk_fail_post, chk_fail_post))] +const BAZ_FAIL_POST: Baz = Baz { baz: 10 }; +#[cfg(any(unchk_fail_pre, chk_fail_pre))] +const BAZ_FAIL_PRE: Baz = Baz { baz: -10 }; + +fn main() { + assert_eq!(nest(BAZ_PASS_PRE_POST), 150); + #[cfg(any(unchk_fail_pre, chk_fail_pre))] + nest(BAZ_FAIL_PRE); + #[cfg(any(unchk_fail_post, chk_fail_post))] + nest(BAZ_FAIL_POST); +} diff --git a/tests/ui/contracts/contract-attributes-tail.rs b/tests/ui/contracts/contract-attributes-tail.rs new file mode 100644 index 000000000000..26855bfa82ac --- /dev/null +++ b/tests/ui/contracts/contract-attributes-tail.rs @@ -0,0 +1,42 @@ +//@ revisions: unchk_pass unchk_fail_pre unchk_fail_post chk_pass chk_fail_pre chk_fail_post +// +//@ [unchk_pass] run-pass +//@ [unchk_fail_pre] run-pass +//@ [unchk_fail_post] run-pass +//@ [chk_pass] run-pass +// +//@ [chk_fail_pre] run-fail +//@ [chk_fail_post] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_pre] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_post] compile-flags: -Zcontract-checks=no +// +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes + +#![feature(rustc_contracts)] + +#[core::contracts::requires(x.baz > 0)] +#[core::contracts::ensures(|ret| *ret > 100)] +fn tail(x: Baz) -> i32 +{ + x.baz + 50 +} + +struct Baz { baz: i32 } + +const BAZ_PASS_PRE_POST: Baz = Baz { baz: 100 }; +#[cfg(any(unchk_fail_post, chk_fail_post))] +const BAZ_FAIL_POST: Baz = Baz { baz: 10 }; +#[cfg(any(unchk_fail_pre, chk_fail_pre))] +const BAZ_FAIL_PRE: Baz = Baz { baz: -10 }; + +fn main() { + assert_eq!(tail(BAZ_PASS_PRE_POST), 150); + #[cfg(any(unchk_fail_pre, chk_fail_pre))] + tail(BAZ_FAIL_PRE); + #[cfg(any(unchk_fail_post, chk_fail_post))] + tail(BAZ_FAIL_POST); +} diff --git a/tests/ui/contracts/contracts-ensures-early-fn-exit.rs b/tests/ui/contracts/contracts-ensures-early-fn-exit.rs new file mode 100644 index 000000000000..faf97473a90f --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-early-fn-exit.rs @@ -0,0 +1,48 @@ +//@ revisions: unchk_pass chk_pass chk_fail_try chk_fail_ret chk_fail_yeet +// +//@ [unchk_pass] run-pass +//@ [chk_pass] run-pass +//@ [chk_fail_try] run-fail +//@ [chk_fail_ret] run-fail +//@ [chk_fail_yeet] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_try] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_ret] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_yeet] compile-flags: -Zcontract-checks=yes +//! This test ensures that ensures clauses are checked for different return points of a function. + +#![feature(rustc_contracts)] +#![feature(yeet_expr)] + +/// This ensures will fail in different return points depending on the input. +#[core::contracts::ensures(|ret: &Option| ret.is_some())] +fn try_sum(x: u32, y: u32, z: u32) -> Option { + // Use Yeet to return early. + if x == u32::MAX && (y > 0 || z > 0) { do yeet } + + // Use `?` to early return. + let partial = x.checked_add(y)?; + + // Explicitly use `return` clause. + if u32::MAX - partial < z { + return None; + } + + Some(partial + z) +} + +fn main() { + // This should always succeed + assert_eq!(try_sum(0, 1, 2), Some(3)); + + #[cfg(any(unchk_pass, chk_fail_yeet))] + assert_eq!(try_sum(u32::MAX, 1, 1), None); + + #[cfg(any(unchk_pass, chk_fail_try))] + assert_eq!(try_sum(u32::MAX - 10, 12, 0), None); + + #[cfg(any(unchk_pass, chk_fail_ret))] + assert_eq!(try_sum(u32::MAX - 10, 2, 100), None); +} diff --git a/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.rs b/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.rs new file mode 100644 index 000000000000..9872fdb1a185 --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.rs @@ -0,0 +1,14 @@ +//@ run-pass +//@ compile-flags: -Zcontract-checks=yes +#![feature(rustc_contracts)] + +#[core::contracts::ensures(|ret| *ret > 0)] +fn outer() -> i32 { + let inner_closure = || -> i32 { 0 }; + inner_closure(); + 10 +} + +fn main() { + outer(); +} diff --git a/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.rs b/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.rs new file mode 100644 index 000000000000..75124259b0d7 --- /dev/null +++ b/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.rs @@ -0,0 +1,16 @@ +//@ run-pass +//@ compile-flags: -Zcontract-checks=yes +#![feature(rustc_contracts)] + +struct Outer { outer: std::cell::Cell } + +#[core::contracts::requires(x.outer.get() > 0)] +fn outer(x: Outer) { + let inner_closure = || { }; + x.outer.set(0); + inner_closure(); +} + +fn main() { + outer(Outer { outer: 1.into() }); +} From b279ff9dcfadcdb6976097d58044d151af81cf51 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 3 Dec 2024 14:29:27 +0000 Subject: [PATCH 38/57] demonstrate how to capture state at precondition time and feed into postcondition predicate. --- .../contract-captures-via-closure-copy.rs | 25 +++++++++++++++++ .../contract-captures-via-closure-noncopy.rs | 22 +++++++++++++++ ...ntract-captures-via-closure-noncopy.stderr | 27 +++++++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 tests/ui/contracts/contract-captures-via-closure-copy.rs create mode 100644 tests/ui/contracts/contract-captures-via-closure-noncopy.rs create mode 100644 tests/ui/contracts/contract-captures-via-closure-noncopy.stderr diff --git a/tests/ui/contracts/contract-captures-via-closure-copy.rs b/tests/ui/contracts/contract-captures-via-closure-copy.rs new file mode 100644 index 000000000000..742895ab0ad8 --- /dev/null +++ b/tests/ui/contracts/contract-captures-via-closure-copy.rs @@ -0,0 +1,25 @@ +//@ run-fail +//@ compile-flags: -Zcontract-checks=yes + +#![feature(rustc_contracts)] + +struct Baz { + baz: i32 +} + +#[track_caller] +#[core::contracts::requires(x.baz > 0)] +#[core::contracts::ensures({let old = x.baz; move |ret:&Baz| ret.baz == old*2 })] +// Relevant thing is this: ^^^^^^^^^^^^^^^ +// because we are capturing state that is Copy +fn doubler(x: Baz) -> Baz { + Baz { baz: x.baz + 10 } +} + +fn main() { + assert_eq!(doubler(Baz { baz: 10 }).baz, 20); + assert_eq!(doubler(Baz { baz: 100 }).baz, 200); + // This is a *run-fail* test because it is still exercising the + // contract machinery, specifically because this second invocation + // of `doubler` shows how the code does not meet its contract. +} diff --git a/tests/ui/contracts/contract-captures-via-closure-noncopy.rs b/tests/ui/contracts/contract-captures-via-closure-noncopy.rs new file mode 100644 index 000000000000..8d7f2fd200e2 --- /dev/null +++ b/tests/ui/contracts/contract-captures-via-closure-noncopy.rs @@ -0,0 +1,22 @@ +//@ compile-flags: -Zcontract-checks=yes + +#![feature(rustc_contracts)] + +struct Baz { + baz: i32 +} + +#[track_caller] +#[core::contracts::requires(x.baz > 0)] +#[core::contracts::ensures({let old = x; move |ret:&Baz| ret.baz == old.baz*2 })] +// Relevant thing is this: ^^^^^^^^^^^ +// because we are capturing state that is non-Copy. +//~^^^ ERROR trait bound `Baz: std::marker::Copy` is not satisfied +fn doubler(x: Baz) -> Baz { + Baz { baz: x.baz + 10 } +} + +fn main() { + assert_eq!(doubler(Baz { baz: 10 }).baz, 20); + assert_eq!(doubler(Baz { baz: 100 }).baz, 200); +} diff --git a/tests/ui/contracts/contract-captures-via-closure-noncopy.stderr b/tests/ui/contracts/contract-captures-via-closure-noncopy.stderr new file mode 100644 index 000000000000..b53809827f98 --- /dev/null +++ b/tests/ui/contracts/contract-captures-via-closure-noncopy.stderr @@ -0,0 +1,27 @@ +error[E0277]: the trait bound `Baz: std::marker::Copy` is not satisfied in `{closure@$DIR/contract-captures-via-closure-noncopy.rs:11:42: 11:57}` + --> $DIR/contract-captures-via-closure-noncopy.rs:11:1 + | +LL | #[core::contracts::ensures({let old = x; move |ret:&Baz| ret.baz == old.baz*2 })] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------------------------------------^^^^ + | | | + | | within this `{closure@$DIR/contract-captures-via-closure-noncopy.rs:11:42: 11:57}` + | | this tail expression is of type `{closure@contract-captures-via-closure-noncopy.rs:11:42}` + | unsatisfied trait bound + | + = help: within `{closure@$DIR/contract-captures-via-closure-noncopy.rs:11:42: 11:57}`, the trait `std::marker::Copy` is not implemented for `Baz` +note: required because it's used within this closure + --> $DIR/contract-captures-via-closure-noncopy.rs:11:42 + | +LL | #[core::contracts::ensures({let old = x; move |ret:&Baz| ret.baz == old.baz*2 })] + | ^^^^^^^^^^^^^^^ +note: required by a bound in `build_check_ensures` + --> $SRC_DIR/core/src/contracts.rs:LL:COL +help: consider annotating `Baz` with `#[derive(Copy)]` + | +LL + #[derive(Copy)] +LL | struct Baz { + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. From 6a6c6b891bb0350b3f16abd3e84ff12dbd1b4c5b Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 3 Dec 2024 16:13:00 +0000 Subject: [PATCH 39/57] Separate contract feature gates for the internal machinery The extended syntax for function signature that includes contract clauses should never be user exposed versus the interface we want to ship externally eventually. --- compiler/rustc_ast_passes/src/feature_gate.rs | 2 + .../rustc_builtin_macros/src/contracts.rs | 20 +++-- compiler/rustc_feature/src/unstable.rs | 4 + compiler/rustc_parse/src/parser/generics.rs | 20 ++++- compiler/rustc_session/src/parse.rs | 5 ++ compiler/rustc_span/src/symbol.rs | 2 + library/core/src/contracts.rs | 4 +- library/core/src/intrinsics/mod.rs | 8 +- library/core/src/lib.rs | 2 +- library/core/src/macros/mod.rs | 8 +- .../contract-ast-extensions-nest.rs | 2 +- .../contract-ast-extensions-tail.rs | 2 +- .../contract-intrinsics.rs | 2 +- .../contract-lang-items.rs | 3 +- ...g-ensures-is-not-inherited-when-nesting.rs | 2 +- ...-requires-is-not-inherited-when-nesting.rs | 2 +- .../internal-feature-gating.rs | 23 ++++++ .../internal-feature-gating.stderr | 73 +++++++++++++++++++ .../feature-gate-rustc-contracts.rs | 11 +++ .../feature-gate-rustc-contracts.stderr | 43 +++++++++++ 20 files changed, 210 insertions(+), 28 deletions(-) rename tests/ui/contracts/{ => internal_machinery}/contract-ast-extensions-nest.rs (96%) rename tests/ui/contracts/{ => internal_machinery}/contract-ast-extensions-tail.rs (96%) rename tests/ui/contracts/{ => internal_machinery}/contract-intrinsics.rs (91%) rename tests/ui/contracts/{ => internal_machinery}/contract-lang-items.rs (90%) rename tests/ui/contracts/{ => internal_machinery}/contracts-lowering-ensures-is-not-inherited-when-nesting.rs (84%) rename tests/ui/contracts/{ => internal_machinery}/contracts-lowering-requires-is-not-inherited-when-nesting.rs (88%) create mode 100644 tests/ui/contracts/internal_machinery/internal-feature-gating.rs create mode 100644 tests/ui/contracts/internal_machinery/internal-feature-gating.stderr create mode 100644 tests/ui/feature-gates/feature-gate-rustc-contracts.rs create mode 100644 tests/ui/feature-gates/feature-gate-rustc-contracts.stderr diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 80b99f944856..a3af942a10f1 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -548,6 +548,8 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(pin_ergonomics, "pinned reference syntax is experimental"); gate_all!(unsafe_fields, "`unsafe` fields are experimental"); gate_all!(unsafe_binders, "unsafe binder types are experimental"); + gate_all!(rustc_contracts, "contracts are experimental"); + gate_all!(rustc_contracts_internals, "contract internal machinery is for internal use only"); if !visitor.features.never_patterns() { if let Some(spans) = spans.get(&sym::never_patterns) { diff --git a/compiler/rustc_builtin_macros/src/contracts.rs b/compiler/rustc_builtin_macros/src/contracts.rs index d269287b555b..be7f276cdaa9 100644 --- a/compiler/rustc_builtin_macros/src/contracts.rs +++ b/compiler/rustc_builtin_macros/src/contracts.rs @@ -36,17 +36,17 @@ impl AttrProcMacro for ExpandEnsures { } fn expand_injecting_circa_where_clause( - _ecx: &mut ExtCtxt<'_>, + ecx: &mut ExtCtxt<'_>, attr_span: Span, annotated: TokenStream, inject: impl FnOnce(&mut Vec) -> Result<(), ErrorGuaranteed>, ) -> Result { let mut new_tts = Vec::with_capacity(annotated.len()); - let mut cursor = annotated.into_trees(); + let mut cursor = annotated.iter(); // Find the `fn name(x:X,...)` and inject the AST contract forms right after // the formal parameters (and return type if any). - while let Some(tt) = cursor.next_ref() { + while let Some(tt) = cursor.next() { new_tts.push(tt.clone()); if let TokenTree::Token(tok, _) = tt && tok.is_ident_named(kw::Fn) @@ -58,7 +58,7 @@ fn expand_injecting_circa_where_clause( // Found the `fn` keyword, now find the formal parameters. // // FIXME: can this fail if you have parentheticals in a generics list, like `fn foo Y>` ? - while let Some(tt) = cursor.next_ref() { + while let Some(tt) = cursor.next() { new_tts.push(tt.clone()); if let TokenTree::Delimited(_, _, token::Delimiter::Parenthesis, _) = tt { @@ -81,7 +81,7 @@ fn expand_injecting_circa_where_clause( // parse the type expression itself. But rather than try to fix things with hacks like that, // time might be better spent extending the attribute expander to suport tt-annotation atop // ast-annotated, which would be an elegant way to sidestep all of this. - let mut opt_next_tt = cursor.next_ref(); + let mut opt_next_tt = cursor.next(); while let Some(next_tt) = opt_next_tt { if let TokenTree::Token(tok, _) = next_tt && tok.is_ident_named(kw::Where) @@ -97,8 +97,7 @@ fn expand_injecting_circa_where_clause( // for anything else, transcribe the tt and keep looking. new_tts.push(next_tt.clone()); - opt_next_tt = cursor.next_ref(); - continue; + opt_next_tt = cursor.next(); } // At this point, we've transcribed everything from the `fn` through the formal parameter list @@ -118,10 +117,15 @@ fn expand_injecting_circa_where_clause( if let Some(tt) = opt_next_tt { new_tts.push(tt.clone()); } - while let Some(tt) = cursor.next_ref() { + while let Some(tt) = cursor.next() { new_tts.push(tt.clone()); } + // Record the span as a contract attribute expansion. + // This is used later to stop users from using the extended syntax directly + // which is gated via `rustc_contracts_internals`. + ecx.psess().contract_attribute_spans.push(attr_span); + Ok(TokenStream::new(new_tts)) } diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 08a5e22db3a5..57bcd8c5eca2 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -608,6 +608,10 @@ declare_features! ( (unstable, return_type_notation, "1.70.0", Some(109417)), /// Allows `extern "rust-cold"`. (unstable, rust_cold_cc, "1.63.0", Some(97544)), + /// Allows use of contracts attributes. + (unstable, rustc_contracts, "CURRENT_RUSTC_VERSION", Some(133866)), + /// Allows access to internal machinery used to implement contracts. + (unstable, rustc_contracts_internals, "CURRENT_RUSTC_VERSION", Some(133866)), /// Allows use of x86 SHA512, SM3 and SM4 target-features and intrinsics (unstable, sha512_sm_x86, "1.82.0", Some(126624)), /// Allows the use of SIMD types in functions declared in `extern` blocks. diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index d5ac8d1588d7..14b949dbc3d5 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -4,7 +4,7 @@ use rustc_ast::{ WhereClause, token, }; use rustc_errors::{Applicability, PResult}; -use rustc_span::{Ident, Span, kw}; +use rustc_span::{Ident, Span, kw, sym}; use thin_vec::ThinVec; use super::{ForceCollect, Parser, Trailing, UsePreAttrPos}; @@ -302,13 +302,27 @@ impl<'a> Parser<'a> { pub(super) fn parse_contract( &mut self, ) -> PResult<'a, Option>> { + let gate = |span| { + if self.psess.contract_attribute_spans.contains(span) { + // span was generated via a builtin contracts attribute, so gate as end-user visible + self.psess.gated_spans.gate(sym::rustc_contracts, span); + } else { + // span was not generated via a builtin contracts attribute, so gate as internal machinery + self.psess.gated_spans.gate(sym::rustc_contracts_internals, span); + } + }; + let requires = if self.eat_keyword_noexpect(exp!(RustcContractRequires).kw) { - Some(self.parse_expr()?) + let precond = self.parse_expr()?; + gate(precond.span); + Some(precond) } else { None }; let ensures = if self.eat_keyword_noexpect(exp!(RustcContractEnsures).kw) { - Some(self.parse_expr()?) + let postcond = self.parse_expr()?; + gate(postcond.span); + Some(postcond) } else { None }; diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 81ae06602cdb..abfd3efc6117 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -207,6 +207,10 @@ pub struct ParseSess { pub config: Cfg, pub check_config: CheckCfg, pub edition: Edition, + /// Places where contract attributes were expanded into unstable AST forms. + /// This is used to allowlist those spans (so that we only check them against the feature + /// gate for the externally visible interface, and not internal implmentation machinery). + pub contract_attribute_spans: AppendOnlyVec, /// Places where raw identifiers were used. This is used to avoid complaining about idents /// clashing with keywords in new editions. pub raw_identifier_spans: AppendOnlyVec, @@ -255,6 +259,7 @@ impl ParseSess { config: Cfg::default(), check_config: CheckCfg::default(), edition: ExpnId::root().expn_data().edition, + contract_attribute_spans: Default::default(), raw_identifier_spans: Default::default(), bad_unicode_identifiers: Lock::new(Default::default()), source_map, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 43bc69e6e7cf..ea2ce5475c26 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1716,6 +1716,8 @@ symbols! { rustc_const_stable, rustc_const_stable_indirect, rustc_const_unstable, + rustc_contracts, + rustc_contracts_internals, rustc_conversion_suggestion, rustc_deallocator, rustc_def_path, diff --git a/library/core/src/contracts.rs b/library/core/src/contracts.rs index 3efd2df0a383..b155dbc213ee 100644 --- a/library/core/src/contracts.rs +++ b/library/core/src/contracts.rs @@ -8,7 +8,7 @@ pub use crate::macros::builtin::contracts_requires as requires; /// Emitted by rustc as a desugaring of `#[requires(PRED)] fn foo(x: X) { ... }` /// into: `fn foo(x: X) { check_requires(|| PRED) ... }` #[cfg(not(bootstrap))] -#[unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)] +#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)] #[lang = "contract_check_requires"] #[track_caller] pub fn check_requires bool>(c: C) { @@ -21,7 +21,7 @@ pub fn check_requires bool>(c: C) { /// into: `fn foo() { let _check = build_check_ensures(|ret| PRED) ... [return _check(R);] ... }` /// (including the implicit return of the tail expression, if any). #[cfg(not(bootstrap))] -#[unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)] +#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)] #[lang = "contract_build_check_ensures"] #[track_caller] pub fn build_check_ensures(c: C) -> impl (FnOnce(Ret) -> Ret) + Copy diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index beea0996775a..a7f0f09f0c6b 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -4051,8 +4051,8 @@ pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) /// checking is turned on, so that we can specify contracts in libstd /// and let an end user opt into turning them on. #[cfg(not(bootstrap))] -#[rustc_const_unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)] -#[unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)] +#[rustc_const_unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)] +#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)] #[inline(always)] #[rustc_intrinsic] pub const fn contract_checks() -> bool { @@ -4063,14 +4063,14 @@ pub const fn contract_checks() -> bool { } #[cfg(not(bootstrap))] -#[unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)] +#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)] #[rustc_intrinsic] pub fn contract_check_requires bool>(c: C) -> bool { c() } #[cfg(not(bootstrap))] -#[unstable(feature = "rustc_contracts", issue = "none" /* compiler-team#759 */)] +#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)] #[rustc_intrinsic] pub fn contract_check_ensures<'a, Ret, C: FnOnce(&'a Ret) -> bool>(ret: &'a Ret, c: C) -> bool { c(ret) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index a31cca5d425c..6a0051244f06 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -248,7 +248,7 @@ pub mod autodiff { } #[cfg(not(bootstrap))] -#[unstable(feature = "rustc_contracts", issue = "none")] +#[unstable(feature = "rustc_contracts", issue = "133866")] pub mod contracts; #[unstable(feature = "cfg_match", issue = "115585")] diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 1a5d2973dfc3..cb37530e90db 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1783,8 +1783,8 @@ pub(crate) mod builtin { /// eventually parsed as a unary closure expression that is /// invoked on a reference to the return value. #[cfg(not(bootstrap))] - #[unstable(feature = "rustc_contracts", issue = "none")] - #[allow_internal_unstable(core_intrinsics)] + #[unstable(feature = "rustc_contracts", issue = "133866")] + #[allow_internal_unstable(rustc_contracts_internals)] #[rustc_builtin_macro] pub macro contracts_ensures($item:item) { /* compiler built-in */ @@ -1796,8 +1796,8 @@ pub(crate) mod builtin { /// eventually parsed as an boolean expression with access to the /// function's formal parameters #[cfg(not(bootstrap))] - #[unstable(feature = "rustc_contracts", issue = "none")] - #[allow_internal_unstable(core_intrinsics)] + #[unstable(feature = "rustc_contracts", issue = "133866")] + #[allow_internal_unstable(rustc_contracts_internals)] #[rustc_builtin_macro] pub macro contracts_requires($item:item) { /* compiler built-in */ diff --git a/tests/ui/contracts/contract-ast-extensions-nest.rs b/tests/ui/contracts/internal_machinery/contract-ast-extensions-nest.rs similarity index 96% rename from tests/ui/contracts/contract-ast-extensions-nest.rs rename to tests/ui/contracts/internal_machinery/contract-ast-extensions-nest.rs index ed137c4a9867..d95ccd4f7b9b 100644 --- a/tests/ui/contracts/contract-ast-extensions-nest.rs +++ b/tests/ui/contracts/internal_machinery/contract-ast-extensions-nest.rs @@ -16,7 +16,7 @@ //@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes //@ [chk_fail_post] compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts)] +#![feature(rustc_contracts_internals)] fn nest(x: Baz) -> i32 rustc_contract_requires(|| x.baz > 0) diff --git a/tests/ui/contracts/contract-ast-extensions-tail.rs b/tests/ui/contracts/internal_machinery/contract-ast-extensions-tail.rs similarity index 96% rename from tests/ui/contracts/contract-ast-extensions-tail.rs rename to tests/ui/contracts/internal_machinery/contract-ast-extensions-tail.rs index b501c3faaed6..636a595e06ad 100644 --- a/tests/ui/contracts/contract-ast-extensions-tail.rs +++ b/tests/ui/contracts/internal_machinery/contract-ast-extensions-tail.rs @@ -16,7 +16,7 @@ //@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes //@ [chk_fail_post] compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts)] +#![feature(rustc_contracts_internals)] fn tail(x: Baz) -> i32 rustc_contract_requires(|| x.baz > 0) diff --git a/tests/ui/contracts/contract-intrinsics.rs b/tests/ui/contracts/internal_machinery/contract-intrinsics.rs similarity index 91% rename from tests/ui/contracts/contract-intrinsics.rs rename to tests/ui/contracts/internal_machinery/contract-intrinsics.rs index 6e3565baf7ac..2e1be01e7cab 100644 --- a/tests/ui/contracts/contract-intrinsics.rs +++ b/tests/ui/contracts/internal_machinery/contract-intrinsics.rs @@ -2,7 +2,7 @@ //@ revisions: yes no none //@ [yes] compile-flags: -Zcontract-checks=yes //@ [no] compile-flags: -Zcontract-checks=no -#![feature(cfg_contract_checks, rustc_contracts, core_intrinsics)] +#![feature(cfg_contract_checks, rustc_contracts_internals, core_intrinsics)] fn main() { #[cfg(none)] // default: disabled diff --git a/tests/ui/contracts/contract-lang-items.rs b/tests/ui/contracts/internal_machinery/contract-lang-items.rs similarity index 90% rename from tests/ui/contracts/contract-lang-items.rs rename to tests/ui/contracts/internal_machinery/contract-lang-items.rs index 1dbf71722fd7..5af467485b10 100644 --- a/tests/ui/contracts/contract-lang-items.rs +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.rs @@ -16,7 +16,8 @@ //@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes //@ [chk_fail_post] compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts)] +#![feature(rustc_contracts)] // to access core::contracts +#![feature(rustc_contracts_internals)] // to access check_requires lang item fn foo(x: Baz) -> i32 { core::contracts::check_requires(|| x.baz > 0); diff --git a/tests/ui/contracts/contracts-lowering-ensures-is-not-inherited-when-nesting.rs b/tests/ui/contracts/internal_machinery/contracts-lowering-ensures-is-not-inherited-when-nesting.rs similarity index 84% rename from tests/ui/contracts/contracts-lowering-ensures-is-not-inherited-when-nesting.rs rename to tests/ui/contracts/internal_machinery/contracts-lowering-ensures-is-not-inherited-when-nesting.rs index 069f26e6796b..0b0151c6df74 100644 --- a/tests/ui/contracts/contracts-lowering-ensures-is-not-inherited-when-nesting.rs +++ b/tests/ui/contracts/internal_machinery/contracts-lowering-ensures-is-not-inherited-when-nesting.rs @@ -1,6 +1,6 @@ //@ run-pass //@ compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts)] +#![feature(rustc_contracts_internals)] fn outer() -> i32 rustc_contract_ensures(|ret| *ret > 0) diff --git a/tests/ui/contracts/contracts-lowering-requires-is-not-inherited-when-nesting.rs b/tests/ui/contracts/internal_machinery/contracts-lowering-requires-is-not-inherited-when-nesting.rs similarity index 88% rename from tests/ui/contracts/contracts-lowering-requires-is-not-inherited-when-nesting.rs rename to tests/ui/contracts/internal_machinery/contracts-lowering-requires-is-not-inherited-when-nesting.rs index fbee8277a968..79c50a18f705 100644 --- a/tests/ui/contracts/contracts-lowering-requires-is-not-inherited-when-nesting.rs +++ b/tests/ui/contracts/internal_machinery/contracts-lowering-requires-is-not-inherited-when-nesting.rs @@ -1,6 +1,6 @@ //@ run-pass //@ compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts)] +#![feature(rustc_contracts_internals)] struct Outer { outer: std::cell::Cell } diff --git a/tests/ui/contracts/internal_machinery/internal-feature-gating.rs b/tests/ui/contracts/internal_machinery/internal-feature-gating.rs new file mode 100644 index 000000000000..7b5f17679421 --- /dev/null +++ b/tests/ui/contracts/internal_machinery/internal-feature-gating.rs @@ -0,0 +1,23 @@ +// gate-test-rustc_contracts_internals + +fn main() { + // intrinsics are guarded by rustc_contracts_internals feature gate. + core::intrinsics::contract_checks(); + //~^ ERROR use of unstable library feature `rustc_contracts_internals` + core::intrinsics::contract_check_requires(|| true); + //~^ ERROR use of unstable library feature `rustc_contracts_internals` + core::intrinsics::contract_check_ensures(&1, |_|true); + //~^ ERROR use of unstable library feature `rustc_contracts_internals` + + // lang items are guarded by rustc_contracts_internals feature gate. + core::contracts::check_requires(|| true); + //~^ ERROR use of unstable library feature `rustc_contracts_internals` + core::contracts::build_check_ensures(|_: &()| true); + //~^ ERROR use of unstable library feature `rustc_contracts_internals` + + // ast extensions are guarded by rustc_contracts_internals feature gate + fn identity_1() -> i32 rustc_contract_requires(|| true) { 10 } + //~^ ERROR contract internal machinery is for internal use only + fn identity_2() -> i32 rustc_contract_ensures(|_| true) { 10 } + //~^ ERROR contract internal machinery is for internal use only +} diff --git a/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr b/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr new file mode 100644 index 000000000000..2acd03b9a358 --- /dev/null +++ b/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr @@ -0,0 +1,73 @@ +error[E0658]: contract internal machinery is for internal use only + --> $DIR/internal-feature-gating.rs:19:51 + | +LL | fn identity_1() -> i32 rustc_contract_requires(|| true) { 10 } + | ^^^^^^^^^ + | + = note: see issue #133866 for more information + = help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: contract internal machinery is for internal use only + --> $DIR/internal-feature-gating.rs:21:50 + | +LL | fn identity_2() -> i32 rustc_contract_ensures(|_| true) { 10 } + | ^^^^^^^^^^ + | + = note: see issue #133866 for more information + = help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `rustc_contracts_internals` + --> $DIR/internal-feature-gating.rs:5:5 + | +LL | core::intrinsics::contract_checks(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #133866 for more information + = help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `rustc_contracts_internals` + --> $DIR/internal-feature-gating.rs:7:5 + | +LL | core::intrinsics::contract_check_requires(|| true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #133866 for more information + = help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `rustc_contracts_internals` + --> $DIR/internal-feature-gating.rs:9:5 + | +LL | core::intrinsics::contract_check_ensures(&1, |_|true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #133866 for more information + = help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `rustc_contracts_internals` + --> $DIR/internal-feature-gating.rs:13:5 + | +LL | core::contracts::check_requires(|| true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #133866 for more information + = help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `rustc_contracts_internals` + --> $DIR/internal-feature-gating.rs:15:5 + | +LL | core::contracts::build_check_ensures(|_: &()| true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #133866 for more information + = help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-rustc-contracts.rs b/tests/ui/feature-gates/feature-gate-rustc-contracts.rs new file mode 100644 index 000000000000..d4249c252cda --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-rustc-contracts.rs @@ -0,0 +1,11 @@ +#![crate_type = "lib"] + +#[core::contracts::requires(x > 0)] +pub fn requires_needs_it(x: i32) { } +//~^^ ERROR use of unstable library feature `rustc_contracts` +//~^^^ ERROR contracts are experimental + +#[core::contracts::ensures(|ret| *ret > 0)] +pub fn ensures_needs_it() -> i32 { 10 } +//~^^ ERROR use of unstable library feature `rustc_contracts` +//~^^^ ERROR contracts are experimental diff --git a/tests/ui/feature-gates/feature-gate-rustc-contracts.stderr b/tests/ui/feature-gates/feature-gate-rustc-contracts.stderr new file mode 100644 index 000000000000..eb7777a4a517 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-rustc-contracts.stderr @@ -0,0 +1,43 @@ +error[E0658]: use of unstable library feature `rustc_contracts` + --> $DIR/feature-gate-rustc-contracts.rs:3:3 + | +LL | #[core::contracts::requires(x > 0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #133866 for more information + = help: add `#![feature(rustc_contracts)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `rustc_contracts` + --> $DIR/feature-gate-rustc-contracts.rs:8:3 + | +LL | #[core::contracts::ensures(|ret| *ret > 0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #133866 for more information + = help: add `#![feature(rustc_contracts)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: contracts are experimental + --> $DIR/feature-gate-rustc-contracts.rs:3:1 + | +LL | #[core::contracts::requires(x > 0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #133866 for more information + = help: add `#![feature(rustc_contracts)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: contracts are experimental + --> $DIR/feature-gate-rustc-contracts.rs:8:1 + | +LL | #[core::contracts::ensures(|ret| *ret > 0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #133866 for more information + = help: add `#![feature(rustc_contracts)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0658`. From 804cce47d96d7b30f3798b51a1377c6697011c54 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Wed, 15 Jan 2025 13:54:04 -0800 Subject: [PATCH 40/57] Refactor contract builtin macro + error handling Instead of parsing the different components of a function signature, eagerly look for either the `where` keyword or the function body. - Also address feedback to use `From` instead of `TryFrom` in cranelift contract and ubcheck codegen. --- .../rustc_builtin_macros/src/contracts.rs | 144 +++++++++--------- compiler/rustc_codegen_cranelift/src/base.rs | 4 +- .../contract-annotation-limitations.rs | 27 ++++ .../contract-annotation-limitations.stderr | 14 ++ .../contracts/contract-attributes-generics.rs | 70 +++++++++ .../disallow-contract-annotation-on-non-fn.rs | 52 +++++++ ...allow-contract-annotation-on-non-fn.stderr | 44 ++++++ 7 files changed, 281 insertions(+), 74 deletions(-) create mode 100644 tests/ui/contracts/contract-annotation-limitations.rs create mode 100644 tests/ui/contracts/contract-annotation-limitations.stderr create mode 100644 tests/ui/contracts/contract-attributes-generics.rs create mode 100644 tests/ui/contracts/disallow-contract-annotation-on-non-fn.rs create mode 100644 tests/ui/contracts/disallow-contract-annotation-on-non-fn.stderr diff --git a/compiler/rustc_builtin_macros/src/contracts.rs b/compiler/rustc_builtin_macros/src/contracts.rs index be7f276cdaa9..fbdd8af99542 100644 --- a/compiler/rustc_builtin_macros/src/contracts.rs +++ b/compiler/rustc_builtin_macros/src/contracts.rs @@ -35,90 +35,90 @@ impl AttrProcMacro for ExpandEnsures { } } -fn expand_injecting_circa_where_clause( +/// Expand the function signature to include the contract clause. +/// +/// The contracts clause will be injected before the function body and the optional where clause. +/// For that, we search for the body / where token, and invoke the `inject` callback to generate the +/// contract clause in the right place. +/// +// FIXME: this kind of manual token tree munging does not have significant precedent among +// rustc builtin macros, probably because most builtin macros use direct AST manipulation to +// accomplish similar goals. But since our attributes need to take arbitrary expressions, and +// our attribute infrastructure does not yet support mixing a token-tree annotation with an AST +// annotated, we end up doing token tree manipulation. +fn expand_contract_clause( ecx: &mut ExtCtxt<'_>, attr_span: Span, annotated: TokenStream, - inject: impl FnOnce(&mut Vec) -> Result<(), ErrorGuaranteed>, + inject: impl FnOnce(&mut TokenStream) -> Result<(), ErrorGuaranteed>, ) -> Result { - let mut new_tts = Vec::with_capacity(annotated.len()); + let mut new_tts = TokenStream::default(); let mut cursor = annotated.iter(); - // Find the `fn name(x:X,...)` and inject the AST contract forms right after - // the formal parameters (and return type if any). - while let Some(tt) = cursor.next() { - new_tts.push(tt.clone()); - if let TokenTree::Token(tok, _) = tt - && tok.is_ident_named(kw::Fn) - { - break; - } + let is_kw = |tt: &TokenTree, sym: Symbol| { + if let TokenTree::Token(token, _) = tt { token.is_ident_named(sym) } else { false } + }; + + // Find the `fn` keyword to check if this is a function. + if cursor + .find(|tt| { + new_tts.push_tree((*tt).clone()); + is_kw(tt, kw::Fn) + }) + .is_none() + { + return Err(ecx + .sess + .dcx() + .span_err(attr_span, "contract annotations can only be used on functions")); } - // Found the `fn` keyword, now find the formal parameters. - // - // FIXME: can this fail if you have parentheticals in a generics list, like `fn foo Y>` ? - while let Some(tt) = cursor.next() { - new_tts.push(tt.clone()); - - if let TokenTree::Delimited(_, _, token::Delimiter::Parenthesis, _) = tt { - break; - } - if let TokenTree::Token(token::Token { kind: token::TokenKind::Semi, .. }, _) = tt { - panic!("contract attribute applied to fn without parameter list."); - } - } - - // There *might* be a return type declaration (and figuring out where that ends would require - // parsing an arbitrary type expression, e.g. `-> Foo` - // - // Instead of trying to figure that out, scan ahead and look for the first occurence of a - // `where`, a `{ ... }`, or a `;`. - // - // FIXME: this might still fall into a trap for something like `-> Ctor`. I - // *think* such cases must be under a Delimited (e.g. `[T; { N }]` or have the braced form - // prefixed by e.g. `const`, so we should still be able to filter them out without having to - // parse the type expression itself. But rather than try to fix things with hacks like that, - // time might be better spent extending the attribute expander to suport tt-annotation atop - // ast-annotated, which would be an elegant way to sidestep all of this. - let mut opt_next_tt = cursor.next(); - while let Some(next_tt) = opt_next_tt { - if let TokenTree::Token(tok, _) = next_tt - && tok.is_ident_named(kw::Where) - { - break; - } - if let TokenTree::Delimited(_, _, token::Delimiter::Brace, _) = next_tt { - break; - } - if let TokenTree::Token(token::Token { kind: token::TokenKind::Semi, .. }, _) = next_tt { - break; + // Found the `fn` keyword, now find either the `where` token or the function body. + let next_tt = loop { + let Some(tt) = cursor.next() else { + return Err(ecx.sess.dcx().span_err( + attr_span, + "contract annotations is only supported in functions with bodies", + )); + }; + // If `tt` is the last element. Check if it is the function body. + if cursor.peek().is_none() { + if let TokenTree::Delimited(_, _, token::Delimiter::Brace, _) = tt { + break tt; + } else { + return Err(ecx.sess.dcx().span_err( + attr_span, + "contract annotations is only supported in functions with bodies", + )); + } } - // for anything else, transcribe the tt and keep looking. - new_tts.push(next_tt.clone()); - opt_next_tt = cursor.next(); - } + if is_kw(tt, kw::Where) { + break tt; + } + new_tts.push_tree(tt.clone()); + }; // At this point, we've transcribed everything from the `fn` through the formal parameter list // and return type declaration, (if any), but `tt` itself has *not* been transcribed. // // Now inject the AST contract form. // - // FIXME: this kind of manual token tree munging does not have significant precedent among - // rustc builtin macros, probably because most builtin macros use direct AST manipulation to - // accomplish similar goals. But since our attributes need to take arbitrary expressions, and - // our attribute infrastructure does not yet support mixing a token-tree annotation with an AST - // annotated, we end up doing token tree manipulation. inject(&mut new_tts)?; - // Above we injected the internal AST requires/ensures contruct. Now copy over all the other + // Above we injected the internal AST requires/ensures construct. Now copy over all the other // token trees. - if let Some(tt) = opt_next_tt { - new_tts.push(tt.clone()); - } + new_tts.push_tree(next_tt.clone()); while let Some(tt) = cursor.next() { - new_tts.push(tt.clone()); + new_tts.push_tree(tt.clone()); + if cursor.peek().is_none() + && !matches!(tt, TokenTree::Delimited(_, _, token::Delimiter::Brace, _)) + { + return Err(ecx.sess.dcx().span_err( + attr_span, + "contract annotations is only supported in functions with bodies", + )); + } } // Record the span as a contract attribute expansion. @@ -126,7 +126,7 @@ fn expand_injecting_circa_where_clause( // which is gated via `rustc_contracts_internals`. ecx.psess().contract_attribute_spans.push(attr_span); - Ok(TokenStream::new(new_tts)) + Ok(new_tts) } fn expand_requires_tts( @@ -135,16 +135,16 @@ fn expand_requires_tts( annotation: TokenStream, annotated: TokenStream, ) -> Result { - expand_injecting_circa_where_clause(_ecx, attr_span, annotated, |new_tts| { - new_tts.push(TokenTree::Token( + expand_contract_clause(_ecx, attr_span, annotated, |new_tts| { + new_tts.push_tree(TokenTree::Token( token::Token::from_ast_ident(Ident::new(kw::RustcContractRequires, attr_span)), Spacing::Joint, )); - new_tts.push(TokenTree::Token( + new_tts.push_tree(TokenTree::Token( token::Token::new(token::TokenKind::OrOr, attr_span), Spacing::Alone, )); - new_tts.push(TokenTree::Delimited( + new_tts.push_tree(TokenTree::Delimited( DelimSpan::from_single(attr_span), DelimSpacing::new(Spacing::JointHidden, Spacing::JointHidden), token::Delimiter::Parenthesis, @@ -160,12 +160,12 @@ fn expand_ensures_tts( annotation: TokenStream, annotated: TokenStream, ) -> Result { - expand_injecting_circa_where_clause(_ecx, attr_span, annotated, |new_tts| { - new_tts.push(TokenTree::Token( + expand_contract_clause(_ecx, attr_span, annotated, |new_tts| { + new_tts.push_tree(TokenTree::Token( token::Token::from_ast_ident(Ident::new(kw::RustcContractEnsures, attr_span)), Spacing::Joint, )); - new_tts.push(TokenTree::Delimited( + new_tts.push_tree(TokenTree::Delimited( DelimSpan::from_single(attr_span), DelimSpacing::new(Spacing::JointHidden, Spacing::JointHidden), token::Delimiter::Parenthesis, diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index de2ce1768fa1..a2b9e5712e50 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -868,7 +868,7 @@ fn codegen_stmt<'tcx>( NullOp::UbChecks => { let val = fx.tcx.sess.ub_checks(); let val = CValue::by_val( - fx.bcx.ins().iconst(types::I8, i64::try_from(val).unwrap()), + fx.bcx.ins().iconst(types::I8, i64::from(val)), fx.layout_of(fx.tcx.types.bool), ); lval.write_cvalue(fx, val); @@ -877,7 +877,7 @@ fn codegen_stmt<'tcx>( NullOp::ContractChecks => { let val = fx.tcx.sess.contract_checks(); let val = CValue::by_val( - fx.bcx.ins().iconst(types::I8, i64::try_from(val).unwrap()), + fx.bcx.ins().iconst(types::I8, i64::from(val)), fx.layout_of(fx.tcx.types.bool), ); lval.write_cvalue(fx, val); diff --git a/tests/ui/contracts/contract-annotation-limitations.rs b/tests/ui/contracts/contract-annotation-limitations.rs new file mode 100644 index 000000000000..f01d526e3f70 --- /dev/null +++ b/tests/ui/contracts/contract-annotation-limitations.rs @@ -0,0 +1,27 @@ +//! Test for some of the existing limitations and the current error messages. +//! Some of these limitations may be removed in the future. + +#![feature(rustc_contracts)] +#![allow(dead_code)] + +/// Represent a 5-star system. +struct Stars(u8); + +impl Stars { + fn is_valid(&self) -> bool { + self.0 <= 5 + } +} + +trait ParseStars { + #[core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] + //~^ ERROR contract annotations is only supported in functions with bodies + fn parse_string(input: String) -> Option; + + #[core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] + //~^ ERROR contract annotations is only supported in functions with bodies + fn parse(input: T) -> Option where T: for<'a> Into<&'a str>; +} + +fn main() { +} diff --git a/tests/ui/contracts/contract-annotation-limitations.stderr b/tests/ui/contracts/contract-annotation-limitations.stderr new file mode 100644 index 000000000000..25b01744aac8 --- /dev/null +++ b/tests/ui/contracts/contract-annotation-limitations.stderr @@ -0,0 +1,14 @@ +error: contract annotations is only supported in functions with bodies + --> $DIR/contract-annotation-limitations.rs:17:5 + | +LL | #[core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: contract annotations is only supported in functions with bodies + --> $DIR/contract-annotation-limitations.rs:21:5 + | +LL | #[core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/contracts/contract-attributes-generics.rs b/tests/ui/contracts/contract-attributes-generics.rs new file mode 100644 index 000000000000..87088ce9de2c --- /dev/null +++ b/tests/ui/contracts/contract-attributes-generics.rs @@ -0,0 +1,70 @@ +//! Test that contracts can be applied to generic functions. + +//@ revisions: unchk_pass chk_pass chk_fail_pre chk_fail_post chk_const_fail +// +//@ [unchk_pass] run-pass +//@ [chk_pass] run-pass +// +//@ [chk_fail_pre] run-fail +//@ [chk_fail_post] run-fail +//@ [chk_const_fail] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +// +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes +//@ [chk_const_fail] compile-flags: -Zcontract-checks=yes + +#![feature(rustc_contracts)] + +use std::ops::Sub; + +/// Dummy fn contract that precondition fails for val < 0, and post-condition fail for val == 1 +#[core::contracts::requires(val > 0u8.into())] +#[core::contracts::ensures(|ret| *ret > 0u8.into())] +fn decrement(val: T) -> T +where T: PartialOrd + Sub + From +{ + val - 1u8.into() +} + +/// Create a structure that takes a constant parameter. +#[allow(dead_code)] +struct Capped(usize); + +/// Now declare a function to create stars which shouldn't exceed 5 stars. +// Add redundant braces to ensure the built-in macro can handle this syntax. +#[allow(unused_braces)] +#[core::contracts::requires(num <= 5)] +unsafe fn stars_unchecked(num: usize) -> Capped<{ 5 }> { + Capped(num) +} + + +fn main() { + check_decrement(); + check_stars(); +} + +fn check_stars() { + // This should always pass. + let _ = unsafe { stars_unchecked(3) }; + + // This violates the contract. + #[cfg(any(unchk_pass, chk_const_fail))] + let _ = unsafe { stars_unchecked(10) }; +} + +fn check_decrement() { + // This should always pass + assert_eq!(decrement(10u8), 9u8); + + // This should fail requires but pass with no contract check. + #[cfg(any(unchk_pass, chk_fail_pre))] + assert_eq!(decrement(-2i128), -3i128); + + // This should fail ensures but pass with no contract check. + #[cfg(any(unchk_pass, chk_fail_post))] + assert_eq!(decrement(1i32), 0i32); +} diff --git a/tests/ui/contracts/disallow-contract-annotation-on-non-fn.rs b/tests/ui/contracts/disallow-contract-annotation-on-non-fn.rs new file mode 100644 index 000000000000..76ed30e85646 --- /dev/null +++ b/tests/ui/contracts/disallow-contract-annotation-on-non-fn.rs @@ -0,0 +1,52 @@ +//! Checks for compilation errors related to adding contracts to non-function items. + +#![feature(rustc_contracts)] +#![allow(dead_code)] + +#[core::contracts::requires(true)] +//~^ ERROR contract annotations can only be used on functions +struct Dummy(usize); + +#[core::contracts::ensures(|v| v == 100)] +//~^ ERROR contract annotations can only be used on functions +const MAX_VAL: usize = 100; + +// FIXME: Improve the error message here. The macro thinks this is a function. +#[core::contracts::ensures(|v| v == 100)] +//~^ ERROR contract annotations is only supported in functions with bodies +type NewDummy = fn(usize) -> Dummy; + +#[core::contracts::ensures(|v| v == 100)] +//~^ ERROR contract annotations is only supported in functions with bodies +const NEW_DUMMY_FN : fn(usize) -> Dummy = || { Dummy(0) }; + +#[core::contracts::requires(true)] +//~^ ERROR contract annotations can only be used on functions +impl Dummy { + + // This should work + #[core::contracts::ensures(|ret| ret.0 == v)] + fn new(v: usize) -> Dummy { + Dummy(v) + } +} + +#[core::contracts::ensures(|dummy| dummy.0 > 0)] +//~^ ERROR contract annotations can only be used on functions +impl From for Dummy { + // This should work. + #[core::contracts::ensures(|ret| ret.0 == v)] + fn from(value: usize) -> Self { + Dummy::new(value) + } +} + +/// You should not be able to annotate a trait either. +#[core::contracts::requires(true)] +//~^ ERROR contract annotations can only be used on functions +pub trait DummyBuilder { + fn build() -> Dummy; +} + +fn main() { +} diff --git a/tests/ui/contracts/disallow-contract-annotation-on-non-fn.stderr b/tests/ui/contracts/disallow-contract-annotation-on-non-fn.stderr new file mode 100644 index 000000000000..4d6d23340ac0 --- /dev/null +++ b/tests/ui/contracts/disallow-contract-annotation-on-non-fn.stderr @@ -0,0 +1,44 @@ +error: contract annotations can only be used on functions + --> $DIR/disallow-contract-annotation-on-non-fn.rs:6:1 + | +LL | #[core::contracts::requires(true)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: contract annotations can only be used on functions + --> $DIR/disallow-contract-annotation-on-non-fn.rs:10:1 + | +LL | #[core::contracts::ensures(|v| v == 100)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: contract annotations is only supported in functions with bodies + --> $DIR/disallow-contract-annotation-on-non-fn.rs:15:1 + | +LL | #[core::contracts::ensures(|v| v == 100)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: contract annotations is only supported in functions with bodies + --> $DIR/disallow-contract-annotation-on-non-fn.rs:19:1 + | +LL | #[core::contracts::ensures(|v| v == 100)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: contract annotations can only be used on functions + --> $DIR/disallow-contract-annotation-on-non-fn.rs:23:1 + | +LL | #[core::contracts::requires(true)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: contract annotations can only be used on functions + --> $DIR/disallow-contract-annotation-on-non-fn.rs:34:1 + | +LL | #[core::contracts::ensures(|dummy| dummy.0 > 0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: contract annotations can only be used on functions + --> $DIR/disallow-contract-annotation-on-non-fn.rs:45:1 + | +LL | #[core::contracts::requires(true)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + From 2bb1464cb6b46d175f92943cb0f9ab534e6cc6eb Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Fri, 17 Jan 2025 14:49:10 -0800 Subject: [PATCH 41/57] Improve contracts intrisics and remove wrapper function 1. Document the new intrinsics. 2. Make the intrinsics actually check the contract if enabled, and remove `contract::check_requires` function. 3. Use panic with no unwind in case contract is using to check for safety, we probably don't want to unwind. Following the same reasoning as UB checks. --- .../rustc_hir_analysis/src/check/intrinsic.rs | 10 ++--- library/core/src/contracts.rs | 27 +++----------- library/core/src/intrinsics/mod.rs | 22 +++++++++-- .../internal_machinery/contract-intrinsics.rs | 37 +++++++++++++------ .../internal_machinery/contract-lang-items.rs | 12 +----- .../internal-feature-gating.rs | 3 -- .../internal-feature-gating.stderr | 18 ++------- 7 files changed, 57 insertions(+), 72 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 6c0cebccefd9..e641fb0fb62e 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -223,17 +223,15 @@ pub fn check_intrinsic_type( }; (n_tps, 0, 0, inputs, output, hir::Safety::Unsafe) } else if intrinsic_name == sym::contract_check_ensures { - // contract_check_ensures::<'a, Ret, C>(&'a Ret, C) -> bool + // contract_check_ensures::<'a, Ret, C>(&'a Ret, C) // where C: impl Fn(&'a Ret) -> bool, // - // so: two type params, one lifetime param, 0 const params, two inputs, returns boolean + // so: two type params, one lifetime param, 0 const params, two inputs, no return let p = generics.param_at(0, tcx); let r = ty::Region::new_early_param(tcx, p.to_early_bound_region_data()); let ref_ret = Ty::new_imm_ref(tcx, r, param(1)); - // let br = ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BrAnon }; - // let ref_ret = Ty::new_imm_ref(tcx, ty::Region::new_bound(tcx, ty::INNERMOST, br), param(0)); - (2, 1, 0, vec![ref_ret, param(2)], tcx.types.bool, hir::Safety::Safe) + (2, 1, 0, vec![ref_ret, param(2)], tcx.types.unit, hir::Safety::Safe) } else { let safety = intrinsic_operation_unsafety(tcx, intrinsic_id); let (n_tps, n_cts, inputs, output) = match intrinsic_name { @@ -628,7 +626,7 @@ pub fn check_intrinsic_type( // contract_checks() -> bool sym::contract_checks => (0, 0, Vec::new(), tcx.types.bool), // contract_check_requires::(C) -> bool, where C: impl Fn() -> bool - sym::contract_check_requires => (1, 0, vec![param(0)], tcx.types.bool), + sym::contract_check_requires => (1, 0, vec![param(0)], tcx.types.unit), sym::simd_eq | sym::simd_ne diff --git a/library/core/src/contracts.rs b/library/core/src/contracts.rs index b155dbc213ee..0668cacb92c6 100644 --- a/library/core/src/contracts.rs +++ b/library/core/src/contracts.rs @@ -1,38 +1,21 @@ //! Unstable module containing the unstable contracts lang items and attribute macros. +#![cfg(not(bootstrap))] -#[cfg(not(bootstrap))] -pub use crate::macros::builtin::contracts_ensures as ensures; -#[cfg(not(bootstrap))] -pub use crate::macros::builtin::contracts_requires as requires; - -/// Emitted by rustc as a desugaring of `#[requires(PRED)] fn foo(x: X) { ... }` -/// into: `fn foo(x: X) { check_requires(|| PRED) ... }` -#[cfg(not(bootstrap))] -#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)] -#[lang = "contract_check_requires"] -#[track_caller] -pub fn check_requires bool>(c: C) { - if core::intrinsics::contract_checks() { - assert!(core::intrinsics::contract_check_requires(c), "failed requires check"); - } -} +pub use crate::macros::builtin::{contracts_ensures as ensures, contracts_requires as requires}; /// Emitted by rustc as a desugaring of `#[ensures(PRED)] fn foo() -> R { ... [return R;] ... }` /// into: `fn foo() { let _check = build_check_ensures(|ret| PRED) ... [return _check(R);] ... }` /// (including the implicit return of the tail expression, if any). -#[cfg(not(bootstrap))] #[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)] #[lang = "contract_build_check_ensures"] #[track_caller] -pub fn build_check_ensures(c: C) -> impl (FnOnce(Ret) -> Ret) + Copy +pub fn build_check_ensures(cond: C) -> impl (Fn(Ret) -> Ret) + Copy where - C: for<'a> FnOnce(&'a Ret) -> bool + Copy + 'static, + C: for<'a> Fn(&'a Ret) -> bool + Copy + 'static, { #[track_caller] move |ret| { - if core::intrinsics::contract_checks() { - assert!(core::intrinsics::contract_check_ensures(&ret, c), "failed ensures check"); - } + crate::intrinsics::contract_check_ensures(&ret, cond); ret } } diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index a7f0f09f0c6b..14f8645d6f1e 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -4062,18 +4062,32 @@ pub const fn contract_checks() -> bool { false } +/// Check if the pre-condition `cond` has been met. +/// +/// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition +/// returns false. #[cfg(not(bootstrap))] #[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)] +#[lang = "contract_check_requires"] #[rustc_intrinsic] -pub fn contract_check_requires bool>(c: C) -> bool { - c() +pub fn contract_check_requires bool>(cond: C) { + if contract_checks() && !cond() { + // Emit no unwind panic in case this was a safety requirement. + crate::panicking::panic_nounwind("failed requires check"); + } } +/// Check if the post-condition `cond` has been met. +/// +/// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition +/// returns false. #[cfg(not(bootstrap))] #[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)] #[rustc_intrinsic] -pub fn contract_check_ensures<'a, Ret, C: FnOnce(&'a Ret) -> bool>(ret: &'a Ret, c: C) -> bool { - c(ret) +pub fn contract_check_ensures<'a, Ret, C: Fn(&'a Ret) -> bool>(ret: &'a Ret, cond: C) { + if contract_checks() && !cond(ret) { + crate::panicking::panic_nounwind("failed ensures check"); + } } /// The intrinsic will return the size stored in that vtable. diff --git a/tests/ui/contracts/internal_machinery/contract-intrinsics.rs b/tests/ui/contracts/internal_machinery/contract-intrinsics.rs index 2e1be01e7cab..8c70c1a85f6d 100644 --- a/tests/ui/contracts/internal_machinery/contract-intrinsics.rs +++ b/tests/ui/contracts/internal_machinery/contract-intrinsics.rs @@ -1,23 +1,36 @@ -//@ run-pass -//@ revisions: yes no none -//@ [yes] compile-flags: -Zcontract-checks=yes -//@ [no] compile-flags: -Zcontract-checks=no +//@ revisions: default unchk_pass chk_pass chk_fail_ensures chk_fail_requires +// +//@ [default] run-pass +//@ [unchk_pass] run-pass +//@ [chk_pass] run-pass +//@ [chk_fail_requires] run-fail +//@ [chk_fail_ensures] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_requires] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_ensures] compile-flags: -Zcontract-checks=yes #![feature(cfg_contract_checks, rustc_contracts_internals, core_intrinsics)] fn main() { - #[cfg(none)] // default: disabled + #[cfg(any(default, unchk_pass))] // default: disabled assert_eq!(core::intrinsics::contract_checks(), false); - #[cfg(yes)] // explicitly enabled + #[cfg(chk_pass)] // explicitly enabled assert_eq!(core::intrinsics::contract_checks(), true); - #[cfg(no)] // explicitly disabled - assert_eq!(core::intrinsics::contract_checks(), false); + // always pass + core::intrinsics::contract_check_requires(|| true); - assert_eq!(core::intrinsics::contract_check_requires(|| true), true); - assert_eq!(core::intrinsics::contract_check_requires(|| false), false); + // fail if enabled + #[cfg(any(default, unchk_pass, chk_fail_requires))] + core::intrinsics::contract_check_requires(|| false); let doubles_to_two = { let old = 2; move |ret| ret + ret == old }; - assert_eq!(core::intrinsics::contract_check_ensures(&1, doubles_to_two), true); - assert_eq!(core::intrinsics::contract_check_ensures(&2, doubles_to_two), false); + // Always pass + core::intrinsics::contract_check_ensures(&1, doubles_to_two); + + // Fail if enabled + #[cfg(any(default, unchk_pass, chk_fail_ensures))] + core::intrinsics::contract_check_ensures(&2, doubles_to_two); } diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.rs b/tests/ui/contracts/internal_machinery/contract-lang-items.rs index 5af467485b10..ff569e011f20 100644 --- a/tests/ui/contracts/internal_machinery/contract-lang-items.rs +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.rs @@ -1,27 +1,21 @@ -//@ revisions: unchk_pass unchk_fail_pre unchk_fail_post chk_pass chk_fail_pre chk_fail_post +//@ revisions: unchk_pass unchk_fail_post chk_pass chk_fail_post // //@ [unchk_pass] run-pass -//@ [unchk_fail_pre] run-pass //@ [unchk_fail_post] run-pass //@ [chk_pass] run-pass // -//@ [chk_fail_pre] run-fail //@ [chk_fail_post] run-fail // //@ [unchk_pass] compile-flags: -Zcontract-checks=no -//@ [unchk_fail_pre] compile-flags: -Zcontract-checks=no //@ [unchk_fail_post] compile-flags: -Zcontract-checks=no // //@ [chk_pass] compile-flags: -Zcontract-checks=yes -//@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes //@ [chk_fail_post] compile-flags: -Zcontract-checks=yes #![feature(rustc_contracts)] // to access core::contracts #![feature(rustc_contracts_internals)] // to access check_requires lang item fn foo(x: Baz) -> i32 { - core::contracts::check_requires(|| x.baz > 0); - let injected_checker = { core::contracts::build_check_ensures(|ret| *ret > 100) }; @@ -36,13 +30,9 @@ struct Baz { baz: i32 } const BAZ_PASS_PRE_POST: Baz = Baz { baz: 100 }; #[cfg(any(unchk_fail_post, chk_fail_post))] const BAZ_FAIL_POST: Baz = Baz { baz: 10 }; -#[cfg(any(unchk_fail_pre, chk_fail_pre))] -const BAZ_FAIL_PRE: Baz = Baz { baz: -10 }; fn main() { assert_eq!(foo(BAZ_PASS_PRE_POST), 150); - #[cfg(any(unchk_fail_pre, chk_fail_pre))] - foo(BAZ_FAIL_PRE); #[cfg(any(unchk_fail_post, chk_fail_post))] foo(BAZ_FAIL_POST); } diff --git a/tests/ui/contracts/internal_machinery/internal-feature-gating.rs b/tests/ui/contracts/internal_machinery/internal-feature-gating.rs index 7b5f17679421..ee9edf3ebb0c 100644 --- a/tests/ui/contracts/internal_machinery/internal-feature-gating.rs +++ b/tests/ui/contracts/internal_machinery/internal-feature-gating.rs @@ -9,9 +9,6 @@ fn main() { core::intrinsics::contract_check_ensures(&1, |_|true); //~^ ERROR use of unstable library feature `rustc_contracts_internals` - // lang items are guarded by rustc_contracts_internals feature gate. - core::contracts::check_requires(|| true); - //~^ ERROR use of unstable library feature `rustc_contracts_internals` core::contracts::build_check_ensures(|_: &()| true); //~^ ERROR use of unstable library feature `rustc_contracts_internals` diff --git a/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr b/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr index 2acd03b9a358..5f9263e03e85 100644 --- a/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr +++ b/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr @@ -1,5 +1,5 @@ error[E0658]: contract internal machinery is for internal use only - --> $DIR/internal-feature-gating.rs:19:51 + --> $DIR/internal-feature-gating.rs:16:51 | LL | fn identity_1() -> i32 rustc_contract_requires(|| true) { 10 } | ^^^^^^^^^ @@ -9,7 +9,7 @@ LL | fn identity_1() -> i32 rustc_contract_requires(|| true) { 10 } = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: contract internal machinery is for internal use only - --> $DIR/internal-feature-gating.rs:21:50 + --> $DIR/internal-feature-gating.rs:18:50 | LL | fn identity_2() -> i32 rustc_contract_ensures(|_| true) { 10 } | ^^^^^^^^^^ @@ -49,17 +49,7 @@ LL | core::intrinsics::contract_check_ensures(&1, |_|true); = 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 `rustc_contracts_internals` - --> $DIR/internal-feature-gating.rs:13:5 - | -LL | core::contracts::check_requires(|| true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #133866 for more information - = help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: use of unstable library feature `rustc_contracts_internals` - --> $DIR/internal-feature-gating.rs:15:5 + --> $DIR/internal-feature-gating.rs:12:5 | LL | core::contracts::build_check_ensures(|_: &()| true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -68,6 +58,6 @@ LL | core::contracts::build_check_ensures(|_: &()| true); = help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0658`. From 2c4923e6bc9608557f0bc59f975006e590fd337d Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Thu, 30 Jan 2025 13:09:14 -0800 Subject: [PATCH 42/57] Update test output to include check_contracts cfg This is now a valid expected value. --- tests/ui/check-cfg/cargo-build-script.stderr | 2 +- tests/ui/check-cfg/cargo-feature.none.stderr | 2 +- tests/ui/check-cfg/cargo-feature.some.stderr | 2 +- tests/ui/check-cfg/cfg-value-for-cfg-name-duplicate.stderr | 2 +- tests/ui/check-cfg/cfg-value-for-cfg-name-multiple.stderr | 2 +- tests/ui/check-cfg/exhaustive-names-values.feature.stderr | 2 +- tests/ui/check-cfg/exhaustive-names-values.full.stderr | 2 +- tests/ui/check-cfg/mix.stderr | 2 +- tests/ui/check-cfg/raw-keywords.edition2015.stderr | 2 +- tests/ui/check-cfg/raw-keywords.edition2021.stderr | 2 +- tests/ui/check-cfg/report-in-external-macros.cargo.stderr | 2 +- tests/ui/check-cfg/report-in-external-macros.rustc.stderr | 2 +- tests/ui/check-cfg/well-known-names.stderr | 1 + 13 files changed, 13 insertions(+), 12 deletions(-) diff --git a/tests/ui/check-cfg/cargo-build-script.stderr b/tests/ui/check-cfg/cargo-build-script.stderr index df0bc47571c7..03a7156a4d69 100644 --- a/tests/ui/check-cfg/cargo-build-script.stderr +++ b/tests/ui/check-cfg/cargo-build-script.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `has_foo` LL | #[cfg(has_foo)] | ^^^^^^^ | - = help: expected names are: `has_bar` and 30 more + = help: expected names are: `has_bar` and 31 more = help: consider using a Cargo feature instead = help: or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint: [lints.rust] diff --git a/tests/ui/check-cfg/cargo-feature.none.stderr b/tests/ui/check-cfg/cargo-feature.none.stderr index 58813a1f6770..b83d1794984d 100644 --- a/tests/ui/check-cfg/cargo-feature.none.stderr +++ b/tests/ui/check-cfg/cargo-feature.none.stderr @@ -25,7 +25,7 @@ warning: unexpected `cfg` condition name: `tokio_unstable` LL | #[cfg(tokio_unstable)] | ^^^^^^^^^^^^^^ | - = help: expected names are: `docsrs`, `feature`, and `test` and 30 more + = help: expected names are: `docsrs`, `feature`, and `test` and 31 more = help: consider using a Cargo feature instead = help: or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint: [lints.rust] diff --git a/tests/ui/check-cfg/cargo-feature.some.stderr b/tests/ui/check-cfg/cargo-feature.some.stderr index 5a12be813387..2cddcbbcd7f9 100644 --- a/tests/ui/check-cfg/cargo-feature.some.stderr +++ b/tests/ui/check-cfg/cargo-feature.some.stderr @@ -25,7 +25,7 @@ warning: unexpected `cfg` condition name: `tokio_unstable` LL | #[cfg(tokio_unstable)] | ^^^^^^^^^^^^^^ | - = help: expected names are: `CONFIG_NVME`, `docsrs`, `feature`, and `test` and 30 more + = help: expected names are: `CONFIG_NVME`, `docsrs`, `feature`, and `test` and 31 more = help: consider using a Cargo feature instead = help: or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint: [lints.rust] diff --git a/tests/ui/check-cfg/cfg-value-for-cfg-name-duplicate.stderr b/tests/ui/check-cfg/cfg-value-for-cfg-name-duplicate.stderr index 7c276c581707..68e1259dbb84 100644 --- a/tests/ui/check-cfg/cfg-value-for-cfg-name-duplicate.stderr +++ b/tests/ui/check-cfg/cfg-value-for-cfg-name-duplicate.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `value` LL | #[cfg(value)] | ^^^^^ | - = help: expected names are: `bar`, `bee`, `cow`, and `foo` and 30 more + = help: expected names are: `bar`, `bee`, `cow`, and `foo` and 31 more = help: to expect this configuration use `--check-cfg=cfg(value)` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/cfg-value-for-cfg-name-multiple.stderr b/tests/ui/check-cfg/cfg-value-for-cfg-name-multiple.stderr index 9687a043e832..138c7fc75849 100644 --- a/tests/ui/check-cfg/cfg-value-for-cfg-name-multiple.stderr +++ b/tests/ui/check-cfg/cfg-value-for-cfg-name-multiple.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `my_value` LL | #[cfg(my_value)] | ^^^^^^^^ | - = help: expected names are: `bar` and `foo` and 30 more + = help: expected names are: `bar` and `foo` and 31 more = help: to expect this configuration use `--check-cfg=cfg(my_value)` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/exhaustive-names-values.feature.stderr b/tests/ui/check-cfg/exhaustive-names-values.feature.stderr index 10302f0a7e46..af66cbd81894 100644 --- a/tests/ui/check-cfg/exhaustive-names-values.feature.stderr +++ b/tests/ui/check-cfg/exhaustive-names-values.feature.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `unknown_key` LL | #[cfg(unknown_key = "value")] | ^^^^^^^^^^^^^^^^^^^^^ | - = help: expected names are: `feature` and 30 more + = help: expected names are: `feature` and 31 more = help: to expect this configuration use `--check-cfg=cfg(unknown_key, values("value"))` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/exhaustive-names-values.full.stderr b/tests/ui/check-cfg/exhaustive-names-values.full.stderr index 10302f0a7e46..af66cbd81894 100644 --- a/tests/ui/check-cfg/exhaustive-names-values.full.stderr +++ b/tests/ui/check-cfg/exhaustive-names-values.full.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `unknown_key` LL | #[cfg(unknown_key = "value")] | ^^^^^^^^^^^^^^^^^^^^^ | - = help: expected names are: `feature` and 30 more + = help: expected names are: `feature` and 31 more = help: to expect this configuration use `--check-cfg=cfg(unknown_key, values("value"))` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/mix.stderr b/tests/ui/check-cfg/mix.stderr index 033aaef848f7..be4d7c772763 100644 --- a/tests/ui/check-cfg/mix.stderr +++ b/tests/ui/check-cfg/mix.stderr @@ -44,7 +44,7 @@ warning: unexpected `cfg` condition name: `uu` LL | #[cfg_attr(uu, unix)] | ^^ | - = help: expected names are: `feature` and 30 more + = help: expected names are: `feature` and 31 more = help: to expect this configuration use `--check-cfg=cfg(uu)` = note: see for more information about checking conditional configuration diff --git a/tests/ui/check-cfg/raw-keywords.edition2015.stderr b/tests/ui/check-cfg/raw-keywords.edition2015.stderr index f19ded9cb671..8ca33e088fc9 100644 --- a/tests/ui/check-cfg/raw-keywords.edition2015.stderr +++ b/tests/ui/check-cfg/raw-keywords.edition2015.stderr @@ -14,7 +14,7 @@ warning: unexpected `cfg` condition name: `r#false` LL | #[cfg(r#false)] | ^^^^^^^ | - = help: expected names are: `async`, `edition2015`, `edition2021`, and `r#true` and 30 more + = help: expected names are: `async`, `edition2015`, `edition2021`, and `r#true` and 31 more = help: to expect this configuration use `--check-cfg=cfg(r#false)` = note: see for more information about checking conditional configuration diff --git a/tests/ui/check-cfg/raw-keywords.edition2021.stderr b/tests/ui/check-cfg/raw-keywords.edition2021.stderr index 6096148a259b..cce55720bdd1 100644 --- a/tests/ui/check-cfg/raw-keywords.edition2021.stderr +++ b/tests/ui/check-cfg/raw-keywords.edition2021.stderr @@ -14,7 +14,7 @@ warning: unexpected `cfg` condition name: `r#false` LL | #[cfg(r#false)] | ^^^^^^^ | - = help: expected names are: `r#async`, `edition2015`, `edition2021`, and `r#true` and 30 more + = help: expected names are: `r#async`, `edition2015`, `edition2021`, and `r#true` and 31 more = help: to expect this configuration use `--check-cfg=cfg(r#false)` = note: see for more information about checking conditional configuration diff --git a/tests/ui/check-cfg/report-in-external-macros.cargo.stderr b/tests/ui/check-cfg/report-in-external-macros.cargo.stderr index a6584d777a3a..989a01f22441 100644 --- a/tests/ui/check-cfg/report-in-external-macros.cargo.stderr +++ b/tests/ui/check-cfg/report-in-external-macros.cargo.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `my_lib_cfg` LL | cfg_macro::my_lib_macro!(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: expected names are: `feature` and 30 more + = help: expected names are: `feature` and 31 more = note: using a cfg inside a macro will use the cfgs from the destination crate and not the ones from the defining crate = help: try referring to `cfg_macro::my_lib_macro` crate for guidance on how handle this unexpected cfg = help: the macro `cfg_macro::my_lib_macro` may come from an old version of the `cfg_macro` crate, try updating your dependency with `cargo update -p cfg_macro` diff --git a/tests/ui/check-cfg/report-in-external-macros.rustc.stderr b/tests/ui/check-cfg/report-in-external-macros.rustc.stderr index 914b5a0efe36..95d10e014f33 100644 --- a/tests/ui/check-cfg/report-in-external-macros.rustc.stderr +++ b/tests/ui/check-cfg/report-in-external-macros.rustc.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `my_lib_cfg` LL | cfg_macro::my_lib_macro!(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: expected names are: `feature` and 30 more + = help: expected names are: `feature` and 31 more = note: using a cfg inside a macro will use the cfgs from the destination crate and not the ones from the defining crate = help: try referring to `cfg_macro::my_lib_macro` crate for guidance on how handle this unexpected cfg = help: to expect this configuration use `--check-cfg=cfg(my_lib_cfg)` diff --git a/tests/ui/check-cfg/well-known-names.stderr b/tests/ui/check-cfg/well-known-names.stderr index 4ff90261158b..000315443f81 100644 --- a/tests/ui/check-cfg/well-known-names.stderr +++ b/tests/ui/check-cfg/well-known-names.stderr @@ -5,6 +5,7 @@ LL | #[cfg(list_all_well_known_cfgs)] | ^^^^^^^^^^^^^^^^^^^^^^^^ | = help: expected names are: `clippy` +`contract_checks` `debug_assertions` `doc` `doctest` From ddbf54b67d9befcf1fb90613d2a6f7f6aa03141e Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Thu, 30 Jan 2025 17:06:09 -0800 Subject: [PATCH 43/57] Rename rustc_contract to contract This has now been approved as a language feature and no longer needs a `rustc_` prefix. Also change the `contracts` feature to be marked as incomplete and `contracts_internals` as internal. --- compiler/rustc_ast_passes/src/feature_gate.rs | 4 +- .../rustc_builtin_macros/src/contracts.rs | 6 +-- compiler/rustc_feature/src/unstable.rs | 10 ++--- compiler/rustc_parse/src/parser/generics.rs | 12 ++--- compiler/rustc_parse/src/parser/token_type.rs | 16 +++---- compiler/rustc_span/src/symbol.rs | 8 ++-- library/core/src/contracts.rs | 2 +- library/core/src/intrinsics/mod.rs | 8 ++-- library/core/src/lib.rs | 2 +- library/core/src/macros/mod.rs | 8 ++-- .../contract-annotation-limitations.rs | 3 +- .../contract-annotation-limitations.stderr | 15 +++++-- ...-attributes-generics.chk_const_fail.stderr | 11 +++++ ...t-attributes-generics.chk_fail_post.stderr | 11 +++++ ...ct-attributes-generics.chk_fail_pre.stderr | 11 +++++ ...ntract-attributes-generics.chk_pass.stderr | 11 +++++ .../contracts/contract-attributes-generics.rs | 3 +- ...ract-attributes-generics.unchk_pass.stderr | 11 +++++ ...tract-attributes-nest.chk_fail_post.stderr | 11 +++++ ...ntract-attributes-nest.chk_fail_pre.stderr | 11 +++++ .../contract-attributes-nest.chk_pass.stderr | 11 +++++ .../ui/contracts/contract-attributes-nest.rs | 3 +- ...act-attributes-nest.unchk_fail_post.stderr | 11 +++++ ...ract-attributes-nest.unchk_fail_pre.stderr | 11 +++++ ...contract-attributes-nest.unchk_pass.stderr | 11 +++++ ...tract-attributes-tail.chk_fail_post.stderr | 11 +++++ ...ntract-attributes-tail.chk_fail_pre.stderr | 11 +++++ .../contract-attributes-tail.chk_pass.stderr | 11 +++++ .../ui/contracts/contract-attributes-tail.rs | 3 +- ...act-attributes-tail.unchk_fail_post.stderr | 11 +++++ ...ract-attributes-tail.unchk_fail_pre.stderr | 11 +++++ ...contract-attributes-tail.unchk_pass.stderr | 11 +++++ .../contract-captures-via-closure-copy.rs | 3 +- .../contract-captures-via-closure-copy.stderr | 11 +++++ .../contract-captures-via-closure-noncopy.rs | 3 +- ...ntract-captures-via-closure-noncopy.stderr | 23 +++++++--- ...-ensures-early-fn-exit.chk_fail_ret.stderr | 11 +++++ ...-ensures-early-fn-exit.chk_fail_try.stderr | 11 +++++ ...ensures-early-fn-exit.chk_fail_yeet.stderr | 11 +++++ ...acts-ensures-early-fn-exit.chk_pass.stderr | 11 +++++ .../contracts-ensures-early-fn-exit.rs | 3 +- ...ts-ensures-early-fn-exit.unchk_pass.stderr | 11 +++++ ...s-ensures-is-not-inherited-when-nesting.rs | 3 +- ...sures-is-not-inherited-when-nesting.stderr | 11 +++++ ...-requires-is-not-inherited-when-nesting.rs | 3 +- ...uires-is-not-inherited-when-nesting.stderr | 11 +++++ .../disallow-contract-annotation-on-non-fn.rs | 3 +- ...allow-contract-annotation-on-non-fn.stderr | 25 +++++++---- .../contract-ast-extensions-nest.rs | 6 +-- .../contract-ast-extensions-tail.rs | 6 +-- .../internal_machinery/contract-intrinsics.rs | 2 +- .../contract-lang-items.chk_fail_post.stderr | 11 +++++ .../contract-lang-items.chk_pass.stderr | 11 +++++ .../internal_machinery/contract-lang-items.rs | 5 ++- ...contract-lang-items.unchk_fail_post.stderr | 11 +++++ .../contract-lang-items.unchk_pass.stderr | 11 +++++ ...g-ensures-is-not-inherited-when-nesting.rs | 4 +- ...-requires-is-not-inherited-when-nesting.rs | 4 +- .../internal-feature-gating.rs | 18 ++++---- .../internal-feature-gating.stderr | 44 +++++++++---------- .../feature-gate-cfg-contract-checks.stderr | 2 +- .../feature-gates/feature-gate-contracts.rs | 11 +++++ .../feature-gate-contracts.stderr | 43 ++++++++++++++++++ .../feature-gate-rustc-contracts.rs | 11 ----- .../feature-gate-rustc-contracts.stderr | 43 ------------------ 65 files changed, 522 insertions(+), 165 deletions(-) create mode 100644 tests/ui/contracts/contract-attributes-generics.chk_const_fail.stderr create mode 100644 tests/ui/contracts/contract-attributes-generics.chk_fail_post.stderr create mode 100644 tests/ui/contracts/contract-attributes-generics.chk_fail_pre.stderr create mode 100644 tests/ui/contracts/contract-attributes-generics.chk_pass.stderr create mode 100644 tests/ui/contracts/contract-attributes-generics.unchk_pass.stderr create mode 100644 tests/ui/contracts/contract-attributes-nest.chk_fail_post.stderr create mode 100644 tests/ui/contracts/contract-attributes-nest.chk_fail_pre.stderr create mode 100644 tests/ui/contracts/contract-attributes-nest.chk_pass.stderr create mode 100644 tests/ui/contracts/contract-attributes-nest.unchk_fail_post.stderr create mode 100644 tests/ui/contracts/contract-attributes-nest.unchk_fail_pre.stderr create mode 100644 tests/ui/contracts/contract-attributes-nest.unchk_pass.stderr create mode 100644 tests/ui/contracts/contract-attributes-tail.chk_fail_post.stderr create mode 100644 tests/ui/contracts/contract-attributes-tail.chk_fail_pre.stderr create mode 100644 tests/ui/contracts/contract-attributes-tail.chk_pass.stderr create mode 100644 tests/ui/contracts/contract-attributes-tail.unchk_fail_post.stderr create mode 100644 tests/ui/contracts/contract-attributes-tail.unchk_fail_pre.stderr create mode 100644 tests/ui/contracts/contract-attributes-tail.unchk_pass.stderr create mode 100644 tests/ui/contracts/contract-captures-via-closure-copy.stderr create mode 100644 tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_ret.stderr create mode 100644 tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_try.stderr create mode 100644 tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_yeet.stderr create mode 100644 tests/ui/contracts/contracts-ensures-early-fn-exit.chk_pass.stderr create mode 100644 tests/ui/contracts/contracts-ensures-early-fn-exit.unchk_pass.stderr create mode 100644 tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.stderr create mode 100644 tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.stderr create mode 100644 tests/ui/contracts/internal_machinery/contract-lang-items.chk_fail_post.stderr create mode 100644 tests/ui/contracts/internal_machinery/contract-lang-items.chk_pass.stderr create mode 100644 tests/ui/contracts/internal_machinery/contract-lang-items.unchk_fail_post.stderr create mode 100644 tests/ui/contracts/internal_machinery/contract-lang-items.unchk_pass.stderr create mode 100644 tests/ui/feature-gates/feature-gate-contracts.rs create mode 100644 tests/ui/feature-gates/feature-gate-contracts.stderr delete mode 100644 tests/ui/feature-gates/feature-gate-rustc-contracts.rs delete mode 100644 tests/ui/feature-gates/feature-gate-rustc-contracts.stderr diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index a3af942a10f1..62e451fa8764 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -548,8 +548,8 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(pin_ergonomics, "pinned reference syntax is experimental"); gate_all!(unsafe_fields, "`unsafe` fields are experimental"); gate_all!(unsafe_binders, "unsafe binder types are experimental"); - gate_all!(rustc_contracts, "contracts are experimental"); - gate_all!(rustc_contracts_internals, "contract internal machinery is for internal use only"); + gate_all!(contracts, "contracts are incomplete"); + gate_all!(contracts_internals, "contract internal machinery is for internal use only"); if !visitor.features.never_patterns() { if let Some(spans) = spans.get(&sym::never_patterns) { diff --git a/compiler/rustc_builtin_macros/src/contracts.rs b/compiler/rustc_builtin_macros/src/contracts.rs index fbdd8af99542..85a30f7bdc9b 100644 --- a/compiler/rustc_builtin_macros/src/contracts.rs +++ b/compiler/rustc_builtin_macros/src/contracts.rs @@ -123,7 +123,7 @@ fn expand_contract_clause( // Record the span as a contract attribute expansion. // This is used later to stop users from using the extended syntax directly - // which is gated via `rustc_contracts_internals`. + // which is gated via `contracts_internals`. ecx.psess().contract_attribute_spans.push(attr_span); Ok(new_tts) @@ -137,7 +137,7 @@ fn expand_requires_tts( ) -> Result { expand_contract_clause(_ecx, attr_span, annotated, |new_tts| { new_tts.push_tree(TokenTree::Token( - token::Token::from_ast_ident(Ident::new(kw::RustcContractRequires, attr_span)), + token::Token::from_ast_ident(Ident::new(kw::ContractRequires, attr_span)), Spacing::Joint, )); new_tts.push_tree(TokenTree::Token( @@ -162,7 +162,7 @@ fn expand_ensures_tts( ) -> Result { expand_contract_clause(_ecx, attr_span, annotated, |new_tts| { new_tts.push_tree(TokenTree::Token( - token::Token::from_ast_ident(Ident::new(kw::RustcContractEnsures, attr_span)), + token::Token::from_ast_ident(Ident::new(kw::ContractEnsures, attr_span)), Spacing::Joint, )); new_tts.push_tree(TokenTree::Delimited( diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 57bcd8c5eca2..5699d4ce3b9b 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -404,7 +404,7 @@ declare_features! ( /// Allows the use of `#[cfg()]`. (unstable, cfg_boolean_literals, "1.83.0", Some(131204)), /// Allows the use of `#[cfg(contract_checks)` to check if contract checks are enabled. - (unstable, cfg_contract_checks, "CURRENT_RUSTC_VERSION", Some(133866)), + (unstable, cfg_contract_checks, "CURRENT_RUSTC_VERSION", Some(128044)), /// Allows the use of `#[cfg(overflow_checks)` to check if integer overflow behaviour. (unstable, cfg_overflow_checks, "1.71.0", Some(111466)), /// Provides the relocation model information as cfg entry @@ -447,6 +447,10 @@ declare_features! ( (unstable, const_trait_impl, "1.42.0", Some(67792)), /// Allows the `?` operator in const contexts. (unstable, const_try, "1.56.0", Some(74935)), + /// Allows use of contracts attributes. + (incomplete, contracts, "CURRENT_RUSTC_VERSION", Some(128044)), + /// Allows access to internal machinery used to implement contracts. + (internal, contracts_internals, "CURRENT_RUSTC_VERSION", Some(128044)), /// Allows coroutines to be cloned. (unstable, coroutine_clone, "1.65.0", Some(95360)), /// Allows defining coroutines. @@ -608,10 +612,6 @@ declare_features! ( (unstable, return_type_notation, "1.70.0", Some(109417)), /// Allows `extern "rust-cold"`. (unstable, rust_cold_cc, "1.63.0", Some(97544)), - /// Allows use of contracts attributes. - (unstable, rustc_contracts, "CURRENT_RUSTC_VERSION", Some(133866)), - /// Allows access to internal machinery used to implement contracts. - (unstable, rustc_contracts_internals, "CURRENT_RUSTC_VERSION", Some(133866)), /// Allows use of x86 SHA512, SM3 and SM4 target-features and intrinsics (unstable, sha512_sm_x86, "1.82.0", Some(126624)), /// Allows the use of SIMD types in functions declared in `extern` blocks. diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index 14b949dbc3d5..86816819be27 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -297,29 +297,29 @@ impl<'a> Parser<'a> { }) } - /// Parses a rustc-internal fn contract - /// (`rustc_contract_requires(WWW) rustc_contract_ensures(ZZZ)`) + /// Parses an experimental fn contract + /// (`contract_requires(WWW) contract_ensures(ZZZ)`) pub(super) fn parse_contract( &mut self, ) -> PResult<'a, Option>> { let gate = |span| { if self.psess.contract_attribute_spans.contains(span) { // span was generated via a builtin contracts attribute, so gate as end-user visible - self.psess.gated_spans.gate(sym::rustc_contracts, span); + self.psess.gated_spans.gate(sym::contracts, span); } else { // span was not generated via a builtin contracts attribute, so gate as internal machinery - self.psess.gated_spans.gate(sym::rustc_contracts_internals, span); + self.psess.gated_spans.gate(sym::contracts_internals, span); } }; - let requires = if self.eat_keyword_noexpect(exp!(RustcContractRequires).kw) { + let requires = if self.eat_keyword_noexpect(exp!(ContractRequires).kw) { let precond = self.parse_expr()?; gate(precond.span); Some(precond) } else { None }; - let ensures = if self.eat_keyword_noexpect(exp!(RustcContractEnsures).kw) { + let ensures = if self.eat_keyword_noexpect(exp!(ContractEnsures).kw) { let postcond = self.parse_expr()?; gate(postcond.span); Some(postcond) diff --git a/compiler/rustc_parse/src/parser/token_type.rs b/compiler/rustc_parse/src/parser/token_type.rs index 50f03e72f82d..40631d9154d3 100644 --- a/compiler/rustc_parse/src/parser/token_type.rs +++ b/compiler/rustc_parse/src/parser/token_type.rs @@ -83,6 +83,8 @@ pub enum TokenType { KwCatch, KwConst, KwContinue, + KwContractEnsures, + KwContractRequires, KwCrate, KwDefault, KwDyn, @@ -108,8 +110,6 @@ pub enum TokenType { KwRef, KwReturn, KwReuse, - KwRustcContractEnsures, - KwRustcContractRequires, KwSafe, KwSelfUpper, KwStatic, @@ -219,6 +219,8 @@ impl TokenType { KwCatch, KwConst, KwContinue, + KwContractEnsures, + KwContractRequires, KwCrate, KwDefault, KwDyn, @@ -244,8 +246,6 @@ impl TokenType { KwRef, KwReturn, KwReuse, - KwRustcContractEnsures, - KwRustcContractRequires, KwSafe, KwSelfUpper, KwStatic, @@ -293,6 +293,8 @@ impl TokenType { TokenType::KwCatch => Some(kw::Catch), TokenType::KwConst => Some(kw::Const), TokenType::KwContinue => Some(kw::Continue), + TokenType::KwContractEnsures => Some(kw::ContractEnsures), + TokenType::KwContractRequires => Some(kw::ContractRequires), TokenType::KwCrate => Some(kw::Crate), TokenType::KwDefault => Some(kw::Default), TokenType::KwDyn => Some(kw::Dyn), @@ -318,8 +320,6 @@ impl TokenType { TokenType::KwRef => Some(kw::Ref), TokenType::KwReturn => Some(kw::Return), TokenType::KwReuse => Some(kw::Reuse), - TokenType::KwRustcContractEnsures => Some(kw::RustcContractEnsures), - TokenType::KwRustcContractRequires => Some(kw::RustcContractRequires), TokenType::KwSafe => Some(kw::Safe), TokenType::KwSelfUpper => Some(kw::SelfUpper), TokenType::KwStatic => Some(kw::Static), @@ -525,6 +525,8 @@ macro_rules! exp { (Catch) => { exp!(@kw, Catch, KwCatch) }; (Const) => { exp!(@kw, Const, KwConst) }; (Continue) => { exp!(@kw, Continue, KwContinue) }; + (ContractEnsures) => { exp!(@kw, ContractEnsures, KwContractEnsures) }; + (ContractRequires) => { exp!(@kw, ContractRequires, KwContractRequires) }; (Crate) => { exp!(@kw, Crate, KwCrate) }; (Default) => { exp!(@kw, Default, KwDefault) }; (Dyn) => { exp!(@kw, Dyn, KwDyn) }; @@ -550,8 +552,6 @@ macro_rules! exp { (Ref) => { exp!(@kw, Ref, KwRef) }; (Return) => { exp!(@kw, Return, KwReturn) }; (Reuse) => { exp!(@kw, Reuse, KwReuse) }; - (RustcContractEnsures) => { exp!(@kw, RustcContractEnsures, KwRustcContractEnsures) }; - (RustcContractRequires) => { exp!(@kw, RustcContractRequires, KwRustcContractRequires) }; (Safe) => { exp!(@kw, Safe, KwSafe) }; (SelfUpper) => { exp!(@kw, SelfUpper, KwSelfUpper) }; (Static) => { exp!(@kw, Static, KwStatic) }; diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index ea2ce5475c26..529dfc6ff7a0 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -118,8 +118,8 @@ symbols! { MacroRules: "macro_rules", Raw: "raw", Reuse: "reuse", - RustcContractEnsures: "rustc_contract_ensures", - RustcContractRequires: "rustc_contract_requires", + ContractEnsures: "contract_ensures", + ContractRequires: "contract_requires", Safe: "safe", Union: "union", Yeet: "yeet", @@ -682,7 +682,9 @@ symbols! { contract_check_ensures, contract_check_requires, contract_checks, + contracts, contracts_ensures, + contracts_internals, contracts_requires, convert_identity, copy, @@ -1716,8 +1718,6 @@ symbols! { rustc_const_stable, rustc_const_stable_indirect, rustc_const_unstable, - rustc_contracts, - rustc_contracts_internals, rustc_conversion_suggestion, rustc_deallocator, rustc_def_path, diff --git a/library/core/src/contracts.rs b/library/core/src/contracts.rs index 0668cacb92c6..c769e219e4d4 100644 --- a/library/core/src/contracts.rs +++ b/library/core/src/contracts.rs @@ -6,7 +6,7 @@ pub use crate::macros::builtin::{contracts_ensures as ensures, contracts_require /// Emitted by rustc as a desugaring of `#[ensures(PRED)] fn foo() -> R { ... [return R;] ... }` /// into: `fn foo() { let _check = build_check_ensures(|ret| PRED) ... [return _check(R);] ... }` /// (including the implicit return of the tail expression, if any). -#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)] +#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] #[lang = "contract_build_check_ensures"] #[track_caller] pub fn build_check_ensures(cond: C) -> impl (Fn(Ret) -> Ret) + Copy diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 14f8645d6f1e..1e4dc12f9b61 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -4051,8 +4051,8 @@ pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) /// checking is turned on, so that we can specify contracts in libstd /// and let an end user opt into turning them on. #[cfg(not(bootstrap))] -#[rustc_const_unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)] -#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)] +#[rustc_const_unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] +#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] #[inline(always)] #[rustc_intrinsic] pub const fn contract_checks() -> bool { @@ -4067,7 +4067,7 @@ pub const fn contract_checks() -> bool { /// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition /// returns false. #[cfg(not(bootstrap))] -#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)] +#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] #[lang = "contract_check_requires"] #[rustc_intrinsic] pub fn contract_check_requires bool>(cond: C) { @@ -4082,7 +4082,7 @@ pub fn contract_check_requires bool>(cond: C) { /// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition /// returns false. #[cfg(not(bootstrap))] -#[unstable(feature = "rustc_contracts_internals", issue = "133866" /* compiler-team#759 */)] +#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] #[rustc_intrinsic] pub fn contract_check_ensures<'a, Ret, C: Fn(&'a Ret) -> bool>(ret: &'a Ret, cond: C) { if contract_checks() && !cond(ret) { diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 6a0051244f06..de8e85f7b36e 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -248,7 +248,7 @@ pub mod autodiff { } #[cfg(not(bootstrap))] -#[unstable(feature = "rustc_contracts", issue = "133866")] +#[unstable(feature = "contracts", issue = "128044")] pub mod contracts; #[unstable(feature = "cfg_match", issue = "115585")] diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index cb37530e90db..4c6fd196bd31 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1783,8 +1783,8 @@ pub(crate) mod builtin { /// eventually parsed as a unary closure expression that is /// invoked on a reference to the return value. #[cfg(not(bootstrap))] - #[unstable(feature = "rustc_contracts", issue = "133866")] - #[allow_internal_unstable(rustc_contracts_internals)] + #[unstable(feature = "contracts", issue = "128044")] + #[allow_internal_unstable(contracts_internals)] #[rustc_builtin_macro] pub macro contracts_ensures($item:item) { /* compiler built-in */ @@ -1796,8 +1796,8 @@ pub(crate) mod builtin { /// eventually parsed as an boolean expression with access to the /// function's formal parameters #[cfg(not(bootstrap))] - #[unstable(feature = "rustc_contracts", issue = "133866")] - #[allow_internal_unstable(rustc_contracts_internals)] + #[unstable(feature = "contracts", issue = "128044")] + #[allow_internal_unstable(contracts_internals)] #[rustc_builtin_macro] pub macro contracts_requires($item:item) { /* compiler built-in */ diff --git a/tests/ui/contracts/contract-annotation-limitations.rs b/tests/ui/contracts/contract-annotation-limitations.rs index f01d526e3f70..10b3bacab5cf 100644 --- a/tests/ui/contracts/contract-annotation-limitations.rs +++ b/tests/ui/contracts/contract-annotation-limitations.rs @@ -1,7 +1,8 @@ //! Test for some of the existing limitations and the current error messages. //! Some of these limitations may be removed in the future. -#![feature(rustc_contracts)] +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] #![allow(dead_code)] /// Represent a 5-star system. diff --git a/tests/ui/contracts/contract-annotation-limitations.stderr b/tests/ui/contracts/contract-annotation-limitations.stderr index 25b01744aac8..14338cf4b868 100644 --- a/tests/ui/contracts/contract-annotation-limitations.stderr +++ b/tests/ui/contracts/contract-annotation-limitations.stderr @@ -1,14 +1,23 @@ error: contract annotations is only supported in functions with bodies - --> $DIR/contract-annotation-limitations.rs:17:5 + --> $DIR/contract-annotation-limitations.rs:18:5 | LL | #[core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: contract annotations is only supported in functions with bodies - --> $DIR/contract-annotation-limitations.rs:21:5 + --> $DIR/contract-annotation-limitations.rs:22:5 | LL | #[core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-annotation-limitations.rs:4:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: aborting due to 2 previous errors; 1 warning emitted diff --git a/tests/ui/contracts/contract-attributes-generics.chk_const_fail.stderr b/tests/ui/contracts/contract-attributes-generics.chk_const_fail.stderr new file mode 100644 index 000000000000..0630811d4f7e --- /dev/null +++ b/tests/ui/contracts/contract-attributes-generics.chk_const_fail.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-generics.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-generics.chk_fail_post.stderr b/tests/ui/contracts/contract-attributes-generics.chk_fail_post.stderr new file mode 100644 index 000000000000..0630811d4f7e --- /dev/null +++ b/tests/ui/contracts/contract-attributes-generics.chk_fail_post.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-generics.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-generics.chk_fail_pre.stderr b/tests/ui/contracts/contract-attributes-generics.chk_fail_pre.stderr new file mode 100644 index 000000000000..0630811d4f7e --- /dev/null +++ b/tests/ui/contracts/contract-attributes-generics.chk_fail_pre.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-generics.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-generics.chk_pass.stderr b/tests/ui/contracts/contract-attributes-generics.chk_pass.stderr new file mode 100644 index 000000000000..0630811d4f7e --- /dev/null +++ b/tests/ui/contracts/contract-attributes-generics.chk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-generics.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-generics.rs b/tests/ui/contracts/contract-attributes-generics.rs index 87088ce9de2c..fd79c6abedd8 100644 --- a/tests/ui/contracts/contract-attributes-generics.rs +++ b/tests/ui/contracts/contract-attributes-generics.rs @@ -16,7 +16,8 @@ //@ [chk_fail_post] compile-flags: -Zcontract-checks=yes //@ [chk_const_fail] compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts)] +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] use std::ops::Sub; diff --git a/tests/ui/contracts/contract-attributes-generics.unchk_pass.stderr b/tests/ui/contracts/contract-attributes-generics.unchk_pass.stderr new file mode 100644 index 000000000000..0630811d4f7e --- /dev/null +++ b/tests/ui/contracts/contract-attributes-generics.unchk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-generics.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-nest.chk_fail_post.stderr b/tests/ui/contracts/contract-attributes-nest.chk_fail_post.stderr new file mode 100644 index 000000000000..9ca95b8bb01a --- /dev/null +++ b/tests/ui/contracts/contract-attributes-nest.chk_fail_post.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-nest.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-nest.chk_fail_pre.stderr b/tests/ui/contracts/contract-attributes-nest.chk_fail_pre.stderr new file mode 100644 index 000000000000..9ca95b8bb01a --- /dev/null +++ b/tests/ui/contracts/contract-attributes-nest.chk_fail_pre.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-nest.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-nest.chk_pass.stderr b/tests/ui/contracts/contract-attributes-nest.chk_pass.stderr new file mode 100644 index 000000000000..9ca95b8bb01a --- /dev/null +++ b/tests/ui/contracts/contract-attributes-nest.chk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-nest.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-nest.rs b/tests/ui/contracts/contract-attributes-nest.rs index 1cda21f10d7e..e1e61b88f282 100644 --- a/tests/ui/contracts/contract-attributes-nest.rs +++ b/tests/ui/contracts/contract-attributes-nest.rs @@ -16,7 +16,8 @@ //@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes //@ [chk_fail_post] compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts)] +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] #[core::contracts::requires(x.baz > 0)] #[core::contracts::ensures(|ret| *ret > 100)] diff --git a/tests/ui/contracts/contract-attributes-nest.unchk_fail_post.stderr b/tests/ui/contracts/contract-attributes-nest.unchk_fail_post.stderr new file mode 100644 index 000000000000..9ca95b8bb01a --- /dev/null +++ b/tests/ui/contracts/contract-attributes-nest.unchk_fail_post.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-nest.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-nest.unchk_fail_pre.stderr b/tests/ui/contracts/contract-attributes-nest.unchk_fail_pre.stderr new file mode 100644 index 000000000000..9ca95b8bb01a --- /dev/null +++ b/tests/ui/contracts/contract-attributes-nest.unchk_fail_pre.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-nest.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-nest.unchk_pass.stderr b/tests/ui/contracts/contract-attributes-nest.unchk_pass.stderr new file mode 100644 index 000000000000..9ca95b8bb01a --- /dev/null +++ b/tests/ui/contracts/contract-attributes-nest.unchk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-nest.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-tail.chk_fail_post.stderr b/tests/ui/contracts/contract-attributes-tail.chk_fail_post.stderr new file mode 100644 index 000000000000..f87e7e19fa3d --- /dev/null +++ b/tests/ui/contracts/contract-attributes-tail.chk_fail_post.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-tail.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-tail.chk_fail_pre.stderr b/tests/ui/contracts/contract-attributes-tail.chk_fail_pre.stderr new file mode 100644 index 000000000000..f87e7e19fa3d --- /dev/null +++ b/tests/ui/contracts/contract-attributes-tail.chk_fail_pre.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-tail.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-tail.chk_pass.stderr b/tests/ui/contracts/contract-attributes-tail.chk_pass.stderr new file mode 100644 index 000000000000..f87e7e19fa3d --- /dev/null +++ b/tests/ui/contracts/contract-attributes-tail.chk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-tail.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-tail.rs b/tests/ui/contracts/contract-attributes-tail.rs index 26855bfa82ac..ce4a6be5b82f 100644 --- a/tests/ui/contracts/contract-attributes-tail.rs +++ b/tests/ui/contracts/contract-attributes-tail.rs @@ -16,7 +16,8 @@ //@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes //@ [chk_fail_post] compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts)] +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] #[core::contracts::requires(x.baz > 0)] #[core::contracts::ensures(|ret| *ret > 100)] diff --git a/tests/ui/contracts/contract-attributes-tail.unchk_fail_post.stderr b/tests/ui/contracts/contract-attributes-tail.unchk_fail_post.stderr new file mode 100644 index 000000000000..f87e7e19fa3d --- /dev/null +++ b/tests/ui/contracts/contract-attributes-tail.unchk_fail_post.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-tail.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-tail.unchk_fail_pre.stderr b/tests/ui/contracts/contract-attributes-tail.unchk_fail_pre.stderr new file mode 100644 index 000000000000..f87e7e19fa3d --- /dev/null +++ b/tests/ui/contracts/contract-attributes-tail.unchk_fail_pre.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-tail.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-tail.unchk_pass.stderr b/tests/ui/contracts/contract-attributes-tail.unchk_pass.stderr new file mode 100644 index 000000000000..f87e7e19fa3d --- /dev/null +++ b/tests/ui/contracts/contract-attributes-tail.unchk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-tail.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-captures-via-closure-copy.rs b/tests/ui/contracts/contract-captures-via-closure-copy.rs index 742895ab0ad8..32c6d2bf4fe1 100644 --- a/tests/ui/contracts/contract-captures-via-closure-copy.rs +++ b/tests/ui/contracts/contract-captures-via-closure-copy.rs @@ -1,7 +1,8 @@ //@ run-fail //@ compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts)] +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] struct Baz { baz: i32 diff --git a/tests/ui/contracts/contract-captures-via-closure-copy.stderr b/tests/ui/contracts/contract-captures-via-closure-copy.stderr new file mode 100644 index 000000000000..d92db601608f --- /dev/null +++ b/tests/ui/contracts/contract-captures-via-closure-copy.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-captures-via-closure-copy.rs:4:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-captures-via-closure-noncopy.rs b/tests/ui/contracts/contract-captures-via-closure-noncopy.rs index 8d7f2fd200e2..976f64c7fd91 100644 --- a/tests/ui/contracts/contract-captures-via-closure-noncopy.rs +++ b/tests/ui/contracts/contract-captures-via-closure-noncopy.rs @@ -1,6 +1,7 @@ //@ compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts)] +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] struct Baz { baz: i32 diff --git a/tests/ui/contracts/contract-captures-via-closure-noncopy.stderr b/tests/ui/contracts/contract-captures-via-closure-noncopy.stderr index b53809827f98..4a47671fee19 100644 --- a/tests/ui/contracts/contract-captures-via-closure-noncopy.stderr +++ b/tests/ui/contracts/contract-captures-via-closure-noncopy.stderr @@ -1,16 +1,25 @@ -error[E0277]: the trait bound `Baz: std::marker::Copy` is not satisfied in `{closure@$DIR/contract-captures-via-closure-noncopy.rs:11:42: 11:57}` - --> $DIR/contract-captures-via-closure-noncopy.rs:11:1 +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-captures-via-closure-noncopy.rs:3:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0277]: the trait bound `Baz: std::marker::Copy` is not satisfied in `{closure@$DIR/contract-captures-via-closure-noncopy.rs:12:42: 12:57}` + --> $DIR/contract-captures-via-closure-noncopy.rs:12:1 | LL | #[core::contracts::ensures({let old = x; move |ret:&Baz| ret.baz == old.baz*2 })] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------------------------------------^^^^ | | | - | | within this `{closure@$DIR/contract-captures-via-closure-noncopy.rs:11:42: 11:57}` - | | this tail expression is of type `{closure@contract-captures-via-closure-noncopy.rs:11:42}` + | | within this `{closure@$DIR/contract-captures-via-closure-noncopy.rs:12:42: 12:57}` + | | this tail expression is of type `{closure@contract-captures-via-closure-noncopy.rs:12:42}` | unsatisfied trait bound | - = help: within `{closure@$DIR/contract-captures-via-closure-noncopy.rs:11:42: 11:57}`, the trait `std::marker::Copy` is not implemented for `Baz` + = help: within `{closure@$DIR/contract-captures-via-closure-noncopy.rs:12:42: 12:57}`, the trait `std::marker::Copy` is not implemented for `Baz` note: required because it's used within this closure - --> $DIR/contract-captures-via-closure-noncopy.rs:11:42 + --> $DIR/contract-captures-via-closure-noncopy.rs:12:42 | LL | #[core::contracts::ensures({let old = x; move |ret:&Baz| ret.baz == old.baz*2 })] | ^^^^^^^^^^^^^^^ @@ -22,6 +31,6 @@ LL + #[derive(Copy)] LL | struct Baz { | -error: aborting due to 1 previous error +error: aborting due to 1 previous error; 1 warning emitted For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_ret.stderr b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_ret.stderr new file mode 100644 index 000000000000..d693fad446a4 --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_ret.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contracts-ensures-early-fn-exit.rs:16:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_try.stderr b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_try.stderr new file mode 100644 index 000000000000..d693fad446a4 --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_try.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contracts-ensures-early-fn-exit.rs:16:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_yeet.stderr b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_yeet.stderr new file mode 100644 index 000000000000..d693fad446a4 --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_yeet.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contracts-ensures-early-fn-exit.rs:16:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_pass.stderr b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_pass.stderr new file mode 100644 index 000000000000..d693fad446a4 --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contracts-ensures-early-fn-exit.rs:16:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contracts-ensures-early-fn-exit.rs b/tests/ui/contracts/contracts-ensures-early-fn-exit.rs index faf97473a90f..034cead3b4e9 100644 --- a/tests/ui/contracts/contracts-ensures-early-fn-exit.rs +++ b/tests/ui/contracts/contracts-ensures-early-fn-exit.rs @@ -13,7 +13,8 @@ //@ [chk_fail_yeet] compile-flags: -Zcontract-checks=yes //! This test ensures that ensures clauses are checked for different return points of a function. -#![feature(rustc_contracts)] +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] #![feature(yeet_expr)] /// This ensures will fail in different return points depending on the input. diff --git a/tests/ui/contracts/contracts-ensures-early-fn-exit.unchk_pass.stderr b/tests/ui/contracts/contracts-ensures-early-fn-exit.unchk_pass.stderr new file mode 100644 index 000000000000..d693fad446a4 --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-early-fn-exit.unchk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contracts-ensures-early-fn-exit.rs:16:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.rs b/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.rs index 9872fdb1a185..f01a852fbff3 100644 --- a/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.rs +++ b/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.rs @@ -1,6 +1,7 @@ //@ run-pass //@ compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts)] +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] #[core::contracts::ensures(|ret| *ret > 0)] fn outer() -> i32 { diff --git a/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.stderr b/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.stderr new file mode 100644 index 000000000000..49a372b53c7d --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contracts-ensures-is-not-inherited-when-nesting.rs:3:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.rs b/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.rs index 75124259b0d7..2c2a4a697855 100644 --- a/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.rs +++ b/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.rs @@ -1,6 +1,7 @@ //@ run-pass //@ compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts)] +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] struct Outer { outer: std::cell::Cell } diff --git a/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.stderr b/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.stderr new file mode 100644 index 000000000000..48898c4434ad --- /dev/null +++ b/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contracts-requires-is-not-inherited-when-nesting.rs:3:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/disallow-contract-annotation-on-non-fn.rs b/tests/ui/contracts/disallow-contract-annotation-on-non-fn.rs index 76ed30e85646..69be906782a6 100644 --- a/tests/ui/contracts/disallow-contract-annotation-on-non-fn.rs +++ b/tests/ui/contracts/disallow-contract-annotation-on-non-fn.rs @@ -1,6 +1,7 @@ //! Checks for compilation errors related to adding contracts to non-function items. -#![feature(rustc_contracts)] +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] #![allow(dead_code)] #[core::contracts::requires(true)] diff --git a/tests/ui/contracts/disallow-contract-annotation-on-non-fn.stderr b/tests/ui/contracts/disallow-contract-annotation-on-non-fn.stderr index 4d6d23340ac0..0a7fff8183e0 100644 --- a/tests/ui/contracts/disallow-contract-annotation-on-non-fn.stderr +++ b/tests/ui/contracts/disallow-contract-annotation-on-non-fn.stderr @@ -1,44 +1,53 @@ error: contract annotations can only be used on functions - --> $DIR/disallow-contract-annotation-on-non-fn.rs:6:1 + --> $DIR/disallow-contract-annotation-on-non-fn.rs:7:1 | LL | #[core::contracts::requires(true)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: contract annotations can only be used on functions - --> $DIR/disallow-contract-annotation-on-non-fn.rs:10:1 + --> $DIR/disallow-contract-annotation-on-non-fn.rs:11:1 | LL | #[core::contracts::ensures(|v| v == 100)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: contract annotations is only supported in functions with bodies - --> $DIR/disallow-contract-annotation-on-non-fn.rs:15:1 + --> $DIR/disallow-contract-annotation-on-non-fn.rs:16:1 | LL | #[core::contracts::ensures(|v| v == 100)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: contract annotations is only supported in functions with bodies - --> $DIR/disallow-contract-annotation-on-non-fn.rs:19:1 + --> $DIR/disallow-contract-annotation-on-non-fn.rs:20:1 | LL | #[core::contracts::ensures(|v| v == 100)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: contract annotations can only be used on functions - --> $DIR/disallow-contract-annotation-on-non-fn.rs:23:1 + --> $DIR/disallow-contract-annotation-on-non-fn.rs:24:1 | LL | #[core::contracts::requires(true)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: contract annotations can only be used on functions - --> $DIR/disallow-contract-annotation-on-non-fn.rs:34:1 + --> $DIR/disallow-contract-annotation-on-non-fn.rs:35:1 | LL | #[core::contracts::ensures(|dummy| dummy.0 > 0)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: contract annotations can only be used on functions - --> $DIR/disallow-contract-annotation-on-non-fn.rs:45:1 + --> $DIR/disallow-contract-annotation-on-non-fn.rs:46:1 | LL | #[core::contracts::requires(true)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 7 previous errors +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/disallow-contract-annotation-on-non-fn.rs:3:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: aborting due to 7 previous errors; 1 warning emitted diff --git a/tests/ui/contracts/internal_machinery/contract-ast-extensions-nest.rs b/tests/ui/contracts/internal_machinery/contract-ast-extensions-nest.rs index d95ccd4f7b9b..6d8cd3949eed 100644 --- a/tests/ui/contracts/internal_machinery/contract-ast-extensions-nest.rs +++ b/tests/ui/contracts/internal_machinery/contract-ast-extensions-nest.rs @@ -16,11 +16,11 @@ //@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes //@ [chk_fail_post] compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts_internals)] +#![feature(contracts_internals)] fn nest(x: Baz) -> i32 - rustc_contract_requires(|| x.baz > 0) - rustc_contract_ensures(|ret| *ret > 100) + contract_requires(|| x.baz > 0) + contract_ensures(|ret| *ret > 100) { loop { return x.baz + 50; diff --git a/tests/ui/contracts/internal_machinery/contract-ast-extensions-tail.rs b/tests/ui/contracts/internal_machinery/contract-ast-extensions-tail.rs index 636a595e06ad..07ec26f921b8 100644 --- a/tests/ui/contracts/internal_machinery/contract-ast-extensions-tail.rs +++ b/tests/ui/contracts/internal_machinery/contract-ast-extensions-tail.rs @@ -16,11 +16,11 @@ //@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes //@ [chk_fail_post] compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts_internals)] +#![feature(contracts_internals)] fn tail(x: Baz) -> i32 - rustc_contract_requires(|| x.baz > 0) - rustc_contract_ensures(|ret| *ret > 100) + contract_requires(|| x.baz > 0) + contract_ensures(|ret| *ret > 100) { x.baz + 50 } diff --git a/tests/ui/contracts/internal_machinery/contract-intrinsics.rs b/tests/ui/contracts/internal_machinery/contract-intrinsics.rs index 8c70c1a85f6d..ae692afd146f 100644 --- a/tests/ui/contracts/internal_machinery/contract-intrinsics.rs +++ b/tests/ui/contracts/internal_machinery/contract-intrinsics.rs @@ -10,7 +10,7 @@ //@ [chk_pass] compile-flags: -Zcontract-checks=yes //@ [chk_fail_requires] compile-flags: -Zcontract-checks=yes //@ [chk_fail_ensures] compile-flags: -Zcontract-checks=yes -#![feature(cfg_contract_checks, rustc_contracts_internals, core_intrinsics)] +#![feature(cfg_contract_checks, contracts_internals, core_intrinsics)] fn main() { #[cfg(any(default, unchk_pass))] // default: disabled diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.chk_fail_post.stderr b/tests/ui/contracts/internal_machinery/contract-lang-items.chk_fail_post.stderr new file mode 100644 index 000000000000..a60ce1602659 --- /dev/null +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.chk_fail_post.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-lang-items.rs:15:12 + | +LL | #![feature(contracts)] // to access core::contracts + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.chk_pass.stderr b/tests/ui/contracts/internal_machinery/contract-lang-items.chk_pass.stderr new file mode 100644 index 000000000000..a60ce1602659 --- /dev/null +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.chk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-lang-items.rs:15:12 + | +LL | #![feature(contracts)] // to access core::contracts + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.rs b/tests/ui/contracts/internal_machinery/contract-lang-items.rs index ff569e011f20..e91bbed294d1 100644 --- a/tests/ui/contracts/internal_machinery/contract-lang-items.rs +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.rs @@ -12,8 +12,9 @@ //@ [chk_pass] compile-flags: -Zcontract-checks=yes //@ [chk_fail_post] compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts)] // to access core::contracts -#![feature(rustc_contracts_internals)] // to access check_requires lang item +#![feature(contracts)] // to access core::contracts +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] +#![feature(contracts_internals)] // to access check_requires lang item fn foo(x: Baz) -> i32 { let injected_checker = { diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_fail_post.stderr b/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_fail_post.stderr new file mode 100644 index 000000000000..a60ce1602659 --- /dev/null +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_fail_post.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-lang-items.rs:15:12 + | +LL | #![feature(contracts)] // to access core::contracts + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_pass.stderr b/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_pass.stderr new file mode 100644 index 000000000000..a60ce1602659 --- /dev/null +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-lang-items.rs:15:12 + | +LL | #![feature(contracts)] // to access core::contracts + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/internal_machinery/contracts-lowering-ensures-is-not-inherited-when-nesting.rs b/tests/ui/contracts/internal_machinery/contracts-lowering-ensures-is-not-inherited-when-nesting.rs index 0b0151c6df74..960ccaed3588 100644 --- a/tests/ui/contracts/internal_machinery/contracts-lowering-ensures-is-not-inherited-when-nesting.rs +++ b/tests/ui/contracts/internal_machinery/contracts-lowering-ensures-is-not-inherited-when-nesting.rs @@ -1,9 +1,9 @@ //@ run-pass //@ compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts_internals)] +#![feature(contracts_internals)] fn outer() -> i32 - rustc_contract_ensures(|ret| *ret > 0) + contract_ensures(|ret| *ret > 0) { let inner_closure = || -> i32 { 0 }; inner_closure(); diff --git a/tests/ui/contracts/internal_machinery/contracts-lowering-requires-is-not-inherited-when-nesting.rs b/tests/ui/contracts/internal_machinery/contracts-lowering-requires-is-not-inherited-when-nesting.rs index 79c50a18f705..bee703de16a0 100644 --- a/tests/ui/contracts/internal_machinery/contracts-lowering-requires-is-not-inherited-when-nesting.rs +++ b/tests/ui/contracts/internal_machinery/contracts-lowering-requires-is-not-inherited-when-nesting.rs @@ -1,11 +1,11 @@ //@ run-pass //@ compile-flags: -Zcontract-checks=yes -#![feature(rustc_contracts_internals)] +#![feature(contracts_internals)] struct Outer { outer: std::cell::Cell } fn outer(x: Outer) - rustc_contract_requires(|| x.outer.get() > 0) + contract_requires(|| x.outer.get() > 0) { let inner_closure = || { }; x.outer.set(0); diff --git a/tests/ui/contracts/internal_machinery/internal-feature-gating.rs b/tests/ui/contracts/internal_machinery/internal-feature-gating.rs index ee9edf3ebb0c..1b76eef6780f 100644 --- a/tests/ui/contracts/internal_machinery/internal-feature-gating.rs +++ b/tests/ui/contracts/internal_machinery/internal-feature-gating.rs @@ -1,20 +1,20 @@ -// gate-test-rustc_contracts_internals +// gate-test-contracts_internals fn main() { - // intrinsics are guarded by rustc_contracts_internals feature gate. + // intrinsics are guarded by contracts_internals feature gate. core::intrinsics::contract_checks(); - //~^ ERROR use of unstable library feature `rustc_contracts_internals` + //~^ ERROR use of unstable library feature `contracts_internals` core::intrinsics::contract_check_requires(|| true); - //~^ ERROR use of unstable library feature `rustc_contracts_internals` + //~^ ERROR use of unstable library feature `contracts_internals` core::intrinsics::contract_check_ensures(&1, |_|true); - //~^ ERROR use of unstable library feature `rustc_contracts_internals` + //~^ ERROR use of unstable library feature `contracts_internals` core::contracts::build_check_ensures(|_: &()| true); - //~^ ERROR use of unstable library feature `rustc_contracts_internals` + //~^ ERROR use of unstable library feature `contracts_internals` - // ast extensions are guarded by rustc_contracts_internals feature gate - fn identity_1() -> i32 rustc_contract_requires(|| true) { 10 } + // ast extensions are guarded by contracts_internals feature gate + fn identity_1() -> i32 contract_requires(|| true) { 10 } //~^ ERROR contract internal machinery is for internal use only - fn identity_2() -> i32 rustc_contract_ensures(|_| true) { 10 } + fn identity_2() -> i32 contract_ensures(|_| true) { 10 } //~^ ERROR contract internal machinery is for internal use only } diff --git a/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr b/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr index 5f9263e03e85..c0e1522f54c6 100644 --- a/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr +++ b/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr @@ -1,61 +1,61 @@ error[E0658]: contract internal machinery is for internal use only - --> $DIR/internal-feature-gating.rs:16:51 + --> $DIR/internal-feature-gating.rs:16:45 | -LL | fn identity_1() -> i32 rustc_contract_requires(|| true) { 10 } - | ^^^^^^^^^ +LL | fn identity_1() -> i32 contract_requires(|| true) { 10 } + | ^^^^^^^^^ | - = note: see issue #133866 for more information - = help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable + = note: see issue #128044 for more information + = help: add `#![feature(contracts_internals)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: contract internal machinery is for internal use only - --> $DIR/internal-feature-gating.rs:18:50 + --> $DIR/internal-feature-gating.rs:18:44 | -LL | fn identity_2() -> i32 rustc_contract_ensures(|_| true) { 10 } - | ^^^^^^^^^^ +LL | fn identity_2() -> i32 contract_ensures(|_| true) { 10 } + | ^^^^^^^^^^ | - = note: see issue #133866 for more information - = help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable + = note: see issue #128044 for more information + = help: add `#![feature(contracts_internals)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: use of unstable library feature `rustc_contracts_internals` +error[E0658]: use of unstable library feature `contracts_internals` --> $DIR/internal-feature-gating.rs:5:5 | LL | core::intrinsics::contract_checks(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: see issue #133866 for more information - = help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable + = note: see issue #128044 for more information + = help: add `#![feature(contracts_internals)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: use of unstable library feature `rustc_contracts_internals` +error[E0658]: use of unstable library feature `contracts_internals` --> $DIR/internal-feature-gating.rs:7:5 | LL | core::intrinsics::contract_check_requires(|| true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: see issue #133866 for more information - = help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable + = note: see issue #128044 for more information + = help: add `#![feature(contracts_internals)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: use of unstable library feature `rustc_contracts_internals` +error[E0658]: use of unstable library feature `contracts_internals` --> $DIR/internal-feature-gating.rs:9:5 | LL | core::intrinsics::contract_check_ensures(&1, |_|true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: see issue #133866 for more information - = help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable + = note: see issue #128044 for more information + = help: add `#![feature(contracts_internals)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: use of unstable library feature `rustc_contracts_internals` +error[E0658]: use of unstable library feature `contracts_internals` --> $DIR/internal-feature-gating.rs:12:5 | LL | core::contracts::build_check_ensures(|_: &()| true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: see issue #133866 for more information - = help: add `#![feature(rustc_contracts_internals)]` to the crate attributes to enable + = note: see issue #128044 for more information + = help: add `#![feature(contracts_internals)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: aborting due to 6 previous errors diff --git a/tests/ui/feature-gates/feature-gate-cfg-contract-checks.stderr b/tests/ui/feature-gates/feature-gate-cfg-contract-checks.stderr index af4e605e5709..89c6d077f97e 100644 --- a/tests/ui/feature-gates/feature-gate-cfg-contract-checks.stderr +++ b/tests/ui/feature-gates/feature-gate-cfg-contract-checks.stderr @@ -4,7 +4,7 @@ error[E0658]: `cfg(contract_checks)` is experimental and subject to change LL | cfg!(contract_checks) | ^^^^^^^^^^^^^^^ | - = note: see issue #133866 for more information + = note: see issue #128044 for more information = help: add `#![feature(cfg_contract_checks)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date diff --git a/tests/ui/feature-gates/feature-gate-contracts.rs b/tests/ui/feature-gates/feature-gate-contracts.rs new file mode 100644 index 000000000000..5544f1d82ee4 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-contracts.rs @@ -0,0 +1,11 @@ +#![crate_type = "lib"] + +#[core::contracts::requires(x > 0)] +pub fn requires_needs_it(x: i32) { } +//~^^ ERROR use of unstable library feature `contracts` +//~^^^ ERROR contracts are incomplete + +#[core::contracts::ensures(|ret| *ret > 0)] +pub fn ensures_needs_it() -> i32 { 10 } +//~^^ ERROR use of unstable library feature `contracts` +//~^^^ ERROR contracts are incomplete diff --git a/tests/ui/feature-gates/feature-gate-contracts.stderr b/tests/ui/feature-gates/feature-gate-contracts.stderr new file mode 100644 index 000000000000..4403e7df50b4 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-contracts.stderr @@ -0,0 +1,43 @@ +error[E0658]: use of unstable library feature `contracts` + --> $DIR/feature-gate-contracts.rs:3:3 + | +LL | #[core::contracts::requires(x > 0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #128044 for more information + = help: add `#![feature(contracts)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `contracts` + --> $DIR/feature-gate-contracts.rs:8:3 + | +LL | #[core::contracts::ensures(|ret| *ret > 0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #128044 for more information + = help: add `#![feature(contracts)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: contracts are incomplete + --> $DIR/feature-gate-contracts.rs:3:1 + | +LL | #[core::contracts::requires(x > 0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #128044 for more information + = help: add `#![feature(contracts)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: contracts are incomplete + --> $DIR/feature-gate-contracts.rs:8:1 + | +LL | #[core::contracts::ensures(|ret| *ret > 0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #128044 for more information + = help: add `#![feature(contracts)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-rustc-contracts.rs b/tests/ui/feature-gates/feature-gate-rustc-contracts.rs deleted file mode 100644 index d4249c252cda..000000000000 --- a/tests/ui/feature-gates/feature-gate-rustc-contracts.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![crate_type = "lib"] - -#[core::contracts::requires(x > 0)] -pub fn requires_needs_it(x: i32) { } -//~^^ ERROR use of unstable library feature `rustc_contracts` -//~^^^ ERROR contracts are experimental - -#[core::contracts::ensures(|ret| *ret > 0)] -pub fn ensures_needs_it() -> i32 { 10 } -//~^^ ERROR use of unstable library feature `rustc_contracts` -//~^^^ ERROR contracts are experimental diff --git a/tests/ui/feature-gates/feature-gate-rustc-contracts.stderr b/tests/ui/feature-gates/feature-gate-rustc-contracts.stderr deleted file mode 100644 index eb7777a4a517..000000000000 --- a/tests/ui/feature-gates/feature-gate-rustc-contracts.stderr +++ /dev/null @@ -1,43 +0,0 @@ -error[E0658]: use of unstable library feature `rustc_contracts` - --> $DIR/feature-gate-rustc-contracts.rs:3:3 - | -LL | #[core::contracts::requires(x > 0)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #133866 for more information - = help: add `#![feature(rustc_contracts)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: use of unstable library feature `rustc_contracts` - --> $DIR/feature-gate-rustc-contracts.rs:8:3 - | -LL | #[core::contracts::ensures(|ret| *ret > 0)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #133866 for more information - = help: add `#![feature(rustc_contracts)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: contracts are experimental - --> $DIR/feature-gate-rustc-contracts.rs:3:1 - | -LL | #[core::contracts::requires(x > 0)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #133866 for more information - = help: add `#![feature(rustc_contracts)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: contracts are experimental - --> $DIR/feature-gate-rustc-contracts.rs:8:1 - | -LL | #[core::contracts::ensures(|ret| *ret > 0)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #133866 for more information - = help: add `#![feature(rustc_contracts)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 4 previous errors - -For more information about this error, try `rustc --explain E0658`. From 03eed121687b4a489bb4d6648ea448dd562165bd Mon Sep 17 00:00:00 2001 From: may Date: Mon, 3 Feb 2025 23:25:24 +0100 Subject: [PATCH 44/57] implement inherent str constructors --- library/core/src/str/mod.rs | 176 ++++++++++++++++++ .../suggest-std-when-using-type.fixed | 5 +- .../suggest-std-when-using-type.rs | 5 +- .../suggest-std-when-using-type.stderr | 16 +- 4 files changed, 180 insertions(+), 22 deletions(-) diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 39fa6c1a25fe..17fb43b7830e 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -160,6 +160,182 @@ impl str { self.len() == 0 } + /// Converts a slice of bytes to a string slice. + /// + /// A string slice ([`&str`]) is made of bytes ([`u8`]), and a byte slice + /// ([`&[u8]`][byteslice]) is made of bytes, so this function converts between + /// the two. Not all byte slices are valid string slices, however: [`&str`] requires + /// that it is valid UTF-8. `from_utf8()` checks to ensure that the bytes are valid + /// UTF-8, and then does the conversion. + /// + /// [`&str`]: str + /// [byteslice]: slice + /// + /// If you are sure that the byte slice is valid UTF-8, and you don't want to + /// incur the overhead of the validity check, there is an unsafe version of + /// this function, [`from_utf8_unchecked`], which has the same + /// behavior but skips the check. + /// + /// If you need a `String` instead of a `&str`, consider + /// [`String::from_utf8`][string]. + /// + /// [string]: ../std/string/struct.String.html#method.from_utf8 + /// + /// Because you can stack-allocate a `[u8; N]`, and you can take a + /// [`&[u8]`][byteslice] of it, this function is one way to have a + /// stack-allocated string. There is an example of this in the + /// examples section below. + /// + /// [byteslice]: slice + /// + /// # Errors + /// + /// Returns `Err` if the slice is not UTF-8 with a description as to why the + /// provided slice is not UTF-8. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::str; + /// + /// // some bytes, in a vector + /// let sparkle_heart = vec![240, 159, 146, 150]; + /// + /// // We can use the ? (try) operator to check if the bytes are valid + /// let sparkle_heart = str::from_utf8(&sparkle_heart)?; + /// + /// assert_eq!("💖", sparkle_heart); + /// # Ok::<_, str::Utf8Error>(()) + /// ``` + /// + /// Incorrect bytes: + /// + /// ``` + /// use std::str; + /// + /// // some invalid bytes, in a vector + /// let sparkle_heart = vec![0, 159, 146, 150]; + /// + /// assert!(str::from_utf8(&sparkle_heart).is_err()); + /// ``` + /// + /// See the docs for [`Utf8Error`] for more details on the kinds of + /// errors that can be returned. + /// + /// A "stack allocated string": + /// + /// ``` + /// use std::str; + /// + /// // some bytes, in a stack-allocated array + /// let sparkle_heart = [240, 159, 146, 150]; + /// + /// // We know these bytes are valid, so just use `unwrap()`. + /// let sparkle_heart: &str = str::from_utf8(&sparkle_heart).unwrap(); + /// + /// assert_eq!("💖", sparkle_heart); + /// ``` + #[unstable(feature = "inherent_str_constructors", issue = "131114")] + pub const fn from_utf8(v: &[u8]) -> Result<&str, Utf8Error> { + converts::from_utf8(v) + } + + /// Converts a mutable slice of bytes to a mutable string slice. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::str; + /// + /// // "Hello, Rust!" as a mutable vector + /// let mut hellorust = vec![72, 101, 108, 108, 111, 44, 32, 82, 117, 115, 116, 33]; + /// + /// // As we know these bytes are valid, we can use `unwrap()` + /// let outstr = str::from_utf8_mut(&mut hellorust).unwrap(); + /// + /// assert_eq!("Hello, Rust!", outstr); + /// ``` + /// + /// Incorrect bytes: + /// + /// ``` + /// use std::str; + /// + /// // Some invalid bytes in a mutable vector + /// let mut invalid = vec![128, 223]; + /// + /// assert!(str::from_utf8_mut(&mut invalid).is_err()); + /// ``` + /// See the docs for [`Utf8Error`] for more details on the kinds of + /// errors that can be returned. + #[unstable(feature = "inherent_str_constructors", issue = "131114")] + #[rustc_const_unstable(feature = "const_str_from_utf8", issue = "91006")] + pub const fn from_utf8_mut(v: &mut [u8]) -> Result<&mut str, Utf8Error> { + converts::from_utf8_mut(v) + } + + /// Converts a slice of bytes to a string slice without checking + /// that the string contains valid UTF-8. + /// + /// See the safe version, [`from_utf8`], for more information. + /// + /// # Safety + /// + /// The bytes passed in must be valid UTF-8. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::str; + /// + /// // some bytes, in a vector + /// let sparkle_heart = vec![240, 159, 146, 150]; + /// + /// let sparkle_heart = unsafe { + /// str::from_utf8_unchecked(&sparkle_heart) + /// }; + /// + /// assert_eq!("💖", sparkle_heart); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "inherent_str_constructors", issue = "131114")] + pub const unsafe fn from_utf8_unchecked(v: &[u8]) -> &str { + // SAFETY: converts::from_utf8_unchecked has the same safety requirements as this function. + unsafe { converts::from_utf8_unchecked(v) } + } + + /// Converts a slice of bytes to a string slice without checking + /// that the string contains valid UTF-8; mutable version. + /// + /// See the immutable version, [`from_utf8_unchecked()`] for more information. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::str; + /// + /// let mut heart = vec![240, 159, 146, 150]; + /// let heart = unsafe { str::from_utf8_unchecked_mut(&mut heart) }; + /// + /// assert_eq!("💖", heart); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "inherent_str_constructors", issue = "131114")] + pub const unsafe fn from_utf8_unchecked_mut(v: &mut [u8]) -> &mut str { + // SAFETY: converts::from_utf8_unchecked_mut has the same safety requirements as this function. + unsafe { converts::from_utf8_unchecked_mut(v) } + } + /// Checks that `index`-th byte is the first byte in a UTF-8 code point /// sequence or the end of the string. /// diff --git a/tests/ui/suggestions/suggest-std-when-using-type.fixed b/tests/ui/suggestions/suggest-std-when-using-type.fixed index ebbb854175bd..0547940f94d3 100644 --- a/tests/ui/suggestions/suggest-std-when-using-type.fixed +++ b/tests/ui/suggestions/suggest-std-when-using-type.fixed @@ -1,8 +1,5 @@ //@ run-rustfix fn main() { let pi = std::f32::consts::PI; //~ ERROR ambiguous associated type - let bytes = "hello world".as_bytes(); - let string = std::str::from_utf8(bytes).unwrap(); - //~^ ERROR no function or associated item named `from_utf8` found - println!("{pi} {bytes:?} {string}"); + println!("{pi}"); } diff --git a/tests/ui/suggestions/suggest-std-when-using-type.rs b/tests/ui/suggestions/suggest-std-when-using-type.rs index 06aab63d6885..cd55c5d13bd1 100644 --- a/tests/ui/suggestions/suggest-std-when-using-type.rs +++ b/tests/ui/suggestions/suggest-std-when-using-type.rs @@ -1,8 +1,5 @@ //@ run-rustfix fn main() { let pi = f32::consts::PI; //~ ERROR ambiguous associated type - let bytes = "hello world".as_bytes(); - let string = str::from_utf8(bytes).unwrap(); - //~^ ERROR no function or associated item named `from_utf8` found - println!("{pi} {bytes:?} {string}"); + println!("{pi}"); } diff --git a/tests/ui/suggestions/suggest-std-when-using-type.stderr b/tests/ui/suggestions/suggest-std-when-using-type.stderr index 6f890b87b24b..7f8545f461d9 100644 --- a/tests/ui/suggestions/suggest-std-when-using-type.stderr +++ b/tests/ui/suggestions/suggest-std-when-using-type.stderr @@ -9,18 +9,6 @@ help: you are looking for the module in `std`, not the primitive type LL | let pi = std::f32::consts::PI; | +++++ -error[E0599]: no function or associated item named `from_utf8` found for type `str` in the current scope - --> $DIR/suggest-std-when-using-type.rs:5:23 - | -LL | let string = str::from_utf8(bytes).unwrap(); - | ^^^^^^^^^ function or associated item not found in `str` - | -help: you are looking for the module in `std`, not the primitive type - | -LL | let string = std::str::from_utf8(bytes).unwrap(); - | +++++ +error: aborting due to 1 previous error -error: aborting due to 2 previous errors - -Some errors have detailed explanations: E0223, E0599. -For more information about an error, try `rustc --explain E0223`. +For more information about this error, try `rustc --explain E0223`. From 15adc38ffc4608855c959ec521fe0124e1dffb2c Mon Sep 17 00:00:00 2001 From: may Date: Tue, 4 Feb 2025 00:34:42 +0100 Subject: [PATCH 45/57] specify a prim@slice in docs i am not quite sure how this failure is in any way related to this pr, since i am only touching inherent functions on str? but sure. --- library/core/src/str/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 17fb43b7830e..5b258a7c844f 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -169,7 +169,7 @@ impl str { /// UTF-8, and then does the conversion. /// /// [`&str`]: str - /// [byteslice]: slice + /// [byteslice]: prim@slice /// /// If you are sure that the byte slice is valid UTF-8, and you don't want to /// incur the overhead of the validity check, there is an unsafe version of From 53b8de1c9a9f027c9d2effc5eeed2fc99cf7068d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= <39484203+jieyouxu@users.noreply.github.com> Date: Fri, 31 Jan 2025 14:43:49 +0800 Subject: [PATCH 46/57] bootstrap: add wrapper macros for `tracing`-gated tracing macros - Add wrapper macros for `error!`, `warn!`, `info!`, `debug!` and `trace!`, which `cfg(feature = "tracing")`-gates the underlying `tracing` macros. - This is not done for `span!` or `event!` because they can return span guards, and you can't really wrap that. - This is also not possible for `tracing::instrument` attribute proc-macro unless you use another attribute proc-macro to wrap that. --- src/bootstrap/src/bin/main.rs | 12 ++--- src/bootstrap/src/lib.rs | 85 +++++++++++++++--------------- src/bootstrap/src/utils/mod.rs | 2 + src/bootstrap/src/utils/tracing.rs | 49 +++++++++++++++++ 4 files changed, 98 insertions(+), 50 deletions(-) create mode 100644 src/bootstrap/src/utils/tracing.rs diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs index 5fcf7eda8df7..441674936c66 100644 --- a/src/bootstrap/src/bin/main.rs +++ b/src/bootstrap/src/bin/main.rs @@ -11,12 +11,12 @@ use std::str::FromStr; use std::{env, process}; use bootstrap::{ - Build, CONFIG_CHANGE_HISTORY, Config, Flags, Subcommand, find_recent_config_change_ids, + Build, CONFIG_CHANGE_HISTORY, Config, Flags, Subcommand, debug, find_recent_config_change_ids, human_readable_changes, t, }; use build_helper::ci::CiEnv; #[cfg(feature = "tracing")] -use tracing::{debug, instrument}; +use tracing::instrument; #[cfg_attr(feature = "tracing", instrument(level = "trace", name = "main"))] fn main() { @@ -29,10 +29,8 @@ fn main() { return; } - #[cfg(feature = "tracing")] debug!("parsing flags"); let flags = Flags::parse(&args); - #[cfg(feature = "tracing")] debug!("parsing config based on flags"); let config = Config::parse(flags); @@ -95,7 +93,6 @@ fn main() { let dump_bootstrap_shims = config.dump_bootstrap_shims; let out_dir = config.out.clone(); - #[cfg(feature = "tracing")] debug!("creating new build based on config"); Build::new(config).build(); @@ -207,8 +204,9 @@ fn check_version(config: &Config) -> Option { // Due to the conditional compilation via the `tracing` cargo feature, this means that `tracing` // usages in bootstrap need to be also gated behind the `tracing` feature: // -// - `tracing` macros (like `trace!`) and anything from `tracing`, `tracing_subscriber` and -// `tracing-tree` will need to be gated by `#[cfg(feature = "tracing")]`. +// - `tracing` macros with log levels (`trace!`, `debug!`, `warn!`, `info`, `error`) should not be +// used *directly*. You should use the wrapped `tracing` macros which gate the actual invocations +// behind `feature = "tracing"`. // - `tracing`'s `#[instrument(..)]` macro will need to be gated like `#![cfg_attr(feature = // "tracing", instrument(..))]`. #[cfg(feature = "tracing")] diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index d56f35f866cb..2dd83d5938e9 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -28,8 +28,6 @@ use std::{env, fs, io, str}; use build_helper::ci::gha; use build_helper::exit; use termcolor::{ColorChoice, StandardStream, WriteColor}; -#[cfg(feature = "tracing")] -use tracing::{debug, instrument, span, trace}; use utils::build_stamp::BuildStamp; use utils::channel::GitInfo; @@ -46,6 +44,8 @@ pub use core::builder::PathSet; pub use core::config::Config; pub use core::config::flags::{Flags, Subcommand}; +#[cfg(feature = "tracing")] +use tracing::{instrument, span}; pub use utils::change_tracker::{ CONFIG_CHANGE_HISTORY, find_recent_config_change_ids, human_readable_changes, }; @@ -541,72 +541,71 @@ impl Build { /// Executes the entire build, as configured by the flags and configuration. #[cfg_attr(feature = "tracing", instrument(level = "debug", name = "Build::build", skip_all))] pub fn build(&mut self) { - #[cfg(feature = "tracing")] trace!("setting up job management"); unsafe { crate::utils::job::setup(self); } - #[cfg(feature = "tracing")] - trace!("downloading rustfmt early"); - // Download rustfmt early so that it can be used in rust-analyzer configs. + trace!("downloading rustfmt early"); let _ = &builder::Builder::new(self).initial_rustfmt(); - #[cfg(feature = "tracing")] - let hardcoded_span = - span!(tracing::Level::DEBUG, "handling hardcoded subcommands (Format, Suggest, Perf)") - .entered(); + // Handle hard-coded subcommands. + { + #[cfg(feature = "tracing")] + let _hardcoded_span = span!( + tracing::Level::DEBUG, + "handling hardcoded subcommands (Format, Suggest, Perf)" + ) + .entered(); - // hardcoded subcommands - match &self.config.cmd { - Subcommand::Format { check, all } => { - return core::build_steps::format::format( - &builder::Builder::new(self), - *check, - *all, - &self.config.paths, - ); - } - Subcommand::Suggest { run } => { - return core::build_steps::suggest::suggest(&builder::Builder::new(self), *run); - } - Subcommand::Perf { .. } => { - return core::build_steps::perf::perf(&builder::Builder::new(self)); - } - _cmd => { - #[cfg(feature = "tracing")] - debug!(cmd = ?_cmd, "not a hardcoded subcommand; returning to normal handling"); + match &self.config.cmd { + Subcommand::Format { check, all } => { + return core::build_steps::format::format( + &builder::Builder::new(self), + *check, + *all, + &self.config.paths, + ); + } + Subcommand::Suggest { run } => { + return core::build_steps::suggest::suggest(&builder::Builder::new(self), *run); + } + Subcommand::Perf { .. } => { + return core::build_steps::perf::perf(&builder::Builder::new(self)); + } + _cmd => { + debug!(cmd = ?_cmd, "not a hardcoded subcommand; returning to normal handling"); + } } + + debug!("handling subcommand normally"); } - #[cfg(feature = "tracing")] - drop(hardcoded_span); - #[cfg(feature = "tracing")] - debug!("handling subcommand normally"); - if !self.config.dry_run() { #[cfg(feature = "tracing")] let _real_run_span = span!(tracing::Level::DEBUG, "executing real run").entered(); + // We first do a dry-run. This is a sanity-check to ensure that + // steps don't do anything expensive in the dry-run. { #[cfg(feature = "tracing")] let _sanity_check_span = span!(tracing::Level::DEBUG, "(1) executing dry-run sanity-check").entered(); - - // We first do a dry-run. This is a sanity-check to ensure that - // steps don't do anything expensive in the dry-run. self.config.dry_run = DryRun::SelfCheck; let builder = builder::Builder::new(self); builder.execute_cli(); } - #[cfg(feature = "tracing")] - let _actual_run_span = - span!(tracing::Level::DEBUG, "(2) executing actual run").entered(); - self.config.dry_run = DryRun::Disabled; - let builder = builder::Builder::new(self); - builder.execute_cli(); + // Actual run. + { + #[cfg(feature = "tracing")] + let _actual_run_span = + span!(tracing::Level::DEBUG, "(2) executing actual run").entered(); + self.config.dry_run = DryRun::Disabled; + let builder = builder::Builder::new(self); + builder.execute_cli(); + } } else { #[cfg(feature = "tracing")] let _dry_run_span = span!(tracing::Level::DEBUG, "executing dry run").entered(); diff --git a/src/bootstrap/src/utils/mod.rs b/src/bootstrap/src/utils/mod.rs index ea56932b4043..caef8ce3088a 100644 --- a/src/bootstrap/src/utils/mod.rs +++ b/src/bootstrap/src/utils/mod.rs @@ -14,6 +14,8 @@ pub(crate) mod render_tests; pub(crate) mod shared_helpers; pub(crate) mod tarball; +pub(crate) mod tracing; + #[cfg(feature = "build-metrics")] pub(crate) mod metrics; diff --git a/src/bootstrap/src/utils/tracing.rs b/src/bootstrap/src/utils/tracing.rs new file mode 100644 index 000000000000..e89decf9e558 --- /dev/null +++ b/src/bootstrap/src/utils/tracing.rs @@ -0,0 +1,49 @@ +//! Wrapper macros for `tracing` macros to avoid having to write `cfg(feature = "tracing")`-gated +//! `debug!`/`trace!` everytime, e.g. +//! +//! ```rust,ignore (example) +//! #[cfg(feature = "tracing")] +//! trace!("..."); +//! ``` +//! +//! When `feature = "tracing"` is inactive, these macros expand to nothing. + +#[macro_export] +macro_rules! trace { + ($($tokens:tt)*) => { + #[cfg(feature = "tracing")] + ::tracing::trace!($($tokens)*) + } +} + +#[macro_export] +macro_rules! debug { + ($($tokens:tt)*) => { + #[cfg(feature = "tracing")] + ::tracing::debug!($($tokens)*) + } +} + +#[macro_export] +macro_rules! warn { + ($($tokens:tt)*) => { + #[cfg(feature = "tracing")] + ::tracing::warn!($($tokens)*) + } +} + +#[macro_export] +macro_rules! info { + ($($tokens:tt)*) => { + #[cfg(feature = "tracing")] + ::tracing::info!($($tokens)*) + } +} + +#[macro_export] +macro_rules! error { + ($($tokens:tt)*) => { + #[cfg(feature = "tracing")] + ::tracing::error!($($tokens)*) + } +} From 2431977ecfe6e88e71ddad8bf0bb9a10f9a6256c Mon Sep 17 00:00:00 2001 From: DuskyElf <91879372+DuskyElf@users.noreply.github.com> Date: Tue, 4 Feb 2025 17:02:37 +0530 Subject: [PATCH 47/57] Rename and Move some UI tests to more suitable subdirs --- src/tools/tidy/src/issues.txt | 15 --------------- src/tools/tidy/src/ui_tests.rs | 2 +- ...tackColoring-not-blowup-stack-issue-40883.rs} | 0 .../closure-in-enum-issue-48838.rs} | 0 .../closure-in-enum-issue-48838.stderr} | 4 ++-- .../enum-inside-enum-issue-40350.rs} | 0 .../if/if-let-no-match-guards-issue-41272.rs} | 0 .../floating-point-0e10-issue-40408.rs} | 0 .../const-expr-invocations-issue-40136.rs} | 0 .../macros-in-trait-positions-issue-40845.rs} | 0 ...macros-in-trait-positions-issue-40845.stderr} | 4 ++-- .../enum-and-break-in-match-issue-41213.rs} | 0 .../for-loop-missing-in.fixed} | 0 .../for-loop-missing-in.rs} | 0 .../for-loop-missing-in.stderr} | 4 ++-- .../deep-level-Send-bound-check-issue-40827.rs} | 0 ...ep-level-Send-bound-check-issue-40827.stderr} | 16 ++++++++-------- .../coercion-check-for-addition-issue-40610.rs} | 0 ...ercion-check-for-addition-issue-40610.stderr} | 2 +- ...check-for-indexing-expression-issue-40861.rs} | 0 ...k-for-indexing-expression-issue-40861.stderr} | 2 +- .../unsized-rvalue-issue-41139.rs} | 0 .../unsized-rvalue-issue-41139.stderr} | 2 +- .../range-expr-root-of-constant-issue-40749.rs} | 0 ...nge-expr-root-of-constant-issue-40749.stderr} | 2 +- .../while-let-scope-issue-40235.rs} | 0 26 files changed, 19 insertions(+), 34 deletions(-) rename tests/ui/{issues/issue-40883.rs => codegen/StackColoring-not-blowup-stack-issue-40883.rs} (100%) rename tests/ui/{issues/issue-48838.rs => enum/closure-in-enum-issue-48838.rs} (100%) rename tests/ui/{issues/issue-48838.stderr => enum/closure-in-enum-issue-48838.stderr} (66%) rename tests/ui/{issues/issue-40350.rs => enum/enum-inside-enum-issue-40350.rs} (100%) rename tests/ui/{issues/issue-41272.rs => expr/if/if-let-no-match-guards-issue-41272.rs} (100%) rename tests/ui/{issues/issue-40408.rs => lexer/floating-point-0e10-issue-40408.rs} (100%) rename tests/ui/{issues/issue-40136.rs => macros/const-expr-invocations-issue-40136.rs} (100%) rename tests/ui/{issues/issue-40845.rs => macros/macros-in-trait-positions-issue-40845.rs} (100%) rename tests/ui/{issues/issue-40845.stderr => macros/macros-in-trait-positions-issue-40845.stderr} (65%) rename tests/ui/{issues/issue-41213.rs => match/enum-and-break-in-match-issue-41213.rs} (100%) rename tests/ui/{issues/issue-40782.fixed => suggestions/for-loop-missing-in.fixed} (100%) rename tests/ui/{issues/issue-40782.rs => suggestions/for-loop-missing-in.rs} (100%) rename tests/ui/{issues/issue-40782.stderr => suggestions/for-loop-missing-in.stderr} (82%) rename tests/ui/{issues/issue-40827.rs => trait-bounds/deep-level-Send-bound-check-issue-40827.rs} (100%) rename tests/ui/{issues/issue-40827.stderr => trait-bounds/deep-level-Send-bound-check-issue-40827.stderr} (76%) rename tests/ui/{issues/issue-40610.rs => typeck/coercion-check-for-addition-issue-40610.rs} (100%) rename tests/ui/{issues/issue-40610.stderr => typeck/coercion-check-for-addition-issue-40610.stderr} (79%) rename tests/ui/{issues/issue-40861.rs => typeck/coercion-check-for-indexing-expression-issue-40861.rs} (100%) rename tests/ui/{issues/issue-40861.stderr => typeck/coercion-check-for-indexing-expression-issue-40861.stderr} (81%) rename tests/ui/{issues/issue-41139.rs => typeck/unsized-rvalue-issue-41139.rs} (100%) rename tests/ui/{issues/issue-41139.stderr => typeck/unsized-rvalue-issue-41139.stderr} (92%) rename tests/ui/{issues/issue-40749.rs => wf/range-expr-root-of-constant-issue-40749.rs} (100%) rename tests/ui/{issues/issue-40749.stderr => wf/range-expr-root-of-constant-issue-40749.stderr} (84%) rename tests/ui/{issues/issue-40235.rs => while/while-let-scope-issue-40235.rs} (100%) diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt index 17786ed6d2fd..39c9a148e9e5 100644 --- a/src/tools/tidy/src/issues.txt +++ b/src/tools/tidy/src/issues.txt @@ -2218,26 +2218,12 @@ ui/issues/issue-3993.rs ui/issues/issue-39970.rs ui/issues/issue-39984.rs ui/issues/issue-40000.rs -ui/issues/issue-40136.rs -ui/issues/issue-40235.rs ui/issues/issue-4025.rs ui/issues/issue-40288-2.rs ui/issues/issue-40288.rs -ui/issues/issue-40350.rs -ui/issues/issue-40408.rs -ui/issues/issue-40610.rs -ui/issues/issue-40749.rs -ui/issues/issue-40782.rs -ui/issues/issue-40827.rs -ui/issues/issue-40845.rs -ui/issues/issue-40861.rs -ui/issues/issue-40883.rs ui/issues/issue-40951.rs ui/issues/issue-41053.rs -ui/issues/issue-41139.rs -ui/issues/issue-41213.rs ui/issues/issue-41229-ref-str.rs -ui/issues/issue-41272.rs ui/issues/issue-41298.rs ui/issues/issue-41479.rs ui/issues/issue-41498.rs @@ -2360,7 +2346,6 @@ ui/issues/issue-4830.rs ui/issues/issue-48364.rs ui/issues/issue-48728.rs ui/issues/issue-4875.rs -ui/issues/issue-48838.rs ui/issues/issue-48984.rs ui/issues/issue-49298.rs ui/issues/issue-4935.rs diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index d66ba157d106..fe51231c4810 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -17,7 +17,7 @@ use ignore::Walk; const ENTRY_LIMIT: u32 = 901; // FIXME: The following limits should be reduced eventually. -const ISSUES_ENTRY_LIMIT: u32 = 1658; +const ISSUES_ENTRY_LIMIT: u32 = 1634; const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[ "rs", // test source files diff --git a/tests/ui/issues/issue-40883.rs b/tests/ui/codegen/StackColoring-not-blowup-stack-issue-40883.rs similarity index 100% rename from tests/ui/issues/issue-40883.rs rename to tests/ui/codegen/StackColoring-not-blowup-stack-issue-40883.rs diff --git a/tests/ui/issues/issue-48838.rs b/tests/ui/enum/closure-in-enum-issue-48838.rs similarity index 100% rename from tests/ui/issues/issue-48838.rs rename to tests/ui/enum/closure-in-enum-issue-48838.rs diff --git a/tests/ui/issues/issue-48838.stderr b/tests/ui/enum/closure-in-enum-issue-48838.stderr similarity index 66% rename from tests/ui/issues/issue-48838.stderr rename to tests/ui/enum/closure-in-enum-issue-48838.stderr index 504ea3e80103..17e6c3433343 100644 --- a/tests/ui/issues/issue-48838.stderr +++ b/tests/ui/enum/closure-in-enum-issue-48838.stderr @@ -1,11 +1,11 @@ error[E0308]: mismatched types - --> $DIR/issue-48838.rs:2:14 + --> $DIR/closure-in-enum-issue-48838.rs:2:14 | LL | Square = |x| x, | ^^^^^ expected `isize`, found closure | = note: expected type `isize` - found closure `{closure@$DIR/issue-48838.rs:2:14: 2:17}` + found closure `{closure@$DIR/closure-in-enum-issue-48838.rs:2:14: 2:17}` error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-40350.rs b/tests/ui/enum/enum-inside-enum-issue-40350.rs similarity index 100% rename from tests/ui/issues/issue-40350.rs rename to tests/ui/enum/enum-inside-enum-issue-40350.rs diff --git a/tests/ui/issues/issue-41272.rs b/tests/ui/expr/if/if-let-no-match-guards-issue-41272.rs similarity index 100% rename from tests/ui/issues/issue-41272.rs rename to tests/ui/expr/if/if-let-no-match-guards-issue-41272.rs diff --git a/tests/ui/issues/issue-40408.rs b/tests/ui/lexer/floating-point-0e10-issue-40408.rs similarity index 100% rename from tests/ui/issues/issue-40408.rs rename to tests/ui/lexer/floating-point-0e10-issue-40408.rs diff --git a/tests/ui/issues/issue-40136.rs b/tests/ui/macros/const-expr-invocations-issue-40136.rs similarity index 100% rename from tests/ui/issues/issue-40136.rs rename to tests/ui/macros/const-expr-invocations-issue-40136.rs diff --git a/tests/ui/issues/issue-40845.rs b/tests/ui/macros/macros-in-trait-positions-issue-40845.rs similarity index 100% rename from tests/ui/issues/issue-40845.rs rename to tests/ui/macros/macros-in-trait-positions-issue-40845.rs diff --git a/tests/ui/issues/issue-40845.stderr b/tests/ui/macros/macros-in-trait-positions-issue-40845.stderr similarity index 65% rename from tests/ui/issues/issue-40845.stderr rename to tests/ui/macros/macros-in-trait-positions-issue-40845.stderr index 66bf053204c0..5c1b5f51e44b 100644 --- a/tests/ui/issues/issue-40845.stderr +++ b/tests/ui/macros/macros-in-trait-positions-issue-40845.stderr @@ -1,11 +1,11 @@ error: cannot find macro `m` in this scope - --> $DIR/issue-40845.rs:1:11 + --> $DIR/macros-in-trait-positions-issue-40845.rs:1:11 | LL | trait T { m!(); } | ^ error: cannot find macro `m` in this scope - --> $DIR/issue-40845.rs:4:10 + --> $DIR/macros-in-trait-positions-issue-40845.rs:4:10 | LL | impl S { m!(); } | ^ diff --git a/tests/ui/issues/issue-41213.rs b/tests/ui/match/enum-and-break-in-match-issue-41213.rs similarity index 100% rename from tests/ui/issues/issue-41213.rs rename to tests/ui/match/enum-and-break-in-match-issue-41213.rs diff --git a/tests/ui/issues/issue-40782.fixed b/tests/ui/suggestions/for-loop-missing-in.fixed similarity index 100% rename from tests/ui/issues/issue-40782.fixed rename to tests/ui/suggestions/for-loop-missing-in.fixed diff --git a/tests/ui/issues/issue-40782.rs b/tests/ui/suggestions/for-loop-missing-in.rs similarity index 100% rename from tests/ui/issues/issue-40782.rs rename to tests/ui/suggestions/for-loop-missing-in.rs diff --git a/tests/ui/issues/issue-40782.stderr b/tests/ui/suggestions/for-loop-missing-in.stderr similarity index 82% rename from tests/ui/issues/issue-40782.stderr rename to tests/ui/suggestions/for-loop-missing-in.stderr index 614980202385..61830b800a61 100644 --- a/tests/ui/issues/issue-40782.stderr +++ b/tests/ui/suggestions/for-loop-missing-in.stderr @@ -1,5 +1,5 @@ error: missing `in` in `for` loop - --> $DIR/issue-40782.rs:4:11 + --> $DIR/for-loop-missing-in.rs:4:11 | LL | for _i 0..2 { | ^ @@ -10,7 +10,7 @@ LL | for _i in 0..2 { | ++ error: missing `in` in `for` loop - --> $DIR/issue-40782.rs:6:12 + --> $DIR/for-loop-missing-in.rs:6:12 | LL | for _i of 0..2 { | ^^ diff --git a/tests/ui/issues/issue-40827.rs b/tests/ui/trait-bounds/deep-level-Send-bound-check-issue-40827.rs similarity index 100% rename from tests/ui/issues/issue-40827.rs rename to tests/ui/trait-bounds/deep-level-Send-bound-check-issue-40827.rs diff --git a/tests/ui/issues/issue-40827.stderr b/tests/ui/trait-bounds/deep-level-Send-bound-check-issue-40827.stderr similarity index 76% rename from tests/ui/issues/issue-40827.stderr rename to tests/ui/trait-bounds/deep-level-Send-bound-check-issue-40827.stderr index 7f5c578ae4ff..7b59fe72f431 100644 --- a/tests/ui/issues/issue-40827.stderr +++ b/tests/ui/trait-bounds/deep-level-Send-bound-check-issue-40827.stderr @@ -1,5 +1,5 @@ error[E0277]: `Rc` cannot be shared between threads safely - --> $DIR/issue-40827.rs:14:7 + --> $DIR/deep-level-Send-bound-check-issue-40827.rs:14:7 | LL | f(Foo(Arc::new(Bar::B(None)))); | - ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Rc` cannot be shared between threads safely @@ -8,24 +8,24 @@ LL | f(Foo(Arc::new(Bar::B(None)))); | = help: within `Bar`, the trait `Sync` is not implemented for `Rc` note: required because it appears within the type `Bar` - --> $DIR/issue-40827.rs:6:6 + --> $DIR/deep-level-Send-bound-check-issue-40827.rs:6:6 | LL | enum Bar { | ^^^ = note: required for `Arc` to implement `Send` note: required because it appears within the type `Foo` - --> $DIR/issue-40827.rs:4:8 + --> $DIR/deep-level-Send-bound-check-issue-40827.rs:4:8 | LL | struct Foo(Arc); | ^^^ note: required by a bound in `f` - --> $DIR/issue-40827.rs:11:9 + --> $DIR/deep-level-Send-bound-check-issue-40827.rs:11:9 | LL | fn f(_: T) {} | ^^^^ required by this bound in `f` error[E0277]: `Rc` cannot be sent between threads safely - --> $DIR/issue-40827.rs:14:7 + --> $DIR/deep-level-Send-bound-check-issue-40827.rs:14:7 | LL | f(Foo(Arc::new(Bar::B(None)))); | - ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Rc` cannot be sent between threads safely @@ -34,18 +34,18 @@ LL | f(Foo(Arc::new(Bar::B(None)))); | = help: within `Bar`, the trait `Send` is not implemented for `Rc` note: required because it appears within the type `Bar` - --> $DIR/issue-40827.rs:6:6 + --> $DIR/deep-level-Send-bound-check-issue-40827.rs:6:6 | LL | enum Bar { | ^^^ = note: required for `Arc` to implement `Send` note: required because it appears within the type `Foo` - --> $DIR/issue-40827.rs:4:8 + --> $DIR/deep-level-Send-bound-check-issue-40827.rs:4:8 | LL | struct Foo(Arc); | ^^^ note: required by a bound in `f` - --> $DIR/issue-40827.rs:11:9 + --> $DIR/deep-level-Send-bound-check-issue-40827.rs:11:9 | LL | fn f(_: T) {} | ^^^^ required by this bound in `f` diff --git a/tests/ui/issues/issue-40610.rs b/tests/ui/typeck/coercion-check-for-addition-issue-40610.rs similarity index 100% rename from tests/ui/issues/issue-40610.rs rename to tests/ui/typeck/coercion-check-for-addition-issue-40610.rs diff --git a/tests/ui/issues/issue-40610.stderr b/tests/ui/typeck/coercion-check-for-addition-issue-40610.stderr similarity index 79% rename from tests/ui/issues/issue-40610.stderr rename to tests/ui/typeck/coercion-check-for-addition-issue-40610.stderr index 1bd1c4dd57d7..5e424862d97a 100644 --- a/tests/ui/issues/issue-40610.stderr +++ b/tests/ui/typeck/coercion-check-for-addition-issue-40610.stderr @@ -1,5 +1,5 @@ error[E0369]: cannot add `()` to `()` - --> $DIR/issue-40610.rs:4:8 + --> $DIR/coercion-check-for-addition-issue-40610.rs:4:8 | LL | () + f(&[1.0]); | -- ^ --------- () diff --git a/tests/ui/issues/issue-40861.rs b/tests/ui/typeck/coercion-check-for-indexing-expression-issue-40861.rs similarity index 100% rename from tests/ui/issues/issue-40861.rs rename to tests/ui/typeck/coercion-check-for-indexing-expression-issue-40861.rs diff --git a/tests/ui/issues/issue-40861.stderr b/tests/ui/typeck/coercion-check-for-indexing-expression-issue-40861.stderr similarity index 81% rename from tests/ui/issues/issue-40861.stderr rename to tests/ui/typeck/coercion-check-for-indexing-expression-issue-40861.stderr index dec9af4b6d1c..13bc0cd94f37 100644 --- a/tests/ui/issues/issue-40861.stderr +++ b/tests/ui/typeck/coercion-check-for-indexing-expression-issue-40861.stderr @@ -1,5 +1,5 @@ error[E0608]: cannot index into a value of type `()` - --> $DIR/issue-40861.rs:4:7 + --> $DIR/coercion-check-for-indexing-expression-issue-40861.rs:4:7 | LL | ()[f(&[1.0])]; | ^^^^^^^^^^^ diff --git a/tests/ui/issues/issue-41139.rs b/tests/ui/typeck/unsized-rvalue-issue-41139.rs similarity index 100% rename from tests/ui/issues/issue-41139.rs rename to tests/ui/typeck/unsized-rvalue-issue-41139.rs diff --git a/tests/ui/issues/issue-41139.stderr b/tests/ui/typeck/unsized-rvalue-issue-41139.stderr similarity index 92% rename from tests/ui/issues/issue-41139.stderr rename to tests/ui/typeck/unsized-rvalue-issue-41139.stderr index d7b35245d8f5..aba0423eeb3e 100644 --- a/tests/ui/issues/issue-41139.stderr +++ b/tests/ui/typeck/unsized-rvalue-issue-41139.stderr @@ -1,5 +1,5 @@ error[E0618]: expected function, found `&dyn Fn() -> (dyn Trait + 'static)` - --> $DIR/issue-41139.rs:10:26 + --> $DIR/unsized-rvalue-issue-41139.rs:10:26 | LL | fn get_function<'a>() -> &'a dyn Fn() -> dyn Trait { | -------------------------------------------------- `get_function` defined here returns `&dyn Fn() -> (dyn Trait + 'static)` diff --git a/tests/ui/issues/issue-40749.rs b/tests/ui/wf/range-expr-root-of-constant-issue-40749.rs similarity index 100% rename from tests/ui/issues/issue-40749.rs rename to tests/ui/wf/range-expr-root-of-constant-issue-40749.rs diff --git a/tests/ui/issues/issue-40749.stderr b/tests/ui/wf/range-expr-root-of-constant-issue-40749.stderr similarity index 84% rename from tests/ui/issues/issue-40749.stderr rename to tests/ui/wf/range-expr-root-of-constant-issue-40749.stderr index f7770e00013c..482773a39440 100644 --- a/tests/ui/issues/issue-40749.stderr +++ b/tests/ui/wf/range-expr-root-of-constant-issue-40749.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-40749.rs:2:9 + --> $DIR/range-expr-root-of-constant-issue-40749.rs:2:9 | LL | [0; ..10]; | ^^^^ expected `usize`, found `RangeTo<{integer}>` diff --git a/tests/ui/issues/issue-40235.rs b/tests/ui/while/while-let-scope-issue-40235.rs similarity index 100% rename from tests/ui/issues/issue-40235.rs rename to tests/ui/while/while-let-scope-issue-40235.rs From 90fee92fcccb7913d5e40e1526f858d67fdedd75 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Thu, 16 Jan 2025 01:47:15 +0000 Subject: [PATCH 48/57] Update `compiler-builtins` to 0.1.145 This includes [1] which is required for LLVM 20. [1]: https://github.com/rust-lang/compiler-builtins/pull/752 --- ...029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch | 4 ++-- library/Cargo.lock | 4 ++-- library/alloc/Cargo.toml | 2 +- library/std/Cargo.toml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch b/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch index bf58e4851585..bac411d1eb03 100644 --- a/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch +++ b/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch @@ -16,8 +16,8 @@ index 7165c3e48af..968552ad435 100644 [dependencies] core = { path = "../core" } --compiler_builtins = { version = "=0.1.143", features = ['rustc-dep-of-std'] } -+compiler_builtins = { version = "=0.1.143", features = ['rustc-dep-of-std', 'no-f16-f128'] } +-compiler_builtins = { version = "=0.1.145", features = ['rustc-dep-of-std'] } ++compiler_builtins = { version = "=0.1.145", features = ['rustc-dep-of-std', 'no-f16-f128'] } [dev-dependencies] rand = { version = "0.8.5", default-features = false, features = ["alloc"] } diff --git a/library/Cargo.lock b/library/Cargo.lock index 30875482dc06..8b78908e6d73 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -61,9 +61,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.143" +version = "0.1.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85ba2077e3eab3dd81be4ece6b7fb2ad0887c1fb813e9a45400baf75c6c7c29" +checksum = "da0705f5abaaab7168ccc14f8f340ded61be2bd3ebea86b9834b6acbc8495de8" dependencies = [ "cc", "rustc-std-workspace-core", diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index 96caac890a35..db7eaf52fb22 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -10,7 +10,7 @@ edition = "2021" [dependencies] core = { path = "../core" } -compiler_builtins = { version = "=0.1.143", features = ['rustc-dep-of-std'] } +compiler_builtins = { version = "=0.1.145", features = ['rustc-dep-of-std'] } [dev-dependencies] rand = { version = "0.8.5", default-features = false, features = ["alloc"] } diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 9eab75b06961..3a965193415e 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -17,7 +17,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] } panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core", public = true } -compiler_builtins = { version = "=0.1.143" } +compiler_builtins = { version = "=0.1.145" } unwind = { path = "../unwind" } hashbrown = { version = "0.15", default-features = false, features = [ 'rustc-dep-of-std', From 5b981a8e9182bc81e76e7eb778611582e5e636be Mon Sep 17 00:00:00 2001 From: DuskyElf <91879372+DuskyElf@users.noreply.github.com> Date: Tue, 4 Feb 2025 21:42:43 +0530 Subject: [PATCH 49/57] Quickfix `//@ check-pass` is enough tests/ui/match/enum-and-break-in-match-issue-41213.rs and tests/ui/while/while-let-scope-issue-40235.rs doesn't need to be run. --- tests/ui/match/enum-and-break-in-match-issue-41213.rs | 2 +- tests/ui/while/while-let-scope-issue-40235.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/match/enum-and-break-in-match-issue-41213.rs b/tests/ui/match/enum-and-break-in-match-issue-41213.rs index 97f80a99a832..7c42a3629c9c 100644 --- a/tests/ui/match/enum-and-break-in-match-issue-41213.rs +++ b/tests/ui/match/enum-and-break-in-match-issue-41213.rs @@ -1,4 +1,4 @@ -//@ run-pass +//@ check-pass #![allow(dead_code)] enum A { A1, diff --git a/tests/ui/while/while-let-scope-issue-40235.rs b/tests/ui/while/while-let-scope-issue-40235.rs index 2bdbb2f229e1..7d5dfc64a905 100644 --- a/tests/ui/while/while-let-scope-issue-40235.rs +++ b/tests/ui/while/while-let-scope-issue-40235.rs @@ -1,4 +1,4 @@ -//@ run-pass +//@ check-pass #![allow(unused_variables)] fn foo() {} From 07cf20e98773677746d09f10ff4bdae8b7ee22c6 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Tue, 4 Feb 2025 11:09:17 -0800 Subject: [PATCH 50/57] Rename slice::take methods to split_off --- library/core/src/slice/mod.rs | 53 ++++++++------- library/coretests/tests/slice.rs | 110 +++++++++++++++---------------- 2 files changed, 83 insertions(+), 80 deletions(-) diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 1993a7491e10..dd2b2fd15f98 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -4294,25 +4294,25 @@ impl [T] { /// /// # Examples /// - /// Taking the first three elements of a slice: + /// Splitting off the first three elements of a slice: /// /// ``` /// #![feature(slice_take)] /// /// let mut slice: &[_] = &['a', 'b', 'c', 'd']; - /// let mut first_three = slice.take(..3).unwrap(); + /// let mut first_three = slice.split_off(..3).unwrap(); /// /// assert_eq!(slice, &['d']); /// assert_eq!(first_three, &['a', 'b', 'c']); /// ``` /// - /// Taking the last two elements of a slice: + /// Splitting off the last two elements of a slice: /// /// ``` /// #![feature(slice_take)] /// /// let mut slice: &[_] = &['a', 'b', 'c', 'd']; - /// let mut tail = slice.take(2..).unwrap(); + /// let mut tail = slice.split_off(2..).unwrap(); /// /// assert_eq!(slice, &['a', 'b']); /// assert_eq!(tail, &['c', 'd']); @@ -4325,16 +4325,19 @@ impl [T] { /// /// let mut slice: &[_] = &['a', 'b', 'c', 'd']; /// - /// assert_eq!(None, slice.take(5..)); - /// assert_eq!(None, slice.take(..5)); - /// assert_eq!(None, slice.take(..=4)); + /// assert_eq!(None, slice.split_off(5..)); + /// assert_eq!(None, slice.split_off(..5)); + /// assert_eq!(None, slice.split_off(..=4)); /// let expected: &[char] = &['a', 'b', 'c', 'd']; - /// assert_eq!(Some(expected), slice.take(..4)); + /// assert_eq!(Some(expected), slice.split_off(..4)); /// ``` #[inline] #[must_use = "method does not modify the slice if the range is out of bounds"] #[unstable(feature = "slice_take", issue = "62280")] - pub fn take<'a, R: OneSidedRange>(self: &mut &'a Self, range: R) -> Option<&'a Self> { + pub fn split_off<'a, R: OneSidedRange>( + self: &mut &'a Self, + range: R, + ) -> Option<&'a Self> { let (direction, split_index) = split_point_of(range)?; if split_index > self.len() { return None; @@ -4363,13 +4366,13 @@ impl [T] { /// /// # Examples /// - /// Taking the first three elements of a slice: + /// Splitting off the first three elements of a slice: /// /// ``` /// #![feature(slice_take)] /// /// let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd']; - /// let mut first_three = slice.take_mut(..3).unwrap(); + /// let mut first_three = slice.split_off_mut(..3).unwrap(); /// /// assert_eq!(slice, &mut ['d']); /// assert_eq!(first_three, &mut ['a', 'b', 'c']); @@ -4381,7 +4384,7 @@ impl [T] { /// #![feature(slice_take)] /// /// let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd']; - /// let mut tail = slice.take_mut(2..).unwrap(); + /// let mut tail = slice.split_off_mut(2..).unwrap(); /// /// assert_eq!(slice, &mut ['a', 'b']); /// assert_eq!(tail, &mut ['c', 'd']); @@ -4394,16 +4397,16 @@ impl [T] { /// /// let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd']; /// - /// assert_eq!(None, slice.take_mut(5..)); - /// assert_eq!(None, slice.take_mut(..5)); - /// assert_eq!(None, slice.take_mut(..=4)); + /// assert_eq!(None, slice.split_off_mut(5..)); + /// assert_eq!(None, slice.split_off_mut(..5)); + /// assert_eq!(None, slice.split_off_mut(..=4)); /// let expected: &mut [_] = &mut ['a', 'b', 'c', 'd']; - /// assert_eq!(Some(expected), slice.take_mut(..4)); + /// assert_eq!(Some(expected), slice.split_off_mut(..4)); /// ``` #[inline] #[must_use = "method does not modify the slice if the range is out of bounds"] #[unstable(feature = "slice_take", issue = "62280")] - pub fn take_mut<'a, R: OneSidedRange>( + pub fn split_off_mut<'a, R: OneSidedRange>( self: &mut &'a mut Self, range: R, ) -> Option<&'a mut Self> { @@ -4435,14 +4438,14 @@ impl [T] { /// #![feature(slice_take)] /// /// let mut slice: &[_] = &['a', 'b', 'c']; - /// let first = slice.take_first().unwrap(); + /// let first = slice.split_off_first().unwrap(); /// /// assert_eq!(slice, &['b', 'c']); /// assert_eq!(first, &'a'); /// ``` #[inline] #[unstable(feature = "slice_take", issue = "62280")] - pub fn take_first<'a>(self: &mut &'a Self) -> Option<&'a T> { + pub fn split_off_first<'a>(self: &mut &'a Self) -> Option<&'a T> { let (first, rem) = self.split_first()?; *self = rem; Some(first) @@ -4459,7 +4462,7 @@ impl [T] { /// #![feature(slice_take)] /// /// let mut slice: &mut [_] = &mut ['a', 'b', 'c']; - /// let first = slice.take_first_mut().unwrap(); + /// let first = slice.split_off_first_mut().unwrap(); /// *first = 'd'; /// /// assert_eq!(slice, &['b', 'c']); @@ -4467,7 +4470,7 @@ impl [T] { /// ``` #[inline] #[unstable(feature = "slice_take", issue = "62280")] - pub fn take_first_mut<'a>(self: &mut &'a mut Self) -> Option<&'a mut T> { + pub fn split_off_first_mut<'a>(self: &mut &'a mut Self) -> Option<&'a mut T> { let (first, rem) = mem::take(self).split_first_mut()?; *self = rem; Some(first) @@ -4484,14 +4487,14 @@ impl [T] { /// #![feature(slice_take)] /// /// let mut slice: &[_] = &['a', 'b', 'c']; - /// let last = slice.take_last().unwrap(); + /// let last = slice.split_off_last().unwrap(); /// /// assert_eq!(slice, &['a', 'b']); /// assert_eq!(last, &'c'); /// ``` #[inline] #[unstable(feature = "slice_take", issue = "62280")] - pub fn take_last<'a>(self: &mut &'a Self) -> Option<&'a T> { + pub fn split_off_last<'a>(self: &mut &'a Self) -> Option<&'a T> { let (last, rem) = self.split_last()?; *self = rem; Some(last) @@ -4508,7 +4511,7 @@ impl [T] { /// #![feature(slice_take)] /// /// let mut slice: &mut [_] = &mut ['a', 'b', 'c']; - /// let last = slice.take_last_mut().unwrap(); + /// let last = slice.split_off_last_mut().unwrap(); /// *last = 'd'; /// /// assert_eq!(slice, &['a', 'b']); @@ -4516,7 +4519,7 @@ impl [T] { /// ``` #[inline] #[unstable(feature = "slice_take", issue = "62280")] - pub fn take_last_mut<'a>(self: &mut &'a mut Self) -> Option<&'a mut T> { + pub fn split_off_last_mut<'a>(self: &mut &'a mut Self) -> Option<&'a mut T> { let (last, rem) = mem::take(self).split_last_mut()?; *self = rem; Some(last) diff --git a/library/coretests/tests/slice.rs b/library/coretests/tests/slice.rs index 510dd4967c96..ea5322da3812 100644 --- a/library/coretests/tests/slice.rs +++ b/library/coretests/tests/slice.rs @@ -2399,18 +2399,18 @@ fn slice_rsplit_once() { assert_eq!(v.rsplit_once(|&x| x == 0), None); } -macro_rules! take_tests { +macro_rules! split_off_tests { (slice: &[], $($tts:tt)*) => { - take_tests!(ty: &[()], slice: &[], $($tts)*); + split_off_tests!(ty: &[()], slice: &[], $($tts)*); }; (slice: &mut [], $($tts:tt)*) => { - take_tests!(ty: &mut [()], slice: &mut [], $($tts)*); + split_off_tests!(ty: &mut [()], slice: &mut [], $($tts)*); }; (slice: &$slice:expr, $($tts:tt)*) => { - take_tests!(ty: &[_], slice: &$slice, $($tts)*); + split_off_tests!(ty: &[_], slice: &$slice, $($tts)*); }; (slice: &mut $slice:expr, $($tts:tt)*) => { - take_tests!(ty: &mut [_], slice: &mut $slice, $($tts)*); + split_off_tests!(ty: &mut [_], slice: &mut $slice, $($tts)*); }; (ty: $ty:ty, slice: $slice:expr, method: $method:ident, $(($test_name:ident, ($($args:expr),*), $output:expr, $remaining:expr),)*) => { $( @@ -2425,64 +2425,64 @@ macro_rules! take_tests { }; } -take_tests! { - slice: &[0, 1, 2, 3], method: take, - (take_in_bounds_range_to, (..1), Some(&[0] as _), &[1, 2, 3]), - (take_in_bounds_range_to_inclusive, (..=0), Some(&[0] as _), &[1, 2, 3]), - (take_in_bounds_range_from, (2..), Some(&[2, 3] as _), &[0, 1]), - (take_oob_range_to, (..5), None, &[0, 1, 2, 3]), - (take_oob_range_to_inclusive, (..=4), None, &[0, 1, 2, 3]), - (take_oob_range_from, (5..), None, &[0, 1, 2, 3]), +split_off_tests! { + slice: &[0, 1, 2, 3], method: split_off, + (split_off_in_bounds_range_to, (..1), Some(&[0] as _), &[1, 2, 3]), + (split_off_in_bounds_range_to_inclusive, (..=0), Some(&[0] as _), &[1, 2, 3]), + (split_off_in_bounds_range_from, (2..), Some(&[2, 3] as _), &[0, 1]), + (split_off_oob_range_to, (..5), None, &[0, 1, 2, 3]), + (split_off_oob_range_to_inclusive, (..=4), None, &[0, 1, 2, 3]), + (split_off_oob_range_from, (5..), None, &[0, 1, 2, 3]), } -take_tests! { - slice: &mut [0, 1, 2, 3], method: take_mut, - (take_mut_in_bounds_range_to, (..1), Some(&mut [0] as _), &mut [1, 2, 3]), - (take_mut_in_bounds_range_to_inclusive, (..=0), Some(&mut [0] as _), &mut [1, 2, 3]), - (take_mut_in_bounds_range_from, (2..), Some(&mut [2, 3] as _), &mut [0, 1]), - (take_mut_oob_range_to, (..5), None, &mut [0, 1, 2, 3]), - (take_mut_oob_range_to_inclusive, (..=4), None, &mut [0, 1, 2, 3]), - (take_mut_oob_range_from, (5..), None, &mut [0, 1, 2, 3]), +split_off_tests! { + slice: &mut [0, 1, 2, 3], method: split_off_mut, + (split_off_mut_in_bounds_range_to, (..1), Some(&mut [0] as _), &mut [1, 2, 3]), + (split_off_mut_in_bounds_range_to_inclusive, (..=0), Some(&mut [0] as _), &mut [1, 2, 3]), + (split_off_mut_in_bounds_range_from, (2..), Some(&mut [2, 3] as _), &mut [0, 1]), + (split_off_mut_oob_range_to, (..5), None, &mut [0, 1, 2, 3]), + (split_off_mut_oob_range_to_inclusive, (..=4), None, &mut [0, 1, 2, 3]), + (split_off_mut_oob_range_from, (5..), None, &mut [0, 1, 2, 3]), } -take_tests! { - slice: &[1, 2], method: take_first, - (take_first_nonempty, (), Some(&1), &[2]), +split_off_tests! { + slice: &[1, 2], method: split_off_first, + (split_off_first_nonempty, (), Some(&1), &[2]), } -take_tests! { - slice: &mut [1, 2], method: take_first_mut, - (take_first_mut_nonempty, (), Some(&mut 1), &mut [2]), +split_off_tests! { + slice: &mut [1, 2], method: split_off_first_mut, + (split_off_first_mut_nonempty, (), Some(&mut 1), &mut [2]), } -take_tests! { - slice: &[1, 2], method: take_last, - (take_last_nonempty, (), Some(&2), &[1]), +split_off_tests! { + slice: &[1, 2], method: split_off_last, + (split_off_last_nonempty, (), Some(&2), &[1]), } -take_tests! { - slice: &mut [1, 2], method: take_last_mut, - (take_last_mut_nonempty, (), Some(&mut 2), &mut [1]), +split_off_tests! { + slice: &mut [1, 2], method: split_off_last_mut, + (split_off_last_mut_nonempty, (), Some(&mut 2), &mut [1]), } -take_tests! { - slice: &[], method: take_first, - (take_first_empty, (), None, &[]), +split_off_tests! { + slice: &[], method: split_off_first, + (split_off_first_empty, (), None, &[]), } -take_tests! { - slice: &mut [], method: take_first_mut, - (take_first_mut_empty, (), None, &mut []), +split_off_tests! { + slice: &mut [], method: split_off_first_mut, + (split_off_first_mut_empty, (), None, &mut []), } -take_tests! { - slice: &[], method: take_last, - (take_last_empty, (), None, &[]), +split_off_tests! { + slice: &[], method: split_off_last, + (split_off_last_empty, (), None, &[]), } -take_tests! { - slice: &mut [], method: take_last_mut, - (take_last_mut_empty, (), None, &mut []), +split_off_tests! { + slice: &mut [], method: split_off_last_mut, + (split_off_last_mut_empty, (), None, &mut []), } #[cfg(not(miri))] // unused in Miri @@ -2497,19 +2497,19 @@ macro_rules! empty_max_mut { } #[cfg(not(miri))] // Comparing usize::MAX many elements takes forever in Miri (and in rustc without optimizations) -take_tests! { - slice: &[(); usize::MAX], method: take, - (take_in_bounds_max_range_to, (..usize::MAX), Some(EMPTY_MAX), &[(); 0]), - (take_oob_max_range_to_inclusive, (..=usize::MAX), None, EMPTY_MAX), - (take_in_bounds_max_range_from, (usize::MAX..), Some(&[] as _), EMPTY_MAX), +split_off_tests! { + slice: &[(); usize::MAX], method: split_off, + (split_off_in_bounds_max_range_to, (..usize::MAX), Some(EMPTY_MAX), &[(); 0]), + (split_off_oob_max_range_to_inclusive, (..=usize::MAX), None, EMPTY_MAX), + (split_off_in_bounds_max_range_from, (usize::MAX..), Some(&[] as _), EMPTY_MAX), } #[cfg(not(miri))] // Comparing usize::MAX many elements takes forever in Miri (and in rustc without optimizations) -take_tests! { - slice: &mut [(); usize::MAX], method: take_mut, - (take_mut_in_bounds_max_range_to, (..usize::MAX), Some(empty_max_mut!()), &mut [(); 0]), - (take_mut_oob_max_range_to_inclusive, (..=usize::MAX), None, empty_max_mut!()), - (take_mut_in_bounds_max_range_from, (usize::MAX..), Some(&mut [] as _), empty_max_mut!()), +split_off_tests! { + slice: &mut [(); usize::MAX], method: split_off_mut, + (split_off_mut_in_bounds_max_range_to, (..usize::MAX), Some(empty_max_mut!()), &mut [(); 0]), + (split_off_mut_oob_max_range_to_inclusive, (..=usize::MAX), None, empty_max_mut!()), + (split_off_mut_in_bounds_max_range_from, (usize::MAX..), Some(&mut [] as _), empty_max_mut!()), } #[test] From 836a989820aa82a7bb35c9fba670ab733406a84f Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Tue, 4 Feb 2025 11:42:29 -0800 Subject: [PATCH 51/57] Add OneSidedRangeBound to eliminate panic in `split_point_of` See discussion in https://github.com/rust-lang/rust/pull/88502/files#r760177240 --- library/core/src/ops/mod.rs | 4 +-- library/core/src/ops/range.rs | 46 ++++++++++++++++++++++++++++++++--- library/core/src/slice/mod.rs | 14 +++++------ 3 files changed, 50 insertions(+), 14 deletions(-) diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs index 40526f9583e6..7b2ced2cc4bd 100644 --- a/library/core/src/ops/mod.rs +++ b/library/core/src/ops/mod.rs @@ -182,10 +182,10 @@ pub use self::function::{Fn, FnMut, FnOnce}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::index::{Index, IndexMut}; pub(crate) use self::index_range::IndexRange; -#[unstable(feature = "one_sided_range", issue = "69780")] -pub use self::range::OneSidedRange; #[stable(feature = "inclusive_range", since = "1.26.0")] pub use self::range::{Bound, RangeBounds, RangeInclusive, RangeToInclusive}; +#[unstable(feature = "one_sided_range", issue = "69780")] +pub use self::range::{OneSidedRange, OneSidedRangeBound}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::range::{Range, RangeFrom, RangeFull, RangeTo}; #[unstable(feature = "try_trait_v2_residual", issue = "91285")] diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs index 727a22e454d3..42e07a0e51da 100644 --- a/library/core/src/ops/range.rs +++ b/library/core/src/ops/range.rs @@ -979,6 +979,19 @@ impl RangeBounds for RangeToInclusive<&T> { } } +/// An internal helper for `split_off` functions indicating +/// which end a `OneSidedRange` is bounded on. +#[unstable(feature = "one_sided_range", issue = "69780")] +#[allow(missing_debug_implementations)] +pub enum OneSidedRangeBound { + /// The range is bounded inclusively from below and is unbounded above. + StartInclusive, + /// The range is bounded exclusively from above and is unbounded below. + End, + /// The range is bounded inclusively from above and is unbounded below. + EndInclusive, +} + /// `OneSidedRange` is implemented for built-in range types that are unbounded /// on one side. For example, `a..`, `..b` and `..=c` implement `OneSidedRange`, /// but `..`, `d..e`, and `f..=g` do not. @@ -986,13 +999,38 @@ impl RangeBounds for RangeToInclusive<&T> { /// Types that implement `OneSidedRange` must return `Bound::Unbounded` /// from one of `RangeBounds::start_bound` or `RangeBounds::end_bound`. #[unstable(feature = "one_sided_range", issue = "69780")] -pub trait OneSidedRange: RangeBounds {} +pub trait OneSidedRange: RangeBounds { + /// An internal-only helper function for `split_off` and + /// `split_off_mut` that returns the bound of the one-sided range. + fn bound(self) -> (OneSidedRangeBound, T); +} #[unstable(feature = "one_sided_range", issue = "69780")] -impl OneSidedRange for RangeTo where Self: RangeBounds {} +impl OneSidedRange for RangeTo +where + Self: RangeBounds, +{ + fn bound(self) -> (OneSidedRangeBound, T) { + (OneSidedRangeBound::End, self.end) + } +} #[unstable(feature = "one_sided_range", issue = "69780")] -impl OneSidedRange for RangeFrom where Self: RangeBounds {} +impl OneSidedRange for RangeFrom +where + Self: RangeBounds, +{ + fn bound(self) -> (OneSidedRangeBound, T) { + (OneSidedRangeBound::StartInclusive, self.start) + } +} #[unstable(feature = "one_sided_range", issue = "69780")] -impl OneSidedRange for RangeToInclusive where Self: RangeBounds {} +impl OneSidedRange for RangeToInclusive +where + Self: RangeBounds, +{ + fn bound(self) -> (OneSidedRangeBound, T) { + (OneSidedRangeBound::EndInclusive, self.end) + } +} diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index dd2b2fd15f98..fe9d7c10db28 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -10,7 +10,7 @@ use crate::cmp::Ordering::{self, Equal, Greater, Less}; use crate::intrinsics::{exact_div, unchecked_sub}; use crate::mem::{self, SizedTypeProperties}; use crate::num::NonZero; -use crate::ops::{Bound, OneSidedRange, Range, RangeBounds, RangeInclusive}; +use crate::ops::{OneSidedRange, OneSidedRangeBound, Range, RangeBounds, RangeInclusive}; use crate::panic::const_panic; use crate::simd::{self, Simd}; use crate::ub_checks::assert_unsafe_precondition; @@ -83,14 +83,12 @@ pub use raw::{from_raw_parts, from_raw_parts_mut}; /// which to split. Returns `None` if the split index would overflow. #[inline] fn split_point_of(range: impl OneSidedRange) -> Option<(Direction, usize)> { - use Bound::*; + use OneSidedRangeBound::{End, EndInclusive, StartInclusive}; - Some(match (range.start_bound(), range.end_bound()) { - (Unbounded, Excluded(i)) => (Direction::Front, *i), - (Unbounded, Included(i)) => (Direction::Front, i.checked_add(1)?), - (Excluded(i), Unbounded) => (Direction::Back, i.checked_add(1)?), - (Included(i), Unbounded) => (Direction::Back, *i), - _ => unreachable!(), + Some(match range.bound() { + (StartInclusive, i) => (Direction::Back, i), + (End, i) => (Direction::Front, i), + (EndInclusive, i) => (Direction::Front, i.checked_add(1)?), }) } From e42cb3bedc0f2daba6772802c3e973477d2dd788 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Sat, 2 Nov 2024 17:49:42 -0700 Subject: [PATCH 52/57] cg_gcc: Directly use rustc_abi instead of reexports --- compiler/rustc_codegen_gcc/src/abi.rs | 6 +++--- compiler/rustc_codegen_gcc/src/builder.rs | 2 +- compiler/rustc_codegen_gcc/src/consts.rs | 2 +- compiler/rustc_codegen_gcc/src/context.rs | 2 +- compiler/rustc_codegen_gcc/src/debuginfo.rs | 4 ++-- compiler/rustc_codegen_gcc/src/declare.rs | 2 +- compiler/rustc_codegen_gcc/src/int.rs | 7 +++---- compiler/rustc_codegen_gcc/src/intrinsic/mod.rs | 14 +++++++------- compiler/rustc_codegen_gcc/src/intrinsic/simd.rs | 2 +- compiler/rustc_codegen_gcc/src/type_.rs | 2 +- compiler/rustc_codegen_gcc/src/type_of.rs | 7 ++++--- 11 files changed, 25 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs index 14fc23593f0e..d94fc5525288 100644 --- a/compiler/rustc_codegen_gcc/src/abi.rs +++ b/compiler/rustc_codegen_gcc/src/abi.rs @@ -8,7 +8,7 @@ use rustc_middle::ty::Ty; use rustc_middle::ty::layout::LayoutOf; #[cfg(feature = "master")] use rustc_session::config; -use rustc_target::abi::call::{ArgAttributes, CastTarget, FnAbi, PassMode, Reg, RegKind}; +use rustc_target::callconv::{ArgAttributes, CastTarget, FnAbi, PassMode, Reg, RegKind}; use crate::builder::Builder; use crate::context::CodegenCx; @@ -132,10 +132,10 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { if cx.sess().opts.optimize == config::OptLevel::No { return ty; } - if attrs.regular.contains(rustc_target::abi::call::ArgAttribute::NoAlias) { + if attrs.regular.contains(rustc_target::callconv::ArgAttribute::NoAlias) { ty = ty.make_restrict() } - if attrs.regular.contains(rustc_target::abi::call::ArgAttribute::NonNull) { + if attrs.regular.contains(rustc_target::callconv::ArgAttribute::NonNull) { non_null_args.push(arg_index as i32 + 1); } ty diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 89e5cf1b8c6b..bba19d062583 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -29,7 +29,7 @@ use rustc_middle::ty::layout::{ use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_span::Span; use rustc_span::def_id::DefId; -use rustc_target::abi::call::FnAbi; +use rustc_target::callconv::FnAbi; use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, Target, WasmCAbi, X86Abi}; use crate::common::{SignType, TypeReflection, type_is_pointer}; diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs index 1631ecfeecf3..fb0ca31c5433 100644 --- a/compiler/rustc_codegen_gcc/src/consts.rs +++ b/compiler/rustc_codegen_gcc/src/consts.rs @@ -1,6 +1,7 @@ #[cfg(feature = "master")] use gccjit::{FnAttribute, VarAttribute, Visibility}; use gccjit::{Function, GlobalKind, LValue, RValue, ToRValue, Type}; +use rustc_abi::{self as abi, Align, HasDataLayout, Primitive, Size, WrappingRange}; use rustc_codegen_ssa::traits::{ BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods, }; @@ -14,7 +15,6 @@ use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Instance}; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::DefId; -use rustc_target::abi::{self, Align, HasDataLayout, Primitive, Size, WrappingRange}; use crate::base; use crate::context::CodegenCx; diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 570ef938dc44..1e1f577bb3a1 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -3,6 +3,7 @@ use std::cell::{Cell, RefCell}; use gccjit::{ Block, CType, Context, Function, FunctionPtrType, FunctionType, LValue, Location, RValue, Type, }; +use rustc_abi::{HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx}; use rustc_codegen_ssa::base::wants_msvc_seh; use rustc_codegen_ssa::errors as ssa_errors; use rustc_codegen_ssa::traits::{BackendTypes, BaseTypeCodegenMethods, MiscCodegenMethods}; @@ -18,7 +19,6 @@ use rustc_middle::ty::{self, ExistentialTraitRef, Instance, Ty, TyCtxt}; use rustc_session::Session; use rustc_span::source_map::respan; use rustc_span::{DUMMY_SP, Span}; -use rustc_target::abi::{HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx}; use rustc_target::spec::{ HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, Target, TlsModel, WasmCAbi, X86Abi, }; diff --git a/compiler/rustc_codegen_gcc/src/debuginfo.rs b/compiler/rustc_codegen_gcc/src/debuginfo.rs index 86d3de225f71..3f6fdea9fb8c 100644 --- a/compiler/rustc_codegen_gcc/src/debuginfo.rs +++ b/compiler/rustc_codegen_gcc/src/debuginfo.rs @@ -1,6 +1,7 @@ use std::ops::Range; use gccjit::{Location, RValue}; +use rustc_abi::Size; use rustc_codegen_ssa::mir::debuginfo::{DebugScope, FunctionDebugContext, VariableKind}; use rustc_codegen_ssa::traits::{DebugInfoBuilderMethods, DebugInfoCodegenMethods}; use rustc_data_structures::sync::Lrc; @@ -10,8 +11,7 @@ use rustc_middle::mir::{self, Body, SourceScope}; use rustc_middle::ty::{ExistentialTraitRef, Instance, Ty}; use rustc_session::config::DebugInfo; use rustc_span::{BytePos, Pos, SourceFile, SourceFileAndLine, Span, Symbol}; -use rustc_target::abi::Size; -use rustc_target::abi::call::FnAbi; +use rustc_target::callconv::FnAbi; use crate::builder::Builder; use crate::context::CodegenCx; diff --git a/compiler/rustc_codegen_gcc/src/declare.rs b/compiler/rustc_codegen_gcc/src/declare.rs index 442488b7fd65..7cdbe3c0c629 100644 --- a/compiler/rustc_codegen_gcc/src/declare.rs +++ b/compiler/rustc_codegen_gcc/src/declare.rs @@ -4,7 +4,7 @@ use gccjit::{Function, FunctionType, GlobalKind, LValue, RValue, Type}; use rustc_codegen_ssa::traits::BaseTypeCodegenMethods; use rustc_middle::ty::Ty; use rustc_span::Symbol; -use rustc_target::abi::call::FnAbi; +use rustc_target::callconv::FnAbi; use crate::abi::{FnAbiGcc, FnAbiGccExt}; use crate::context::CodegenCx; diff --git a/compiler/rustc_codegen_gcc/src/int.rs b/compiler/rustc_codegen_gcc/src/int.rs index fe6a65bed03b..4a1db8d662a9 100644 --- a/compiler/rustc_codegen_gcc/src/int.rs +++ b/compiler/rustc_codegen_gcc/src/int.rs @@ -3,12 +3,11 @@ //! 128-bit integers on 32-bit platforms and thus require to be handled manually. use gccjit::{BinaryOp, ComparisonOp, FunctionType, Location, RValue, ToRValue, Type, UnaryOp}; +use rustc_abi::{Endian, ExternAbi}; use rustc_codegen_ssa::common::{IntPredicate, TypeKind}; use rustc_codegen_ssa::traits::{BackendTypes, BaseTypeCodegenMethods, BuilderMethods, OverflowOp}; use rustc_middle::ty::{self, Ty}; -use rustc_target::abi::Endian; -use rustc_target::abi::call::{ArgAbi, ArgAttributes, Conv, FnAbi, PassMode}; -use rustc_target::spec; +use rustc_target::callconv::{ArgAbi, ArgAttributes, Conv, FnAbi, PassMode}; use crate::builder::{Builder, ToGccComp}; use crate::common::{SignType, TypeReflection}; @@ -401,7 +400,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { conv: Conv::C, can_unwind: false, }; - fn_abi.adjust_for_foreign_abi(self.cx, spec::abi::Abi::C { unwind: false }).unwrap(); + fn_abi.adjust_for_foreign_abi(self.cx, ExternAbi::C { unwind: false }).unwrap(); let ret_indirect = matches!(fn_abi.ret.mode, PassMode::Indirect { .. }); diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 48606f5f91c0..a1123fafe2f3 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -7,6 +7,9 @@ use std::iter; #[cfg(feature = "master")] use gccjit::FunctionType; use gccjit::{ComparisonOp, Function, RValue, ToRValue, Type, UnaryOp}; +#[cfg(feature = "master")] +use rustc_abi::ExternAbi; +use rustc_abi::HasDataLayout; use rustc_codegen_ssa::MemFlags; use rustc_codegen_ssa::base::wants_msvc_seh; use rustc_codegen_ssa::common::IntPredicate; @@ -25,11 +28,8 @@ use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt}; use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf}; use rustc_middle::ty::{self, Instance, Ty}; use rustc_span::{Span, Symbol, sym}; -use rustc_target::abi::HasDataLayout; -use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode}; +use rustc_target::callconv::{ArgAbi, FnAbi, PassMode}; use rustc_target::spec::PanicStrategy; -#[cfg(feature = "master")] -use rustc_target::spec::abi::Abi; #[cfg(feature = "master")] use crate::abi::FnAbiGccExt; @@ -1238,7 +1238,7 @@ fn get_rust_try_fn<'a, 'gcc, 'tcx>( tcx.types.unit, false, rustc_hir::Safety::Unsafe, - Abi::Rust, + ExternAbi::Rust, )), ); // `unsafe fn(*mut i8, *mut i8) -> ()` @@ -1249,7 +1249,7 @@ fn get_rust_try_fn<'a, 'gcc, 'tcx>( tcx.types.unit, false, rustc_hir::Safety::Unsafe, - Abi::Rust, + ExternAbi::Rust, )), ); // `unsafe fn(unsafe fn(*mut i8) -> (), *mut i8, unsafe fn(*mut i8, *mut i8) -> ()) -> i32` @@ -1258,7 +1258,7 @@ fn get_rust_try_fn<'a, 'gcc, 'tcx>( tcx.types.i32, false, rustc_hir::Safety::Unsafe, - Abi::Rust, + ExternAbi::Rust, )); let rust_try = gen_fn(cx, "__rust_try", rust_fn_sig, codegen); cx.rust_try_fn.set(Some(rust_try)); diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs index 1be452e5d05d..12a7dab155b4 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs @@ -3,6 +3,7 @@ use std::iter::FromIterator; use gccjit::{BinaryOp, RValue, ToRValue, Type}; #[cfg(feature = "master")] use gccjit::{ComparisonOp, UnaryOp}; +use rustc_abi::{Align, Size}; use rustc_codegen_ssa::base::compare_simd_types; use rustc_codegen_ssa::common::{IntPredicate, TypeKind}; #[cfg(feature = "master")] @@ -17,7 +18,6 @@ use rustc_middle::mir::BinOp; use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::{self, Ty}; use rustc_span::{Span, Symbol, sym}; -use rustc_target::abi::{Align, Size}; use crate::builder::Builder; #[cfg(not(feature = "master"))] diff --git a/compiler/rustc_codegen_gcc/src/type_.rs b/compiler/rustc_codegen_gcc/src/type_.rs index 4ea5544721de..cb08723431a9 100644 --- a/compiler/rustc_codegen_gcc/src/type_.rs +++ b/compiler/rustc_codegen_gcc/src/type_.rs @@ -4,13 +4,13 @@ use std::convert::TryInto; #[cfg(feature = "master")] use gccjit::CType; use gccjit::{RValue, Struct, Type}; +use rustc_abi::{AddressSpace, Align, Integer, Size}; use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::traits::{ BaseTypeCodegenMethods, DerivedTypeCodegenMethods, TypeMembershipCodegenMethods, }; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::{bug, ty}; -use rustc_target::abi::{AddressSpace, Align, Integer, Size}; use crate::common::TypeReflection; use crate::context::CodegenCx; diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs index 0efdf36da485..8b8b54753e7f 100644 --- a/compiler/rustc_codegen_gcc/src/type_of.rs +++ b/compiler/rustc_codegen_gcc/src/type_of.rs @@ -3,7 +3,9 @@ use std::fmt::Write; use gccjit::{Struct, Type}; use rustc_abi as abi; use rustc_abi::Primitive::*; -use rustc_abi::{BackendRepr, FieldsShape, Integer, PointeeInfo, Size, Variants}; +use rustc_abi::{ + BackendRepr, FieldsShape, Integer, PointeeInfo, Reg, Size, TyAbiInterface, Variants, +}; use rustc_codegen_ssa::traits::{ BaseTypeCodegenMethods, DerivedTypeCodegenMethods, LayoutTypeCodegenMethods, }; @@ -11,8 +13,7 @@ use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, CoroutineArgsExt, Ty, TypeVisitableExt}; -use rustc_target::abi::TyAbiInterface; -use rustc_target::abi::call::{CastTarget, FnAbi, Reg}; +use rustc_target::callconv::{CastTarget, FnAbi}; use crate::abi::{FnAbiGcc, FnAbiGccExt, GccType}; use crate::context::CodegenCx; From 0772400dbff0293307c6a3d19e60c391699a146f Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Wed, 5 Feb 2025 08:04:37 +0000 Subject: [PATCH 53/57] Arbitrary self types v2: recursion test Add a test for infinite recursion of an arbitrary self type. These diagnostics aren't perfect (especially the repetition of the statement that there's too much recursion) but for now at least let's add a test to confirm that such diagnostics are emitted. --- .../arbitrary_self_type_infinite_recursion.rs | 24 ++++++++++ ...itrary_self_type_infinite_recursion.stderr | 47 +++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 tests/ui/self/arbitrary_self_type_infinite_recursion.rs create mode 100644 tests/ui/self/arbitrary_self_type_infinite_recursion.stderr diff --git a/tests/ui/self/arbitrary_self_type_infinite_recursion.rs b/tests/ui/self/arbitrary_self_type_infinite_recursion.rs new file mode 100644 index 000000000000..6fbf35c0b867 --- /dev/null +++ b/tests/ui/self/arbitrary_self_type_infinite_recursion.rs @@ -0,0 +1,24 @@ +#![feature(arbitrary_self_types)] + +struct MySmartPtr(T); + +impl core::ops::Receiver for MySmartPtr { + type Target = MySmartPtr; +} + +struct Content; + +impl Content { + fn method(self: MySmartPtr) { // note self type + //~^ reached the recursion limit + //~| reached the recursion limit + //~| invalid `self` parameter type + } +} + +fn main() { + let p = MySmartPtr(Content); + p.method(); + //~^ reached the recursion limit + //~| no method named `method` +} diff --git a/tests/ui/self/arbitrary_self_type_infinite_recursion.stderr b/tests/ui/self/arbitrary_self_type_infinite_recursion.stderr new file mode 100644 index 000000000000..5e652efb3645 --- /dev/null +++ b/tests/ui/self/arbitrary_self_type_infinite_recursion.stderr @@ -0,0 +1,47 @@ +error[E0055]: reached the recursion limit while auto-dereferencing `MySmartPtr` + --> $DIR/arbitrary_self_type_infinite_recursion.rs:12:19 + | +LL | fn method(self: MySmartPtr) { // note self type + | ^^^^^^^^^^^^^^^^ deref recursion limit reached + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`arbitrary_self_type_infinite_recursion`) + +error[E0055]: reached the recursion limit while auto-dereferencing `MySmartPtr` + --> $DIR/arbitrary_self_type_infinite_recursion.rs:12:19 + | +LL | fn method(self: MySmartPtr) { // note self type + | ^^^^^^^^^^^^^^^^ deref recursion limit reached + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`arbitrary_self_type_infinite_recursion`) + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0307]: invalid `self` parameter type: `MySmartPtr` + --> $DIR/arbitrary_self_type_infinite_recursion.rs:12:19 + | +LL | fn method(self: MySmartPtr) { // note self type + | ^^^^^^^^^^^^^^^^ + | + = note: type of `self` must be `Self` or some type implementing `Receiver` + = help: consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box`, `self: Rc`, or `self: Arc` + +error[E0055]: reached the recursion limit while auto-dereferencing `MySmartPtr` + --> $DIR/arbitrary_self_type_infinite_recursion.rs:21:5 + | +LL | p.method(); + | ^^^^^^ deref recursion limit reached + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`arbitrary_self_type_infinite_recursion`) + +error[E0599]: no method named `method` found for struct `MySmartPtr` in the current scope + --> $DIR/arbitrary_self_type_infinite_recursion.rs:21:5 + | +LL | struct MySmartPtr(T); + | -------------------- method `method` not found for this struct +... +LL | p.method(); + | ^^^^^^ method not found in `MySmartPtr` + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0055, E0307, E0599. +For more information about an error, try `rustc --explain E0055`. From 24d94b2db9c18a30e8564ac8144a30b52dd31577 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 30 Jan 2025 09:15:33 +0000 Subject: [PATCH 54/57] Add regression test for negative unsigned literals in patterns --- tests/ui/type/pattern_types/signed_ranges.rs | 22 +++++++++++++++++++ .../type/pattern_types/signed_ranges.stderr | 17 ++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 tests/ui/type/pattern_types/signed_ranges.rs create mode 100644 tests/ui/type/pattern_types/signed_ranges.stderr diff --git a/tests/ui/type/pattern_types/signed_ranges.rs b/tests/ui/type/pattern_types/signed_ranges.rs new file mode 100644 index 000000000000..73ae89ac813e --- /dev/null +++ b/tests/ui/type/pattern_types/signed_ranges.rs @@ -0,0 +1,22 @@ +#![feature(pattern_types)] +#![feature(pattern_type_macro)] + +use std::pat::pattern_type; + +type Sign = pattern_type!(u32 is -10..); +//~^ ERROR: cannot apply unary operator `-` + +type SignedChar = pattern_type!(char is -'A'..); +//~^ ERROR: cannot apply unary operator `-` + +fn main() { + match 42_u8 { + -10..253 => {} + _ => {} + } + + match 'A' { + -'\0'..'a' => {} + _ => {} + } +} diff --git a/tests/ui/type/pattern_types/signed_ranges.stderr b/tests/ui/type/pattern_types/signed_ranges.stderr new file mode 100644 index 000000000000..63e346927f78 --- /dev/null +++ b/tests/ui/type/pattern_types/signed_ranges.stderr @@ -0,0 +1,17 @@ +error[E0600]: cannot apply unary operator `-` to type `u32` + --> $DIR/signed_ranges.rs:6:34 + | +LL | type Sign = pattern_type!(u32 is -10..); + | ^^^ cannot apply unary operator `-` + | + = note: unsigned values cannot be negated + +error[E0600]: cannot apply unary operator `-` to type `char` + --> $DIR/signed_ranges.rs:9:41 + | +LL | type SignedChar = pattern_type!(char is -'A'..); + | ^^^^ cannot apply unary operator `-` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0600`. From f44794f45506df2499083e66e5d225c7437ed158 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 30 Jan 2025 17:14:35 +0000 Subject: [PATCH 55/57] Only allow negation on literals in patterns if it's on integers or floats --- compiler/rustc_hir_typeck/src/pat.rs | 12 ++++++-- tests/ui/type/pattern_types/signed_ranges.rs | 2 ++ .../type/pattern_types/signed_ranges.stderr | 28 +++++++++++++++++-- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index c947ecde6560..2e7415cec5d3 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -570,8 +570,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_pat_expr_unadjusted(&self, lt: &'tcx hir::PatExpr<'tcx>) -> Ty<'tcx> { let ty = match <.kind { - rustc_hir::PatExprKind::Lit { lit, .. } => { - self.check_expr_lit(lit, Expectation::NoExpectation) + rustc_hir::PatExprKind::Lit { lit, negated } => { + let ty = self.check_expr_lit(lit, Expectation::NoExpectation); + if *negated { + self.register_bound( + ty, + self.tcx.require_lang_item(LangItem::Neg, Some(lt.span)), + ObligationCause::dummy_with_span(lt.span), + ); + } + ty } rustc_hir::PatExprKind::ConstBlock(c) => { self.check_expr_const_block(c, Expectation::NoExpectation) diff --git a/tests/ui/type/pattern_types/signed_ranges.rs b/tests/ui/type/pattern_types/signed_ranges.rs index 73ae89ac813e..3725fbda7292 100644 --- a/tests/ui/type/pattern_types/signed_ranges.rs +++ b/tests/ui/type/pattern_types/signed_ranges.rs @@ -12,11 +12,13 @@ type SignedChar = pattern_type!(char is -'A'..); fn main() { match 42_u8 { -10..253 => {} + //~^ ERROR `u8: Neg` is not satisfied _ => {} } match 'A' { -'\0'..'a' => {} + //~^ ERROR `char: Neg` is not satisfied _ => {} } } diff --git a/tests/ui/type/pattern_types/signed_ranges.stderr b/tests/ui/type/pattern_types/signed_ranges.stderr index 63e346927f78..79bf8501b639 100644 --- a/tests/ui/type/pattern_types/signed_ranges.stderr +++ b/tests/ui/type/pattern_types/signed_ranges.stderr @@ -1,3 +1,26 @@ +error[E0277]: the trait bound `u8: Neg` is not satisfied + --> $DIR/signed_ranges.rs:14:9 + | +LL | -10..253 => {} + | ^^^ the trait `Neg` is not implemented for `u8` + | + = help: the following other types implement trait `Neg`: + &f128 + &f16 + &f32 + &f64 + &i128 + &i16 + &i32 + &i64 + and 12 others + +error[E0277]: the trait bound `char: Neg` is not satisfied + --> $DIR/signed_ranges.rs:20:9 + | +LL | -'\0'..'a' => {} + | ^^^^^ the trait `Neg` is not implemented for `char` + error[E0600]: cannot apply unary operator `-` to type `u32` --> $DIR/signed_ranges.rs:6:34 | @@ -12,6 +35,7 @@ error[E0600]: cannot apply unary operator `-` to type `char` LL | type SignedChar = pattern_type!(char is -'A'..); | ^^^^ cannot apply unary operator `-` -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0600`. +Some errors have detailed explanations: E0277, E0600. +For more information about an error, try `rustc --explain E0277`. From cd3dd5bd230b1d98ed46e3609a64865eb1d5fc58 Mon Sep 17 00:00:00 2001 From: Wesley Wiser Date: Thu, 6 Feb 2025 04:25:40 +0000 Subject: [PATCH 56/57] Add tests for -Zdwarf-version lto behavior --- .../auxiliary/dwarf-mixed-versions-lto-aux.rs | 5 +++++ tests/assembly/dwarf-mixed-versions-lto.rs | 19 +++++++++++++++++++ .../auxiliary/dwarf-mixed-versions-lto-aux.rs | 5 +++++ tests/ui/lto/dwarf-mixed-versions-lto.rs | 15 +++++++++++++++ tests/ui/lto/dwarf-mixed-versions-lto.stderr | 4 ++++ 5 files changed, 48 insertions(+) create mode 100644 tests/assembly/auxiliary/dwarf-mixed-versions-lto-aux.rs create mode 100644 tests/assembly/dwarf-mixed-versions-lto.rs create mode 100644 tests/ui/lto/auxiliary/dwarf-mixed-versions-lto-aux.rs create mode 100644 tests/ui/lto/dwarf-mixed-versions-lto.rs create mode 100644 tests/ui/lto/dwarf-mixed-versions-lto.stderr diff --git a/tests/assembly/auxiliary/dwarf-mixed-versions-lto-aux.rs b/tests/assembly/auxiliary/dwarf-mixed-versions-lto-aux.rs new file mode 100644 index 000000000000..faff6e7e2d05 --- /dev/null +++ b/tests/assembly/auxiliary/dwarf-mixed-versions-lto-aux.rs @@ -0,0 +1,5 @@ +//@ compile-flags: -g --crate-type=rlib -Zdwarf-version=4 + +pub fn check_is_even(number: &u64) -> bool { + number % 2 == 0 +} diff --git a/tests/assembly/dwarf-mixed-versions-lto.rs b/tests/assembly/dwarf-mixed-versions-lto.rs new file mode 100644 index 000000000000..5b8e5ff4f4a4 --- /dev/null +++ b/tests/assembly/dwarf-mixed-versions-lto.rs @@ -0,0 +1,19 @@ +// This test ensures that if LTO occurs between crates with different DWARF versions, we +// will choose the highest DWARF version for the final binary. This matches Clang's behavior. + +//@ only-linux +//@ aux-build:dwarf-mixed-versions-lto-aux.rs +//@ compile-flags: -C lto -g -Zdwarf-version=5 +//@ assembly-output: emit-asm +//@ no-prefer-dynamic + +extern crate dwarf_mixed_versions_lto_aux; + +fn main() { + dwarf_mixed_versions_lto_aux::check_is_even(&0); +} + +// CHECK: .section .debug_info +// CHECK-NOT: {{\.(short|hword)}} 2 +// CHECK-NOT: {{\.(short|hword)}} 4 +// CHECK: {{\.(short|hword)}} 5 diff --git a/tests/ui/lto/auxiliary/dwarf-mixed-versions-lto-aux.rs b/tests/ui/lto/auxiliary/dwarf-mixed-versions-lto-aux.rs new file mode 100644 index 000000000000..3c81127ee65c --- /dev/null +++ b/tests/ui/lto/auxiliary/dwarf-mixed-versions-lto-aux.rs @@ -0,0 +1,5 @@ +//@ compile-flags: -g --crate-type=rlib -Zdwarf-version=4 + +pub fn say_hi() { + println!("hello there") +} diff --git a/tests/ui/lto/dwarf-mixed-versions-lto.rs b/tests/ui/lto/dwarf-mixed-versions-lto.rs new file mode 100644 index 000000000000..14ef65a868e0 --- /dev/null +++ b/tests/ui/lto/dwarf-mixed-versions-lto.rs @@ -0,0 +1,15 @@ +// This test verifies that we do not produce a warning when performing LTO on a +// crate graph that contains a mix of different DWARF version settings. This +// matches Clang's behavior. + +//@ ignore-msvc Platform must use DWARF +//@ aux-build:dwarf-mixed-versions-lto-aux.rs +//@ compile-flags: -C lto -g -Zdwarf-version=5 +//@ no-prefer-dynamic +//@ build-pass + +extern crate dwarf_mixed_versions_lto_aux; + +fn main() { + dwarf_mixed_versions_lto_aux::say_hi(); +} diff --git a/tests/ui/lto/dwarf-mixed-versions-lto.stderr b/tests/ui/lto/dwarf-mixed-versions-lto.stderr new file mode 100644 index 000000000000..15988383c29c --- /dev/null +++ b/tests/ui/lto/dwarf-mixed-versions-lto.stderr @@ -0,0 +1,4 @@ +warning: linking module flags 'Dwarf Version': IDs have conflicting values ('i32 4' from with 'i32 5' from dwarf_mixed_versions_lto.7f4a44b55cf2f174-cgu.0) + +warning: 1 warning emitted + From bbc40e78226c2b4a84b44c717b5ad0983e29944c Mon Sep 17 00:00:00 2001 From: Wesley Wiser Date: Thu, 6 Feb 2025 04:50:17 +0000 Subject: [PATCH 57/57] Pick the max DWARF version when LTO'ing modules with different versions Currently, when rustc compiles code with `-Clto` enabled that was built with different choices for `-Zdwarf-version`, a warning will be reported. It's very easy to observe this by compiling most anything (eg, "hello world") and specifying `-Clto -Zdwarf-version=5` since the standard library is distributed with `-Zdwarf-version=4`. This behavior isn't actually useful for a few reasons: - from observation, LLVM chooses to pick the highest DWARF version anyway after issuing the warning - Clang specifies that in this case, the max version should be picked without a warning and as a general principle, we want to support x-lang LTO with Clang which implies using the same module flag merge behaviors - Debuggers need to be able to handle a variety of versions withing the same debugging session as you can easily have some parts of a binary (or some dynamic libraries within an application) all compiled with different DWARF versions This commit changes the module flag merge behavior to match Clang and use the highest version of DWARF. It also adds a test to ensure this behavior is respected in the case of two crates being LTO'd together and updates the test added in the previous commit to ensure no warning is printed. --- compiler/rustc_codegen_llvm/src/debuginfo/mod.rs | 6 +++++- tests/ui/lto/dwarf-mixed-versions-lto.stderr | 4 ---- 2 files changed, 5 insertions(+), 5 deletions(-) delete mode 100644 tests/ui/lto/dwarf-mixed-versions-lto.stderr diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 496178c6b1d9..471cdc17148c 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -97,7 +97,11 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { // Android has the same issue (#22398) llvm::add_module_flag_u32( self.llmod, - llvm::ModuleFlagMergeBehavior::Warning, + // In the case where multiple CGUs with different dwarf version + // values are being merged together, such as with cross-crate + // LTO, then we want to use the highest version of dwarf + // we can. This matches Clang's behavior as well. + llvm::ModuleFlagMergeBehavior::Max, "Dwarf Version", sess.dwarf_version(), ); diff --git a/tests/ui/lto/dwarf-mixed-versions-lto.stderr b/tests/ui/lto/dwarf-mixed-versions-lto.stderr deleted file mode 100644 index 15988383c29c..000000000000 --- a/tests/ui/lto/dwarf-mixed-versions-lto.stderr +++ /dev/null @@ -1,4 +0,0 @@ -warning: linking module flags 'Dwarf Version': IDs have conflicting values ('i32 4' from with 'i32 5' from dwarf_mixed_versions_lto.7f4a44b55cf2f174-cgu.0) - -warning: 1 warning emitted -