diff --git a/Cargo.lock b/Cargo.lock index b8ccf2701041e..c1076f05ef129 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -466,6 +466,8 @@ version = "1.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" dependencies = [ + "jobserver", + "libc", "shlex", ] @@ -655,6 +657,26 @@ dependencies = [ "serde", ] +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + +[[package]] +name = "codespan-reporting" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" +dependencies = [ + "serde", + "termcolor", + "unicode-width 0.2.1", +] + [[package]] name = "collect-license-metadata" version = "0.1.0" @@ -913,6 +935,68 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "cxx" +version = "1.0.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3523cc02ad831111491dd64b27ad999f1ae189986728e477604e61b81f828df" +dependencies = [ + "cc", + "cxxbridge-cmd", + "cxxbridge-flags", + "cxxbridge-macro", + "foldhash", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212b754247a6f07b10fa626628c157593f0abf640a3dd04cce2760eca970f909" +dependencies = [ + "cc", + "codespan-reporting", + "indexmap", + "proc-macro2", + "quote", + "scratch", + "syn 2.0.104", +] + +[[package]] +name = "cxxbridge-cmd" +version = "1.0.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f426a20413ec2e742520ba6837c9324b55ffac24ead47491a6e29f933c5b135a" +dependencies = [ + "clap", + "codespan-reporting", + "indexmap", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258b6069020b4e5da6415df94a50ee4f586a6c38b037a180e940a43d06a070d" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8dec184b52be5008d6eaf7e62fc1802caf1ad1227d11b3b7df2c409c7ffc3f4" +dependencies = [ + "indexmap", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.104", +] + [[package]] name = "darling" version = "0.20.11" @@ -1373,6 +1457,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "genmc-sys" +version = "0.1.0" +dependencies = [ + "cc", + "cmake", + "cxx", + "cxx-build", + "git2", +] + [[package]] name = "getopts" version = "0.2.23" @@ -1427,6 +1522,21 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "git2" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2deb07a133b1520dc1a5690e9bd08950108873d7ed5de38dcc74d3b5ebffa110" +dependencies = [ + "bitflags", + "libc", + "libgit2-sys", + "log", + "openssl-probe", + "openssl-sys", + "url", +] + [[package]] name = "glob" version = "0.3.2" @@ -2060,6 +2170,19 @@ dependencies = [ "cc", ] +[[package]] +name = "libgit2-sys" +version = "0.18.2+1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c42fe03df2bd3c53a3a9c7317ad91d80c81cd1fb0caec8d7cc4cd2bfa10c222" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", +] + [[package]] name = "libloading" version = "0.8.8" @@ -2099,6 +2222,15 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "link-cplusplus" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a6f6da007f968f9def0d65a05b187e2960183de70c160204ecfccf0ee330212" +dependencies = [ + "cc", +] + [[package]] name = "linkchecker" version = "0.1.0" @@ -2308,6 +2440,7 @@ dependencies = [ "chrono-tz", "colored 3.0.0", "directories", + "genmc-sys", "getrandom 0.3.3", "ipc-channel", "libc", @@ -4877,6 +5010,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "scratch" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f6280af86e5f559536da57a45ebc84948833b3bee313a7dd25232e09c878a52" + [[package]] name = "self_cell" version = "1.2.0" diff --git a/bootstrap.example.toml b/bootstrap.example.toml index 73e93ccbe4202..ef49113b70f44 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -475,6 +475,9 @@ # Note that if any value is manually given to bootstrap such as # `./x test tidy --extra-checks=js`, this value is ignored. # Use `--extra-checks=''` to temporarily disable all extra checks. +# +# Automatically enabled in the "tools" profile. +# Set to the empty string to force disable (recommeded for hdd systems). #build.tidy-extra-checks = "" # Indicates whether ccache is used when building certain artifacts (e.g. LLVM). diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs index 56fb12d3c22ea..d1502d2b1e62e 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs @@ -285,8 +285,8 @@ pub(super) fn build_type_with_children<'ll, 'tcx>( // Item(T), // } // ``` - let is_expanding_recursive = - debug_context(cx).adt_stack.borrow().iter().any(|(parent_def_id, parent_args)| { + let is_expanding_recursive = adt_def.is_enum() + && debug_context(cx).adt_stack.borrow().iter().any(|(parent_def_id, parent_args)| { if def_id == *parent_def_id { args.iter().zip(parent_args.iter()).any(|(arg, parent_arg)| { if let (Some(arg), Some(parent_arg)) = (arg.as_type(), parent_arg.as_type()) diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index cfae1b3ec98e5..94501da69a766 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -8,8 +8,8 @@ edition = "2024" ar_archive_writer = "0.4.2" bitflags = "2.4.1" bstr = "1.11.3" -# Pinned so `cargo update` bumps don't cause breakage. Please also update the -# `cc` in `rustc_llvm` if you update the `cc` here. +# `cc` updates often break things, so we pin it here. Cargo enforces "max 1 semver-compat version +# per crate", so if you change this, you need to also change it in `rustc_llvm`. cc = "=1.2.16" itertools = "0.12" pathdiff = "0.2.0" diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 95400ac2ca3b0..46a4a18682479 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -713,8 +713,7 @@ impl HumanEmitter { Style::LineNumber, ); } - buffer.puts(line_offset, 0, &self.maybe_anonymized(line_index), Style::LineNumber); - + self.draw_line_num(buffer, line_index, line_offset, width_offset - 3); self.draw_col_separator_no_space(buffer, line_offset, width_offset - 2); left } @@ -2128,11 +2127,11 @@ impl HumanEmitter { // Account for a suggestion to completely remove a line(s) with whitespace (#94192). let line_end = sm.lookup_char_pos(parts[0].span.hi()).line; for line in line_start..=line_end { - buffer.puts( + self.draw_line_num( + &mut buffer, + line, row_num - 1 + line - line_start, - 0, - &self.maybe_anonymized(line), - Style::LineNumber, + max_line_num_len, ); buffer.puts( row_num - 1 + line - line_start, @@ -2612,12 +2611,7 @@ impl HumanEmitter { // For more info: https://github.com/rust-lang/rust/issues/92741 let lines_to_remove = file_lines.lines.iter().take(file_lines.lines.len() - 1); for (index, line_to_remove) in lines_to_remove.enumerate() { - buffer.puts( - *row_num - 1, - 0, - &self.maybe_anonymized(line_num + index), - Style::LineNumber, - ); + self.draw_line_num(buffer, line_num + index, *row_num - 1, max_line_num_len); buffer.puts(*row_num - 1, max_line_num_len + 1, "- ", Style::Removal); let line = normalize_whitespace( &file_lines.file.get_line(line_to_remove.line_index).unwrap(), @@ -2634,11 +2628,11 @@ impl HumanEmitter { let last_line_index = file_lines.lines[file_lines.lines.len() - 1].line_index; let last_line = &file_lines.file.get_line(last_line_index).unwrap(); if last_line != line_to_add { - buffer.puts( + self.draw_line_num( + buffer, + line_num + file_lines.lines.len() - 1, *row_num - 1, - 0, - &self.maybe_anonymized(line_num + file_lines.lines.len() - 1), - Style::LineNumber, + max_line_num_len, ); buffer.puts(*row_num - 1, max_line_num_len + 1, "- ", Style::Removal); buffer.puts( @@ -2661,7 +2655,7 @@ impl HumanEmitter { // 2 - .await // | // *row_num -= 1; - buffer.puts(*row_num, 0, &self.maybe_anonymized(line_num), Style::LineNumber); + self.draw_line_num(buffer, line_num, *row_num, max_line_num_len); buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition); buffer.append(*row_num, &normalize_whitespace(line_to_add), Style::NoStyle); } else { @@ -2671,7 +2665,7 @@ impl HumanEmitter { *row_num -= 2; } } else if is_multiline { - buffer.puts(*row_num, 0, &self.maybe_anonymized(line_num), Style::LineNumber); + self.draw_line_num(buffer, line_num, *row_num, max_line_num_len); match &highlight_parts { [SubstitutionHighlight { start: 0, end }] if *end == line_to_add.len() => { buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition); @@ -2702,11 +2696,11 @@ impl HumanEmitter { Style::NoStyle, ); } else if let DisplaySuggestion::Add = show_code_change { - buffer.puts(*row_num, 0, &self.maybe_anonymized(line_num), Style::LineNumber); + self.draw_line_num(buffer, line_num, *row_num, max_line_num_len); buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition); buffer.append(*row_num, &normalize_whitespace(line_to_add), Style::NoStyle); } else { - buffer.puts(*row_num, 0, &self.maybe_anonymized(line_num), Style::LineNumber); + self.draw_line_num(buffer, line_num, *row_num, max_line_num_len); self.draw_col_separator(buffer, *row_num, max_line_num_len + 1); buffer.append(*row_num, &normalize_whitespace(line_to_add), Style::NoStyle); } @@ -3016,6 +3010,22 @@ impl HumanEmitter { OutputTheme::Unicode => "…", } } + + fn draw_line_num( + &self, + buffer: &mut StyledBuffer, + line_num: usize, + line_offset: usize, + max_line_num_len: usize, + ) { + let line_num = self.maybe_anonymized(line_num); + buffer.puts( + line_offset, + max_line_num_len.saturating_sub(str_width(&line_num)), + &line_num, + Style::LineNumber, + ); + } } #[derive(Debug, Clone, Copy)] diff --git a/compiler/rustc_llvm/Cargo.toml b/compiler/rustc_llvm/Cargo.toml index 39de4783238b0..85a2a9c09f08c 100644 --- a/compiler/rustc_llvm/Cargo.toml +++ b/compiler/rustc_llvm/Cargo.toml @@ -10,8 +10,8 @@ libc = "0.2.73" [build-dependencies] # tidy-alphabetical-start -# Pinned so `cargo update` bumps don't cause breakage. Please also update the -# pinned `cc` in `rustc_codegen_ssa` if you update `cc` here. +# `cc` updates often break things, so we pin it here. Cargo enforces "max 1 semver-compat version +# per crate", so if you change this, you need to also change it in `rustc_codegen_ssa`. cc = "=1.2.16" # tidy-alphabetical-end diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 33bf4e3e29fbe..16df58cd76df4 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -955,9 +955,13 @@ impl<'tcx> ThirBuildCx<'tcx> { dcx.emit_fatal(LoopMatchBadRhs { span: block_body_expr.span }) }; - fn local(expr: &rustc_hir::Expr<'_>) -> Option { + fn local( + cx: &mut ThirBuildCx<'_>, + expr: &rustc_hir::Expr<'_>, + ) -> Option { if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind && let Res::Local(hir_id) = path.res + && !cx.is_upvar(hir_id) { return Some(hir_id); } @@ -965,11 +969,11 @@ impl<'tcx> ThirBuildCx<'tcx> { None } - let Some(scrutinee_hir_id) = local(scrutinee) else { + let Some(scrutinee_hir_id) = local(self, scrutinee) else { dcx.emit_fatal(LoopMatchInvalidMatch { span: scrutinee.span }) }; - if local(state) != Some(scrutinee_hir_id) { + if local(self, state) != Some(scrutinee_hir_id) { dcx.emit_fatal(LoopMatchInvalidUpdate { scrutinee: scrutinee.span, lhs: state.span, @@ -1260,10 +1264,7 @@ impl<'tcx> ThirBuildCx<'tcx> { fn convert_var(&mut self, var_hir_id: hir::HirId) -> ExprKind<'tcx> { // We want upvars here not captures. // Captures will be handled in MIR. - let is_upvar = self - .tcx - .upvars_mentioned(self.body_owner) - .is_some_and(|upvars| upvars.contains_key(&var_hir_id)); + let is_upvar = self.is_upvar(var_hir_id); debug!( "convert_var({:?}): is_upvar={}, body_owner={:?}", @@ -1443,6 +1444,12 @@ impl<'tcx> ThirBuildCx<'tcx> { } } + fn is_upvar(&mut self, var_hir_id: hir::HirId) -> bool { + self.tcx + .upvars_mentioned(self.body_owner) + .is_some_and(|upvars| upvars.contains_key(&var_hir_id)) + } + /// Converts a list of named fields (i.e., for struct-like struct/enum ADTs) into FieldExpr. fn field_refs(&mut self, fields: &'tcx [hir::ExprField<'tcx>]) -> Box<[FieldExpr]> { fields diff --git a/compiler/rustc_parse/src/parser/tests.rs b/compiler/rustc_parse/src/parser/tests.rs index 15679d23bc565..43a1d779a75d7 100644 --- a/compiler/rustc_parse/src/parser/tests.rs +++ b/compiler/rustc_parse/src/parser/tests.rs @@ -2114,15 +2114,15 @@ fn foo() { error: foo --> test.rs:3:6 | -3 | X0 Y0 Z0 + 3 | X0 Y0 Z0 | _______^ -4 | | X1 Y1 Z1 + 4 | | X1 Y1 Z1 | | ____^____- | ||____| | | `X` is a good letter -5 | | 1 -6 | | 2 -7 | | 3 + 5 | | 1 + 6 | | 2 + 7 | | 3 ... | 15 | | X2 Y2 Z2 16 | | X3 Y3 Z3 @@ -2133,15 +2133,15 @@ error: foo error: foo ╭▸ test.rs:3:6 │ -3 │ X0 Y0 Z0 + 3 │ X0 Y0 Z0 │ ┏━━━━━━━┛ -4 │ ┃ X1 Y1 Z1 + 4 │ ┃ X1 Y1 Z1 │ ┃┌────╿────┘ │ ┗│━━━━┥ │ │ `X` is a good letter -5 │ │ 1 -6 │ │ 2 -7 │ │ 3 + 5 │ │ 1 + 6 │ │ 2 + 7 │ │ 3 ‡ │ 15 │ │ X2 Y2 Z2 16 │ │ X3 Y3 Z3 @@ -2189,15 +2189,15 @@ fn foo() { error: foo --> test.rs:3:6 | -3 | X0 Y0 Z0 + 3 | X0 Y0 Z0 | _______^ -4 | | 1 -5 | | 2 -6 | | 3 -7 | | X1 Y1 Z1 + 4 | | 1 + 5 | | 2 + 6 | | 3 + 7 | | X1 Y1 Z1 | | _________- -8 | || 4 -9 | || 5 + 8 | || 4 + 9 | || 5 10 | || 6 11 | || X2 Y2 Z2 | ||__________- `Z` is a good letter too @@ -2211,15 +2211,15 @@ error: foo error: foo ╭▸ test.rs:3:6 │ -3 │ X0 Y0 Z0 + 3 │ X0 Y0 Z0 │ ┏━━━━━━━┛ -4 │ ┃ 1 -5 │ ┃ 2 -6 │ ┃ 3 -7 │ ┃ X1 Y1 Z1 + 4 │ ┃ 1 + 5 │ ┃ 2 + 6 │ ┃ 3 + 7 │ ┃ X1 Y1 Z1 │ ┃┌─────────┘ -8 │ ┃│ 4 -9 │ ┃│ 5 + 8 │ ┃│ 4 + 9 │ ┃│ 5 10 │ ┃│ 6 11 │ ┃│ X2 Y2 Z2 │ ┃└──────────┘ `Z` is a good letter too diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs index ad3b6439a6105..f33a33e6b752f 100644 --- a/library/core/src/ops/range.rs +++ b/library/core/src/ops/range.rs @@ -1141,6 +1141,12 @@ impl<'a, T: ?Sized + 'a> RangeBounds for (Bound<&'a T>, Bound<&'a T>) { } } +// This impl intentionally does not have `T: ?Sized`; +// see https://github.com/rust-lang/rust/pull/61584 for discussion of why. +// +/// If you need to use this implementation where `T` is unsized, +/// consider using the `RangeBounds` impl for a 2-tuple of [`Bound<&T>`][Bound], +/// i.e. replace `start..` with `(Bound::Included(start), Bound::Unbounded)`. #[stable(feature = "collections_range", since = "1.28.0")] impl RangeBounds for RangeFrom<&T> { fn start_bound(&self) -> Bound<&T> { @@ -1151,6 +1157,12 @@ impl RangeBounds for RangeFrom<&T> { } } +// This impl intentionally does not have `T: ?Sized`; +// see https://github.com/rust-lang/rust/pull/61584 for discussion of why. +// +/// If you need to use this implementation where `T` is unsized, +/// consider using the `RangeBounds` impl for a 2-tuple of [`Bound<&T>`][Bound], +/// i.e. replace `..end` with `(Bound::Unbounded, Bound::Excluded(end))`. #[stable(feature = "collections_range", since = "1.28.0")] impl RangeBounds for RangeTo<&T> { fn start_bound(&self) -> Bound<&T> { @@ -1161,6 +1173,12 @@ impl RangeBounds for RangeTo<&T> { } } +// This impl intentionally does not have `T: ?Sized`; +// see https://github.com/rust-lang/rust/pull/61584 for discussion of why. +// +/// If you need to use this implementation where `T` is unsized, +/// consider using the `RangeBounds` impl for a 2-tuple of [`Bound<&T>`][Bound], +/// i.e. replace `start..end` with `(Bound::Included(start), Bound::Excluded(end))`. #[stable(feature = "collections_range", since = "1.28.0")] impl RangeBounds for Range<&T> { fn start_bound(&self) -> Bound<&T> { @@ -1171,6 +1189,12 @@ impl RangeBounds for Range<&T> { } } +// This impl intentionally does not have `T: ?Sized`; +// see https://github.com/rust-lang/rust/pull/61584 for discussion of why. +// +/// If you need to use this implementation where `T` is unsized, +/// consider using the `RangeBounds` impl for a 2-tuple of [`Bound<&T>`][Bound], +/// i.e. replace `start..=end` with `(Bound::Included(start), Bound::Included(end))`. #[stable(feature = "collections_range", since = "1.28.0")] impl RangeBounds for RangeInclusive<&T> { fn start_bound(&self) -> Bound<&T> { @@ -1181,6 +1205,12 @@ impl RangeBounds for RangeInclusive<&T> { } } +// This impl intentionally does not have `T: ?Sized`; +// see https://github.com/rust-lang/rust/pull/61584 for discussion of why. +// +/// If you need to use this implementation where `T` is unsized, +/// consider using the `RangeBounds` impl for a 2-tuple of [`Bound<&T>`][Bound], +/// i.e. replace `..=end` with `(Bound::Unbounded, Bound::Included(end))`. #[stable(feature = "collections_range", since = "1.28.0")] impl RangeBounds for RangeToInclusive<&T> { fn start_bound(&self) -> Bound<&T> { diff --git a/library/core/src/range.rs b/library/core/src/range.rs index 5cd7956291ca2..7158fa0fcf069 100644 --- a/library/core/src/range.rs +++ b/library/core/src/range.rs @@ -167,6 +167,12 @@ impl RangeBounds for Range { } } +// This impl intentionally does not have `T: ?Sized`; +// see https://github.com/rust-lang/rust/pull/61584 for discussion of why. +// +/// If you need to use this implementation where `T` is unsized, +/// consider using the `RangeBounds` impl for a 2-tuple of [`Bound<&T>`][Bound], +/// i.e. replace `start..end` with `(Bound::Included(start), Bound::Excluded(end))`. #[unstable(feature = "new_range_api", issue = "125687")] impl RangeBounds for Range<&T> { fn start_bound(&self) -> Bound<&T> { @@ -346,6 +352,12 @@ impl RangeBounds for RangeInclusive { } } +// This impl intentionally does not have `T: ?Sized`; +// see https://github.com/rust-lang/rust/pull/61584 for discussion of why. +// +/// If you need to use this implementation where `T` is unsized, +/// consider using the `RangeBounds` impl for a 2-tuple of [`Bound<&T>`][Bound], +/// i.e. replace `start..=end` with `(Bound::Included(start), Bound::Included(end))`. #[unstable(feature = "new_range_api", issue = "125687")] impl RangeBounds for RangeInclusive<&T> { fn start_bound(&self) -> Bound<&T> { @@ -491,6 +503,12 @@ impl RangeBounds for RangeFrom { } } +// This impl intentionally does not have `T: ?Sized`; +// see https://github.com/rust-lang/rust/pull/61584 for discussion of why. +// +/// If you need to use this implementation where `T` is unsized, +/// consider using the `RangeBounds` impl for a 2-tuple of [`Bound<&T>`][Bound], +/// i.e. replace `start..` with `(Bound::Included(start), Bound::Unbounded)`. #[unstable(feature = "new_range_api", issue = "125687")] impl RangeBounds for RangeFrom<&T> { fn start_bound(&self) -> Bound<&T> { diff --git a/library/std/src/sync/mod.rs b/library/std/src/sync/mod.rs index e67b4f6f22f5a..6ef3bf25cf67c 100644 --- a/library/std/src/sync/mod.rs +++ b/library/std/src/sync/mod.rs @@ -225,6 +225,8 @@ pub use self::poison::{MappedMutexGuard, MappedRwLockReadGuard, MappedRwLockWrit pub mod mpmc; pub mod mpsc; +#[unstable(feature = "sync_nonpoison", issue = "134645")] +pub mod nonpoison; #[unstable(feature = "sync_poison_mod", issue = "134646")] pub mod poison; diff --git a/library/std/src/sync/nonpoison.rs b/library/std/src/sync/nonpoison.rs new file mode 100644 index 0000000000000..2bbf226dc2cde --- /dev/null +++ b/library/std/src/sync/nonpoison.rs @@ -0,0 +1,37 @@ +//! Non-poisoning synchronous locks. +//! +//! The difference from the locks in the [`poison`] module is that the locks in this module will not +//! become poisoned when a thread panics while holding a guard. +//! +//! [`poison`]: super::poison + +use crate::fmt; + +/// A type alias for the result of a nonblocking locking method. +#[unstable(feature = "sync_nonpoison", issue = "134645")] +pub type TryLockResult = Result; + +/// A lock could not be acquired at this time because the operation would otherwise block. +#[unstable(feature = "sync_nonpoison", issue = "134645")] +pub struct WouldBlock; + +#[unstable(feature = "sync_nonpoison", issue = "134645")] +impl fmt::Debug for WouldBlock { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "WouldBlock".fmt(f) + } +} + +#[unstable(feature = "sync_nonpoison", issue = "134645")] +impl fmt::Display for WouldBlock { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "try_lock failed because the operation would block".fmt(f) + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +pub use self::mutex::MappedMutexGuard; +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +pub use self::mutex::{Mutex, MutexGuard}; + +mod mutex; diff --git a/library/std/src/sync/nonpoison/mutex.rs b/library/std/src/sync/nonpoison/mutex.rs new file mode 100644 index 0000000000000..b6861c78f001d --- /dev/null +++ b/library/std/src/sync/nonpoison/mutex.rs @@ -0,0 +1,611 @@ +use crate::cell::UnsafeCell; +use crate::fmt; +use crate::marker::PhantomData; +use crate::mem::{self, ManuallyDrop}; +use crate::ops::{Deref, DerefMut}; +use crate::ptr::NonNull; +use crate::sync::nonpoison::{TryLockResult, WouldBlock}; +use crate::sys::sync as sys; + +/// A mutual exclusion primitive useful for protecting shared data that does not keep track of +/// lock poisoning. +/// +/// For more information about mutexes, check out the documentation for the poisoning variant of +/// this lock at [`poison::Mutex`]. +/// +/// [`poison::Mutex`]: crate::sync::poison::Mutex +/// +/// # Examples +/// +/// Note that this `Mutex` does **not** propagate threads that panic while holding the lock via +/// poisoning. If you need this functionality, see [`poison::Mutex`]. +/// +/// ``` +/// #![feature(nonpoison_mutex)] +/// +/// use std::thread; +/// use std::sync::{Arc, nonpoison::Mutex}; +/// +/// let mutex = Arc::new(Mutex::new(0u32)); +/// let mut handles = Vec::new(); +/// +/// for n in 0..10 { +/// let m = Arc::clone(&mutex); +/// let handle = thread::spawn(move || { +/// let mut guard = m.lock(); +/// *guard += 1; +/// panic!("panic from thread {n} {guard}") +/// }); +/// handles.push(handle); +/// } +/// +/// for h in handles { +/// let _ = h.join(); +/// } +/// +/// println!("Finished, locked {} times", mutex.lock()); +/// ``` +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +#[cfg_attr(not(test), rustc_diagnostic_item = "NonPoisonMutex")] +pub struct Mutex { + inner: sys::Mutex, + data: UnsafeCell, +} + +/// `T` must be `Send` for a [`Mutex`] to be `Send` because it is possible to acquire +/// the owned `T` from the `Mutex` via [`into_inner`]. +/// +/// [`into_inner`]: Mutex::into_inner +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +unsafe impl Send for Mutex {} + +/// `T` must be `Send` for [`Mutex`] to be `Sync`. +/// This ensures that the protected data can be accessed safely from multiple threads +/// without causing data races or other unsafe behavior. +/// +/// [`Mutex`] provides mutable access to `T` to one thread at a time. However, it's essential +/// for `T` to be `Send` because it's not safe for non-`Send` structures to be accessed in +/// this manner. For instance, consider [`Rc`], a non-atomic reference counted smart pointer, +/// which is not `Send`. With `Rc`, we can have multiple copies pointing to the same heap +/// allocation with a non-atomic reference count. If we were to use `Mutex>`, it would +/// only protect one instance of `Rc` from shared access, leaving other copies vulnerable +/// to potential data races. +/// +/// Also note that it is not necessary for `T` to be `Sync` as `&T` is only made available +/// to one thread at a time if `T` is not `Sync`. +/// +/// [`Rc`]: crate::rc::Rc +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +unsafe impl Sync for Mutex {} + +/// An RAII implementation of a "scoped lock" of a mutex. When this structure is +/// dropped (falls out of scope), the lock will be unlocked. +/// +/// The data protected by the mutex can be accessed through this guard via its +/// [`Deref`] and [`DerefMut`] implementations. +/// +/// This structure is created by the [`lock`] and [`try_lock`] methods on +/// [`Mutex`]. +/// +/// [`lock`]: Mutex::lock +/// [`try_lock`]: Mutex::try_lock +#[must_use = "if unused the Mutex will immediately unlock"] +#[must_not_suspend = "holding a MutexGuard across suspend \ + points can cause deadlocks, delays, \ + and cause Futures to not implement `Send`"] +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +#[clippy::has_significant_drop] +#[cfg_attr(not(test), rustc_diagnostic_item = "NonPoisonMutexGuard")] +pub struct MutexGuard<'a, T: ?Sized + 'a> { + lock: &'a Mutex, +} + +/// A [`MutexGuard`] is not `Send` to maximize platform portablity. +/// +/// On platforms that use POSIX threads (commonly referred to as pthreads) there is a requirement to +/// release mutex locks on the same thread they were acquired. +/// For this reason, [`MutexGuard`] must not implement `Send` to prevent it being dropped from +/// another thread. +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +impl !Send for MutexGuard<'_, T> {} + +/// `T` must be `Sync` for a [`MutexGuard`] to be `Sync` +/// because it is possible to get a `&T` from `&MutexGuard` (via `Deref`). +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +unsafe impl Sync for MutexGuard<'_, T> {} + +// FIXME(nonpoison_condvar): Use this link instead: [`Condvar`]: crate::sync::nonpoison::Condvar +/// An RAII mutex guard returned by `MutexGuard::map`, which can point to a +/// subfield of the protected data. When this structure is dropped (falls out +/// of scope), the lock will be unlocked. +/// +/// The main difference between `MappedMutexGuard` and [`MutexGuard`] is that the +/// former cannot be used with [`Condvar`], since that could introduce soundness issues if the +/// locked object is modified by another thread while the `Mutex` is unlocked. +/// +/// The data protected by the mutex can be accessed through this guard via its +/// [`Deref`] and [`DerefMut`] implementations. +/// +/// This structure is created by the [`map`] and [`filter_map`] methods on +/// [`MutexGuard`]. +/// +/// [`map`]: MutexGuard::map +/// [`filter_map`]: MutexGuard::filter_map +/// [`Condvar`]: crate::sync::Condvar +#[must_use = "if unused the Mutex will immediately unlock"] +#[must_not_suspend = "holding a MappedMutexGuard across suspend \ + points can cause deadlocks, delays, \ + and cause Futures to not implement `Send`"] +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +// #[unstable(feature = "nonpoison_mutex", issue = "134645")] +#[clippy::has_significant_drop] +pub struct MappedMutexGuard<'a, T: ?Sized + 'a> { + // NB: we use a pointer instead of `&'a mut T` to avoid `noalias` violations, because a + // `MappedMutexGuard` argument doesn't hold uniqueness for its whole scope, only until it drops. + // `NonNull` is covariant over `T`, so we add a `PhantomData<&'a mut T>` field + // below for the correct variance over `T` (invariance). + data: NonNull, + inner: &'a sys::Mutex, + _variance: PhantomData<&'a mut T>, +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +// #[unstable(feature = "nonpoison_mutex", issue = "134645")] +impl !Send for MappedMutexGuard<'_, T> {} +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +// #[unstable(feature = "nonpoison_mutex", issue = "134645")] +unsafe impl Sync for MappedMutexGuard<'_, T> {} + +impl Mutex { + /// Creates a new mutex in an unlocked state ready for use. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_mutex)] + /// + /// use std::sync::nonpoison::Mutex; + /// + /// let mutex = Mutex::new(0); + /// ``` + #[unstable(feature = "nonpoison_mutex", issue = "134645")] + #[inline] + pub const fn new(t: T) -> Mutex { + Mutex { inner: sys::Mutex::new(), data: UnsafeCell::new(t) } + } + + /// Returns the contained value by cloning it. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_mutex)] + /// #![feature(lock_value_accessors)] + /// + /// use std::sync::nonpoison::Mutex; + /// + /// let mut mutex = Mutex::new(7); + /// + /// assert_eq!(mutex.get_cloned(), 7); + /// ``` + #[unstable(feature = "lock_value_accessors", issue = "133407")] + // #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn get_cloned(&self) -> T + where + T: Clone, + { + self.lock().clone() + } + + /// Sets the contained value. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_mutex)] + /// #![feature(lock_value_accessors)] + /// + /// use std::sync::nonpoison::Mutex; + /// + /// let mut mutex = Mutex::new(7); + /// + /// assert_eq!(mutex.get_cloned(), 7); + /// mutex.set(11); + /// assert_eq!(mutex.get_cloned(), 11); + /// ``` + #[unstable(feature = "lock_value_accessors", issue = "133407")] + // #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn set(&self, value: T) { + if mem::needs_drop::() { + // If the contained value has a non-trivial destructor, we + // call that destructor after the lock has been released. + drop(self.replace(value)) + } else { + *self.lock() = value; + } + } + + /// Replaces the contained value with `value`, and returns the old contained value. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_mutex)] + /// #![feature(lock_value_accessors)] + /// + /// use std::sync::nonpoison::Mutex; + /// + /// let mut mutex = Mutex::new(7); + /// + /// assert_eq!(mutex.replace(11), 7); + /// assert_eq!(mutex.get_cloned(), 11); + /// ``` + #[unstable(feature = "lock_value_accessors", issue = "133407")] + // #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn replace(&self, value: T) -> T { + let mut guard = self.lock(); + mem::replace(&mut *guard, value) + } +} + +impl Mutex { + /// Acquires a mutex, blocking the current thread until it is able to do so. + /// + /// This function will block the local thread until it is available to acquire + /// the mutex. Upon returning, the thread is the only thread with the lock + /// held. An RAII guard is returned to allow scoped unlock of the lock. When + /// the guard goes out of scope, the mutex will be unlocked. + /// + /// The exact behavior on locking a mutex in the thread which already holds + /// the lock is left unspecified. However, this function will not return on + /// the second call (it might panic or deadlock, for example). + /// + /// # Panics + /// + /// This function might panic when called if the lock is already held by + /// the current thread. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_mutex)] + /// + /// use std::sync::{Arc, nonpoison::Mutex}; + /// use std::thread; + /// + /// let mutex = Arc::new(Mutex::new(0)); + /// let c_mutex = Arc::clone(&mutex); + /// + /// thread::spawn(move || { + /// *c_mutex.lock() = 10; + /// }).join().expect("thread::spawn failed"); + /// assert_eq!(*mutex.lock(), 10); + /// ``` + #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn lock(&self) -> MutexGuard<'_, T> { + unsafe { + self.inner.lock(); + MutexGuard::new(self) + } + } + + /// Attempts to acquire this lock. + /// + /// This function does not block. If the lock could not be acquired at this time, then + /// [`WouldBlock`] is returned. Otherwise, an RAII guard is returned. + /// + /// The lock will be unlocked when the guard is dropped. + /// + /// # Errors + /// + /// If the mutex could not be acquired because it is already locked, then this call will return + /// the [`WouldBlock`] error. + /// + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Mutex}; + /// use std::thread; + /// + /// let mutex = Arc::new(Mutex::new(0)); + /// let c_mutex = Arc::clone(&mutex); + /// + /// thread::spawn(move || { + /// let mut lock = c_mutex.try_lock(); + /// if let Ok(ref mut mutex) = lock { + /// **mutex = 10; + /// } else { + /// println!("try_lock failed"); + /// } + /// }).join().expect("thread::spawn failed"); + /// assert_eq!(*mutex.lock().unwrap(), 10); + /// ``` + #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn try_lock(&self) -> TryLockResult> { + unsafe { if self.inner.try_lock() { Ok(MutexGuard::new(self)) } else { Err(WouldBlock) } } + } + + /// Consumes this mutex, returning the underlying data. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_mutex)] + /// + /// use std::sync::nonpoison::Mutex; + /// + /// let mutex = Mutex::new(0); + /// assert_eq!(mutex.into_inner(), 0); + /// ``` + #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn into_inner(self) -> T + where + T: Sized, + { + self.data.into_inner() + } + + /// Returns a mutable reference to the underlying data. + /// + /// Since this call borrows the `Mutex` mutably, no actual locking needs to + /// take place -- the mutable borrow statically guarantees no locks exist. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_mutex)] + /// + /// use std::sync::nonpoison::Mutex; + /// + /// let mut mutex = Mutex::new(0); + /// *mutex.get_mut() = 10; + /// assert_eq!(*mutex.lock(), 10); + /// ``` + #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn get_mut(&mut self) -> &mut T { + self.data.get_mut() + } + + /// Returns a raw pointer to the underlying data. + /// + /// The returned pointer is always non-null and properly aligned, but it is + /// the user's responsibility to ensure that any reads and writes through it + /// are properly synchronized to avoid data races, and that it is not read + /// or written through after the mutex is dropped. + #[unstable(feature = "mutex_data_ptr", issue = "140368")] + // #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn data_ptr(&self) -> *mut T { + self.data.get() + } +} + +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +impl From for Mutex { + /// Creates a new mutex in an unlocked state ready for use. + /// This is equivalent to [`Mutex::new`]. + fn from(t: T) -> Self { + Mutex::new(t) + } +} + +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +impl Default for Mutex { + /// Creates a `Mutex`, with the `Default` value for T. + fn default() -> Mutex { + Mutex::new(Default::default()) + } +} + +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +impl fmt::Debug for Mutex { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut d = f.debug_struct("Mutex"); + match self.try_lock() { + Ok(guard) => { + d.field("data", &&*guard); + } + Err(WouldBlock) => { + d.field("data", &""); + } + } + d.finish_non_exhaustive() + } +} + +impl<'mutex, T: ?Sized> MutexGuard<'mutex, T> { + unsafe fn new(lock: &'mutex Mutex) -> MutexGuard<'mutex, T> { + return MutexGuard { lock }; + } +} + +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +impl Deref for MutexGuard<'_, T> { + type Target = T; + + fn deref(&self) -> &T { + unsafe { &*self.lock.data.get() } + } +} + +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +impl DerefMut for MutexGuard<'_, T> { + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *self.lock.data.get() } + } +} + +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +impl Drop for MutexGuard<'_, T> { + #[inline] + fn drop(&mut self) { + unsafe { + self.lock.inner.unlock(); + } + } +} + +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +impl fmt::Debug for MutexGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +impl fmt::Display for MutexGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +impl<'a, T: ?Sized> MutexGuard<'a, T> { + /// Makes a [`MappedMutexGuard`] for a component of the borrowed data, e.g. + /// an enum variant. + /// + /// The `Mutex` is already locked, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `MutexGuard::map(...)`. A method would interfere with methods of the + /// same name on the contents of the `MutexGuard` used through `Deref`. + #[unstable(feature = "mapped_lock_guards", issue = "117108")] + // #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn map(orig: Self, f: F) -> MappedMutexGuard<'a, U> + where + F: FnOnce(&mut T) -> &mut U, + U: ?Sized, + { + // SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + // The signature of the closure guarantees that it will not "leak" the lifetime of the reference + // passed to it. If the closure panics, the guard will be dropped. + let data = NonNull::from(f(unsafe { &mut *orig.lock.data.get() })); + let orig = ManuallyDrop::new(orig); + MappedMutexGuard { data, inner: &orig.lock.inner, _variance: PhantomData } + } + + /// Makes a [`MappedMutexGuard`] for a component of the borrowed data. The + /// original guard is returned as an `Err(...)` if the closure returns + /// `None`. + /// + /// The `Mutex` is already locked, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `MutexGuard::filter_map(...)`. A method would interfere with methods of the + /// same name on the contents of the `MutexGuard` used through `Deref`. + #[unstable(feature = "mapped_lock_guards", issue = "117108")] + // #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn filter_map(orig: Self, f: F) -> Result, Self> + where + F: FnOnce(&mut T) -> Option<&mut U>, + U: ?Sized, + { + // SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + // The signature of the closure guarantees that it will not "leak" the lifetime of the reference + // passed to it. If the closure panics, the guard will be dropped. + match f(unsafe { &mut *orig.lock.data.get() }) { + Some(data) => { + let data = NonNull::from(data); + let orig = ManuallyDrop::new(orig); + Ok(MappedMutexGuard { data, inner: &orig.lock.inner, _variance: PhantomData }) + } + None => Err(orig), + } + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +impl Deref for MappedMutexGuard<'_, T> { + type Target = T; + + fn deref(&self) -> &T { + unsafe { self.data.as_ref() } + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +impl DerefMut for MappedMutexGuard<'_, T> { + fn deref_mut(&mut self) -> &mut T { + unsafe { self.data.as_mut() } + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +impl Drop for MappedMutexGuard<'_, T> { + #[inline] + fn drop(&mut self) { + unsafe { + self.inner.unlock(); + } + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +impl fmt::Debug for MappedMutexGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +impl fmt::Display for MappedMutexGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +impl<'a, T: ?Sized> MappedMutexGuard<'a, T> { + /// Makes a [`MappedMutexGuard`] for a component of the borrowed data, e.g. + /// an enum variant. + /// + /// The `Mutex` is already locked, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `MappedMutexGuard::map(...)`. A method would interfere with methods of the + /// same name on the contents of the `MutexGuard` used through `Deref`. + #[unstable(feature = "mapped_lock_guards", issue = "117108")] + // #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn map(mut orig: Self, f: F) -> MappedMutexGuard<'a, U> + where + F: FnOnce(&mut T) -> &mut U, + U: ?Sized, + { + // SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + // The signature of the closure guarantees that it will not "leak" the lifetime of the reference + // passed to it. If the closure panics, the guard will be dropped. + let data = NonNull::from(f(unsafe { orig.data.as_mut() })); + let orig = ManuallyDrop::new(orig); + MappedMutexGuard { data, inner: orig.inner, _variance: PhantomData } + } + + /// Makes a [`MappedMutexGuard`] for a component of the borrowed data. The + /// original guard is returned as an `Err(...)` if the closure returns + /// `None`. + /// + /// The `Mutex` is already locked, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `MappedMutexGuard::filter_map(...)`. A method would interfere with methods of the + /// same name on the contents of the `MutexGuard` used through `Deref`. + #[unstable(feature = "mapped_lock_guards", issue = "117108")] + // #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn filter_map(mut orig: Self, f: F) -> Result, Self> + where + F: FnOnce(&mut T) -> Option<&mut U>, + U: ?Sized, + { + // SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + // The signature of the closure guarantees that it will not "leak" the lifetime of the reference + // passed to it. If the closure panics, the guard will be dropped. + match f(unsafe { orig.data.as_mut() }) { + Some(data) => { + let data = NonNull::from(data); + let orig = ManuallyDrop::new(orig); + Ok(MappedMutexGuard { data, inner: orig.inner, _variance: PhantomData }) + } + None => Err(orig), + } + } +} diff --git a/library/std/src/sync/poison.rs b/library/std/src/sync/poison.rs index 0c05f152ef84c..b901a5701a488 100644 --- a/library/std/src/sync/poison.rs +++ b/library/std/src/sync/poison.rs @@ -13,7 +13,9 @@ //! depend on the primitive. See [#Overview] below. //! //! For the alternative implementations that do not employ poisoning, -//! see `std::sync::nonpoisoning`. +//! see [`std::sync::nonpoison`]. +//! +//! [`std::sync::nonpoison`]: crate::sync::nonpoison //! //! # Overview //! @@ -56,8 +58,6 @@ //! while it is locked exclusively (write mode). If a panic occurs in any reader, //! then the lock will not be poisoned. -// FIXME(sync_nonpoison) add links to sync::nonpoison to the doc comment above. - #[stable(feature = "rust1", since = "1.0.0")] pub use self::condvar::{Condvar, WaitTimeoutResult}; #[unstable(feature = "mapped_lock_guards", issue = "117108")] diff --git a/library/std/src/sync/poison/condvar.rs b/library/std/src/sync/poison/condvar.rs index 7f0f3f652bcb7..0e9d4233c6577 100644 --- a/library/std/src/sync/poison/condvar.rs +++ b/library/std/src/sync/poison/condvar.rs @@ -13,7 +13,7 @@ use crate::time::{Duration, Instant}; #[stable(feature = "wait_timeout", since = "1.5.0")] pub struct WaitTimeoutResult(bool); -// FIXME(sync_nonpoison): `WaitTimeoutResult` is actually poisoning-agnostic, it seems. +// FIXME(nonpoison_condvar): `WaitTimeoutResult` is actually poisoning-agnostic, it seems. // Should we take advantage of this fact? impl WaitTimeoutResult { /// Returns `true` if the wait was known to have timed out. diff --git a/library/std/src/sync/poison/mutex.rs b/library/std/src/sync/poison/mutex.rs index 30325be685c32..64744f18c746e 100644 --- a/library/std/src/sync/poison/mutex.rs +++ b/library/std/src/sync/poison/mutex.rs @@ -650,7 +650,7 @@ impl fmt::Debug for Mutex { d.field("data", &&**err.get_ref()); } Err(TryLockError::WouldBlock) => { - d.field("data", &format_args!("")); + d.field("data", &""); } } d.field("poisoned", &self.poison.get()); diff --git a/library/std/src/sys/pal/hermit/thread.rs b/library/std/src/sys/pal/hermit/thread.rs index 9bc5a16b80023..95fe4f902d338 100644 --- a/library/std/src/sys/pal/hermit/thread.rs +++ b/library/std/src/sys/pal/hermit/thread.rs @@ -58,7 +58,11 @@ impl Thread { } } - pub unsafe fn new(stack: usize, p: Box) -> io::Result { + pub unsafe fn new( + stack: usize, + _name: Option<&str>, + p: Box, + ) -> io::Result { unsafe { Thread::new_with_coreid(stack, p, -1 /* = no specific core */) } diff --git a/library/std/src/sys/pal/itron/thread.rs b/library/std/src/sys/pal/itron/thread.rs index 813e1cbcd58fb..0d28051fcc46e 100644 --- a/library/std/src/sys/pal/itron/thread.rs +++ b/library/std/src/sys/pal/itron/thread.rs @@ -86,7 +86,11 @@ impl Thread { /// # Safety /// /// See `thread::Builder::spawn_unchecked` for safety requirements. - pub unsafe fn new(stack: usize, p: Box) -> io::Result { + pub unsafe fn new( + stack: usize, + _name: Option<&str>, + p: Box, + ) -> io::Result { let inner = Box::new(ThreadInner { start: UnsafeCell::new(ManuallyDrop::new(p)), lifecycle: AtomicUsize::new(LIFECYCLE_INIT), diff --git a/library/std/src/sys/pal/sgx/thread.rs b/library/std/src/sys/pal/sgx/thread.rs index 85f6dcd96b4a5..a236c3627060f 100644 --- a/library/std/src/sys/pal/sgx/thread.rs +++ b/library/std/src/sys/pal/sgx/thread.rs @@ -96,7 +96,11 @@ pub mod wait_notify { impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(_stack: usize, p: Box) -> io::Result { + pub unsafe fn new( + _stack: usize, + _name: Option<&str>, + p: Box, + ) -> io::Result { let mut queue_lock = task_queue::lock(); unsafe { usercalls::launch_thread()? }; let (task, handle) = task_queue::Task::new(p); diff --git a/library/std/src/sys/pal/teeos/thread.rs b/library/std/src/sys/pal/teeos/thread.rs index b9cdc7a2a58bb..a91d95626e703 100644 --- a/library/std/src/sys/pal/teeos/thread.rs +++ b/library/std/src/sys/pal/teeos/thread.rs @@ -22,7 +22,11 @@ unsafe extern "C" { impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(stack: usize, p: Box) -> io::Result { + pub unsafe fn new( + stack: usize, + _name: Option<&str>, + p: Box, + ) -> io::Result { let p = Box::into_raw(Box::new(p)); let mut native: libc::pthread_t = unsafe { mem::zeroed() }; let mut attr: libc::pthread_attr_t = unsafe { mem::zeroed() }; diff --git a/library/std/src/sys/pal/uefi/thread.rs b/library/std/src/sys/pal/uefi/thread.rs index e4776ec42fbba..75c364362b2d8 100644 --- a/library/std/src/sys/pal/uefi/thread.rs +++ b/library/std/src/sys/pal/uefi/thread.rs @@ -11,7 +11,11 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 64 * 1024; impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(_stack: usize, _p: Box) -> io::Result { + pub unsafe fn new( + _stack: usize, + _name: Option<&str>, + _p: Box, + ) -> io::Result { unsupported() } diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index a3be2cdf738f5..d89100e69196a 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -8,8 +8,8 @@ pub struct Handler { } impl Handler { - pub unsafe fn new() -> Handler { - make_handler(false) + pub unsafe fn new(thread_name: Option>) -> Handler { + make_handler(false, thread_name) } fn null() -> Handler { @@ -72,7 +72,6 @@ mod imp { use crate::sync::OnceLock; use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, AtomicUsize, Ordering}; use crate::sys::pal::unix::os; - use crate::thread::with_current_name; use crate::{io, mem, panic, ptr}; // Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages @@ -158,13 +157,12 @@ mod imp { if !NEED_ALTSTACK.load(Ordering::Relaxed) { // haven't set up our sigaltstack yet NEED_ALTSTACK.store(true, Ordering::Release); - let handler = unsafe { make_handler(true) }; + let handler = unsafe { make_handler(true, None) }; MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed); mem::forget(handler); if let Some(guard_page_range) = guard_page_range.take() { - let thread_name = with_current_name(|name| name.map(Box::from)); - set_current_info(guard_page_range, thread_name); + set_current_info(guard_page_range, Some(Box::from("main"))); } } @@ -230,14 +228,13 @@ mod imp { /// # Safety /// Mutates the alternate signal stack #[forbid(unsafe_op_in_unsafe_fn)] - pub unsafe fn make_handler(main_thread: bool) -> Handler { + pub unsafe fn make_handler(main_thread: bool, thread_name: Option>) -> Handler { if !NEED_ALTSTACK.load(Ordering::Acquire) { return Handler::null(); } if !main_thread { if let Some(guard_page_range) = unsafe { current_guard() } { - let thread_name = with_current_name(|name| name.map(Box::from)); set_current_info(guard_page_range, thread_name); } } @@ -634,7 +631,10 @@ mod imp { pub unsafe fn cleanup() {} - pub unsafe fn make_handler(_main_thread: bool) -> super::Handler { + pub unsafe fn make_handler( + _main_thread: bool, + _thread_name: Option>, + ) -> super::Handler { super::Handler::null() } @@ -717,7 +717,10 @@ mod imp { pub unsafe fn cleanup() {} - pub unsafe fn make_handler(main_thread: bool) -> super::Handler { + pub unsafe fn make_handler( + main_thread: bool, + _thread_name: Option>, + ) -> super::Handler { if !main_thread { reserve_stack(); } diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index e4f5520d8a33e..7f6440152d4bc 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -22,6 +22,11 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 256 * 1024; #[cfg(any(target_os = "espidf", target_os = "nuttx"))] pub const DEFAULT_MIN_STACK_SIZE: usize = 0; // 0 indicates that the stack size configured in the ESP-IDF/NuttX menuconfig system should be used +struct ThreadData { + name: Option>, + f: Box, +} + pub struct Thread { id: libc::pthread_t, } @@ -34,8 +39,12 @@ unsafe impl Sync for Thread {} impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - pub unsafe fn new(stack: usize, p: Box) -> io::Result { - let p = Box::into_raw(Box::new(p)); + pub unsafe fn new( + stack: usize, + name: Option<&str>, + f: Box, + ) -> io::Result { + let data = Box::into_raw(Box::new(ThreadData { name: name.map(Box::from), f })); let mut native: libc::pthread_t = mem::zeroed(); let mut attr: mem::MaybeUninit = mem::MaybeUninit::uninit(); assert_eq!(libc::pthread_attr_init(attr.as_mut_ptr()), 0); @@ -73,7 +82,7 @@ impl Thread { }; } - let ret = libc::pthread_create(&mut native, attr.as_ptr(), thread_start, p as *mut _); + let ret = libc::pthread_create(&mut native, attr.as_ptr(), thread_start, data as *mut _); // Note: if the thread creation fails and this assert fails, then p will // be leaked. However, an alternative design could cause double-free // which is clearly worse. @@ -82,19 +91,20 @@ impl Thread { return if ret != 0 { // The thread failed to start and as a result p was not consumed. Therefore, it is // safe to reconstruct the box so that it gets deallocated. - drop(Box::from_raw(p)); + drop(Box::from_raw(data)); Err(io::Error::from_raw_os_error(ret)) } else { Ok(Thread { id: native }) }; - extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void { + extern "C" fn thread_start(data: *mut libc::c_void) -> *mut libc::c_void { unsafe { + let data = Box::from_raw(data as *mut ThreadData); // Next, set up our stack overflow handler which may get triggered if we run // out of stack. - let _handler = stack_overflow::Handler::new(); + let _handler = stack_overflow::Handler::new(data.name); // Finally, let's run some code. - Box::from_raw(main as *mut Box)(); + (data.f)(); } ptr::null_mut() } diff --git a/library/std/src/sys/pal/unsupported/thread.rs b/library/std/src/sys/pal/unsupported/thread.rs index 8a3119fa292d1..5a1e3fde98606 100644 --- a/library/std/src/sys/pal/unsupported/thread.rs +++ b/library/std/src/sys/pal/unsupported/thread.rs @@ -10,7 +10,11 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 64 * 1024; impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(_stack: usize, _p: Box) -> io::Result { + pub unsafe fn new( + _stack: usize, + _name: Option<&str>, + _p: Box, + ) -> io::Result { unsupported() } diff --git a/library/std/src/sys/pal/wasi/thread.rs b/library/std/src/sys/pal/wasi/thread.rs index 5f21a553673a3..a46c74630c97a 100644 --- a/library/std/src/sys/pal/wasi/thread.rs +++ b/library/std/src/sys/pal/wasi/thread.rs @@ -73,7 +73,7 @@ impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements cfg_if::cfg_if! { if #[cfg(target_feature = "atomics")] { - pub unsafe fn new(stack: usize, p: Box) -> io::Result { + pub unsafe fn new(stack: usize, _name: Option<&str>, p: Box) -> io::Result { let p = Box::into_raw(Box::new(p)); let mut native: libc::pthread_t = unsafe { mem::zeroed() }; let mut attr: libc::pthread_attr_t = unsafe { mem::zeroed() }; @@ -120,7 +120,7 @@ impl Thread { } } } else { - pub unsafe fn new(_stack: usize, _p: Box) -> io::Result { + pub unsafe fn new(_stack: usize, _name: Option<&str>, _p: Box) -> io::Result { crate::sys::unsupported() } } diff --git a/library/std/src/sys/pal/wasm/atomics/thread.rs b/library/std/src/sys/pal/wasm/atomics/thread.rs index 44ce3eab109f4..ebfabaafc794d 100644 --- a/library/std/src/sys/pal/wasm/atomics/thread.rs +++ b/library/std/src/sys/pal/wasm/atomics/thread.rs @@ -10,7 +10,11 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024; impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(_stack: usize, _p: Box) -> io::Result { + pub unsafe fn new( + _stack: usize, + _name: Option<&str>, + _p: Box, + ) -> io::Result { unsupported() } diff --git a/library/std/src/sys/pal/windows/thread.rs b/library/std/src/sys/pal/windows/thread.rs index 147851717553a..b45f76fb546f0 100644 --- a/library/std/src/sys/pal/windows/thread.rs +++ b/library/std/src/sys/pal/windows/thread.rs @@ -20,7 +20,11 @@ pub struct Thread { impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - pub unsafe fn new(stack: usize, p: Box) -> io::Result { + pub unsafe fn new( + stack: usize, + _name: Option<&str>, + p: Box, + ) -> io::Result { let p = Box::into_raw(Box::new(p)); // CreateThread rounds up values for the stack size to the nearest page size (at least 4kb). diff --git a/library/std/src/sys/pal/xous/thread.rs b/library/std/src/sys/pal/xous/thread.rs index 1b344e984dc36..f2404a62abf97 100644 --- a/library/std/src/sys/pal/xous/thread.rs +++ b/library/std/src/sys/pal/xous/thread.rs @@ -20,7 +20,11 @@ pub const GUARD_PAGE_SIZE: usize = 4096; impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(stack: usize, p: Box) -> io::Result { + pub unsafe fn new( + stack: usize, + _name: Option<&str>, + p: Box, + ) -> io::Result { let p = Box::into_raw(Box::new(p)); let mut stack_size = crate::cmp::max(stack, MIN_STACK_SIZE); diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 8cd1e0163a12c..dff981c900c08 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -595,7 +595,7 @@ impl Builder { // Similarly, the `sys` implementation must guarantee that no references to the closure // exist after the thread has terminated, which is signaled by `Thread::join` // returning. - native: unsafe { imp::Thread::new(stack_size, main)? }, + native: unsafe { imp::Thread::new(stack_size, my_thread.name(), main)? }, thread: my_thread, packet: my_packet, }) diff --git a/library/std/tests/sync/lib.rs b/library/std/tests/sync/lib.rs index 51190f0894fb7..94f1fe96b6a26 100644 --- a/library/std/tests/sync/lib.rs +++ b/library/std/tests/sync/lib.rs @@ -6,7 +6,10 @@ #![feature(reentrant_lock)] #![feature(rwlock_downgrade)] #![feature(std_internals)] +#![feature(sync_nonpoison)] +#![feature(nonpoison_mutex)] #![allow(internal_features)] +#![feature(macro_metavar_expr_concat)] // For concatenating identifiers in macros. mod barrier; mod condvar; @@ -29,3 +32,55 @@ mod rwlock; #[path = "../common/mod.rs"] mod common; + +#[track_caller] +fn result_unwrap(x: Result) -> T { + x.unwrap() +} + +/// A macro that generates two test cases for both the poison and nonpoison locks. +/// +/// To write a test that tests both `poison` and `nonpoison` locks, import any of the types +/// under both `poison` and `nonpoison` using the module name `locks` instead. For example, write +/// `use locks::Mutex;` instead of `use std::sync::poiosn::Mutex`. This will import the correct type +/// for each test variant. +/// +/// Write a test as normal in the `test_body`, but instead of calling `unwrap` on `poison` methods +/// that return a `LockResult` or similar, call the function `maybe_unwrap(...)` on the result. +/// +/// For example, call `maybe_unwrap(mutex.lock())` instead of `mutex.lock().unwrap()` or +/// `maybe_unwrap(rwlock.read())` instead of `rwlock.read().unwrap()`. +/// +/// For the `poison` types, `maybe_unwrap` will simply unwrap the `Result` (usually this is a form +/// of `LockResult`, but it could also be other kinds of results). For the `nonpoison` types, it is +/// a no-op (the identity function). +/// +/// The test names will be prefiex with `poison_` or `nonpoison_`. +macro_rules! nonpoison_and_poison_unwrap_test { + ( + name: $name:ident, + test_body: {$($test_body:tt)*} + ) => { + // Creates the nonpoison test. + #[test] + fn ${concat(nonpoison_, $name)}() { + #[allow(unused_imports)] + use ::std::convert::identity as maybe_unwrap; + use ::std::sync::nonpoison as locks; + + $($test_body)* + } + + // Creates the poison test with the suffix `_unwrap_poisoned`. + #[test] + fn ${concat(poison_, $name)}() { + #[allow(unused_imports)] + use super::result_unwrap as maybe_unwrap; + use ::std::sync::poison as locks; + + $($test_body)* + } + } +} + +use nonpoison_and_poison_unwrap_test; diff --git a/library/std/tests/sync/mutex.rs b/library/std/tests/sync/mutex.rs index ac82914d6de46..90cefc0d59466 100644 --- a/library/std/tests/sync/mutex.rs +++ b/library/std/tests/sync/mutex.rs @@ -6,7 +6,71 @@ use std::sync::mpsc::channel; use std::sync::{Arc, Condvar, MappedMutexGuard, Mutex, MutexGuard, TryLockError}; use std::{hint, mem, thread}; -struct Packet(Arc<(Mutex, Condvar)>); +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Nonpoison & Poison Tests +//////////////////////////////////////////////////////////////////////////////////////////////////// +use super::nonpoison_and_poison_unwrap_test; + +nonpoison_and_poison_unwrap_test!( + name: smoke, + test_body: { + use locks::Mutex; + + let m = Mutex::new(()); + drop(maybe_unwrap(m.lock())); + drop(maybe_unwrap(m.lock())); + } +); + +nonpoison_and_poison_unwrap_test!( + name: lots_and_lots, + test_body: { + use locks::Mutex; + + const J: u32 = 1000; + const K: u32 = 3; + + let m = Arc::new(Mutex::new(0)); + + fn inc(m: &Mutex) { + for _ in 0..J { + *maybe_unwrap(m.lock()) += 1; + } + } + + let (tx, rx) = channel(); + for _ in 0..K { + let tx2 = tx.clone(); + let m2 = m.clone(); + thread::spawn(move || { + inc(&m2); + tx2.send(()).unwrap(); + }); + let tx2 = tx.clone(); + let m2 = m.clone(); + thread::spawn(move || { + inc(&m2); + tx2.send(()).unwrap(); + }); + } + + drop(tx); + for _ in 0..2 * K { + rx.recv().unwrap(); + } + assert_eq!(*maybe_unwrap(m.lock()), J * K * 2); + } +); + +nonpoison_and_poison_unwrap_test!( + name: try_lock, + test_body: { + use locks::Mutex; + + let m = Mutex::new(()); + *m.try_lock().unwrap() = (); + } +); #[derive(Eq, PartialEq, Debug)] struct NonCopy(i32); @@ -26,58 +90,278 @@ fn test_needs_drop() { assert!(mem::needs_drop::()); } -#[derive(Clone, Eq, PartialEq, Debug)] -struct Cloneable(i32); +nonpoison_and_poison_unwrap_test!( + name: test_into_inner, + test_body: { + use locks::Mutex; -#[test] -fn smoke() { - let m = Mutex::new(()); - drop(m.lock().unwrap()); - drop(m.lock().unwrap()); -} + let m = Mutex::new(NonCopy(10)); + assert_eq!(maybe_unwrap(m.into_inner()), NonCopy(10)); + } +); -#[test] -fn lots_and_lots() { - const J: u32 = 1000; - const K: u32 = 3; +nonpoison_and_poison_unwrap_test!( + name: test_into_inner_drop, + test_body: { + use locks::Mutex; - let m = Arc::new(Mutex::new(0)); + struct Foo(Arc); + impl Drop for Foo { + fn drop(&mut self) { + self.0.fetch_add(1, Ordering::SeqCst); + } + } - fn inc(m: &Mutex) { - for _ in 0..J { - *m.lock().unwrap() += 1; + let num_drops = Arc::new(AtomicUsize::new(0)); + let m = Mutex::new(Foo(num_drops.clone())); + assert_eq!(num_drops.load(Ordering::SeqCst), 0); + { + let _inner = maybe_unwrap(m.into_inner()); + assert_eq!(num_drops.load(Ordering::SeqCst), 0); } + assert_eq!(num_drops.load(Ordering::SeqCst), 1); } +); - let (tx, rx) = channel(); - for _ in 0..K { - let tx2 = tx.clone(); - let m2 = m.clone(); - thread::spawn(move || { - inc(&m2); - tx2.send(()).unwrap(); - }); - let tx2 = tx.clone(); - let m2 = m.clone(); - thread::spawn(move || { - inc(&m2); - tx2.send(()).unwrap(); - }); +nonpoison_and_poison_unwrap_test!( + name: test_get_mut, + test_body: { + use locks::Mutex; + + let mut m = Mutex::new(NonCopy(10)); + *maybe_unwrap(m.get_mut()) = NonCopy(20); + assert_eq!(maybe_unwrap(m.into_inner()), NonCopy(20)); } +); - drop(tx); - for _ in 0..2 * K { - rx.recv().unwrap(); +nonpoison_and_poison_unwrap_test!( + name: test_get_cloned, + test_body: { + use locks::Mutex; + + #[derive(Clone, Eq, PartialEq, Debug)] + struct Cloneable(i32); + + let m = Mutex::new(Cloneable(10)); + + assert_eq!(maybe_unwrap(m.get_cloned()), Cloneable(10)); } - assert_eq!(*m.lock().unwrap(), J * K * 2); -} +); + +nonpoison_and_poison_unwrap_test!( + name: test_set, + test_body: { + use locks::Mutex; + + fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = Mutex::new(init()); + + assert_eq!(*maybe_unwrap(m.lock()), init()); + maybe_unwrap(m.set(value())); + assert_eq!(*maybe_unwrap(m.lock()), value()); + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); + } +); + +// Ensure that old values that are replaced by `set` are correctly dropped. +nonpoison_and_poison_unwrap_test!( + name: test_set_drop, + test_body: { + use locks::Mutex; + + struct Foo(Arc); + impl Drop for Foo { + fn drop(&mut self) { + self.0.fetch_add(1, Ordering::SeqCst); + } + } + + let num_drops = Arc::new(AtomicUsize::new(0)); + let m = Mutex::new(Foo(num_drops.clone())); + assert_eq!(num_drops.load(Ordering::SeqCst), 0); + + let different = Foo(Arc::new(AtomicUsize::new(42))); + maybe_unwrap(m.set(different)); + assert_eq!(num_drops.load(Ordering::SeqCst), 1); + } +); + +nonpoison_and_poison_unwrap_test!( + name: test_replace, + test_body: { + use locks::Mutex; + + fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = Mutex::new(init()); + + assert_eq!(*maybe_unwrap(m.lock()), init()); + assert_eq!(maybe_unwrap(m.replace(value())), init()); + assert_eq!(*maybe_unwrap(m.lock()), value()); + } + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); + } +); + +// FIXME(nonpoison_condvar): Move this to the `condvar.rs` test file once `nonpoison::condvar` gets +// implemented. #[test] -fn try_lock() { - let m = Mutex::new(()); - *m.try_lock().unwrap() = (); +fn test_mutex_arc_condvar() { + struct Packet(Arc<(Mutex, Condvar)>); + + let packet = Packet(Arc::new((Mutex::new(false), Condvar::new()))); + let packet2 = Packet(packet.0.clone()); + + let (tx, rx) = channel(); + + let _t = thread::spawn(move || { + // Wait until our parent has taken the lock. + rx.recv().unwrap(); + let &(ref lock, ref cvar) = &*packet2.0; + + // Set the data to `true` and wake up our parent. + let mut guard = lock.lock().unwrap(); + *guard = true; + cvar.notify_one(); + }); + + let &(ref lock, ref cvar) = &*packet.0; + let mut guard = lock.lock().unwrap(); + // Wake up our child. + tx.send(()).unwrap(); + + // Wait until our child has set the data to `true`. + assert!(!*guard); + while !*guard { + guard = cvar.wait(guard).unwrap(); + } } +nonpoison_and_poison_unwrap_test!( + name: test_mutex_arc_nested, + test_body: { + use locks::Mutex; + + // Tests nested mutexes and access + // to underlying data. + let arc = Arc::new(Mutex::new(1)); + let arc2 = Arc::new(Mutex::new(arc)); + let (tx, rx) = channel(); + let _t = thread::spawn(move || { + let lock = maybe_unwrap(arc2.lock()); + let lock2 = maybe_unwrap(lock.lock()); + assert_eq!(*lock2, 1); + tx.send(()).unwrap(); + }); + rx.recv().unwrap(); + } +); + +nonpoison_and_poison_unwrap_test!( + name: test_mutex_unsized, + test_body: { + use locks::Mutex; + + let mutex: &Mutex<[i32]> = &Mutex::new([1, 2, 3]); + { + let b = &mut *maybe_unwrap(mutex.lock()); + b[0] = 4; + b[2] = 5; + } + let comp: &[i32] = &[4, 2, 5]; + assert_eq!(&*maybe_unwrap(mutex.lock()), comp); + } +); + +nonpoison_and_poison_unwrap_test!( + name: test_mapping_mapped_guard, + test_body: { + use locks::{Mutex, MutexGuard, MappedMutexGuard}; + + let arr = [0; 4]; + let lock = Mutex::new(arr); + let guard = maybe_unwrap(lock.lock()); + let guard = MutexGuard::map(guard, |arr| &mut arr[..2]); + let mut guard = MappedMutexGuard::map(guard, |slice| &mut slice[1..]); + assert_eq!(guard.len(), 1); + guard[0] = 42; + drop(guard); + assert_eq!(*maybe_unwrap(lock.lock()), [0, 42, 0, 0]); + } +); + +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +nonpoison_and_poison_unwrap_test!( + name: test_panics, + test_body: { + use locks::Mutex; + + let mutex = Mutex::new(42); + + let catch_unwind_result1 = panic::catch_unwind(AssertUnwindSafe(|| { + let _guard1 = maybe_unwrap(mutex.lock()); + + panic!("test panic with mutex once"); + })); + assert!(catch_unwind_result1.is_err()); + + let catch_unwind_result2 = panic::catch_unwind(AssertUnwindSafe(|| { + let _guard2 = maybe_unwrap(mutex.lock()); + + panic!("test panic with mutex twice"); + })); + assert!(catch_unwind_result2.is_err()); + + let catch_unwind_result3 = panic::catch_unwind(AssertUnwindSafe(|| { + let _guard3 = maybe_unwrap(mutex.lock()); + + panic!("test panic with mutex thrice"); + })); + assert!(catch_unwind_result3.is_err()); + } +); + +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +nonpoison_and_poison_unwrap_test!( + name: test_mutex_arc_access_in_unwind, + test_body: { + use locks::Mutex; + + let arc = Arc::new(Mutex::new(1)); + let arc2 = arc.clone(); + let _ = thread::spawn(move || -> () { + struct Unwinder { + i: Arc>, + } + impl Drop for Unwinder { + fn drop(&mut self) { + *maybe_unwrap(self.i.lock()) += 1; + } + } + let _u = Unwinder { i: arc2 }; + panic!(); + }) + .join(); + let lock = maybe_unwrap(arc.lock()); + assert_eq!(*lock, 2); + } +); + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Poison Tests +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/// Creates a mutex that is immediately poisoned. fn new_poisoned_mutex(value: T) -> Mutex { let mutex = Mutex::new(value); @@ -93,30 +377,6 @@ fn new_poisoned_mutex(value: T) -> Mutex { mutex } -#[test] -fn test_into_inner() { - let m = Mutex::new(NonCopy(10)); - assert_eq!(m.into_inner().unwrap(), NonCopy(10)); -} - -#[test] -fn test_into_inner_drop() { - struct Foo(Arc); - impl Drop for Foo { - fn drop(&mut self) { - self.0.fetch_add(1, Ordering::SeqCst); - } - } - let num_drops = Arc::new(AtomicUsize::new(0)); - let m = Mutex::new(Foo(num_drops.clone())); - assert_eq!(num_drops.load(Ordering::SeqCst), 0); - { - let _inner = m.into_inner().unwrap(); - assert_eq!(num_drops.load(Ordering::SeqCst), 0); - } - assert_eq!(num_drops.load(Ordering::SeqCst), 1); -} - #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_into_inner_poison() { @@ -128,16 +388,12 @@ fn test_into_inner_poison() { } } -#[test] -fn test_get_cloned() { - let m = Mutex::new(Cloneable(10)); - - assert_eq!(m.get_cloned().unwrap(), Cloneable(10)); -} - #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_get_cloned_poison() { + #[derive(Clone, Eq, PartialEq, Debug)] + struct Cloneable(i32); + let m = new_poisoned_mutex(Cloneable(10)); match m.get_cloned() { @@ -146,13 +402,6 @@ fn test_get_cloned_poison() { } } -#[test] -fn test_get_mut() { - let mut m = Mutex::new(NonCopy(10)); - *m.get_mut().unwrap() = NonCopy(20); - assert_eq!(m.into_inner().unwrap(), NonCopy(20)); -} - #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_get_mut_poison() { @@ -164,23 +413,6 @@ fn test_get_mut_poison() { } } -#[test] -fn test_set() { - fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) - where - T: Debug + Eq, - { - let m = Mutex::new(init()); - - assert_eq!(*m.lock().unwrap(), init()); - m.set(value()).unwrap(); - assert_eq!(*m.lock().unwrap(), value()); - } - - inner(|| NonCopy(10), || NonCopy(20)); - inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); -} - #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_set_poison() { @@ -203,23 +435,6 @@ fn test_set_poison() { inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); } -#[test] -fn test_replace() { - fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) - where - T: Debug + Eq, - { - let m = Mutex::new(init()); - - assert_eq!(*m.lock().unwrap(), init()); - assert_eq!(m.replace(value()).unwrap(), init()); - assert_eq!(*m.lock().unwrap(), value()); - } - - inner(|| NonCopy(10), || NonCopy(20)); - inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); -} - #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_replace_poison() { @@ -242,32 +457,11 @@ fn test_replace_poison() { inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); } -#[test] -fn test_mutex_arc_condvar() { - let packet = Packet(Arc::new((Mutex::new(false), Condvar::new()))); - let packet2 = Packet(packet.0.clone()); - let (tx, rx) = channel(); - let _t = thread::spawn(move || { - // wait until parent gets in - rx.recv().unwrap(); - let &(ref lock, ref cvar) = &*packet2.0; - let mut lock = lock.lock().unwrap(); - *lock = true; - cvar.notify_one(); - }); - - let &(ref lock, ref cvar) = &*packet.0; - let mut lock = lock.lock().unwrap(); - tx.send(()).unwrap(); - assert!(!*lock); - while !*lock { - lock = cvar.wait(lock).unwrap(); - } -} - #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_arc_condvar_poison() { + struct Packet(Arc<(Mutex, Condvar)>); + let packet = Packet(Arc::new((Mutex::new(1), Condvar::new()))); let packet2 = Packet(packet.0.clone()); let (tx, rx) = channel(); @@ -326,69 +520,6 @@ fn test_mutex_arc_poison_mapped() { assert!(arc.is_poisoned()); } -#[test] -fn test_mutex_arc_nested() { - // Tests nested mutexes and access - // to underlying data. - let arc = Arc::new(Mutex::new(1)); - let arc2 = Arc::new(Mutex::new(arc)); - let (tx, rx) = channel(); - let _t = thread::spawn(move || { - let lock = arc2.lock().unwrap(); - let lock2 = lock.lock().unwrap(); - assert_eq!(*lock2, 1); - tx.send(()).unwrap(); - }); - rx.recv().unwrap(); -} - -#[test] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn test_mutex_arc_access_in_unwind() { - let arc = Arc::new(Mutex::new(1)); - let arc2 = arc.clone(); - let _ = thread::spawn(move || -> () { - struct Unwinder { - i: Arc>, - } - impl Drop for Unwinder { - fn drop(&mut self) { - *self.i.lock().unwrap() += 1; - } - } - let _u = Unwinder { i: arc2 }; - panic!(); - }) - .join(); - let lock = arc.lock().unwrap(); - assert_eq!(*lock, 2); -} - -#[test] -fn test_mutex_unsized() { - let mutex: &Mutex<[i32]> = &Mutex::new([1, 2, 3]); - { - let b = &mut *mutex.lock().unwrap(); - b[0] = 4; - b[2] = 5; - } - let comp: &[i32] = &[4, 2, 5]; - assert_eq!(&*mutex.lock().unwrap(), comp); -} - -#[test] -fn test_mapping_mapped_guard() { - let arr = [0; 4]; - let mut lock = Mutex::new(arr); - let guard = lock.lock().unwrap(); - let guard = MutexGuard::map(guard, |arr| &mut arr[..2]); - let mut guard = MappedMutexGuard::map(guard, |slice| &mut slice[1..]); - assert_eq!(guard.len(), 1); - guard[0] = 42; - drop(guard); - assert_eq!(*lock.get_mut().unwrap(), [0, 42, 0, 0]); -} - #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn panic_while_mapping_unlocked_poison() { diff --git a/src/bootstrap/defaults/bootstrap.tools.toml b/src/bootstrap/defaults/bootstrap.tools.toml index 57c2706f60a56..5abe636bd9683 100644 --- a/src/bootstrap/defaults/bootstrap.tools.toml +++ b/src/bootstrap/defaults/bootstrap.tools.toml @@ -14,6 +14,8 @@ test-stage = 2 doc-stage = 2 # Contributors working on tools will probably expect compiler docs to be generated, so they can figure out how to use the API. compiler-docs = true +# Contributors working on tools are the most likely to change non-rust programs. +tidy-extra-checks = "auto:js,auto:py,auto:cpp,auto:spellcheck" [llvm] # Will download LLVM from CI if available on your platform. diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 6e04f11542438..9644ade00b346 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -45,7 +45,9 @@ use crate::core::config::{ DebuginfoLevel, DryRun, GccCiMode, LlvmLibunwind, Merge, ReplaceOpt, RustcLto, SplitDebuginfo, StringOrBool, set, threads_from_config, }; -use crate::core::download::is_download_ci_available; +use crate::core::download::{ + DownloadContext, download_beta_toolchain, is_download_ci_available, maybe_download_rustfmt, +}; use crate::utils::channel; use crate::utils::exec::{ExecutionContext, command}; use crate::utils::helpers::{exe, get_host_target}; @@ -795,13 +797,19 @@ impl Config { ); } + config.patch_binaries_for_nix = patch_binaries_for_nix; + config.bootstrap_cache_path = bootstrap_cache_path; + config.llvm_assertions = + toml.llvm.as_ref().is_some_and(|llvm| llvm.assertions.unwrap_or(false)); + config.initial_rustc = if let Some(rustc) = rustc { if !flags_skip_stage0_validation { config.check_stage0_version(&rustc, "rustc"); } rustc } else { - config.download_beta_toolchain(); + let dwn_ctx = DownloadContext::from(&config); + download_beta_toolchain(dwn_ctx); config .out .join(config.host_target) @@ -827,7 +835,8 @@ impl Config { } cargo } else { - config.download_beta_toolchain(); + let dwn_ctx = DownloadContext::from(&config); + download_beta_toolchain(dwn_ctx); config.initial_sysroot.join("bin").join(exe("cargo", config.host_target)) }; @@ -863,7 +872,6 @@ impl Config { config.reuse = reuse.map(PathBuf::from); config.submodules = submodules; config.android_ndk = android_ndk; - config.bootstrap_cache_path = bootstrap_cache_path; set(&mut config.low_priority, low_priority); set(&mut config.compiler_docs, compiler_docs); set(&mut config.library_docs_private_items, library_docs_private_items); @@ -882,7 +890,6 @@ impl Config { set(&mut config.local_rebuild, local_rebuild); set(&mut config.print_step_timings, print_step_timings); set(&mut config.print_step_rusage, print_step_rusage); - config.patch_binaries_for_nix = patch_binaries_for_nix; config.verbose = cmp::max(config.verbose, flags_verbose as usize); @@ -891,9 +898,6 @@ impl Config { config.apply_install_config(toml.install); - config.llvm_assertions = - toml.llvm.as_ref().is_some_and(|llvm| llvm.assertions.unwrap_or(false)); - let file_content = t!(fs::read_to_string(config.src.join("src/ci/channel"))); let ci_channel = file_content.trim_end(); @@ -994,8 +998,12 @@ impl Config { config.apply_dist_config(toml.dist); - config.initial_rustfmt = - if let Some(r) = rustfmt { Some(r) } else { config.maybe_download_rustfmt() }; + config.initial_rustfmt = if let Some(r) = rustfmt { + Some(r) + } else { + let dwn_ctx = DownloadContext::from(&config); + maybe_download_rustfmt(dwn_ctx) + }; if matches!(config.lld_mode, LldMode::SelfContained) && !config.lld_enabled diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index 373fcd52052e0..7ec6c62a07d02 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -7,9 +7,9 @@ use std::sync::OnceLock; use xz2::bufread::XzDecoder; -use crate::core::config::BUILDER_CONFIG_FILENAME; +use crate::core::config::{BUILDER_CONFIG_FILENAME, TargetSelection}; use crate::utils::build_stamp::BuildStamp; -use crate::utils::exec::command; +use crate::utils::exec::{ExecutionContext, command}; use crate::utils::helpers::{exe, hex_encode, move_file}; use crate::{Config, t}; @@ -24,17 +24,6 @@ fn extract_curl_version(out: String) -> semver::Version { .unwrap_or(semver::Version::new(1, 0, 0)) } -fn curl_version(config: &Config) -> semver::Version { - let mut curl = command("curl"); - curl.arg("-V"); - let curl = curl.run_capture_stdout(config); - if curl.is_failure() { - return semver::Version::new(1, 0, 0); - } - let output = curl.stdout(); - extract_curl_version(output) -} - /// Generic helpers that are useful anywhere in bootstrap. impl Config { pub fn is_verbose(&self) -> bool { @@ -49,10 +38,7 @@ impl Config { } pub(crate) fn remove(&self, f: &Path) { - if self.dry_run() { - return; - } - fs::remove_file(f).unwrap_or_else(|_| panic!("failed to remove {f:?}")); + remove(&self.exec_ctx, f); } /// Create a temporary directory in `out` and return its path. @@ -68,49 +54,7 @@ impl Config { /// Whether or not `fix_bin_or_dylib` needs to be run; can only be true /// on NixOS fn should_fix_bins_and_dylibs(&self) -> bool { - let val = *SHOULD_FIX_BINS_AND_DYLIBS.get_or_init(|| { - let uname = command("uname").allow_failure().arg("-s").run_capture_stdout(self); - if uname.is_failure() { - return false; - } - let output = uname.stdout(); - if !output.starts_with("Linux") { - return false; - } - // If the user has asked binaries to be patched for Nix, then - // don't check for NixOS or `/lib`. - // NOTE: this intentionally comes after the Linux check: - // - patchelf only works with ELF files, so no need to run it on Mac or Windows - // - On other Unix systems, there is no stable syscall interface, so Nix doesn't manage the global libc. - if let Some(explicit_value) = self.patch_binaries_for_nix { - return explicit_value; - } - - // Use `/etc/os-release` instead of `/etc/NIXOS`. - // The latter one does not exist on NixOS when using tmpfs as root. - let is_nixos = match File::open("/etc/os-release") { - Err(e) if e.kind() == ErrorKind::NotFound => false, - Err(e) => panic!("failed to access /etc/os-release: {e}"), - Ok(os_release) => BufReader::new(os_release).lines().any(|l| { - let l = l.expect("reading /etc/os-release"); - matches!(l.trim(), "ID=nixos" | "ID='nixos'" | "ID=\"nixos\"") - }), - }; - if !is_nixos { - let in_nix_shell = env::var("IN_NIX_SHELL"); - if let Ok(in_nix_shell) = in_nix_shell { - eprintln!( - "The IN_NIX_SHELL environment variable is `{in_nix_shell}`; \ - you may need to set `patch-binaries-for-nix=true` in bootstrap.toml" - ); - } - } - is_nixos - }); - if val { - eprintln!("INFO: You seem to be using Nix."); - } - val + should_fix_bins_and_dylibs(self.patch_binaries_for_nix, &self.exec_ctx) } /// Modifies the interpreter section of 'fname' to fix the dynamic linker, @@ -121,259 +65,22 @@ impl Config { /// /// Please see for more information fn fix_bin_or_dylib(&self, fname: &Path) { - assert_eq!(SHOULD_FIX_BINS_AND_DYLIBS.get(), Some(&true)); - println!("attempting to patch {}", fname.display()); - - // Only build `.nix-deps` once. - static NIX_DEPS_DIR: OnceLock = OnceLock::new(); - let mut nix_build_succeeded = true; - let nix_deps_dir = NIX_DEPS_DIR.get_or_init(|| { - // Run `nix-build` to "build" each dependency (which will likely reuse - // the existing `/nix/store` copy, or at most download a pre-built copy). - // - // Importantly, we create a gc-root called `.nix-deps` in the `build/` - // directory, but still reference the actual `/nix/store` path in the rpath - // as it makes it significantly more robust against changes to the location of - // the `.nix-deps` location. - // - // bintools: Needed for the path of `ld-linux.so` (via `nix-support/dynamic-linker`). - // zlib: Needed as a system dependency of `libLLVM-*.so`. - // patchelf: Needed for patching ELF binaries (see doc comment above). - let nix_deps_dir = self.out.join(".nix-deps"); - const NIX_EXPR: &str = " - with (import {}); - symlinkJoin { - name = \"rust-stage0-dependencies\"; - paths = [ - zlib - patchelf - stdenv.cc.bintools - ]; - } - "; - nix_build_succeeded = command("nix-build") - .allow_failure() - .args([Path::new("-E"), Path::new(NIX_EXPR), Path::new("-o"), &nix_deps_dir]) - .run_capture_stdout(self) - .is_success(); - nix_deps_dir - }); - if !nix_build_succeeded { - return; - } - - let mut patchelf = command(nix_deps_dir.join("bin/patchelf")); - patchelf.args(&[ - OsString::from("--add-rpath"), - OsString::from(t!(fs::canonicalize(nix_deps_dir)).join("lib")), - ]); - if !path_is_dylib(fname) { - // Finally, set the correct .interp for binaries - let dynamic_linker_path = nix_deps_dir.join("nix-support/dynamic-linker"); - let dynamic_linker = t!(fs::read_to_string(dynamic_linker_path)); - patchelf.args(["--set-interpreter", dynamic_linker.trim_end()]); - } - patchelf.arg(fname); - let _ = patchelf.allow_failure().run_capture_stdout(self); + fix_bin_or_dylib(&self.out, fname, &self.exec_ctx); } fn download_file(&self, url: &str, dest_path: &Path, help_on_error: &str) { - self.verbose(|| println!("download {url}")); - // Use a temporary file in case we crash while downloading, to avoid a corrupt download in cache/. - let tempfile = self.tempdir().join(dest_path.file_name().unwrap()); - // While bootstrap itself only supports http and https downloads, downstream forks might - // need to download components from other protocols. The match allows them adding more - // protocols without worrying about merge conflicts if we change the HTTP implementation. - match url.split_once("://").map(|(proto, _)| proto) { - Some("http") | Some("https") => { - self.download_http_with_retries(&tempfile, url, help_on_error) - } - Some(other) => panic!("unsupported protocol {other} in {url}"), - None => panic!("no protocol in {url}"), - } - t!( - move_file(&tempfile, dest_path), - format!("failed to rename {tempfile:?} to {dest_path:?}") - ); - } - - fn download_http_with_retries(&self, tempfile: &Path, url: &str, help_on_error: &str) { - println!("downloading {url}"); - // Try curl. If that fails and we are on windows, fallback to PowerShell. - // options should be kept in sync with - // src/bootstrap/src/core/download.rs - // for consistency - let mut curl = command("curl").allow_failure(); - curl.args([ - // follow redirect - "--location", - // timeout if speed is < 10 bytes/sec for > 30 seconds - "--speed-time", - "30", - "--speed-limit", - "10", - // timeout if cannot connect within 30 seconds - "--connect-timeout", - "30", - // output file - "--output", - tempfile.to_str().unwrap(), - // if there is an error, don't restart the download, - // instead continue where it left off. - "--continue-at", - "-", - // retry up to 3 times. note that this means a maximum of 4 - // attempts will be made, since the first attempt isn't a *re*try. - "--retry", - "3", - // show errors, even if --silent is specified - "--show-error", - // set timestamp of downloaded file to that of the server - "--remote-time", - // fail on non-ok http status - "--fail", - ]); - // Don't print progress in CI; the \r wrapping looks bad and downloads don't take long enough for progress to be useful. - if self.is_running_on_ci { - curl.arg("--silent"); - } else { - curl.arg("--progress-bar"); - } - // --retry-all-errors was added in 7.71.0, don't use it if curl is old. - if curl_version(self) >= semver::Version::new(7, 71, 0) { - curl.arg("--retry-all-errors"); - } - curl.arg(url); - if !curl.run(self) { - if self.host_target.contains("windows-msvc") { - eprintln!("Fallback to PowerShell"); - for _ in 0..3 { - let powershell = command("PowerShell.exe").allow_failure().args([ - "/nologo", - "-Command", - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;", - &format!( - "(New-Object System.Net.WebClient).DownloadFile('{}', '{}')", - url, tempfile.to_str().expect("invalid UTF-8 not supported with powershell downloads"), - ), - ]).run_capture_stdout(self); - - if powershell.is_failure() { - return; - } - - eprintln!("\nspurious failure, trying again"); - } - } - if !help_on_error.is_empty() { - eprintln!("{help_on_error}"); - } - crate::exit!(1); - } + let dwn_ctx: DownloadContext<'_> = self.into(); + download_file(dwn_ctx, url, dest_path, help_on_error); } fn unpack(&self, tarball: &Path, dst: &Path, pattern: &str) { - eprintln!("extracting {} to {}", tarball.display(), dst.display()); - if !dst.exists() { - t!(fs::create_dir_all(dst)); - } - - // `tarball` ends with `.tar.xz`; strip that suffix - // example: `rust-dev-nightly-x86_64-unknown-linux-gnu` - let uncompressed_filename = - Path::new(tarball.file_name().expect("missing tarball filename")).file_stem().unwrap(); - let directory_prefix = Path::new(Path::new(uncompressed_filename).file_stem().unwrap()); - - // decompress the file - let data = t!(File::open(tarball), format!("file {} not found", tarball.display())); - let decompressor = XzDecoder::new(BufReader::new(data)); - - let mut tar = tar::Archive::new(decompressor); - - let is_ci_rustc = dst.ends_with("ci-rustc"); - let is_ci_llvm = dst.ends_with("ci-llvm"); - - // `compile::Sysroot` needs to know the contents of the `rustc-dev` tarball to avoid adding - // it to the sysroot unless it was explicitly requested. But parsing the 100 MB tarball is slow. - // Cache the entries when we extract it so we only have to read it once. - let mut recorded_entries = if is_ci_rustc { recorded_entries(dst, pattern) } else { None }; - - for member in t!(tar.entries()) { - let mut member = t!(member); - let original_path = t!(member.path()).into_owned(); - // skip the top-level directory - if original_path == directory_prefix { - continue; - } - let mut short_path = t!(original_path.strip_prefix(directory_prefix)); - let is_builder_config = short_path.to_str() == Some(BUILDER_CONFIG_FILENAME); - - if !(short_path.starts_with(pattern) - || ((is_ci_rustc || is_ci_llvm) && is_builder_config)) - { - continue; - } - short_path = short_path.strip_prefix(pattern).unwrap_or(short_path); - let dst_path = dst.join(short_path); - self.verbose(|| { - println!("extracting {} to {}", original_path.display(), dst.display()) - }); - if !t!(member.unpack_in(dst)) { - panic!("path traversal attack ??"); - } - if let Some(record) = &mut recorded_entries { - t!(writeln!(record, "{}", short_path.to_str().unwrap())); - } - let src_path = dst.join(original_path); - if src_path.is_dir() && dst_path.exists() { - continue; - } - t!(move_file(src_path, dst_path)); - } - let dst_dir = dst.join(directory_prefix); - if dst_dir.exists() { - t!(fs::remove_dir_all(&dst_dir), format!("failed to remove {}", dst_dir.display())); - } + unpack(&self.exec_ctx, tarball, dst, pattern); } /// Returns whether the SHA256 checksum of `path` matches `expected`. + #[cfg(test)] pub(crate) fn verify(&self, path: &Path, expected: &str) -> bool { - use sha2::Digest; - - self.verbose(|| println!("verifying {}", path.display())); - - if self.dry_run() { - return false; - } - - let mut hasher = sha2::Sha256::new(); - - let file = t!(File::open(path)); - let mut reader = BufReader::new(file); - - loop { - let buffer = t!(reader.fill_buf()); - let l = buffer.len(); - // break if EOF - if l == 0 { - break; - } - hasher.update(buffer); - reader.consume(l); - } - - let checksum = hex_encode(hasher.finalize().as_slice()); - let verified = checksum == expected; - - if !verified { - println!( - "invalid checksum: \n\ - found: {checksum}\n\ - expected: {expected}", - ); - } - - verified + verify(&self.exec_ctx, path, expected) } } @@ -388,6 +95,7 @@ fn recorded_entries(dst: &Path, pattern: &str) -> Option> { Some(BufWriter::new(t!(File::create(dst.join(name))))) } +#[derive(Clone)] enum DownloadSource { CI, Dist, @@ -420,63 +128,6 @@ impl Config { cargo_clippy } - #[cfg(test)] - pub(crate) fn maybe_download_rustfmt(&self) -> Option { - Some(PathBuf::new()) - } - - /// NOTE: rustfmt is a completely different toolchain than the bootstrap compiler, so it can't - /// reuse target directories or artifacts - #[cfg(not(test))] - pub(crate) fn maybe_download_rustfmt(&self) -> Option { - use build_helper::stage0_parser::VersionMetadata; - - if self.dry_run() { - return Some(PathBuf::new()); - } - - let VersionMetadata { date, version } = self.stage0_metadata.rustfmt.as_ref()?; - let channel = format!("{version}-{date}"); - - let host = self.host_target; - let bin_root = self.out.join(host).join("rustfmt"); - let rustfmt_path = bin_root.join("bin").join(exe("rustfmt", host)); - let rustfmt_stamp = BuildStamp::new(&bin_root).with_prefix("rustfmt").add_stamp(channel); - if rustfmt_path.exists() && rustfmt_stamp.is_up_to_date() { - return Some(rustfmt_path); - } - - self.download_component( - DownloadSource::Dist, - format!("rustfmt-{version}-{build}.tar.xz", build = host.triple), - "rustfmt-preview", - date, - "rustfmt", - ); - self.download_component( - DownloadSource::Dist, - format!("rustc-{version}-{build}.tar.xz", build = host.triple), - "rustc", - date, - "rustfmt", - ); - - if self.should_fix_bins_and_dylibs() { - self.fix_bin_or_dylib(&bin_root.join("bin").join("rustfmt")); - self.fix_bin_or_dylib(&bin_root.join("bin").join("cargo-fmt")); - let lib_dir = bin_root.join("lib"); - for lib in t!(fs::read_dir(&lib_dir), lib_dir.display().to_string()) { - let lib = t!(lib); - if path_is_dylib(&lib.path()) { - self.fix_bin_or_dylib(&lib.path()); - } - } - } - - t!(rustfmt_stamp.write()); - Some(rustfmt_path) - } - pub(crate) fn ci_rust_std_contents(&self) -> Vec { self.ci_component_contents(".rust-std-contents") } @@ -514,30 +165,6 @@ impl Config { ); } - #[cfg(test)] - pub(crate) fn download_beta_toolchain(&self) {} - - #[cfg(not(test))] - pub(crate) fn download_beta_toolchain(&self) { - self.verbose(|| println!("downloading stage0 beta artifacts")); - - let date = &self.stage0_metadata.compiler.date; - let version = &self.stage0_metadata.compiler.version; - let extra_components = ["cargo"]; - - let download_beta_component = |config: &Config, filename, prefix: &_, date: &_| { - config.download_component(DownloadSource::Dist, filename, prefix, date, "stage0") - }; - - self.download_toolchain( - version, - "stage0", - date, - &extra_components, - download_beta_component, - ); - } - fn download_toolchain( &self, version: &str, @@ -607,91 +234,8 @@ impl Config { key: &str, destination: &str, ) { - if self.dry_run() { - return; - } - - let cache_dst = - self.bootstrap_cache_path.as_ref().cloned().unwrap_or_else(|| self.out.join("cache")); - - let cache_dir = cache_dst.join(key); - if !cache_dir.exists() { - t!(fs::create_dir_all(&cache_dir)); - } - - let bin_root = self.out.join(self.host_target).join(destination); - let tarball = cache_dir.join(&filename); - let (base_url, url, should_verify) = match mode { - DownloadSource::CI => { - let dist_server = if self.llvm_assertions { - self.stage0_metadata.config.artifacts_with_llvm_assertions_server.clone() - } else { - self.stage0_metadata.config.artifacts_server.clone() - }; - let url = format!( - "{}/{filename}", - key.strip_suffix(&format!("-{}", self.llvm_assertions)).unwrap() - ); - (dist_server, url, false) - } - DownloadSource::Dist => { - let dist_server = env::var("RUSTUP_DIST_SERVER") - .unwrap_or(self.stage0_metadata.config.dist_server.to_string()); - // NOTE: make `dist` part of the URL because that's how it's stored in src/stage0 - (dist_server, format!("dist/{key}/{filename}"), true) - } - }; - - // For the stage0 compiler, put special effort into ensuring the checksums are valid. - let checksum = if should_verify { - let error = format!( - "src/stage0 doesn't contain a checksum for {url}. \ - Pre-built artifacts might not be available for this \ - target at this time, see https://doc.rust-lang.org/nightly\ - /rustc/platform-support.html for more information." - ); - let sha256 = self.stage0_metadata.checksums_sha256.get(&url).expect(&error); - if tarball.exists() { - if self.verify(&tarball, sha256) { - self.unpack(&tarball, &bin_root, prefix); - return; - } else { - self.verbose(|| { - println!( - "ignoring cached file {} due to failed verification", - tarball.display() - ) - }); - self.remove(&tarball); - } - } - Some(sha256) - } else if tarball.exists() { - self.unpack(&tarball, &bin_root, prefix); - return; - } else { - None - }; - - let mut help_on_error = ""; - if destination == "ci-rustc" { - help_on_error = "ERROR: failed to download pre-built rustc from CI - -NOTE: old builds get deleted after a certain time -HELP: if trying to compile an old commit of rustc, disable `download-rustc` in bootstrap.toml: - -[rust] -download-rustc = false -"; - } - self.download_file(&format!("{base_url}/{url}"), &tarball, help_on_error); - if let Some(sha256) = checksum - && !self.verify(&tarball, sha256) - { - panic!("failed to verify {}", tarball.display()); - } - - self.unpack(&tarball, &bin_root, prefix); + let dwn_ctx: DownloadContext<'_> = self.into(); + download_component(dwn_ctx, mode, filename, prefix, key, destination); } #[cfg(test)] @@ -852,6 +396,39 @@ download-rustc = false } } +/// Only should be used for pre config initialization downloads. +pub(crate) struct DownloadContext<'a> { + host_target: TargetSelection, + out: &'a Path, + patch_binaries_for_nix: Option, + exec_ctx: &'a ExecutionContext, + stage0_metadata: &'a build_helper::stage0_parser::Stage0, + llvm_assertions: bool, + bootstrap_cache_path: &'a Option, + is_running_on_ci: bool, +} + +impl<'a> AsRef> for DownloadContext<'a> { + fn as_ref(&self) -> &DownloadContext<'a> { + self + } +} + +impl<'a> From<&'a Config> for DownloadContext<'a> { + fn from(value: &'a Config) -> Self { + DownloadContext { + host_target: value.host_target, + out: &value.out, + patch_binaries_for_nix: value.patch_binaries_for_nix, + exec_ctx: &value.exec_ctx, + stage0_metadata: &value.stage0_metadata, + llvm_assertions: value.llvm_assertions, + bootstrap_cache_path: &value.bootstrap_cache_path, + is_running_on_ci: value.is_running_on_ci, + } + } +} + fn path_is_dylib(path: &Path) -> bool { // The .so is not necessarily the extension, it might be libLLVM.so.18.1 path.to_str().is_some_and(|path| path.contains(".so")) @@ -897,3 +474,596 @@ pub(crate) fn is_download_ci_available(target_triple: &str, llvm_assertions: boo SUPPORTED_PLATFORMS.contains(&target_triple) } } + +#[cfg(test)] +pub(crate) fn maybe_download_rustfmt<'a>( + dwn_ctx: impl AsRef>, +) -> Option { + Some(PathBuf::new()) +} + +/// NOTE: rustfmt is a completely different toolchain than the bootstrap compiler, so it can't +/// reuse target directories or artifacts +#[cfg(not(test))] +pub(crate) fn maybe_download_rustfmt<'a>( + dwn_ctx: impl AsRef>, +) -> Option { + use build_helper::stage0_parser::VersionMetadata; + + let dwn_ctx = dwn_ctx.as_ref(); + + if dwn_ctx.exec_ctx.dry_run() { + return Some(PathBuf::new()); + } + + let VersionMetadata { date, version } = dwn_ctx.stage0_metadata.rustfmt.as_ref()?; + let channel = format!("{version}-{date}"); + + let host = dwn_ctx.host_target; + let bin_root = dwn_ctx.out.join(host).join("rustfmt"); + let rustfmt_path = bin_root.join("bin").join(exe("rustfmt", host)); + let rustfmt_stamp = BuildStamp::new(&bin_root).with_prefix("rustfmt").add_stamp(channel); + if rustfmt_path.exists() && rustfmt_stamp.is_up_to_date() { + return Some(rustfmt_path); + } + + download_component( + dwn_ctx, + DownloadSource::Dist, + format!("rustfmt-{version}-{build}.tar.xz", build = host.triple), + "rustfmt-preview", + date, + "rustfmt", + ); + + download_component( + dwn_ctx, + DownloadSource::Dist, + format!("rustc-{version}-{build}.tar.xz", build = host.triple), + "rustc", + date, + "rustfmt", + ); + + if should_fix_bins_and_dylibs(dwn_ctx.patch_binaries_for_nix, dwn_ctx.exec_ctx) { + fix_bin_or_dylib(dwn_ctx.out, &bin_root.join("bin").join("rustfmt"), dwn_ctx.exec_ctx); + fix_bin_or_dylib(dwn_ctx.out, &bin_root.join("bin").join("cargo-fmt"), dwn_ctx.exec_ctx); + let lib_dir = bin_root.join("lib"); + for lib in t!(fs::read_dir(&lib_dir), lib_dir.display().to_string()) { + let lib = t!(lib); + if path_is_dylib(&lib.path()) { + fix_bin_or_dylib(dwn_ctx.out, &lib.path(), dwn_ctx.exec_ctx); + } + } + } + + t!(rustfmt_stamp.write()); + Some(rustfmt_path) +} + +#[cfg(test)] +pub(crate) fn download_beta_toolchain<'a>(dwn_ctx: impl AsRef>) {} + +#[cfg(not(test))] +pub(crate) fn download_beta_toolchain<'a>(dwn_ctx: impl AsRef>) { + let dwn_ctx = dwn_ctx.as_ref(); + dwn_ctx.exec_ctx.verbose(|| { + println!("downloading stage0 beta artifacts"); + }); + + let date = dwn_ctx.stage0_metadata.compiler.date.clone(); + let version = dwn_ctx.stage0_metadata.compiler.version.clone(); + let extra_components = ["cargo"]; + let sysroot = "stage0"; + download_toolchain( + dwn_ctx, + &version, + sysroot, + &date, + &extra_components, + "stage0", + DownloadSource::Dist, + ); +} + +fn download_toolchain<'a>( + dwn_ctx: impl AsRef>, + version: &str, + sysroot: &str, + stamp_key: &str, + extra_components: &[&str], + destination: &str, + mode: DownloadSource, +) { + let dwn_ctx = dwn_ctx.as_ref(); + let host = dwn_ctx.host_target.triple; + let bin_root = dwn_ctx.out.join(host).join(sysroot); + let rustc_stamp = BuildStamp::new(&bin_root).with_prefix("rustc").add_stamp(stamp_key); + + if !bin_root.join("bin").join(exe("rustc", dwn_ctx.host_target)).exists() + || !rustc_stamp.is_up_to_date() + { + if bin_root.exists() { + t!(fs::remove_dir_all(&bin_root)); + } + let filename = format!("rust-std-{version}-{host}.tar.xz"); + let pattern = format!("rust-std-{host}"); + download_component(dwn_ctx, mode.clone(), filename, &pattern, stamp_key, destination); + let filename = format!("rustc-{version}-{host}.tar.xz"); + download_component(dwn_ctx, mode.clone(), filename, "rustc", stamp_key, destination); + + for component in extra_components { + let filename = format!("{component}-{version}-{host}.tar.xz"); + download_component(dwn_ctx, mode.clone(), filename, component, stamp_key, destination); + } + + if should_fix_bins_and_dylibs(dwn_ctx.patch_binaries_for_nix, dwn_ctx.exec_ctx) { + fix_bin_or_dylib(dwn_ctx.out, &bin_root.join("bin").join("rustc"), dwn_ctx.exec_ctx); + fix_bin_or_dylib(dwn_ctx.out, &bin_root.join("bin").join("rustdoc"), dwn_ctx.exec_ctx); + fix_bin_or_dylib( + dwn_ctx.out, + &bin_root.join("libexec").join("rust-analyzer-proc-macro-srv"), + dwn_ctx.exec_ctx, + ); + let lib_dir = bin_root.join("lib"); + for lib in t!(fs::read_dir(&lib_dir), lib_dir.display().to_string()) { + let lib = t!(lib); + if path_is_dylib(&lib.path()) { + fix_bin_or_dylib(dwn_ctx.out, &lib.path(), dwn_ctx.exec_ctx); + } + } + } + + t!(rustc_stamp.write()); + } +} + +pub(crate) fn remove(exec_ctx: &ExecutionContext, f: &Path) { + if exec_ctx.dry_run() { + return; + } + fs::remove_file(f).unwrap_or_else(|_| panic!("failed to remove {f:?}")); +} + +fn fix_bin_or_dylib(out: &Path, fname: &Path, exec_ctx: &ExecutionContext) { + assert_eq!(SHOULD_FIX_BINS_AND_DYLIBS.get(), Some(&true)); + println!("attempting to patch {}", fname.display()); + + // Only build `.nix-deps` once. + static NIX_DEPS_DIR: OnceLock = OnceLock::new(); + let mut nix_build_succeeded = true; + let nix_deps_dir = NIX_DEPS_DIR.get_or_init(|| { + // Run `nix-build` to "build" each dependency (which will likely reuse + // the existing `/nix/store` copy, or at most download a pre-built copy). + // + // Importantly, we create a gc-root called `.nix-deps` in the `build/` + // directory, but still reference the actual `/nix/store` path in the rpath + // as it makes it significantly more robust against changes to the location of + // the `.nix-deps` location. + // + // bintools: Needed for the path of `ld-linux.so` (via `nix-support/dynamic-linker`). + // zlib: Needed as a system dependency of `libLLVM-*.so`. + // patchelf: Needed for patching ELF binaries (see doc comment above). + let nix_deps_dir = out.join(".nix-deps"); + const NIX_EXPR: &str = " + with (import {}); + symlinkJoin { + name = \"rust-stage0-dependencies\"; + paths = [ + zlib + patchelf + stdenv.cc.bintools + ]; + } + "; + nix_build_succeeded = command("nix-build") + .allow_failure() + .args([Path::new("-E"), Path::new(NIX_EXPR), Path::new("-o"), &nix_deps_dir]) + .run_capture_stdout(exec_ctx) + .is_success(); + nix_deps_dir + }); + if !nix_build_succeeded { + return; + } + + let mut patchelf = command(nix_deps_dir.join("bin/patchelf")); + patchelf.args(&[ + OsString::from("--add-rpath"), + OsString::from(t!(fs::canonicalize(nix_deps_dir)).join("lib")), + ]); + if !path_is_dylib(fname) { + // Finally, set the correct .interp for binaries + let dynamic_linker_path = nix_deps_dir.join("nix-support/dynamic-linker"); + let dynamic_linker = t!(fs::read_to_string(dynamic_linker_path)); + patchelf.args(["--set-interpreter", dynamic_linker.trim_end()]); + } + patchelf.arg(fname); + let _ = patchelf.allow_failure().run_capture_stdout(exec_ctx); +} + +fn should_fix_bins_and_dylibs( + patch_binaries_for_nix: Option, + exec_ctx: &ExecutionContext, +) -> bool { + let val = *SHOULD_FIX_BINS_AND_DYLIBS.get_or_init(|| { + let uname = command("uname").allow_failure().arg("-s").run_capture_stdout(exec_ctx); + if uname.is_failure() { + return false; + } + let output = uname.stdout(); + if !output.starts_with("Linux") { + return false; + } + // If the user has asked binaries to be patched for Nix, then + // don't check for NixOS or `/lib`. + // NOTE: this intentionally comes after the Linux check: + // - patchelf only works with ELF files, so no need to run it on Mac or Windows + // - On other Unix systems, there is no stable syscall interface, so Nix doesn't manage the global libc. + if let Some(explicit_value) = patch_binaries_for_nix { + return explicit_value; + } + + // Use `/etc/os-release` instead of `/etc/NIXOS`. + // The latter one does not exist on NixOS when using tmpfs as root. + let is_nixos = match File::open("/etc/os-release") { + Err(e) if e.kind() == ErrorKind::NotFound => false, + Err(e) => panic!("failed to access /etc/os-release: {e}"), + Ok(os_release) => BufReader::new(os_release).lines().any(|l| { + let l = l.expect("reading /etc/os-release"); + matches!(l.trim(), "ID=nixos" | "ID='nixos'" | "ID=\"nixos\"") + }), + }; + if !is_nixos { + let in_nix_shell = env::var("IN_NIX_SHELL"); + if let Ok(in_nix_shell) = in_nix_shell { + eprintln!( + "The IN_NIX_SHELL environment variable is `{in_nix_shell}`; \ + you may need to set `patch-binaries-for-nix=true` in bootstrap.toml" + ); + } + } + is_nixos + }); + if val { + eprintln!("INFO: You seem to be using Nix."); + } + val +} + +fn download_component<'a>( + dwn_ctx: impl AsRef>, + mode: DownloadSource, + filename: String, + prefix: &str, + key: &str, + destination: &str, +) { + let dwn_ctx = dwn_ctx.as_ref(); + + if dwn_ctx.exec_ctx.dry_run() { + return; + } + + let cache_dst = + dwn_ctx.bootstrap_cache_path.as_ref().cloned().unwrap_or_else(|| dwn_ctx.out.join("cache")); + + let cache_dir = cache_dst.join(key); + if !cache_dir.exists() { + t!(fs::create_dir_all(&cache_dir)); + } + + let bin_root = dwn_ctx.out.join(dwn_ctx.host_target).join(destination); + let tarball = cache_dir.join(&filename); + let (base_url, url, should_verify) = match mode { + DownloadSource::CI => { + let dist_server = if dwn_ctx.llvm_assertions { + dwn_ctx.stage0_metadata.config.artifacts_with_llvm_assertions_server.clone() + } else { + dwn_ctx.stage0_metadata.config.artifacts_server.clone() + }; + let url = format!( + "{}/{filename}", + key.strip_suffix(&format!("-{}", dwn_ctx.llvm_assertions)).unwrap() + ); + (dist_server, url, false) + } + DownloadSource::Dist => { + let dist_server = env::var("RUSTUP_DIST_SERVER") + .unwrap_or(dwn_ctx.stage0_metadata.config.dist_server.to_string()); + // NOTE: make `dist` part of the URL because that's how it's stored in src/stage0 + (dist_server, format!("dist/{key}/{filename}"), true) + } + }; + + // For the stage0 compiler, put special effort into ensuring the checksums are valid. + let checksum = if should_verify { + let error = format!( + "src/stage0 doesn't contain a checksum for {url}. \ + Pre-built artifacts might not be available for this \ + target at this time, see https://doc.rust-lang.org/nightly\ + /rustc/platform-support.html for more information." + ); + let sha256 = dwn_ctx.stage0_metadata.checksums_sha256.get(&url).expect(&error); + if tarball.exists() { + if verify(dwn_ctx.exec_ctx, &tarball, sha256) { + unpack(dwn_ctx.exec_ctx, &tarball, &bin_root, prefix); + return; + } else { + dwn_ctx.exec_ctx.verbose(|| { + println!( + "ignoring cached file {} due to failed verification", + tarball.display() + ) + }); + remove(dwn_ctx.exec_ctx, &tarball); + } + } + Some(sha256) + } else if tarball.exists() { + unpack(dwn_ctx.exec_ctx, &tarball, &bin_root, prefix); + return; + } else { + None + }; + + let mut help_on_error = ""; + if destination == "ci-rustc" { + help_on_error = "ERROR: failed to download pre-built rustc from CI + +NOTE: old builds get deleted after a certain time +HELP: if trying to compile an old commit of rustc, disable `download-rustc` in bootstrap.toml: + +[rust] +download-rustc = false +"; + } + download_file(dwn_ctx, &format!("{base_url}/{url}"), &tarball, help_on_error); + if let Some(sha256) = checksum + && !verify(dwn_ctx.exec_ctx, &tarball, sha256) + { + panic!("failed to verify {}", tarball.display()); + } + + unpack(dwn_ctx.exec_ctx, &tarball, &bin_root, prefix); +} + +pub(crate) fn verify(exec_ctx: &ExecutionContext, path: &Path, expected: &str) -> bool { + use sha2::Digest; + + exec_ctx.verbose(|| { + println!("verifying {}", path.display()); + }); + + if exec_ctx.dry_run() { + return false; + } + + let mut hasher = sha2::Sha256::new(); + + let file = t!(File::open(path)); + let mut reader = BufReader::new(file); + + loop { + let buffer = t!(reader.fill_buf()); + let l = buffer.len(); + // break if EOF + if l == 0 { + break; + } + hasher.update(buffer); + reader.consume(l); + } + + let checksum = hex_encode(hasher.finalize().as_slice()); + let verified = checksum == expected; + + if !verified { + println!( + "invalid checksum: \n\ + found: {checksum}\n\ + expected: {expected}", + ); + } + + verified +} + +fn unpack(exec_ctx: &ExecutionContext, tarball: &Path, dst: &Path, pattern: &str) { + eprintln!("extracting {} to {}", tarball.display(), dst.display()); + if !dst.exists() { + t!(fs::create_dir_all(dst)); + } + + // `tarball` ends with `.tar.xz`; strip that suffix + // example: `rust-dev-nightly-x86_64-unknown-linux-gnu` + let uncompressed_filename = + Path::new(tarball.file_name().expect("missing tarball filename")).file_stem().unwrap(); + let directory_prefix = Path::new(Path::new(uncompressed_filename).file_stem().unwrap()); + + // decompress the file + let data = t!(File::open(tarball), format!("file {} not found", tarball.display())); + let decompressor = XzDecoder::new(BufReader::new(data)); + + let mut tar = tar::Archive::new(decompressor); + + let is_ci_rustc = dst.ends_with("ci-rustc"); + let is_ci_llvm = dst.ends_with("ci-llvm"); + + // `compile::Sysroot` needs to know the contents of the `rustc-dev` tarball to avoid adding + // it to the sysroot unless it was explicitly requested. But parsing the 100 MB tarball is slow. + // Cache the entries when we extract it so we only have to read it once. + let mut recorded_entries = if is_ci_rustc { recorded_entries(dst, pattern) } else { None }; + + for member in t!(tar.entries()) { + let mut member = t!(member); + let original_path = t!(member.path()).into_owned(); + // skip the top-level directory + if original_path == directory_prefix { + continue; + } + let mut short_path = t!(original_path.strip_prefix(directory_prefix)); + let is_builder_config = short_path.to_str() == Some(BUILDER_CONFIG_FILENAME); + + if !(short_path.starts_with(pattern) || ((is_ci_rustc || is_ci_llvm) && is_builder_config)) + { + continue; + } + short_path = short_path.strip_prefix(pattern).unwrap_or(short_path); + let dst_path = dst.join(short_path); + + exec_ctx.verbose(|| { + println!("extracting {} to {}", original_path.display(), dst.display()); + }); + + if !t!(member.unpack_in(dst)) { + panic!("path traversal attack ??"); + } + if let Some(record) = &mut recorded_entries { + t!(writeln!(record, "{}", short_path.to_str().unwrap())); + } + let src_path = dst.join(original_path); + if src_path.is_dir() && dst_path.exists() { + continue; + } + t!(move_file(src_path, dst_path)); + } + let dst_dir = dst.join(directory_prefix); + if dst_dir.exists() { + t!(fs::remove_dir_all(&dst_dir), format!("failed to remove {}", dst_dir.display())); + } +} + +fn download_file<'a>( + dwn_ctx: impl AsRef>, + url: &str, + dest_path: &Path, + help_on_error: &str, +) { + let dwn_ctx = dwn_ctx.as_ref(); + + dwn_ctx.exec_ctx.verbose(|| { + println!("download {url}"); + }); + // Use a temporary file in case we crash while downloading, to avoid a corrupt download in cache/. + let tempfile = tempdir(dwn_ctx.out).join(dest_path.file_name().unwrap()); + // While bootstrap itself only supports http and https downloads, downstream forks might + // need to download components from other protocols. The match allows them adding more + // protocols without worrying about merge conflicts if we change the HTTP implementation. + match url.split_once("://").map(|(proto, _)| proto) { + Some("http") | Some("https") => download_http_with_retries( + dwn_ctx.host_target, + dwn_ctx.is_running_on_ci, + dwn_ctx.exec_ctx, + &tempfile, + url, + help_on_error, + ), + Some(other) => panic!("unsupported protocol {other} in {url}"), + None => panic!("no protocol in {url}"), + } + t!(move_file(&tempfile, dest_path), format!("failed to rename {tempfile:?} to {dest_path:?}")); +} + +/// Create a temporary directory in `out` and return its path. +/// +/// NOTE: this temporary directory is shared between all steps; +/// if you need an empty directory, create a new subdirectory inside it. +pub(crate) fn tempdir(out: &Path) -> PathBuf { + let tmp = out.join("tmp"); + t!(fs::create_dir_all(&tmp)); + tmp +} + +fn download_http_with_retries( + host_target: TargetSelection, + is_running_on_ci: bool, + exec_ctx: &ExecutionContext, + tempfile: &Path, + url: &str, + help_on_error: &str, +) { + println!("downloading {url}"); + // Try curl. If that fails and we are on windows, fallback to PowerShell. + // options should be kept in sync with + // src/bootstrap/src/core/download.rs + // for consistency + let mut curl = command("curl").allow_failure(); + curl.args([ + // follow redirect + "--location", + // timeout if speed is < 10 bytes/sec for > 30 seconds + "--speed-time", + "30", + "--speed-limit", + "10", + // timeout if cannot connect within 30 seconds + "--connect-timeout", + "30", + // output file + "--output", + tempfile.to_str().unwrap(), + // if there is an error, don't restart the download, + // instead continue where it left off. + "--continue-at", + "-", + // retry up to 3 times. note that this means a maximum of 4 + // attempts will be made, since the first attempt isn't a *re*try. + "--retry", + "3", + // show errors, even if --silent is specified + "--show-error", + // set timestamp of downloaded file to that of the server + "--remote-time", + // fail on non-ok http status + "--fail", + ]); + // Don't print progress in CI; the \r wrapping looks bad and downloads don't take long enough for progress to be useful. + if is_running_on_ci { + curl.arg("--silent"); + } else { + curl.arg("--progress-bar"); + } + // --retry-all-errors was added in 7.71.0, don't use it if curl is old. + if curl_version(exec_ctx) >= semver::Version::new(7, 71, 0) { + curl.arg("--retry-all-errors"); + } + curl.arg(url); + if !curl.run(exec_ctx) { + if host_target.contains("windows-msvc") { + eprintln!("Fallback to PowerShell"); + for _ in 0..3 { + let powershell = command("PowerShell.exe").allow_failure().args([ + "/nologo", + "-Command", + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;", + &format!( + "(New-Object System.Net.WebClient).DownloadFile('{}', '{}')", + url, tempfile.to_str().expect("invalid UTF-8 not supported with powershell downloads"), + ), + ]).run_capture_stdout(exec_ctx); + + if powershell.is_failure() { + return; + } + + eprintln!("\nspurious failure, trying again"); + } + } + if !help_on_error.is_empty() { + eprintln!("{help_on_error}"); + } + crate::exit!(1); + } +} + +fn curl_version(exec_ctx: &ExecutionContext) -> semver::Version { + let mut curl = command("curl"); + curl.arg("-V"); + let curl = curl.run_capture_stdout(exec_ctx); + if curl.is_failure() { + return semver::Version::new(1, 0, 0); + } + let output = curl.stdout(); + extract_curl_version(output) +} diff --git a/src/bootstrap/src/utils/proc_macro_deps.rs b/src/bootstrap/src/utils/proc_macro_deps.rs index 21c7fc89d7dfd..777c8601aa1dc 100644 --- a/src/bootstrap/src/utils/proc_macro_deps.rs +++ b/src/bootstrap/src/utils/proc_macro_deps.rs @@ -3,6 +3,7 @@ /// See pub static CRATES: &[&str] = &[ // tidy-alphabetical-start + "allocator-api2", "annotate-snippets", "anstyle", "askama_parser", @@ -16,13 +17,17 @@ pub static CRATES: &[&str] = &[ "darling_core", "derive_builder_core", "digest", + "equivalent", "fluent-bundle", "fluent-langneg", "fluent-syntax", "fnv", + "foldhash", "generic-array", + "hashbrown", "heck", "ident_case", + "indexmap", "intl-memoizer", "intl_pluralrules", "libc", diff --git a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile index e1d83d360872c..8dcbf1fed8f6d 100644 --- a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile @@ -81,7 +81,7 @@ RUN /tmp/build-fuchsia-toolchain.sh COPY host-x86_64/dist-various-2/build-x86_64-fortanix-unknown-sgx-toolchain.sh /tmp/ RUN /tmp/build-x86_64-fortanix-unknown-sgx-toolchain.sh -RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-25/wasi-sdk-25.0-x86_64-linux.tar.gz | \ +RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-27/wasi-sdk-27.0-x86_64-linux.tar.gz | \ tar -xz ENV WASI_SDK_PATH=/tmp/wasi-sdk-25.0-x86_64-linux diff --git a/src/ci/docker/host-x86_64/test-various/Dockerfile b/src/ci/docker/host-x86_64/test-various/Dockerfile index 662a26400ce9d..560f4183a6302 100644 --- a/src/ci/docker/host-x86_64/test-various/Dockerfile +++ b/src/ci/docker/host-x86_64/test-various/Dockerfile @@ -40,7 +40,7 @@ WORKDIR / COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh -RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-25/wasi-sdk-25.0-x86_64-linux.tar.gz | \ +RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-27/wasi-sdk-27.0-x86_64-linux.tar.gz | \ tar -xz ENV WASI_SDK_PATH=/wasi-sdk-25.0-x86_64-linux diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 57456e906de55..fed4296fa2221 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -380,7 +380,7 @@ impl Options { early_dcx: &mut EarlyDiagCtxt, matches: &getopts::Matches, args: Vec, - ) -> Option<(InputMode, Options, RenderOptions)> { + ) -> Option<(InputMode, Options, RenderOptions, Vec)> { // Check for unstable options. nightly_options::check_nightly_options(early_dcx, matches, &opts()); @@ -643,10 +643,13 @@ impl Options { let extension_css = matches.opt_str("e").map(|s| PathBuf::from(&s)); - if let Some(ref p) = extension_css - && !p.is_file() - { - dcx.fatal("option --extend-css argument must be a file"); + let mut loaded_paths = Vec::new(); + + if let Some(ref p) = extension_css { + loaded_paths.push(p.clone()); + if !p.is_file() { + dcx.fatal("option --extend-css argument must be a file"); + } } let mut themes = Vec::new(); @@ -690,6 +693,7 @@ impl Options { )) .emit(); } + loaded_paths.push(theme_file.clone()); themes.push(StylePath { path: theme_file }); } } @@ -708,6 +712,7 @@ impl Options { &mut id_map, edition, &None, + &mut loaded_paths, ) else { dcx.fatal("`ExternalHtml::load` failed"); }; @@ -799,7 +804,8 @@ impl Options { let scrape_examples_options = ScrapeExamplesOptions::new(matches, dcx); let with_examples = matches.opt_strs("with-examples"); - let call_locations = crate::scrape_examples::load_call_locations(with_examples, dcx); + let call_locations = + crate::scrape_examples::load_call_locations(with_examples, dcx, &mut loaded_paths); let doctest_build_args = matches.opt_strs("doctest-build-arg"); let unstable_features = @@ -885,7 +891,7 @@ impl Options { parts_out_dir, disable_minification, }; - Some((input, options, render_options)) + Some((input, options, render_options, loaded_paths)) } } diff --git a/src/librustdoc/externalfiles.rs b/src/librustdoc/externalfiles.rs index ea2aa963eddf3..42ade5b90048b 100644 --- a/src/librustdoc/externalfiles.rs +++ b/src/librustdoc/externalfiles.rs @@ -1,4 +1,4 @@ -use std::path::Path; +use std::path::{Path, PathBuf}; use std::{fs, str}; use rustc_errors::DiagCtxtHandle; @@ -32,12 +32,13 @@ impl ExternalHtml { id_map: &mut IdMap, edition: Edition, playground: &Option, + loaded_paths: &mut Vec, ) -> Option { let codes = ErrorCodes::from(nightly_build); - let ih = load_external_files(in_header, dcx)?; + let ih = load_external_files(in_header, dcx, loaded_paths)?; let bc = { - let mut bc = load_external_files(before_content, dcx)?; - let m_bc = load_external_files(md_before_content, dcx)?; + let mut bc = load_external_files(before_content, dcx, loaded_paths)?; + let m_bc = load_external_files(md_before_content, dcx, loaded_paths)?; Markdown { content: &m_bc, links: &[], @@ -52,8 +53,8 @@ impl ExternalHtml { bc }; let ac = { - let mut ac = load_external_files(after_content, dcx)?; - let m_ac = load_external_files(md_after_content, dcx)?; + let mut ac = load_external_files(after_content, dcx, loaded_paths)?; + let m_ac = load_external_files(md_after_content, dcx, loaded_paths)?; Markdown { content: &m_ac, links: &[], @@ -79,8 +80,10 @@ pub(crate) enum LoadStringError { pub(crate) fn load_string>( file_path: P, dcx: DiagCtxtHandle<'_>, + loaded_paths: &mut Vec, ) -> Result { let file_path = file_path.as_ref(); + loaded_paths.push(file_path.to_owned()); let contents = match fs::read(file_path) { Ok(bytes) => bytes, Err(e) => { @@ -101,10 +104,14 @@ pub(crate) fn load_string>( } } -fn load_external_files(names: &[String], dcx: DiagCtxtHandle<'_>) -> Option { +fn load_external_files( + names: &[String], + dcx: DiagCtxtHandle<'_>, + loaded_paths: &mut Vec, +) -> Option { let mut out = String::new(); for name in names { - let Ok(s) = load_string(name, dcx) else { return None }; + let Ok(s) = load_string(name, dcx, loaded_paths) else { return None }; out.push_str(&s); out.push('\n'); } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index a3cdc4f687f2d..7431ff8e1adef 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -799,7 +799,7 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { // Note that we discard any distinction between different non-zero exit // codes from `from_matches` here. - let (input, options, render_options) = + let (input, options, render_options, loaded_paths) = match config::Options::from_matches(early_dcx, &matches, args) { Some(opts) => opts, None => return, @@ -870,6 +870,12 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { interface::run_compiler(config, |compiler| { let sess = &compiler.sess; + // Register the loaded external files in the source map so they show up in depinfo. + // We can't load them via the source map because it gets created after we process the options. + for external_path in &loaded_paths { + let _ = sess.source_map().load_file(external_path); + } + if sess.opts.describe_lints { rustc_driver::describe_lints(sess, registered_lints); return; diff --git a/src/librustdoc/scrape_examples.rs b/src/librustdoc/scrape_examples.rs index fceacb69fb55e..4d29c74e1eb53 100644 --- a/src/librustdoc/scrape_examples.rs +++ b/src/librustdoc/scrape_examples.rs @@ -333,9 +333,11 @@ pub(crate) fn run( pub(crate) fn load_call_locations( with_examples: Vec, dcx: DiagCtxtHandle<'_>, + loaded_paths: &mut Vec, ) -> AllCallLocations { let mut all_calls: AllCallLocations = FxIndexMap::default(); for path in with_examples { + loaded_paths.push(path.clone().into()); let bytes = match fs::read(&path) { Ok(bytes) => bytes, Err(e) => dcx.fatal(format!("failed to load examples: {e}")), diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index 11c0f08debe6f..c47f9695624bc 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -45,11 +45,17 @@ jobs: os: macos-latest - host_target: i686-pc-windows-msvc os: windows-latest + - host_target: aarch64-pc-windows-msvc + os: windows-11-arm runs-on: ${{ matrix.os }} env: HOST_TARGET: ${{ matrix.host_target }} steps: - uses: actions/checkout@v4 + - name: apt update + if: ${{ startsWith(matrix.os, 'ubuntu') }} + # The runners seem to have outdated apt repos sometimes + run: sudo apt update - name: install qemu if: ${{ matrix.qemu }} run: sudo apt install qemu-user qemu-user-binfmt @@ -63,6 +69,12 @@ jobs: sudo apt update # Install needed packages sudo apt install $(echo "libatomic1: zlib1g-dev:" | sed 's/:/:${{ matrix.multiarch }}/g') + - name: Install rustup on Windows ARM + if: ${{ matrix.os == 'windows-11-arm' }} + run: | + curl -LOs https://static.rust-lang.org/rustup/dist/aarch64-pc-windows-msvc/rustup-init.exe + ./rustup-init.exe -y --no-modify-path + echo "$USERPROFILE/.cargo/bin" >> "$GITHUB_PATH" - uses: ./.github/workflows/setup with: toolchain_flags: "--host ${{ matrix.host_target }}" @@ -147,35 +159,48 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 256 # get a bit more of the history - - name: install josh-proxy - run: cargo +stable install josh-proxy --git https://github.com/josh-project/josh --tag r24.10.04 + - name: install josh-sync + run: cargo +stable install --locked --git https://github.com/rust-lang/josh-sync - name: setup bot git name and email run: | git config --global user.name 'The Miri Cronjob Bot' git config --global user.email 'miri@cron.bot' - name: Install nightly toolchain run: rustup toolchain install nightly --profile minimal - - name: get changes from rustc - run: ./miri rustc-pull - name: Install rustup-toolchain-install-master run: cargo install -f rustup-toolchain-install-master - - name: format changes (if any) + - name: Push changes to a branch and create PR run: | + # Make it easier to see what happens. + set -x + # Temporarily disable early exit to examine the status code of rustc-josh-sync + set +e + rustc-josh-sync pull + exitcode=$? + set -e + + # If there were no changes to pull, rustc-josh-sync returns status code 2. + # In that case, skip the rest of the job. + if [ $exitcode -eq 2 ]; then + echo "Nothing changed in rustc, skipping PR" + exit 0 + elif [ $exitcode -ne 0 ]; then + # If return code was not 0 or 2, rustc-josh-sync actually failed + echo "error: rustc-josh-sync failed" + exit ${exitcode} + fi + + # Format changes ./miri toolchain ./miri fmt --check || (./miri fmt && git commit -am "fmt") - - name: Push changes to a branch and create PR - run: | - # `git diff --exit-code` "succeeds" if the diff is empty. - if git diff --exit-code HEAD^; then echo "Nothing changed in rustc, skipping PR"; exit 0; fi - # The diff is non-empty, create a PR. + + # Create a PR BRANCH="rustup-$(date -u +%Y-%m-%d)" git switch -c $BRANCH git push -u origin $BRANCH gh pr create -B master --title 'Automatic Rustup' --body 'Please close and re-open this PR to trigger CI, then enable auto-merge.' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - ZULIP_BOT_EMAIL: ${{ secrets.ZULIP_BOT_EMAIL }} - ZULIP_API_TOKEN: ${{ secrets.ZULIP_API_TOKEN }} cron-fail-notify: name: cronjob failure notification @@ -198,7 +223,7 @@ jobs: It would appear that the [Miri cron job build]('"https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"') failed. This likely means that rustc changed the miri directory and - we now need to do a [`./miri rustc-pull`](https://github.com/rust-lang/miri/blob/master/CONTRIBUTING.md#importing-changes-from-the-rustc-repo). + we now need to do a [`rustc-josh-sync pull`](https://github.com/rust-lang/miri/blob/master/CONTRIBUTING.md#importing-changes-from-the-rustc-repo). Would you mind investigating this issue? diff --git a/src/tools/miri/.gitignore b/src/tools/miri/.gitignore index ed2d0ba7ba079..4a238dc031342 100644 --- a/src/tools/miri/.gitignore +++ b/src/tools/miri/.gitignore @@ -1,5 +1,4 @@ target -/doc tex/*/out *.dot *.out diff --git a/src/tools/miri/CONTRIBUTING.md b/src/tools/miri/CONTRIBUTING.md index 637c0dd2fdf1b..7d78fdddbad3d 100644 --- a/src/tools/miri/CONTRIBUTING.md +++ b/src/tools/miri/CONTRIBUTING.md @@ -297,14 +297,14 @@ You can also directly run Miri on a Rust source file: ## Advanced topic: Syncing with the rustc repo -We use the [`josh` proxy](https://github.com/josh-project/josh) to transmit changes between the +We use the [`josh-sync`](https://github.com/rust-lang/josh-sync) tool to transmit changes between the rustc and Miri repositories. You can install it as follows: ```sh -cargo +stable install josh-proxy --git https://github.com/josh-project/josh --tag r24.10.04 +cargo install --locked --git https://github.com/rust-lang/josh-sync ``` -Josh will automatically be started and stopped by `./miri`. +The commands below will automatically install and manage the [Josh](https://github.com/josh-project/josh) proxy that performs the actual work. ### Importing changes from the rustc repo @@ -312,10 +312,12 @@ Josh will automatically be started and stopped by `./miri`. We assume we start on an up-to-date master branch in the Miri repo. +1) First, create a branch for the pull, e.g. `git checkout -b rustup` +2) Then run the following: ```sh # Fetch and merge rustc side of the history. Takes ca 5 min the first time. # This will also update the `rustc-version` file. -./miri rustc-pull +rustc-josh-sync pull # Update local toolchain and apply formatting. ./miri toolchain && ./miri fmt git commit -am "rustup" @@ -328,12 +330,12 @@ needed. ### Exporting changes to the rustc repo -We will use the josh proxy to push to your fork of rustc. Run the following in the Miri repo, +We will use the `josh-sync` tool to push to your fork of rustc. Run the following in the Miri repo, assuming we are on an up-to-date master branch: ```sh # Push the Miri changes to your rustc fork (substitute your github handle for YOUR_NAME). -./miri rustc-push YOUR_NAME miri +rustc-josh-sync push miri YOUR_NAME ``` This will create a new branch called `miri` in your fork, and the output should include a link that diff --git a/src/tools/miri/Cargo.lock b/src/tools/miri/Cargo.lock index 0af4181dc154f..b46f0f8342093 100644 --- a/src/tools/miri/Cargo.lock +++ b/src/tools/miri/Cargo.lock @@ -166,10 +166,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.30" +version = "1.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7" +checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" dependencies = [ + "jobserver", + "libc", "shlex", ] @@ -214,6 +216,52 @@ dependencies = [ "inout", ] +[[package]] +name = "clap" +version = "4.5.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d" +dependencies = [ + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" + +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + +[[package]] +name = "codespan-reporting" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" +dependencies = [ + "serde", + "termcolor", + "unicode-width 0.2.1", +] + [[package]] name = "color-eyre" version = "0.6.5" @@ -313,6 +361,68 @@ dependencies = [ "typenum", ] +[[package]] +name = "cxx" +version = "1.0.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3523cc02ad831111491dd64b27ad999f1ae189986728e477604e61b81f828df" +dependencies = [ + "cc", + "cxxbridge-cmd", + "cxxbridge-flags", + "cxxbridge-macro", + "foldhash", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212b754247a6f07b10fa626628c157593f0abf640a3dd04cce2760eca970f909" +dependencies = [ + "cc", + "codespan-reporting", + "indexmap", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-cmd" +version = "1.0.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f426a20413ec2e742520ba6837c9324b55ffac24ead47491a6e29f933c5b135a" +dependencies = [ + "clap", + "codespan-reporting", + "indexmap", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258b6069020b4e5da6415df94a50ee4f586a6c38b037a180e940a43d06a070d" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8dec184b52be5008d6eaf7e62fc1802caf1ad1227d11b3b7df2c409c7ffc3f4" +dependencies = [ + "indexmap", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + [[package]] name = "directories" version = "6.0.0" @@ -334,12 +444,29 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "encode_unicode" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "errno" version = "0.3.13" @@ -372,6 +499,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -382,6 +524,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "genmc-sys" +version = "0.1.0" +dependencies = [ + "cc", + "cmake", + "cxx", + "cxx-build", + "git2", +] + [[package]] name = "getrandom" version = "0.2.16" @@ -411,12 +564,150 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "git2" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2deb07a133b1520dc1a5690e9bd08950108873d7ed5de38dcc74d3b5ebffa110" +dependencies = [ + "bitflags", + "libc", + "libgit2-sys", + "log", + "openssl-probe", + "openssl-sys", + "url", +] + +[[package]] +name = "hashbrown" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" + +[[package]] +name = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + [[package]] name = "indenter" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +[[package]] +name = "indexmap" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "indicatif" version = "0.17.11" @@ -463,6 +754,16 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "jobserver" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +dependencies = [ + "getrandom 0.3.3", + "libc", +] + [[package]] name = "js-sys" version = "0.3.77" @@ -510,6 +811,19 @@ dependencies = [ "cc", ] +[[package]] +name = "libgit2-sys" +version = "0.18.2+1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c42fe03df2bd3c53a3a9c7317ad91d80c81cd1fb0caec8d7cc4cd2bfa10c222" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", +] + [[package]] name = "libloading" version = "0.8.8" @@ -530,12 +844,39 @@ dependencies = [ "libc", ] +[[package]] +name = "libz-sys" +version = "1.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "link-cplusplus" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a6f6da007f968f9def0d65a05b187e2960183de70c160204ecfccf0ee330212" +dependencies = [ + "cc", +] + [[package]] name = "linux-raw-sys" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + [[package]] name = "lock_api" version = "0.4.13" @@ -612,6 +953,7 @@ dependencies = [ "chrono-tz", "colored 3.0.0", "directories", + "genmc-sys", "getrandom 0.3.3", "ipc-channel", "libc", @@ -672,6 +1014,24 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "option-ext" version = "0.2.0" @@ -722,6 +1082,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + [[package]] name = "perf-event-open-sys" version = "3.0.0" @@ -755,12 +1121,27 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + [[package]] name = "portable-atomic" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -946,6 +1327,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "scratch" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f6280af86e5f559536da57a45ebc84948833b3bee313a7dd25232e09c878a52" + [[package]] name = "semver" version = "1.0.26" @@ -1025,6 +1412,18 @@ dependencies = [ "color-eyre", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "syn" version = "2.0.104" @@ -1036,6 +1435,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tempfile" version = "3.20.0" @@ -1049,6 +1459,15 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -1108,6 +1527,16 @@ dependencies = [ "libc", ] +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tracing" version = "0.1.41" @@ -1199,6 +1628,23 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "uuid" version = "1.17.0" @@ -1216,6 +1662,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.5" @@ -1305,6 +1757,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "windows" version = "0.58.0" @@ -1524,6 +1985,36 @@ dependencies = [ "bitflags", ] +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.8.26" @@ -1543,3 +2034,57 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml index d293af5cea20d..91dadf78a2f72 100644 --- a/src/tools/miri/Cargo.toml +++ b/src/tools/miri/Cargo.toml @@ -48,6 +48,10 @@ nix = { version = "0.30.1", features = ["mman", "ptrace", "signal"], optional = ipc-channel = { version = "0.20.0", optional = true } capstone = { version = "0.13", optional = true } +# FIXME(genmc,macos): Add `target_os = "macos"` once https://github.com/dtolnay/cxx/issues/1535 is fixed. +[target.'cfg(all(target_os = "linux", target_pointer_width = "64", target_endian = "little"))'.dependencies] +genmc-sys = { path = "./genmc-sys/", version = "0.1.0", optional = true } + [dev-dependencies] ui_test = "0.30.2" colored = "3" @@ -66,7 +70,7 @@ harness = false [features] default = ["stack-cache", "native-lib"] -genmc = [] +genmc = ["dep:genmc-sys"] # this enables a GPL dependency! stack-cache = [] stack-cache-consistency-check = ["stack-cache"] tracing = ["serde_json"] diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index b72b974bdbdd6..efb9053f69a5a 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -1,9 +1,9 @@ //! Implements the various phases of `cargo miri run/test`. use std::env; -use std::fs::{self, File}; +use std::fs::File; use std::io::BufReader; -use std::path::{Path, PathBuf}; +use std::path::{self, Path, PathBuf}; use std::process::Command; use rustc_version::VersionMeta; @@ -222,12 +222,12 @@ pub fn phase_cargo_miri(mut args: impl Iterator) { // that to be the Miri driver, but acting as rustc, in host mode. // // In `main`, we need the value of `RUSTC` to distinguish RUSTC_WRAPPER invocations from rustdoc - // or TARGET_RUNNER invocations, so we canonicalize it here to make it exceedingly unlikely that + // or TARGET_RUNNER invocations, so we make it absolute to make it exceedingly unlikely that // there would be a collision with other invocations of cargo-miri (as rustdoc or as runner). We // explicitly do this even if RUSTC_STAGE is set, since for these builds we do *not* want the // bootstrap `rustc` thing in our way! Instead, we have MIRI_HOST_SYSROOT to use for host // builds. - cmd.env("RUSTC", fs::canonicalize(find_miri()).unwrap()); + cmd.env("RUSTC", path::absolute(find_miri()).unwrap()); // In case we get invoked as RUSTC without the wrapper, let's be a host rustc. This makes no // sense for cross-interpretation situations, but without the wrapper, this will use the host // sysroot, so asking it to behave like a target build makes even less sense. diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index 5767d17827952..b66530e77b8a8 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -142,7 +142,6 @@ case $HOST_TARGET in # Host GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests # Extra tier 1 - MANY_SEEDS=64 TEST_TARGET=i686-unknown-linux-gnu run_tests MANY_SEEDS=64 TEST_TARGET=x86_64-apple-darwin run_tests MANY_SEEDS=64 TEST_TARGET=x86_64-pc-windows-gnu run_tests ;; @@ -161,8 +160,6 @@ case $HOST_TARGET in aarch64-unknown-linux-gnu) # Host GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests - # Extra tier 1 candidate - MANY_SEEDS=64 TEST_TARGET=aarch64-pc-windows-msvc run_tests # Extra tier 2 MANY_SEEDS=16 TEST_TARGET=arm-unknown-linux-gnueabi run_tests # 32bit ARM MANY_SEEDS=16 TEST_TARGET=aarch64-pc-windows-gnullvm run_tests # gnullvm ABI @@ -189,13 +186,20 @@ case $HOST_TARGET in ;; i686-pc-windows-msvc) # Host - # Without GC_STRESS as this is the slowest runner. + # Without GC_STRESS as this is a very slow runner. MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 run_tests # Extra tier 1 # We really want to ensure a Linux target works on a Windows host, # and a 64bit target works on a 32bit host. TEST_TARGET=x86_64-unknown-linux-gnu run_tests ;; + aarch64-pc-windows-msvc) + # Host + # Without GC_STRESS as this is a very slow runner. + MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests + # Extra tier 1 + MANY_SEEDS=64 TEST_TARGET=i686-unknown-linux-gnu run_tests + ;; *) echo "FATAL: unknown host target: $HOST_TARGET" exit 1 diff --git a/src/tools/miri/doc/genmc.md b/src/tools/miri/doc/genmc.md new file mode 100644 index 0000000000000..5aabe90b5dab2 --- /dev/null +++ b/src/tools/miri/doc/genmc.md @@ -0,0 +1,62 @@ +# **(WIP)** Documentation for Miri-GenMC + +[GenMC](https://github.com/MPI-SWS/genmc) is a stateless model checker for exploring concurrent executions of a program. +Miri-GenMC integrates that model checker into Miri. + +**NOTE: Currently, no actual GenMC functionality is part of Miri, this is still WIP.** + + + +## Usage + +**IMPORTANT: The license of GenMC and thus the `genmc-sys` crate in the Miri repo is currently "GPL-3.0-or-later", so a binary produced with the `genmc` feature is subject to the requirements of the GPL. As long as that remains the case, the `genmc` feature of Miri is OFF-BY-DEFAULT and must be OFF for all Miri releases.** + +For testing/developing Miri-GenMC (while keeping in mind the licensing issues): +- clone the Miri repo. +- build Miri-GenMC with `./miri build --features=genmc`. +- OR: install Miri-GenMC in the current system with `./miri install --features=genmc` + +Basic usage: +```shell +MIRIFLAGS="-Zmiri-genmc" cargo miri run +``` + + + + + +## Tips + + + +## Limitations + +Some or all of these limitations might get removed in the future: + +- Borrow tracking is currently incompatible (stacked/tree borrows). +- Only Linux is supported for now. +- No support for 32-bit or big-endian targets. +- No cross-target interpretation. + + + +## Development + +GenMC is written in C++, which complicates development a bit. +The prerequisites for building Miri-GenMC are: +- A compiler with C++23 support. +- LLVM developments headers and clang. + + +The actual code for GenMC is not contained in the Miri repo itself, but in a [separate GenMC repo](https://github.com/MPI-SWS/genmc) (with its own maintainers). +These sources need to be available to build Miri-GenMC. +The process for obtaining them is as follows: +- By default, a fixed commit of GenMC is downloaded to `genmc-sys/genmc-src` and built automatically. + (The commit is determined by `GENMC_COMMIT` in `genmc-sys/build.rs`.) +- If you want to overwrite that, set the `GENMC_SRC_PATH` environment variable to a path that contains the GenMC sources. + If you place this directory inside the Miri folder, it is recommended to call it `genmc-src` as that tells `./miri fmt` to avoid + formatting the Rust files inside that folder. + + + + diff --git a/src/tools/miri/etc/rust_analyzer_helix.toml b/src/tools/miri/etc/rust_analyzer_helix.toml index 91e4070478c8d..c46b246049ffd 100644 --- a/src/tools/miri/etc/rust_analyzer_helix.toml +++ b/src/tools/miri/etc/rust_analyzer_helix.toml @@ -5,6 +5,7 @@ source = "discover" linkedProjects = [ "Cargo.toml", "cargo-miri/Cargo.toml", + "genmc-sys/Cargo.toml", "miri-script/Cargo.toml", ] diff --git a/src/tools/miri/etc/rust_analyzer_vscode.json b/src/tools/miri/etc/rust_analyzer_vscode.json index 6917c6a1fd859..8e647f5331f06 100644 --- a/src/tools/miri/etc/rust_analyzer_vscode.json +++ b/src/tools/miri/etc/rust_analyzer_vscode.json @@ -3,6 +3,7 @@ "rust-analyzer.linkedProjects": [ "Cargo.toml", "cargo-miri/Cargo.toml", + "genmc-sys/Cargo.toml", "miri-script/Cargo.toml", ], "rust-analyzer.check.invocationStrategy": "once", diff --git a/src/tools/miri/genmc-sys/.gitignore b/src/tools/miri/genmc-sys/.gitignore new file mode 100644 index 0000000000000..276a053cd0558 --- /dev/null +++ b/src/tools/miri/genmc-sys/.gitignore @@ -0,0 +1 @@ +genmc-src*/ diff --git a/src/tools/miri/genmc-sys/Cargo.toml b/src/tools/miri/genmc-sys/Cargo.toml new file mode 100644 index 0000000000000..737ab9073bfb8 --- /dev/null +++ b/src/tools/miri/genmc-sys/Cargo.toml @@ -0,0 +1,17 @@ +[package] +authors = ["Miri Team"] +# The parts in this repo are MIT OR Apache-2.0, but we are linking in +# code from https://github.com/MPI-SWS/genmc which is GPL-3.0-or-later. +license = "(MIT OR Apache-2.0) AND GPL-3.0-or-later" +name = "genmc-sys" +version = "0.1.0" +edition = "2024" + +[dependencies] +cxx = { version = "1.0.160", features = ["c++20"] } + +[build-dependencies] +cc = "1.2.16" +cmake = "0.1.54" +git2 = { version = "0.20.2", default-features = false, features = ["https"] } +cxx-build = { version = "1.0.160", features = ["parallel"] } diff --git a/src/tools/miri/genmc-sys/build.rs b/src/tools/miri/genmc-sys/build.rs new file mode 100644 index 0000000000000..479a3bd7186b7 --- /dev/null +++ b/src/tools/miri/genmc-sys/build.rs @@ -0,0 +1,269 @@ +use std::path::{Path, PathBuf}; +use std::str::FromStr; + +// Build script for running Miri with GenMC. +// Check out doc/genmc.md for more info. + +/// Path where the downloaded GenMC repository will be stored (relative to the `genmc-sys` directory). +/// Note that this directory is *not* cleaned up automatically by `cargo clean`. +const GENMC_DOWNLOAD_PATH: &str = "./genmc-src/"; + +/// Name of the library of the GenMC model checker. +const GENMC_MODEL_CHECKER: &str = "genmc_lib"; + +/// Path where the `cxx_bridge!` macro is used to define the Rust-C++ interface. +const RUST_CXX_BRIDGE_FILE_PATH: &str = "src/lib.rs"; + +/// The profile with which to build GenMC. +const GENMC_CMAKE_PROFILE: &str = "RelWithDebInfo"; + +mod downloading { + use std::path::PathBuf; + use std::str::FromStr; + + use git2::{Commit, Oid, Remote, Repository, StatusOptions}; + + use super::GENMC_DOWNLOAD_PATH; + + /// The GenMC repository the we get our commit from. + pub(crate) const GENMC_GITHUB_URL: &str = "https://github.com/MPI-SWS/genmc.git"; + /// The GenMC commit we depend on. It must be available on the specified GenMC repository. + pub(crate) const GENMC_COMMIT: &str = "3438dd2c1202cd4a47ed7881d099abf23e4167ab"; + + pub(crate) fn download_genmc() -> PathBuf { + let Ok(genmc_download_path) = PathBuf::from_str(GENMC_DOWNLOAD_PATH); + let commit_oid = Oid::from_str(GENMC_COMMIT).expect("Commit should be valid."); + + match Repository::open(&genmc_download_path) { + Ok(repo) => { + assert_repo_unmodified(&repo); + let commit = update_local_repo(&repo, commit_oid); + checkout_commit(&repo, &commit); + } + Err(_) => { + let repo = clone_remote_repo(&genmc_download_path); + let Ok(commit) = repo.find_commit(commit_oid) else { + panic!( + "Cloned GenMC repository does not contain required commit '{GENMC_COMMIT}'" + ); + }; + checkout_commit(&repo, &commit); + } + }; + + genmc_download_path + } + + fn get_remote(repo: &Repository) -> Remote<'_> { + let remote = repo.find_remote("origin").unwrap_or_else(|e| { + panic!( + "Could not load commit ({GENMC_COMMIT}) from remote repository '{GENMC_GITHUB_URL}'. Error: {e}" + ); + }); + + // Ensure that the correct remote URL is set. + let remote_url = remote.url(); + if let Some(remote_url) = remote_url + && remote_url == GENMC_GITHUB_URL + { + return remote; + } + + // Update remote URL. + println!( + "cargo::warning=GenMC repository remote URL has changed from '{remote_url:?}' to '{GENMC_GITHUB_URL}'" + ); + repo.remote_set_url("origin", GENMC_GITHUB_URL) + .expect("cannot rename url of remote 'origin'"); + + // Reacquire the `Remote`, since `remote_set_url` doesn't update Remote objects already in memory. + repo.find_remote("origin").unwrap() + } + + // Check if the required commit exists already, otherwise try fetching it. + fn update_local_repo(repo: &Repository, commit_oid: Oid) -> Commit<'_> { + repo.find_commit(commit_oid).unwrap_or_else(|_find_error| { + println!("GenMC repository at path '{GENMC_DOWNLOAD_PATH}' does not contain commit '{GENMC_COMMIT}'."); + // The commit is not in the checkout. Try `git fetch` and hope that we find the commit then. + let mut remote = get_remote(repo); + remote.fetch(&[GENMC_COMMIT], None, None).expect("Failed to fetch from remote."); + + repo.find_commit(commit_oid) + .expect("Remote repository should contain expected commit") + }) + } + + fn clone_remote_repo(genmc_download_path: &PathBuf) -> Repository { + Repository::clone(GENMC_GITHUB_URL, &genmc_download_path).unwrap_or_else(|e| { + panic!("Cannot clone GenMC repo from '{GENMC_GITHUB_URL}': {e:?}"); + }) + } + + /// Set the state of the repo to a specific commit + fn checkout_commit(repo: &Repository, commit: &Commit<'_>) { + repo.checkout_tree(commit.as_object(), None).expect("Failed to checkout"); + repo.set_head_detached(commit.id()).expect("Failed to set HEAD"); + println!("Successfully set checked out commit {commit:?}"); + } + + /// Check that the downloaded repository is unmodified. + /// If it is modified, explain that it shouldn't be, and hint at how to do local development with GenMC. + /// We don't overwrite any changes made to the directory, to prevent data loss. + fn assert_repo_unmodified(repo: &Repository) { + let statuses = repo + .statuses(Some( + StatusOptions::new() + .include_untracked(true) + .include_ignored(false) + .include_unmodified(false), + )) + .expect("should be able to get repository status"); + if statuses.is_empty() { + return; + } + + panic!( + "Downloaded GenMC repository at path '{GENMC_DOWNLOAD_PATH}' has been modified. Please undo any changes made, or delete the '{GENMC_DOWNLOAD_PATH}' directory to have it downloaded again.\n\ + HINT: For local development, set the environment variable 'GENMC_SRC_PATH' to the path of a GenMC repository." + ); + } +} + +// FIXME(genmc,llvm): Remove once the LLVM dependency of the GenMC model checker is removed. +/// The linked LLVM version is in the generated `config.h`` file, which we parse and use to link to LLVM. +/// Returns c++ compiler definitions required for building with/including LLVM, and the include path for LLVM headers. +fn link_to_llvm(config_file: &Path) -> (String, String) { + /// Search a string for a line matching `//@VARIABLE_NAME: VARIABLE CONTENT` + fn extract_value<'a>(input: &'a str, name: &str) -> Option<&'a str> { + input + .lines() + .find_map(|line| line.strip_prefix("//@")?.strip_prefix(name)?.strip_prefix(": ")) + } + + let file_content = std::fs::read_to_string(&config_file).unwrap_or_else(|err| { + panic!("GenMC config file ({}) should exist, but got errror {err:?}", config_file.display()) + }); + + let llvm_definitions = extract_value(&file_content, "LLVM_DEFINITIONS") + .expect("Config file should contain LLVM_DEFINITIONS"); + let llvm_include_dirs = extract_value(&file_content, "LLVM_INCLUDE_DIRS") + .expect("Config file should contain LLVM_INCLUDE_DIRS"); + let llvm_library_dir = extract_value(&file_content, "LLVM_LIBRARY_DIR") + .expect("Config file should contain LLVM_LIBRARY_DIR"); + let llvm_config_path = extract_value(&file_content, "LLVM_CONFIG_PATH") + .expect("Config file should contain LLVM_CONFIG_PATH"); + + // Add linker search path. + let lib_dir = PathBuf::from_str(llvm_library_dir).unwrap(); + println!("cargo::rustc-link-search=native={}", lib_dir.display()); + + // Add libraries to link. + let output = std::process::Command::new(llvm_config_path) + .arg("--libs") // Print the libraries to link to (space-separated list) + .output() + .expect("failed to execute llvm-config"); + let llvm_link_libs = + String::try_from(output.stdout).expect("llvm-config output should be a valid string"); + + for link_lib in llvm_link_libs.trim().split(" ") { + let link_lib = + link_lib.strip_prefix("-l").expect("Linker parameter should start with \"-l\""); + println!("cargo::rustc-link-lib=dylib={link_lib}"); + } + + (llvm_definitions.to_string(), llvm_include_dirs.to_string()) +} + +/// Build the GenMC model checker library and the Rust-C++ interop library with cxx.rs +fn compile_cpp_dependencies(genmc_path: &Path) { + // Part 1: + // Compile the GenMC library using cmake. + + let cmakelists_path = genmc_path.join("CMakeLists.txt"); + + // FIXME(genmc,cargo): Switch to using `CARGO_CFG_DEBUG_ASSERTIONS` once https://github.com/rust-lang/cargo/issues/15760 is completed. + // Enable/disable additional debug checks, prints and options for GenMC, based on the Rust profile (debug/release) + let enable_genmc_debug = matches!(std::env::var("PROFILE").as_deref().unwrap(), "debug"); + + let mut config = cmake::Config::new(cmakelists_path); + config.profile(GENMC_CMAKE_PROFILE); + config.define("GENMC_DEBUG", if enable_genmc_debug { "ON" } else { "OFF" }); + + // The actual compilation happens here: + let genmc_install_dir = config.build(); + + // Add the model checker library to be linked and tell rustc where to find it: + let cmake_lib_dir = genmc_install_dir.join("lib").join("genmc"); + println!("cargo::rustc-link-search=native={}", cmake_lib_dir.display()); + println!("cargo::rustc-link-lib=static={GENMC_MODEL_CHECKER}"); + + // FIXME(genmc,llvm): Remove once the LLVM dependency of the GenMC model checker is removed. + let config_file = genmc_install_dir.join("include").join("genmc").join("config.h"); + let (llvm_definitions, llvm_include_dirs) = link_to_llvm(&config_file); + + // Part 2: + // Compile the cxx_bridge (the link between the Rust and C++ code). + + let genmc_include_dir = genmc_install_dir.join("include").join("genmc"); + + // FIXME(genmc,llvm): remove once LLVM dependency is removed. + // These definitions are parsed into a cmake list and then printed to the config.h file, so they are ';' separated. + let definitions = llvm_definitions.split(";"); + + let mut bridge = cxx_build::bridge("src/lib.rs"); + // FIXME(genmc,cmake): Remove once the GenMC debug setting is available in the config.h file. + if enable_genmc_debug { + bridge.define("ENABLE_GENMC_DEBUG", None); + } + for definition in definitions { + bridge.flag(definition); + } + bridge + .opt_level(2) + .debug(true) // Same settings that GenMC uses (default for cmake `RelWithDebInfo`) + .warnings(false) // NOTE: enabling this produces a lot of warnings. + .std("c++23") + .include(genmc_include_dir) + .include(llvm_include_dirs) + .include("./src_cpp") + .file("./src_cpp/MiriInterface.hpp") + .file("./src_cpp/MiriInterface.cpp") + .compile("genmc_interop"); + + // Link the Rust-C++ interface library generated by cxx_build: + println!("cargo::rustc-link-lib=static=genmc_interop"); +} + +fn main() { + // Make sure we don't accidentally distribute a binary with GPL code. + if option_env!("RUSTC_STAGE").is_some() { + panic!( + "genmc should not be enabled in the rustc workspace since it includes a GPL dependency" + ); + } + + // Select which path to use for the GenMC repo: + let genmc_path = if let Ok(genmc_src_path) = std::env::var("GENMC_SRC_PATH") { + let genmc_src_path = + PathBuf::from_str(&genmc_src_path).expect("GENMC_SRC_PATH should contain a valid path"); + assert!( + genmc_src_path.exists(), + "GENMC_SRC_PATH={} does not exist!", + genmc_src_path.display() + ); + genmc_src_path + } else { + downloading::download_genmc() + }; + + // Build all required components: + compile_cpp_dependencies(&genmc_path); + + // Only rebuild if anything changes: + // Note that we don't add the downloaded GenMC repo, since that should never be modified + // manually. Adding that path here would also trigger an unnecessary rebuild after the repo is + // cloned (since cargo detects that as a file modification). + println!("cargo::rerun-if-changed={RUST_CXX_BRIDGE_FILE_PATH}"); + println!("cargo::rerun-if-changed=./src"); + println!("cargo::rerun-if-changed=./src_cpp"); +} diff --git a/src/tools/miri/genmc-sys/src/lib.rs b/src/tools/miri/genmc-sys/src/lib.rs new file mode 100644 index 0000000000000..ab46d729ea167 --- /dev/null +++ b/src/tools/miri/genmc-sys/src/lib.rs @@ -0,0 +1,30 @@ +pub use self::ffi::*; + +impl Default for GenmcParams { + fn default() -> Self { + Self { + print_random_schedule_seed: false, + do_symmetry_reduction: false, + // FIXME(GenMC): Add defaults for remaining parameters + } + } +} + +#[cxx::bridge] +mod ffi { + /// Parameters that will be given to GenMC for setting up the model checker. + /// (The fields of this struct are visible to both Rust and C++) + #[derive(Clone, Debug)] + struct GenmcParams { + pub print_random_schedule_seed: bool, + pub do_symmetry_reduction: bool, + // FIXME(GenMC): Add remaining parameters. + } + unsafe extern "C++" { + include!("MiriInterface.hpp"); + + type MiriGenMCShim; + + fn createGenmcHandle(config: &GenmcParams) -> UniquePtr; + } +} diff --git a/src/tools/miri/genmc-sys/src_cpp/MiriInterface.cpp b/src/tools/miri/genmc-sys/src_cpp/MiriInterface.cpp new file mode 100644 index 0000000000000..0827bb3d40746 --- /dev/null +++ b/src/tools/miri/genmc-sys/src_cpp/MiriInterface.cpp @@ -0,0 +1,50 @@ +#include "MiriInterface.hpp" + +#include "genmc-sys/src/lib.rs.h" + +auto MiriGenMCShim::createHandle(const GenmcParams &config) + -> std::unique_ptr +{ + auto conf = std::make_shared(); + + // Miri needs all threads to be replayed, even fully completed ones. + conf->replayCompletedThreads = true; + + // We only support the RC11 memory model for Rust. + conf->model = ModelType::RC11; + + conf->printRandomScheduleSeed = config.print_random_schedule_seed; + + // FIXME(genmc): disable any options we don't support currently: + conf->ipr = false; + conf->disableBAM = true; + conf->instructionCaching = false; + + ERROR_ON(config.do_symmetry_reduction, "Symmetry reduction is currently unsupported in GenMC mode."); + conf->symmetryReduction = config.do_symmetry_reduction; + + // FIXME(genmc): Should there be a way to change this option from Miri? + conf->schedulePolicy = SchedulePolicy::WF; + + // FIXME(genmc): implement estimation mode: + conf->estimate = false; + conf->estimationMax = 1000; + const auto mode = conf->estimate ? GenMCDriver::Mode(GenMCDriver::EstimationMode{}) + : GenMCDriver::Mode(GenMCDriver::VerificationMode{}); + + // Running Miri-GenMC without race detection is not supported. + // Disabling this option also changes the behavior of the replay scheduler to only schedule at atomic operations, which is required with Miri. + // This happens because Miri can generate multiple GenMC events for a single MIR terminator. Without this option, + // the scheduler might incorrectly schedule an atomic MIR terminator because the first event it creates is a non-atomic (e.g., `StorageLive`). + conf->disableRaceDetection = false; + + // Miri can already check for unfreed memory. Also, GenMC cannot distinguish between memory + // that is allowed to leak and memory that is not. + conf->warnUnfreedMemory = false; + + // FIXME(genmc): check config: + // checkConfigOptions(*conf); + + auto driver = std::make_unique(std::move(conf), mode); + return driver; +} diff --git a/src/tools/miri/genmc-sys/src_cpp/MiriInterface.hpp b/src/tools/miri/genmc-sys/src_cpp/MiriInterface.hpp new file mode 100644 index 0000000000000..e55522ef41897 --- /dev/null +++ b/src/tools/miri/genmc-sys/src_cpp/MiriInterface.hpp @@ -0,0 +1,44 @@ +#ifndef GENMC_MIRI_INTERFACE_HPP +#define GENMC_MIRI_INTERFACE_HPP + +#include "rust/cxx.h" + +#include "config.h" + +#include "Config/Config.hpp" +#include "Verification/GenMCDriver.hpp" + +#include + +/**** Types available to Miri ****/ + +// Config struct defined on the Rust side and translated to C++ by cxx.rs: +struct GenmcParams; + +struct MiriGenMCShim : private GenMCDriver +{ + +public: + MiriGenMCShim(std::shared_ptr conf, Mode mode /* = VerificationMode{} */) + : GenMCDriver(std::move(conf), nullptr, mode) + { + std::cerr << "C++: GenMC handle created!" << std::endl; + } + + virtual ~MiriGenMCShim() + { + std::cerr << "C++: GenMC handle destroyed!" << std::endl; + } + + static std::unique_ptr createHandle(const GenmcParams &config); +}; + +/**** Functions available to Miri ****/ + +// NOTE: CXX doesn't support exposing static methods to Rust currently, so we expose this function instead. +static inline auto createGenmcHandle(const GenmcParams &config) -> std::unique_ptr +{ + return MiriGenMCShim::createHandle(config); +} + +#endif /* GENMC_MIRI_INTERFACE_HPP */ diff --git a/src/tools/miri/josh-sync.toml b/src/tools/miri/josh-sync.toml new file mode 100644 index 0000000000000..86208b3742d27 --- /dev/null +++ b/src/tools/miri/josh-sync.toml @@ -0,0 +1,2 @@ +repo = "miri" +filter = ":rev(75dd959a3a40eb5b4574f8d2e23aa6efbeb33573:prefix=src/tools/miri):/src/tools/miri" diff --git a/src/tools/miri/miri-script/Cargo.lock b/src/tools/miri/miri-script/Cargo.lock index a049bfcbccd65..044a678869e2f 100644 --- a/src/tools/miri/miri-script/Cargo.lock +++ b/src/tools/miri/miri-script/Cargo.lock @@ -116,27 +116,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" -[[package]] -name = "directories" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.60.2", -] - [[package]] name = "dunce" version = "1.0.5" @@ -165,17 +144,6 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" -[[package]] -name = "getrandom" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.11.1+wasi-snapshot-preview1", -] - [[package]] name = "getrandom" version = "0.3.3" @@ -185,7 +153,7 @@ dependencies = [ "cfg-if", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasi", ] [[package]] @@ -221,16 +189,6 @@ version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" -[[package]] -name = "libredox" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1580801010e535496706ba011c15f8532df6b42297d2e471fec38ceadd8c0638" -dependencies = [ - "bitflags", - "libc", -] - [[package]] name = "linux-raw-sys" version = "0.9.4" @@ -249,7 +207,6 @@ version = "0.1.0" dependencies = [ "anyhow", "clap", - "directories", "dunce", "itertools", "path_macro", @@ -275,12 +232,6 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - [[package]] name = "path_macro" version = "1.0.0" @@ -311,17 +262,6 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" -[[package]] -name = "redox_users" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" -dependencies = [ - "getrandom 0.2.16", - "libredox", - "thiserror", -] - [[package]] name = "rustc_version" version = "0.4.1" @@ -427,32 +367,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand", - "getrandom 0.3.3", + "getrandom", "once_cell", "rustix", "windows-sys 0.59.0", ] -[[package]] -name = "thiserror" -version = "2.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "unicode-ident" version = "1.0.18" @@ -475,12 +395,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - [[package]] name = "wasi" version = "0.14.2+wasi-0.2.4" diff --git a/src/tools/miri/miri-script/Cargo.toml b/src/tools/miri/miri-script/Cargo.toml index b3f82cd1d5043..39858880e8c60 100644 --- a/src/tools/miri/miri-script/Cargo.toml +++ b/src/tools/miri/miri-script/Cargo.toml @@ -22,7 +22,6 @@ anyhow = "1.0" xshell = "0.2.6" rustc_version = "0.4" dunce = "1.0.4" -directories = "6" serde = "1" serde_json = "1" serde_derive = "1" diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs index 9aaad9ca04a9a..ee09b9b4b735b 100644 --- a/src/tools/miri/miri-script/src/commands.rs +++ b/src/tools/miri/miri-script/src/commands.rs @@ -2,11 +2,9 @@ use std::collections::BTreeMap; use std::ffi::{OsStr, OsString}; use std::fmt::Write as _; use std::fs::{self, File}; -use std::io::{self, BufRead, BufReader, BufWriter, Write as _}; -use std::ops::Not; +use std::io::{self, BufRead, BufReader, BufWriter}; use std::path::PathBuf; -use std::time::Duration; -use std::{env, net, process}; +use std::{env, process}; use anyhow::{Context, Result, anyhow, bail}; use path_macro::path; @@ -18,11 +16,6 @@ use xshell::{Shell, cmd}; use crate::Command; use crate::util::*; -/// Used for rustc syncs. -const JOSH_FILTER: &str = - ":rev(75dd959a3a40eb5b4574f8d2e23aa6efbeb33573:prefix=src/tools/miri):/src/tools/miri"; -const JOSH_PORT: u16 = 42042; - impl MiriEnv { /// Prepares the environment: builds miri and cargo-miri and a sysroot. /// Returns the location of the sysroot. @@ -99,66 +92,6 @@ impl Command { Ok(()) } - fn start_josh() -> Result { - // Determine cache directory. - let local_dir = { - let user_dirs = - directories::ProjectDirs::from("org", "rust-lang", "miri-josh").unwrap(); - user_dirs.cache_dir().to_owned() - }; - - // Start josh, silencing its output. - let mut cmd = process::Command::new("josh-proxy"); - cmd.arg("--local").arg(local_dir); - cmd.arg("--remote").arg("https://github.com"); - cmd.arg("--port").arg(JOSH_PORT.to_string()); - cmd.arg("--no-background"); - cmd.stdout(process::Stdio::null()); - cmd.stderr(process::Stdio::null()); - let josh = cmd.spawn().context("failed to start josh-proxy, make sure it is installed")?; - - // Create a wrapper that stops it on drop. - struct Josh(process::Child); - impl Drop for Josh { - fn drop(&mut self) { - #[cfg(unix)] - { - // Try to gracefully shut it down. - process::Command::new("kill") - .args(["-s", "INT", &self.0.id().to_string()]) - .output() - .expect("failed to SIGINT josh-proxy"); - // Sadly there is no "wait with timeout"... so we just give it some time to finish. - std::thread::sleep(Duration::from_millis(100)); - // Now hopefully it is gone. - if self.0.try_wait().expect("failed to wait for josh-proxy").is_some() { - return; - } - } - // If that didn't work (or we're not on Unix), kill it hard. - eprintln!( - "I have to kill josh-proxy the hard way, let's hope this does not break anything." - ); - self.0.kill().expect("failed to SIGKILL josh-proxy"); - } - } - - // Wait until the port is open. We try every 10ms until 1s passed. - for _ in 0..100 { - // This will generally fail immediately when the port is still closed. - let josh_ready = net::TcpStream::connect_timeout( - &net::SocketAddr::from(([127, 0, 0, 1], JOSH_PORT)), - Duration::from_millis(1), - ); - if josh_ready.is_ok() { - return Ok(Josh(josh)); - } - // Not ready yet. - std::thread::sleep(Duration::from_millis(10)); - } - bail!("Even after waiting for 1s, josh-proxy is still not available.") - } - pub fn exec(self) -> Result<()> { // First, and crucially only once, run the auto-actions -- but not for all commands. match &self { @@ -170,11 +103,7 @@ impl Command { | Command::Fmt { .. } | Command::Doc { .. } | Command::Clippy { .. } => Self::auto_actions()?, - | Command::Toolchain { .. } - | Command::Bench { .. } - | Command::RustcPull { .. } - | Command::RustcPush { .. } - | Command::Squash => {} + | Command::Toolchain { .. } | Command::Bench { .. } | Command::Squash => {} } // Then run the actual command. match self { @@ -191,8 +120,6 @@ impl Command { Command::Bench { target, no_install, save_baseline, load_baseline, benches } => Self::bench(target, no_install, save_baseline, load_baseline, benches), Command::Toolchain { flags } => Self::toolchain(flags), - Command::RustcPull { commit } => Self::rustc_pull(commit.clone()), - Command::RustcPush { github_user, branch } => Self::rustc_push(github_user, branch), Command::Squash => Self::squash(), } } @@ -233,156 +160,6 @@ impl Command { Ok(()) } - fn rustc_pull(commit: Option) -> Result<()> { - let sh = Shell::new()?; - sh.change_dir(miri_dir()?); - let commit = commit.map(Result::Ok).unwrap_or_else(|| { - let rust_repo_head = - cmd!(sh, "git ls-remote https://github.com/rust-lang/rust/ HEAD").read()?; - rust_repo_head - .split_whitespace() - .next() - .map(|front| front.trim().to_owned()) - .ok_or_else(|| anyhow!("Could not obtain Rust repo HEAD from remote.")) - })?; - // Make sure the repo is clean. - if cmd!(sh, "git status --untracked-files=no --porcelain").read()?.is_empty().not() { - bail!("working directory must be clean before running `./miri rustc-pull`"); - } - // Make sure josh is running. - let josh = Self::start_josh()?; - let josh_url = - format!("http://localhost:{JOSH_PORT}/rust-lang/rust.git@{commit}{JOSH_FILTER}.git"); - - // Update rust-version file. As a separate commit, since making it part of - // the merge has confused the heck out of josh in the past. - // We pass `--no-verify` to avoid running git hooks like `./miri fmt` that could in turn - // trigger auto-actions. - // We do this before the merge so that if there are merge conflicts, we have - // the right rust-version file while resolving them. - sh.write_file("rust-version", format!("{commit}\n"))?; - const PREPARING_COMMIT_MESSAGE: &str = "Preparing for merge from rustc"; - cmd!(sh, "git commit rust-version --no-verify -m {PREPARING_COMMIT_MESSAGE}") - .run() - .context("FAILED to commit rust-version file, something went wrong")?; - - // Fetch given rustc commit. - cmd!(sh, "git fetch {josh_url}") - .run() - .inspect_err(|_| { - // Try to un-do the previous `git commit`, to leave the repo in the state we found it. - cmd!(sh, "git reset --hard HEAD^") - .run() - .expect("FAILED to clean up again after failed `git fetch`, sorry for that"); - }) - .context("FAILED to fetch new commits, something went wrong (committing the rust-version file has been undone)")?; - - // This should not add any new root commits. So count those before and after merging. - let num_roots = || -> Result { - Ok(cmd!(sh, "git rev-list HEAD --max-parents=0 --count") - .read() - .context("failed to determine the number of root commits")? - .parse::()?) - }; - let num_roots_before = num_roots()?; - - // Merge the fetched commit. - const MERGE_COMMIT_MESSAGE: &str = "Merge from rustc"; - cmd!(sh, "git merge FETCH_HEAD --no-verify --no-ff -m {MERGE_COMMIT_MESSAGE}") - .run() - .context("FAILED to merge new commits, something went wrong")?; - - // Check that the number of roots did not increase. - if num_roots()? != num_roots_before { - bail!("Josh created a new root commit. This is probably not the history you want."); - } - - drop(josh); - Ok(()) - } - - fn rustc_push(github_user: String, branch: String) -> Result<()> { - let sh = Shell::new()?; - sh.change_dir(miri_dir()?); - let base = sh.read_file("rust-version")?.trim().to_owned(); - // Make sure the repo is clean. - if cmd!(sh, "git status --untracked-files=no --porcelain").read()?.is_empty().not() { - bail!("working directory must be clean before running `./miri rustc-push`"); - } - // Make sure josh is running. - let josh = Self::start_josh()?; - let josh_url = - format!("http://localhost:{JOSH_PORT}/{github_user}/rust.git{JOSH_FILTER}.git"); - - // Find a repo we can do our preparation in. - if let Ok(rustc_git) = env::var("RUSTC_GIT") { - // If rustc_git is `Some`, we'll use an existing fork for the branch updates. - sh.change_dir(rustc_git); - } else { - // Otherwise, do this in the local Miri repo. - println!( - "This will pull a copy of the rust-lang/rust history into this Miri checkout, growing it by about 1GB." - ); - print!( - "To avoid that, abort now and set the `RUSTC_GIT` environment variable to an existing rustc checkout. Proceed? [y/N] " - ); - std::io::stdout().flush()?; - let mut answer = String::new(); - std::io::stdin().read_line(&mut answer)?; - if answer.trim().to_lowercase() != "y" { - std::process::exit(1); - } - }; - // Prepare the branch. Pushing works much better if we use as base exactly - // the commit that we pulled from last time, so we use the `rust-version` - // file to find out which commit that would be. - println!("Preparing {github_user}/rust (base: {base})..."); - if cmd!(sh, "git fetch https://github.com/{github_user}/rust {branch}") - .ignore_stderr() - .read() - .is_ok() - { - println!( - "The branch '{branch}' seems to already exist in 'https://github.com/{github_user}/rust'. Please delete it and try again." - ); - std::process::exit(1); - } - cmd!(sh, "git fetch https://github.com/rust-lang/rust {base}").run()?; - cmd!(sh, "git push https://github.com/{github_user}/rust {base}:refs/heads/{branch}") - .ignore_stdout() - .ignore_stderr() // silence the "create GitHub PR" message - .run()?; - println!(); - - // Do the actual push. - sh.change_dir(miri_dir()?); - println!("Pushing miri changes..."); - cmd!(sh, "git push {josh_url} HEAD:{branch}").run()?; - println!(); - - // Do a round-trip check to make sure the push worked as expected. - cmd!(sh, "git fetch {josh_url} {branch}").ignore_stderr().read()?; - let head = cmd!(sh, "git rev-parse HEAD").read()?; - let fetch_head = cmd!(sh, "git rev-parse FETCH_HEAD").read()?; - if head != fetch_head { - bail!( - "Josh created a non-roundtrip push! Do NOT merge this into rustc!\n\ - Expected {head}, got {fetch_head}." - ); - } - println!( - "Confirmed that the push round-trips back to Miri properly. Please create a rustc PR:" - ); - println!( - // Open PR with `subtree update` title to silence the `no-merges` triagebot check - // See https://github.com/rust-lang/rust/pull/114157 - " https://github.com/rust-lang/rust/compare/{github_user}:{branch}?quick_pull=1&title=Miri+subtree+update&body=r?+@ghost" - ); - - drop(josh); - Ok(()) - } - fn squash() -> Result<()> { let sh = Shell::new()?; sh.change_dir(miri_dir()?); @@ -757,8 +534,8 @@ impl Command { if ty.is_file() { name.ends_with(".rs") } else { - // dir or symlink. skip `target` and `.git`. - &name != "target" && &name != ".git" + // dir or symlink. skip `target`, `.git` and `genmc-src*` + &name != "target" && &name != ".git" && !name.starts_with("genmc-src") } }) .filter_ok(|item| item.file_type().is_file()) diff --git a/src/tools/miri/miri-script/src/main.rs b/src/tools/miri/miri-script/src/main.rs index e41df662ca28f..761ec5979fafe 100644 --- a/src/tools/miri/miri-script/src/main.rs +++ b/src/tools/miri/miri-script/src/main.rs @@ -142,25 +142,6 @@ pub enum Command { #[arg(trailing_var_arg = true, allow_hyphen_values = true)] flags: Vec, }, - /// Pull and merge Miri changes from the rustc repo. - /// - /// The fetched commit is stored in the `rust-version` file, so the next `./miri toolchain` will - /// install the rustc that just got pulled. - RustcPull { - /// The commit to fetch (default: latest rustc commit). - commit: Option, - }, - /// Push Miri changes back to the rustc repo. - /// - /// This will pull a copy of the rustc history into the Miri repo, unless you set the RUSTC_GIT - /// env var to an existing clone of the rustc repo. - RustcPush { - /// The Github user that owns the rustc fork to which we should push. - github_user: String, - /// The branch to push to. - #[arg(default_value = "miri-sync")] - branch: String, - }, /// Squash the commits of the current feature branch into one. Squash, } @@ -184,8 +165,7 @@ impl Command { flags.extend(remainder); Ok(()) } - Self::Bench { .. } | Self::RustcPull { .. } | Self::RustcPush { .. } | Self::Squash => - bail!("unexpected \"--\" found in arguments"), + Self::Bench { .. } | Self::Squash => bail!("unexpected \"--\" found in arguments"), } } } diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index d734ec333a5ca..2178caf63968a 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -6707bf0f59485cf054ac1095725df43220e4be20 +733dab558992d902d6d17576de1da768094e2cf3 diff --git a/src/tools/miri/src/bin/log/setup.rs b/src/tools/miri/src/bin/log/setup.rs index da0ba528b2c4d..a9392d010f8e8 100644 --- a/src/tools/miri/src/bin/log/setup.rs +++ b/src/tools/miri/src/bin/log/setup.rs @@ -60,7 +60,7 @@ fn init_logger_once(early_dcx: &EarlyDiagCtxt) { #[cfg(not(feature = "tracing"))] { crate::fatal_error!( - "fatal error: cannot enable MIRI_TRACING since Miri was not built with the \"tracing\" feature" + "Cannot enable MIRI_TRACING since Miri was not built with the \"tracing\" feature" ); } diff --git a/src/tools/miri/src/bin/log/tracing_chrome.rs b/src/tools/miri/src/bin/log/tracing_chrome.rs index 5a96633c99e86..3379816550cfe 100644 --- a/src/tools/miri/src/bin/log/tracing_chrome.rs +++ b/src/tools/miri/src/bin/log/tracing_chrome.rs @@ -12,6 +12,7 @@ //! ```rust //! tracing::info_span!("my_span", tracing_separate_thread = tracing::field::Empty, /* ... */) //! ``` +//! - use i64 instead of u64 for the "id" in [ChromeLayer::get_root_id] to be compatible with Perfetto //! //! Depending on the tracing-chrome crate from crates.io is unfortunately not possible, since it //! depends on `tracing_core` which conflicts with rustc_private's `tracing_core` (meaning it would @@ -285,9 +286,9 @@ struct Callsite { } enum Message { - Enter(f64, Callsite, Option), + Enter(f64, Callsite, Option), Event(f64, Callsite), - Exit(f64, Callsite, Option), + Exit(f64, Callsite, Option), NewThread(usize, String), Flush, Drop, @@ -519,14 +520,17 @@ where } } - fn get_root_id(&self, span: SpanRef) -> Option { + fn get_root_id(&self, span: SpanRef) -> Option { + // Returns `Option` instead of `Option` because apparently Perfetto gives an + // error if an id does not fit in a 64-bit signed integer in 2's complement. We cast the + // span id from `u64` to `i64` with wraparound, since negative values are fine. match self.trace_style { TraceStyle::Threaded => { if span.fields().field("tracing_separate_thread").is_some() { // assign an independent "id" to spans with argument "tracing_separate_thread", // so they appear a separate trace line in trace visualization tools, see // https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview#heading=h.jh64i9l3vwa1 - Some(span.id().into_u64()) + Some(span.id().into_u64().cast_signed()) // the comment above explains the cast } else { None } @@ -539,6 +543,7 @@ where .unwrap_or(span) .id() .into_u64() + .cast_signed() // the comment above explains the cast ), } } diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 89fa980ff646a..ae1b25f8857a0 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -67,8 +67,6 @@ use crate::log::setup::{deinit_loggers, init_early_loggers, init_late_loggers}; struct MiriCompilerCalls { miri_config: Option, many_seeds: Option, - /// Settings for using GenMC with Miri. - genmc_config: Option, } struct ManySeedsConfig { @@ -77,12 +75,8 @@ struct ManySeedsConfig { } impl MiriCompilerCalls { - fn new( - miri_config: MiriConfig, - many_seeds: Option, - genmc_config: Option, - ) -> Self { - Self { miri_config: Some(miri_config), many_seeds, genmc_config } + fn new(miri_config: MiriConfig, many_seeds: Option) -> Self { + Self { miri_config: Some(miri_config), many_seeds } } } @@ -192,8 +186,8 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { optimizations is usually marginal at best."); } - if let Some(genmc_config) = &self.genmc_config { - let _genmc_ctx = Rc::new(GenmcCtx::new(&config, genmc_config)); + if let Some(_genmc_config) = &config.genmc_config { + let _genmc_ctx = Rc::new(GenmcCtx::new(&config)); todo!("GenMC mode not yet implemented"); }; @@ -487,7 +481,6 @@ fn main() { let mut many_seeds_keep_going = false; let mut miri_config = MiriConfig::default(); miri_config.env = env_snapshot; - let mut genmc_config = None; let mut rustc_args = vec![]; let mut after_dashdash = false; @@ -603,9 +596,9 @@ fn main() { } else if arg == "-Zmiri-many-seeds-keep-going" { many_seeds_keep_going = true; } else if let Some(trimmed_arg) = arg.strip_prefix("-Zmiri-genmc") { - // FIXME(GenMC): Currently, GenMC mode is incompatible with aliasing model checking. - miri_config.borrow_tracker = None; - GenmcConfig::parse_arg(&mut genmc_config, trimmed_arg); + if let Err(msg) = GenmcConfig::parse_arg(&mut miri_config.genmc_config, trimmed_arg) { + fatal_error!("{msg}"); + } } else if let Some(param) = arg.strip_prefix("-Zmiri-env-forward=") { miri_config.forwarded_env_vars.push(param.to_owned()); } else if let Some(param) = arg.strip_prefix("-Zmiri-env-set=") { @@ -740,13 +733,18 @@ fn main() { many_seeds.map(|seeds| ManySeedsConfig { seeds, keep_going: many_seeds_keep_going }); // Validate settings for data race detection and GenMC mode. - assert_eq!(genmc_config.is_some(), miri_config.genmc_mode); - if genmc_config.is_some() { + if miri_config.genmc_config.is_some() { if !miri_config.data_race_detector { fatal_error!("Cannot disable data race detection in GenMC mode (currently)"); } else if !miri_config.weak_memory_emulation { fatal_error!("Cannot disable weak memory emulation in GenMC mode"); } + if miri_config.borrow_tracker.is_some() { + eprintln!( + "warning: borrow tracking has been disabled, it is not (yet) supported in GenMC mode." + ); + miri_config.borrow_tracker = None; + } } else if miri_config.weak_memory_emulation && !miri_config.data_race_detector { fatal_error!( "Weak memory emulation cannot be enabled when the data race detector is disabled" @@ -765,8 +763,5 @@ fn main() { ); } } - run_compiler_and_exit( - &rustc_args, - &mut MiriCompilerCalls::new(miri_config, many_seeds, genmc_config), - ) + run_compiler_and_exit(&rustc_args, &mut MiriCompilerCalls::new(miri_config, many_seeds)) } diff --git a/src/tools/miri/src/concurrency/genmc/config.rs b/src/tools/miri/src/concurrency/genmc/config.rs index f91211a670f65..c56adab90fe2b 100644 --- a/src/tools/miri/src/concurrency/genmc/config.rs +++ b/src/tools/miri/src/concurrency/genmc/config.rs @@ -1,19 +1,35 @@ -use crate::MiriConfig; +use super::GenmcParams; +/// Configuration for GenMC mode. +/// The `params` field is shared with the C++ side. +/// The remaining options are kept on the Rust side. #[derive(Debug, Default, Clone)] pub struct GenmcConfig { - // TODO: add fields + pub(super) params: GenmcParams, + do_estimation: bool, + // FIXME(GenMC): add remaining options. } impl GenmcConfig { /// Function for parsing command line options for GenMC mode. + /// /// All GenMC arguments start with the string "-Zmiri-genmc". + /// Passing any GenMC argument will enable GenMC mode. /// - /// `trimmed_arg` should be the argument to be parsed, with the suffix "-Zmiri-genmc" removed - pub fn parse_arg(genmc_config: &mut Option, trimmed_arg: &str) { + /// `trimmed_arg` should be the argument to be parsed, with the suffix "-Zmiri-genmc" removed. + pub fn parse_arg( + genmc_config: &mut Option, + trimmed_arg: &str, + ) -> Result<(), String> { + // FIXME(genmc): Ensure host == target somewhere. + if genmc_config.is_none() { *genmc_config = Some(Default::default()); } - todo!("implement parsing of GenMC options") + if trimmed_arg.is_empty() { + return Ok(()); // this corresponds to "-Zmiri-genmc" + } + // FIXME(GenMC): implement remaining parameters. + todo!(); } } diff --git a/src/tools/miri/src/concurrency/genmc/dummy.rs b/src/tools/miri/src/concurrency/genmc/dummy.rs index 3d0558fb68530..79d27c4be1591 100644 --- a/src/tools/miri/src/concurrency/genmc/dummy.rs +++ b/src/tools/miri/src/concurrency/genmc/dummy.rs @@ -16,7 +16,7 @@ pub struct GenmcCtx {} pub struct GenmcConfig {} impl GenmcCtx { - pub fn new(_miri_config: &MiriConfig, _genmc_config: &GenmcConfig) -> Self { + pub fn new(_miri_config: &MiriConfig) -> Self { unreachable!() } @@ -227,10 +227,15 @@ impl VisitProvenance for GenmcCtx { } impl GenmcConfig { - pub fn parse_arg(_genmc_config: &mut Option, trimmed_arg: &str) { - unimplemented!( - "GenMC feature im Miri is disabled, cannot handle argument: \"-Zmiri-genmc{trimmed_arg}\"" - ); + pub fn parse_arg( + _genmc_config: &mut Option, + trimmed_arg: &str, + ) -> Result<(), String> { + if cfg!(feature = "genmc") { + Err(format!("GenMC is disabled in this build of Miri")) + } else { + Err(format!("GenMC is not supported on this target")) + } } pub fn should_print_graph(&self, _rep: usize) -> bool { diff --git a/src/tools/miri/src/concurrency/genmc/mod.rs b/src/tools/miri/src/concurrency/genmc/mod.rs index 0dfd4b9b80f98..3617775e27eaf 100644 --- a/src/tools/miri/src/concurrency/genmc/mod.rs +++ b/src/tools/miri/src/concurrency/genmc/mod.rs @@ -2,6 +2,7 @@ use std::cell::Cell; +use genmc_sys::{GenmcParams, createGenmcHandle}; use rustc_abi::{Align, Size}; use rustc_const_eval::interpret::{InterpCx, InterpResult, interp_ok}; use rustc_middle::mir; @@ -24,9 +25,19 @@ pub struct GenmcCtx { impl GenmcCtx { /// Create a new `GenmcCtx` from a given config. - pub fn new(miri_config: &MiriConfig, genmc_config: &GenmcConfig) -> Self { - assert!(miri_config.genmc_mode); - todo!() + pub fn new(miri_config: &MiriConfig) -> Self { + let genmc_config = miri_config.genmc_config.as_ref().unwrap(); + + let handle = createGenmcHandle(&genmc_config.params); + assert!(!handle.is_null()); + + eprintln!("Miri: GenMC handle creation successful!"); + + drop(handle); + eprintln!("Miri: Dropping GenMC handle successful!"); + + // FIXME(GenMC): implement + std::process::exit(0); } pub fn get_stuck_execution_count(&self) -> usize { diff --git a/src/tools/miri/src/concurrency/mod.rs b/src/tools/miri/src/concurrency/mod.rs index c2ea8a00decd8..435615efd9fa7 100644 --- a/src/tools/miri/src/concurrency/mod.rs +++ b/src/tools/miri/src/concurrency/mod.rs @@ -8,7 +8,17 @@ mod vector_clock; pub mod weak_memory; // Import either the real genmc adapter or a dummy module. -#[cfg_attr(not(feature = "genmc"), path = "genmc/dummy.rs")] +// On unsupported platforms, we always include the dummy module, even if the `genmc` feature is enabled. +// FIXME(genmc,macos): Add `target_os = "macos"` once `https://github.com/dtolnay/cxx/issues/1535` is fixed. +#[cfg_attr( + not(all( + feature = "genmc", + target_os = "linux", + target_pointer_width = "64", + target_endian = "little" + )), + path = "genmc/dummy.rs" +)] mod genmc; pub use self::data_race_handler::{AllocDataRaceHandler, GlobalDataRaceHandler}; diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index 878afdf251731..fe1ef86ccd31e 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -375,7 +375,7 @@ impl Timeout { } /// The clock to use for the timeout you are asking for. -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq)] pub enum TimeoutClock { Monotonic, RealTime, diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 8fdf8d643eae0..9ecbd31c5b9f9 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -382,7 +382,7 @@ pub fn report_error<'tcx>( helps.push(note_span!(span, "{:?} was deallocated here:", alloc_id)); } } - AbiMismatchArgument { .. } => { + AbiMismatchArgument { .. } | AbiMismatchReturn { .. } => { helps.push(note!("this means these two types are not *guaranteed* to be ABI-compatible across all targets")); helps.push(note!("if you think this code should be accepted anyway, please report an issue with Miri")); } diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index 3c80e60b772ab..4c531a8d1f526 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -125,8 +125,8 @@ pub struct MiriConfig { pub data_race_detector: bool, /// Determine if weak memory emulation should be enabled. Requires data race detection to be enabled. pub weak_memory_emulation: bool, - /// Determine if we are running in GenMC mode. In this mode, Miri will explore multiple concurrent executions of the given program. - pub genmc_mode: bool, + /// Determine if we are running in GenMC mode and with which settings. In GenMC mode, Miri will explore multiple concurrent executions of the given program. + pub genmc_config: Option, /// Track when an outdated (weak memory) load happens. pub track_outdated_loads: bool, /// Rate of spurious failures for compare_exchange_weak atomic operations, @@ -192,7 +192,7 @@ impl Default for MiriConfig { track_alloc_accesses: false, data_race_detector: true, weak_memory_emulation: true, - genmc_mode: false, + genmc_config: None, track_outdated_loads: false, cmpxchg_weak_failure_rate: 0.8, // 80% measureme_out: None, @@ -334,8 +334,8 @@ pub fn create_ecx<'tcx>( helpers::try_resolve_path(tcx, &["core", "ascii", "escape_default"], Namespace::ValueNS); if !matches!(sentinel, Some(s) if tcx.is_mir_available(s.def.def_id())) { tcx.dcx().fatal( - "the current sysroot was built without `-Zalways-encode-mir`, or libcore seems missing. \ - Use `cargo miri setup` to prepare a sysroot that is suitable for Miri." + "the current sysroot was built without `-Zalways-encode-mir`, or libcore seems missing.\n\ + Note that directly invoking the `miri` binary is not supported; please use `cargo miri` instead." ); } diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 15699828e00f5..ab7e35710d349 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -3,7 +3,7 @@ use std::time::Duration; use std::{cmp, iter}; use rand::RngCore; -use rustc_abi::{Align, CanonAbi, ExternAbi, FieldIdx, FieldsShape, Size, Variants}; +use rustc_abi::{Align, ExternAbi, FieldIdx, FieldsShape, Size, Variants}; use rustc_apfloat::Float; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; use rustc_hir::Safety; @@ -14,11 +14,10 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::ExportedSymbol; use rustc_middle::ty::layout::{LayoutOf, MaybeResult, TyAndLayout}; -use rustc_middle::ty::{self, Binder, FloatTy, FnSig, IntTy, Ty, TyCtxt, UintTy}; +use rustc_middle::ty::{self, FloatTy, IntTy, Ty, TyCtxt, UintTy}; use rustc_session::config::CrateType; use rustc_span::{Span, Symbol}; use rustc_symbol_mangling::mangle_internal_symbol; -use rustc_target::callconv::FnAbi; use crate::*; @@ -437,7 +436,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// For now, arguments must be scalars (so that the caller does not have to know the layout). /// /// If you do not provide a return place, a dangling zero-sized place will be created - /// for your convenience. + /// for your convenience. This is only valid if the return type is `()`. fn call_function( &mut self, f: ty::Instance<'tcx>, @@ -452,7 +451,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let mir = this.load_mir(f.def, None)?; let dest = match dest { Some(dest) => dest.clone(), - None => MPlaceTy::fake_alloc_zst(this.layout_of(mir.return_ty())?), + None => MPlaceTy::fake_alloc_zst(this.machine.layouts.unit), }; // Construct a function pointer type representing the caller perspective. @@ -465,6 +464,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ); let caller_fn_abi = this.fn_abi_of_fn_ptr(ty::Binder::dummy(sig), ty::List::empty())?; + // This will also show proper errors if there is any ABI mismatch. this.init_stack_frame( f, mir, @@ -929,21 +929,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { self.read_c_str_with_char_size(ptr, wchar_t.size, wchar_t.align.abi) } - /// Check that the calling convention is what we expect. - fn check_callconv<'a>( - &self, - fn_abi: &FnAbi<'tcx, Ty<'tcx>>, - exp_abi: CanonAbi, - ) -> InterpResult<'a, ()> { - if fn_abi.conv != exp_abi { - throw_ub_format!( - r#"calling a function with calling convention "{exp_abi}" using caller calling convention "{}""#, - fn_abi.conv - ); - } - interp_ok(()) - } - fn frame_in_std(&self) -> bool { let this = self.eval_context_ref(); let frame = this.frame(); @@ -967,162 +952,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { crate_name == "std" || crate_name == "std_miri_test" } - fn check_abi_and_shim_symbol_clash( - &mut self, - abi: &FnAbi<'tcx, Ty<'tcx>>, - exp_abi: CanonAbi, - link_name: Symbol, - ) -> InterpResult<'tcx, ()> { - self.check_callconv(abi, exp_abi)?; - if let Some((body, instance)) = self.eval_context_mut().lookup_exported_symbol(link_name)? { - // If compiler-builtins is providing the symbol, then don't treat it as a clash. - // We'll use our built-in implementation in `emulate_foreign_item_inner` for increased - // performance. Note that this means we won't catch any undefined behavior in - // compiler-builtins when running other crates, but Miri can still be run on - // compiler-builtins itself (or any crate that uses it as a normal dependency) - if self.eval_context_ref().tcx.is_compiler_builtins(instance.def_id().krate) { - return interp_ok(()); - } - - throw_machine_stop!(TerminationInfo::SymbolShimClashing { - link_name, - span: body.span.data(), - }) - } - interp_ok(()) - } - - fn check_shim<'a, const N: usize>( - &mut self, - abi: &FnAbi<'tcx, Ty<'tcx>>, - exp_abi: CanonAbi, - link_name: Symbol, - args: &'a [OpTy<'tcx>], - ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> { - self.check_abi_and_shim_symbol_clash(abi, exp_abi, link_name)?; - - if abi.c_variadic { - throw_ub_format!( - "calling a non-variadic function with a variadic caller-side signature" - ); - } - if let Ok(ops) = args.try_into() { - return interp_ok(ops); - } - throw_ub_format!( - "incorrect number of arguments for `{link_name}`: got {}, expected {}", - args.len(), - N - ) - } - - /// Check that the given `caller_fn_abi` matches the expected ABI described by - /// `callee_abi`, `callee_input_tys`, `callee_output_ty`, and then returns the list of - /// arguments. - fn check_shim_abi<'a, const N: usize>( - &mut self, - link_name: Symbol, - caller_fn_abi: &FnAbi<'tcx, Ty<'tcx>>, - callee_abi: ExternAbi, - callee_input_tys: [Ty<'tcx>; N], - callee_output_ty: Ty<'tcx>, - caller_args: &'a [OpTy<'tcx>], - ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> { - let this = self.eval_context_mut(); - let mut inputs_and_output = callee_input_tys.to_vec(); - inputs_and_output.push(callee_output_ty); - let fn_sig_binder = Binder::dummy(FnSig { - inputs_and_output: this.machine.tcx.mk_type_list(&inputs_and_output), - c_variadic: false, - // This does not matter for the ABI. - safety: Safety::Safe, - abi: callee_abi, - }); - let callee_fn_abi = this.fn_abi_of_fn_ptr(fn_sig_binder, Default::default())?; - - this.check_abi_and_shim_symbol_clash(caller_fn_abi, callee_fn_abi.conv, link_name)?; - - if caller_fn_abi.c_variadic { - throw_ub_format!( - "ABI mismatch: calling a non-variadic function with a variadic caller-side signature" - ); - } - - if callee_fn_abi.fixed_count != caller_fn_abi.fixed_count { - throw_ub_format!( - "ABI mismatch: expected {} arguments, found {} arguments ", - callee_fn_abi.fixed_count, - caller_fn_abi.fixed_count - ); - } - - if callee_fn_abi.can_unwind && !caller_fn_abi.can_unwind { - throw_ub_format!( - "ABI mismatch: callee may unwind, but caller-side signature prohibits unwinding", - ); - } - - if !this.check_argument_compat(&caller_fn_abi.ret, &callee_fn_abi.ret)? { - throw_ub!(AbiMismatchReturn { - caller_ty: caller_fn_abi.ret.layout.ty, - callee_ty: callee_fn_abi.ret.layout.ty - }); - } - - if let Some(index) = caller_fn_abi - .args - .iter() - .zip(callee_fn_abi.args.iter()) - .map(|(caller_arg, callee_arg)| this.check_argument_compat(caller_arg, callee_arg)) - .collect::>>()? - .into_iter() - .position(|b| !b) - { - throw_ub!(AbiMismatchArgument { - arg_idx: index, - caller_ty: caller_fn_abi.args[index].layout.ty, - callee_ty: callee_fn_abi.args[index].layout.ty - }); - } - - if let Ok(ops) = caller_args.try_into() { - return interp_ok(ops); - } - unreachable!() - } - - /// Check shim for variadic function. - /// Returns a tuple that consisting of an array of fixed args, and a slice of varargs. - fn check_shim_variadic<'a, const N: usize>( - &mut self, - abi: &FnAbi<'tcx, Ty<'tcx>>, - exp_abi: CanonAbi, - link_name: Symbol, - args: &'a [OpTy<'tcx>], - ) -> InterpResult<'tcx, (&'a [OpTy<'tcx>; N], &'a [OpTy<'tcx>])> - where - &'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>, - { - self.check_abi_and_shim_symbol_clash(abi, exp_abi, link_name)?; - - if !abi.c_variadic { - throw_ub_format!( - "calling a variadic function with a non-variadic caller-side signature" - ); - } - if abi.fixed_count != u32::try_from(N).unwrap() { - throw_ub_format!( - "incorrect number of fixed arguments for variadic function `{}`: got {}, expected {N}", - link_name.as_str(), - abi.fixed_count - ) - } - if let Some(args) = args.split_first_chunk() { - return interp_ok(args); - } - panic!("mismatch between signature and `args` slice"); - } - /// Mark a machine allocation that was just created as immutable. fn mark_immutable(&mut self, mplace: &MPlaceTy<'tcx>) { let this = self.eval_context_mut(); @@ -1258,8 +1087,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "failed to evaluate static in required link_section: {def_id:?}\n{err:?}" ) }); - let val = this.read_immediate(&const_val)?; - array.push(val); + match const_val.layout.ty.kind() { + ty::FnPtr(..) => { + array.push(this.read_immediate(&const_val)?); + } + ty::Array(elem_ty, _) if matches!(elem_ty.kind(), ty::FnPtr(..)) => { + let mut elems = this.project_array_fields(&const_val)?; + while let Some((_idx, elem)) = elems.next(this)? { + array.push(this.read_immediate(&elem)?); + } + } + _ => + throw_unsup_format!( + "only function pointers and arrays of function pointers are supported in well-known linker sections" + ), + } } interp_ok(()) })?; @@ -1318,39 +1160,6 @@ impl<'tcx> MiriMachine<'tcx> { } } -/// Check that the number of args is what we expect. -pub fn check_intrinsic_arg_count<'a, 'tcx, const N: usize>( - args: &'a [OpTy<'tcx>], -) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> -where - &'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>, -{ - if let Ok(ops) = args.try_into() { - return interp_ok(ops); - } - throw_ub_format!( - "incorrect number of arguments for intrinsic: got {}, expected {}", - args.len(), - N - ) -} - -/// Check that the number of varargs is at least the minimum what we expect. -/// Fixed args should not be included. -pub fn check_min_vararg_count<'a, 'tcx, const N: usize>( - name: &'a str, - args: &'a [OpTy<'tcx>], -) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> { - if let Some((ops, _)) = args.split_first_chunk() { - return interp_ok(ops); - } - throw_ub_format!( - "not enough variadic arguments for `{name}`: got {}, expected at least {}", - args.len(), - N - ) -} - pub fn isolation_abort_error<'tcx>(name: &str) -> InterpResult<'tcx> { throw_machine_stop!(TerminationInfo::UnsupportedInIsolation(format!( "{name} not available when isolation is enabled", @@ -1466,7 +1275,7 @@ pub struct MaybeEnteredTraceSpan { #[macro_export] macro_rules! enter_trace_span { ($name:ident :: $subname:ident $($tt:tt)*) => {{ - enter_trace_span!(stringify!($name), $name = %stringify!(subname) $($tt)*) + enter_trace_span!(stringify!($name), $name = %stringify!($subname) $($tt)*) }}; ($($tt:tt)*) => { diff --git a/src/tools/miri/src/intrinsics/atomic.rs b/src/tools/miri/src/intrinsics/atomic.rs index 0a59a707a101c..bcc3e9ec885fd 100644 --- a/src/tools/miri/src/intrinsics/atomic.rs +++ b/src/tools/miri/src/intrinsics/atomic.rs @@ -2,7 +2,7 @@ use rustc_middle::mir::BinOp; use rustc_middle::ty::AtomicOrdering; use rustc_middle::{mir, ty}; -use self::helpers::check_intrinsic_arg_count; +use super::check_intrinsic_arg_count; use crate::*; pub enum AtomicOp { diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index 4efa7dd4dcf81..b5e81460773ba 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -14,11 +14,28 @@ use rustc_middle::ty::{self, FloatTy, ScalarInt}; use rustc_span::{Symbol, sym}; use self::atomic::EvalContextExt as _; -use self::helpers::{ToHost, ToSoft, check_intrinsic_arg_count}; +use self::helpers::{ToHost, ToSoft}; use self::simd::EvalContextExt as _; use crate::math::{IeeeExt, apply_random_float_error_ulp}; use crate::*; +/// Check that the number of args is what we expect. +fn check_intrinsic_arg_count<'a, 'tcx, const N: usize>( + args: &'a [OpTy<'tcx>], +) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> +where + &'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>, +{ + if let Ok(ops) = args.try_into() { + return interp_ok(ops); + } + throw_ub_format!( + "incorrect number of arguments for intrinsic: got {}, expected {}", + args.len(), + N + ) +} + impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn call_intrinsic( @@ -114,7 +131,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )); } "catch_unwind" => { - this.handle_catch_unwind(args, dest, ret)?; + let [try_fn, data, catch_fn] = check_intrinsic_arg_count(args)?; + this.handle_catch_unwind(try_fn, data, catch_fn, dest, ret)?; // This pushed a stack frame, don't jump to `ret`. return interp_ok(EmulateItemResult::AlreadyJumped); } diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs index e63992aa95f9d..b26516c0ff0e8 100644 --- a/src/tools/miri/src/intrinsics/simd.rs +++ b/src/tools/miri/src/intrinsics/simd.rs @@ -6,9 +6,8 @@ use rustc_middle::ty::FloatTy; use rustc_middle::{mir, ty}; use rustc_span::{Symbol, sym}; -use crate::helpers::{ - ToHost, ToSoft, bool_to_simd_element, check_intrinsic_arg_count, simd_element_to_bool, -}; +use super::check_intrinsic_arg_count; +use crate::helpers::{ToHost, ToSoft, bool_to_simd_element, simd_element_to_bool}; use crate::*; #[derive(Copy, Clone)] diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index ae70257653c8a..507d4f7b42896 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -7,6 +7,7 @@ #![feature(never_type)] #![feature(try_blocks)] #![feature(io_error_more)] +#![feature(if_let_guard)] #![feature(variant_count)] #![feature(yeet_expr)] #![feature(nonzero_ops)] @@ -158,6 +159,7 @@ pub use crate::shims::foreign_items::{DynSym, EvalContextExt as _}; pub use crate::shims::io_error::{EvalContextExt as _, IoError, LibcError}; pub use crate::shims::os_str::EvalContextExt as _; pub use crate::shims::panic::EvalContextExt as _; +pub use crate::shims::sig::EvalContextExt as _; pub use crate::shims::time::EvalContextExt as _; pub use crate::shims::tls::TlsData; pub use crate::shims::unwind::{CatchUnwindData, EvalContextExt as _}; diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 7271d3f619c75..8f0814a070cbf 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -76,13 +76,8 @@ pub struct FrameExtra<'tcx> { impl<'tcx> std::fmt::Debug for FrameExtra<'tcx> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // Omitting `timing`, it does not support `Debug`. - let FrameExtra { - borrow_tracker, - catch_unwind, - timing: _, - is_user_relevant, - data_race, - } = self; + let FrameExtra { borrow_tracker, catch_unwind, timing: _, is_user_relevant, data_race } = + self; f.debug_struct("FrameData") .field("borrow_tracker", borrow_tracker) .field("catch_unwind", catch_unwind) @@ -607,6 +602,9 @@ pub struct MiriMachine<'tcx> { } impl<'tcx> MiriMachine<'tcx> { + /// Create a new MiriMachine. + /// + /// Invariant: `genmc_ctx.is_some() == config.genmc_config.is_some()` pub(crate) fn new( config: &MiriConfig, layout_cx: LayoutCx<'tcx>, @@ -630,7 +628,7 @@ impl<'tcx> MiriMachine<'tcx> { }); let rng = StdRng::seed_from_u64(config.seed.unwrap_or(0)); let borrow_tracker = config.borrow_tracker.map(|bt| bt.instantiate_global_state(config)); - let data_race = if config.genmc_mode { + let data_race = if config.genmc_config.is_some() { // `genmc_ctx` persists across executions, so we don't create a new one here. GlobalDataRaceHandler::Genmc(genmc_ctx.unwrap()) } else if config.data_race_detector { diff --git a/src/tools/miri/src/shims/aarch64.rs b/src/tools/miri/src/shims/aarch64.rs index 44ad5081ad571..6e422b4ab716a 100644 --- a/src/tools/miri/src/shims/aarch64.rs +++ b/src/tools/miri/src/shims/aarch64.rs @@ -20,7 +20,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let unprefixed_name = link_name.as_str().strip_prefix("llvm.aarch64.").unwrap(); match unprefixed_name { "isb" => { - let [arg] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [arg] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let arg = this.read_scalar(arg)?.to_i32()?; match arg { // SY ("full system scope") @@ -38,7 +38,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // `left` input, the second half of the output from the `right` input. // https://developer.arm.com/architectures/instruction-sets/intrinsics/vpmaxq_u8 "neon.umaxp.v16i8" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (right, right_len) = this.project_to_simd(right)?; diff --git a/src/tools/miri/src/shims/backtrace.rs b/src/tools/miri/src/shims/backtrace.rs index 18d60915d2059..bd3914b652ac9 100644 --- a/src/tools/miri/src/shims/backtrace.rs +++ b/src/tools/miri/src/shims/backtrace.rs @@ -15,7 +15,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let [flags] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + let [flags] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let flags = this.read_scalar(flags)?.to_u64()?; if flags != 0 { @@ -37,7 +37,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let ptr_ty = this.machine.layouts.mut_raw_ptr.ty; let ptr_layout = this.layout_of(ptr_ty)?; - let [flags, buf] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + let [flags, buf] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let flags = this.read_scalar(flags)?.to_u64()?; let buf_place = this.deref_pointer_as(buf, ptr_layout)?; @@ -117,7 +117,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let [ptr, flags] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + let [ptr, flags] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let flags = this.read_scalar(flags)?.to_u64()?; @@ -195,7 +195,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let [ptr, flags, name_ptr, filename_ptr] = - this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let flags = this.read_scalar(flags)?.to_u64()?; if flags != 0 { diff --git a/src/tools/miri/src/shims/files.rs b/src/tools/miri/src/shims/files.rs index 606d1ffbea6c0..0d4642c6ad0ef 100644 --- a/src/tools/miri/src/shims/files.rs +++ b/src/tools/miri/src/shims/files.rs @@ -1,7 +1,7 @@ use std::any::Any; use std::collections::BTreeMap; use std::fs::{File, Metadata}; -use std::io::{IsTerminal, Seek, SeekFrom, Write}; +use std::io::{ErrorKind, IsTerminal, Seek, SeekFrom, Write}; use std::marker::CoercePointee; use std::ops::Deref; use std::rc::{Rc, Weak}; @@ -167,6 +167,11 @@ pub trait FileDescription: std::fmt::Debug + FileDescriptionExt { throw_unsup_format!("cannot write to {}", self.name()); } + /// Determines whether this FD non-deterministically has its reads and writes shortened. + fn nondet_short_accesses(&self) -> bool { + true + } + /// Seeks to the given offset (which can be relative to the beginning, end, or current position). /// Returns the new position from the start of the stream. fn seek<'tcx>( @@ -334,6 +339,15 @@ impl FileDescription for FileHandle { ) -> InterpResult<'tcx> { assert!(communicate_allowed, "isolation should have prevented even opening a file"); + if !self.writable { + // Linux hosts return EBADF here which we can't translate via the platform-independent + // code since it does not map to any `io::ErrorKind` -- so if we don't do anything + // special, we'd throw an "unsupported error code" here. Windows returns something that + // gets translated to `PermissionDenied`. That seems like a good value so let's just use + // this everywhere, even if it means behavior on Unix targets does not match the real + // thing. + return finish.call(ecx, Err(ErrorKind::PermissionDenied.into())); + } let result = ecx.write_to_host(&self.file, len, ptr)?; finish.call(ecx, result) } diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 94cda57658a13..21545b680299c 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -288,16 +288,17 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { match link_name.as_str() { // Miri-specific extern functions "miri_start_unwind" => { - let [payload] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + let [payload] = + this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; this.handle_miri_start_unwind(payload)?; return interp_ok(EmulateItemResult::NeedsUnwind); } "miri_run_provenance_gc" => { - let [] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; this.run_provenance_gc(); } "miri_get_alloc_id" => { - let [ptr] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let ptr = this.read_pointer(ptr)?; let (alloc_id, _, _) = this.ptr_get_alloc_id(ptr, 0).map_err_kind(|_e| { err_machine_stop!(TerminationInfo::Abort(format!( @@ -307,7 +308,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Scalar::from_u64(alloc_id.0.get()), dest)?; } "miri_print_borrow_state" => { - let [id, show_unnamed] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + let [id, show_unnamed] = + this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let id = this.read_scalar(id)?.to_u64()?; let show_unnamed = this.read_scalar(show_unnamed)?.to_bool()?; if let Some(id) = std::num::NonZero::new(id).map(AllocId) @@ -322,7 +324,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // This associates a name to a tag. Very useful for debugging, and also makes // tests more strict. let [ptr, nth_parent, name] = - this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let ptr = this.read_pointer(ptr)?; let nth_parent = this.read_scalar(nth_parent)?.to_u8()?; let name = this.read_immediate(name)?; @@ -335,7 +337,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.give_pointer_debug_name(ptr, nth_parent, &name)?; } "miri_static_root" => { - let [ptr] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let ptr = this.read_pointer(ptr)?; let (alloc_id, offset, _) = this.ptr_get_alloc_id(ptr, 0)?; if offset != Size::ZERO { @@ -346,7 +348,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.machine.static_roots.push(alloc_id); } "miri_host_to_target_path" => { - let [ptr, out, out_size] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + let [ptr, out, out_size] = + this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let ptr = this.read_pointer(ptr)?; let out = this.read_pointer(out)?; let out_size = this.read_scalar(out_size)?.to_target_usize(this)?; @@ -382,7 +385,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Writes some bytes to the interpreter's stdout/stderr. See the // README for details. "miri_write_to_stdout" | "miri_write_to_stderr" => { - let [msg] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + let [msg] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let msg = this.read_immediate(msg)?; let msg = this.read_byte_slice(&msg)?; // Note: we're ignoring errors writing to host stdout/stderr. @@ -396,7 +399,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { "miri_promise_symbolic_alignment" => { use rustc_abi::AlignFromBytesError; - let [ptr, align] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + let [ptr, align] = + this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let ptr = this.read_pointer(ptr)?; let align = this.read_target_usize(align)?; if !align.is_power_of_two() { @@ -437,12 +441,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Aborting the process. "exit" => { - let [code] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [code] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let code = this.read_scalar(code)?.to_i32()?; throw_machine_stop!(TerminationInfo::Exit { code, leak_check: false }); } "abort" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; throw_machine_stop!(TerminationInfo::Abort( "the program aborted execution".to_owned() )) @@ -450,7 +454,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Standard C allocation "malloc" => { - let [size] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [size] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let size = this.read_target_usize(size)?; if size <= this.max_size_of_val().bytes() { let res = this.malloc(size, AllocInit::Uninit)?; @@ -464,7 +468,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } } "calloc" => { - let [items, elem_size] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [items, elem_size] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let items = this.read_target_usize(items)?; let elem_size = this.read_target_usize(elem_size)?; if let Some(size) = this.compute_size_in_bytes(Size::from_bytes(elem_size), items) { @@ -479,12 +484,13 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } } "free" => { - let [ptr] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let ptr = this.read_pointer(ptr)?; this.free(ptr)?; } "realloc" => { - let [old_ptr, new_size] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [old_ptr, new_size] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let old_ptr = this.read_pointer(old_ptr)?; let new_size = this.read_target_usize(new_size)?; if new_size <= this.max_size_of_val().bytes() { @@ -504,7 +510,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let default = |ecx: &mut MiriInterpCx<'tcx>| { // Only call `check_shim` when `#[global_allocator]` isn't used. When that // macro is used, we act like no shim exists, so that the exported function can run. - let [size, align] = ecx.check_shim(abi, CanonAbi::Rust, link_name, args)?; + let [size, align] = + ecx.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let size = ecx.read_target_usize(size)?; let align = ecx.read_target_usize(align)?; @@ -537,7 +544,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.emulate_allocator(|this| { // See the comment for `__rust_alloc` why `check_shim` is only called in the // default case. - let [size, align] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + let [size, align] = + this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let size = this.read_target_usize(size)?; let align = this.read_target_usize(align)?; @@ -559,7 +567,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // See the comment for `__rust_alloc` why `check_shim` is only called in the // default case. let [ptr, old_size, align] = - ecx.check_shim(abi, CanonAbi::Rust, link_name, args)?; + ecx.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let ptr = ecx.read_pointer(ptr)?; let old_size = ecx.read_target_usize(old_size)?; let align = ecx.read_target_usize(align)?; @@ -590,7 +598,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // See the comment for `__rust_alloc` why `check_shim` is only called in the // default case. let [ptr, old_size, align, new_size] = - this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let ptr = this.read_pointer(ptr)?; let old_size = this.read_target_usize(old_size)?; let align = this.read_target_usize(align)?; @@ -613,20 +621,21 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } name if name == this.mangle_internal_symbol("__rust_no_alloc_shim_is_unstable_v2") => { // This is a no-op shim that only exists to prevent making the allocator shims instantly stable. - let [] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; } name if name == this.mangle_internal_symbol("__rust_alloc_error_handler_should_panic_v2") => { // Gets the value of the `oom` option. - let [] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let val = this.tcx.sess.opts.unstable_opts.oom.should_panic(); this.write_int(val, dest)?; } // C memory handling functions "memcmp" => { - let [left, right, n] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, n] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let left = this.read_pointer(left)?; let right = this.read_pointer(right)?; let n = Size::from_bytes(this.read_target_usize(n)?); @@ -650,7 +659,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Scalar::from_i32(result), dest)?; } "memrchr" => { - let [ptr, val, num] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [ptr, val, num] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let ptr = this.read_pointer(ptr)?; let val = this.read_scalar(val)?.to_i32()?; let num = this.read_target_usize(num)?; @@ -676,7 +686,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } } "memchr" => { - let [ptr, val, num] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [ptr, val, num] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let ptr = this.read_pointer(ptr)?; let val = this.read_scalar(val)?.to_i32()?; let num = this.read_target_usize(num)?; @@ -699,7 +710,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } } "strlen" => { - let [ptr] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let ptr = this.read_pointer(ptr)?; // This reads at least 1 byte, so we are already enforcing that this is a valid pointer. let n = this.read_c_str(ptr)?.len(); @@ -709,7 +720,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; } "wcslen" => { - let [ptr] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let ptr = this.read_pointer(ptr)?; // This reads at least 1 byte, so we are already enforcing that this is a valid pointer. let n = this.read_wchar_t_str(ptr)?.len(); @@ -719,7 +730,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; } "memcpy" => { - let [ptr_dest, ptr_src, n] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [ptr_dest, ptr_src, n] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let ptr_dest = this.read_pointer(ptr_dest)?; let ptr_src = this.read_pointer(ptr_src)?; let n = this.read_target_usize(n)?; @@ -733,7 +745,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_pointer(ptr_dest, dest)?; } "strcpy" => { - let [ptr_dest, ptr_src] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [ptr_dest, ptr_src] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let ptr_dest = this.read_pointer(ptr_dest)?; let ptr_src = this.read_pointer(ptr_src)?; @@ -764,7 +777,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { | "erff" | "erfcf" => { - let [f] = this.check_shim(abi, CanonAbi::C , link_name, args)?; + let [f] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; let f = this.read_scalar(f)?.to_f32()?; // Using host floats (but it's fine, these operations do not have guaranteed precision). let f_host = f.to_host(); @@ -802,7 +815,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { | "atan2f" | "fdimf" => { - let [f1, f2] = this.check_shim(abi, CanonAbi::C , link_name, args)?; + let [f1, f2] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; let f1 = this.read_scalar(f1)?.to_f32()?; let f2 = this.read_scalar(f2)?.to_f32()?; // underscore case for windows, here and below @@ -841,7 +854,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { | "erf" | "erfc" => { - let [f] = this.check_shim(abi, CanonAbi::C , link_name, args)?; + let [f] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; let f = this.read_scalar(f)?.to_f64()?; // Using host floats (but it's fine, these operations do not have guaranteed precision). let f_host = f.to_host(); @@ -879,7 +892,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { | "atan2" | "fdim" => { - let [f1, f2] = this.check_shim(abi, CanonAbi::C , link_name, args)?; + let [f1, f2] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; let f1 = this.read_scalar(f1)?.to_f64()?; let f2 = this.read_scalar(f2)?.to_f64()?; // underscore case for windows, here and below @@ -908,7 +921,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { | "ldexp" | "scalbn" => { - let [x, exp] = this.check_shim(abi, CanonAbi::C , link_name, args)?; + let [x, exp] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; // For radix-2 (binary) systems, `ldexp` and `scalbn` are the same. let x = this.read_scalar(x)?.to_f64()?; let exp = this.read_scalar(exp)?.to_i32()?; @@ -918,7 +931,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } "lgammaf_r" => { - let [x, signp] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [x, signp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let x = this.read_scalar(x)?.to_f32()?; let signp = this.deref_pointer_as(signp, this.machine.layouts.i32)?; @@ -934,7 +947,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } "lgamma_r" => { - let [x, signp] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [x, signp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let x = this.read_scalar(x)?.to_f64()?; let signp = this.deref_pointer_as(signp, this.machine.layouts.i32)?; @@ -952,7 +965,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // LLVM intrinsics "llvm.prefetch" => { - let [p, rw, loc, ty] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [p, rw, loc, ty] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let _ = this.read_pointer(p)?; let rw = this.read_scalar(rw)?.to_i32()?; @@ -979,7 +993,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Used to implement the x86 `_mm{,256,512}_popcnt_epi{8,16,32,64}` and wasm // `{i,u}8x16_popcnt` functions. name if name.starts_with("llvm.ctpop.v") => { - let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (op, op_len) = this.project_to_simd(op)?; let (dest, dest_len) = this.project_to_simd(dest)?; @@ -1015,7 +1029,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } // FIXME: Move this to an `arm` submodule. "llvm.arm.hint" if this.tcx.sess.target.arch == "arm" => { - let [arg] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [arg] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let arg = this.read_scalar(arg)?.to_i32()?; // Note that different arguments might have different target feature requirements. match arg { diff --git a/src/tools/miri/src/shims/mod.rs b/src/tools/miri/src/shims/mod.rs index 2a7709829ee6b..7f594d4fdd6bc 100644 --- a/src/tools/miri/src/shims/mod.rs +++ b/src/tools/miri/src/shims/mod.rs @@ -18,6 +18,7 @@ pub mod global_ctor; pub mod io_error; pub mod os_str; pub mod panic; +pub mod sig; pub mod time; pub mod tls; pub mod unwind; diff --git a/src/tools/miri/src/shims/sig.rs b/src/tools/miri/src/shims/sig.rs new file mode 100644 index 0000000000000..bc5e7f584f500 --- /dev/null +++ b/src/tools/miri/src/shims/sig.rs @@ -0,0 +1,266 @@ +//! Everything related to checking the signature of shim invocations. + +use rustc_abi::{CanonAbi, ExternAbi}; +use rustc_hir::Safety; +use rustc_middle::ty::{Binder, FnSig, Ty}; +use rustc_span::Symbol; +use rustc_target::callconv::FnAbi; + +use crate::*; + +/// Describes the expected signature of a shim. +pub struct ShimSig<'tcx, const ARGS: usize> { + pub abi: ExternAbi, + pub args: [Ty<'tcx>; ARGS], + pub ret: Ty<'tcx>, +} + +/// Construct a `ShimSig` with convenient syntax: +/// ```rust,ignore +/// shim_sig!(this, extern "C" fn (*const T, i32) -> usize) +/// ``` +#[macro_export] +macro_rules! shim_sig { + (extern $abi:literal fn($($arg:ty),*) -> $ret:ty) => { + |this| $crate::shims::sig::ShimSig { + abi: std::str::FromStr::from_str($abi).expect("incorrect abi specified"), + args: [$(shim_sig_arg!(this, $arg)),*], + ret: shim_sig_arg!(this, $ret), + } + }; +} + +/// Helper for `shim_sig!`. +#[macro_export] +macro_rules! shim_sig_arg { + // Unfortuantely we cannot take apart a `ty`-typed token at compile time, + // so we have to stringify it and match at runtime. + ($this:ident, $x:ty) => {{ + match stringify!($x) { + "i8" => $this.tcx.types.i8, + "i16" => $this.tcx.types.i16, + "i32" => $this.tcx.types.i32, + "i64" => $this.tcx.types.i64, + "i128" => $this.tcx.types.i128, + "isize" => $this.tcx.types.isize, + "u8" => $this.tcx.types.u8, + "u16" => $this.tcx.types.u16, + "u32" => $this.tcx.types.u32, + "u64" => $this.tcx.types.u64, + "u128" => $this.tcx.types.u128, + "usize" => $this.tcx.types.usize, + "()" => $this.tcx.types.unit, + "*const _" => $this.machine.layouts.const_raw_ptr.ty, + "*mut _" => $this.machine.layouts.mut_raw_ptr.ty, + ty if let Some(libc_ty) = ty.strip_prefix("libc::") => $this.libc_ty_layout(libc_ty).ty, + ty => panic!("unsupported signature type {ty:?}"), + } + }}; +} + +/// Helper function to compare two ABIs. +fn check_shim_abi<'tcx>( + this: &MiriInterpCx<'tcx>, + callee_abi: &FnAbi<'tcx, Ty<'tcx>>, + caller_abi: &FnAbi<'tcx, Ty<'tcx>>, +) -> InterpResult<'tcx> { + if callee_abi.conv != caller_abi.conv { + throw_ub_format!( + r#"calling a function with calling convention "{callee}" using caller calling convention "{caller}""#, + callee = callee_abi.conv, + caller = caller_abi.conv, + ); + } + if callee_abi.can_unwind && !caller_abi.can_unwind { + throw_ub_format!( + "ABI mismatch: callee may unwind, but caller-side signature prohibits unwinding", + ); + } + if caller_abi.c_variadic && !callee_abi.c_variadic { + throw_ub_format!( + "ABI mismatch: calling a non-variadic function with a variadic caller-side signature" + ); + } + if !caller_abi.c_variadic && callee_abi.c_variadic { + throw_ub_format!( + "ABI mismatch: calling a variadic function with a non-variadic caller-side signature" + ); + } + + if callee_abi.fixed_count != caller_abi.fixed_count { + throw_ub_format!( + "ABI mismatch: expected {} arguments, found {} arguments ", + callee_abi.fixed_count, + caller_abi.fixed_count + ); + } + + if !this.check_argument_compat(&caller_abi.ret, &callee_abi.ret)? { + throw_ub!(AbiMismatchReturn { + caller_ty: caller_abi.ret.layout.ty, + callee_ty: callee_abi.ret.layout.ty + }); + } + + for (idx, (caller_arg, callee_arg)) in + caller_abi.args.iter().zip(callee_abi.args.iter()).enumerate() + { + if !this.check_argument_compat(caller_arg, callee_arg)? { + throw_ub!(AbiMismatchArgument { + arg_idx: idx, + caller_ty: caller_abi.args[idx].layout.ty, + callee_ty: callee_abi.args[idx].layout.ty + }); + } + } + + interp_ok(()) +} + +fn check_shim_symbol_clash<'tcx>( + this: &mut MiriInterpCx<'tcx>, + link_name: Symbol, +) -> InterpResult<'tcx, ()> { + if let Some((body, instance)) = this.lookup_exported_symbol(link_name)? { + // If compiler-builtins is providing the symbol, then don't treat it as a clash. + // We'll use our built-in implementation in `emulate_foreign_item_inner` for increased + // performance. Note that this means we won't catch any undefined behavior in + // compiler-builtins when running other crates, but Miri can still be run on + // compiler-builtins itself (or any crate that uses it as a normal dependency) + if this.tcx.is_compiler_builtins(instance.def_id().krate) { + return interp_ok(()); + } + + throw_machine_stop!(TerminationInfo::SymbolShimClashing { + link_name, + span: body.span.data(), + }) + } + interp_ok(()) +} + +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + fn check_shim_sig_lenient<'a, const N: usize>( + &mut self, + abi: &FnAbi<'tcx, Ty<'tcx>>, + exp_abi: CanonAbi, + link_name: Symbol, + args: &'a [OpTy<'tcx>], + ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> { + let this = self.eval_context_mut(); + check_shim_symbol_clash(this, link_name)?; + + if abi.conv != exp_abi { + throw_ub_format!( + r#"calling a function with calling convention "{exp_abi}" using caller calling convention "{}""#, + abi.conv + ); + } + if abi.c_variadic { + throw_ub_format!( + "calling a non-variadic function with a variadic caller-side signature" + ); + } + + if let Ok(ops) = args.try_into() { + return interp_ok(ops); + } + throw_ub_format!( + "incorrect number of arguments for `{link_name}`: got {}, expected {}", + args.len(), + N + ) + } + + /// Check that the given `caller_fn_abi` matches the expected ABI described by `shim_sig`, and + /// then returns the list of arguments. + fn check_shim_sig<'a, const N: usize>( + &mut self, + shim_sig: fn(&MiriInterpCx<'tcx>) -> ShimSig<'tcx, N>, + link_name: Symbol, + caller_fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + caller_args: &'a [OpTy<'tcx>], + ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> { + let this = self.eval_context_mut(); + let shim_sig = shim_sig(this); + + // Compute full callee ABI. + let mut inputs_and_output = Vec::with_capacity(N.strict_add(1)); + inputs_and_output.extend(&shim_sig.args); + inputs_and_output.push(shim_sig.ret); + let fn_sig_binder = Binder::dummy(FnSig { + inputs_and_output: this.machine.tcx.mk_type_list(&inputs_and_output), + c_variadic: false, + // This does not matter for the ABI. + safety: Safety::Safe, + abi: shim_sig.abi, + }); + let callee_fn_abi = this.fn_abi_of_fn_ptr(fn_sig_binder, Default::default())?; + + // Check everything. + check_shim_abi(this, callee_fn_abi, caller_fn_abi)?; + check_shim_symbol_clash(this, link_name)?; + + // Return arguments. + if let Ok(ops) = caller_args.try_into() { + return interp_ok(ops); + } + unreachable!() + } + + /// Check shim for variadic function. + /// Returns a tuple that consisting of an array of fixed args, and a slice of varargs. + fn check_shim_sig_variadic_lenient<'a, const N: usize>( + &mut self, + abi: &FnAbi<'tcx, Ty<'tcx>>, + exp_abi: CanonAbi, + link_name: Symbol, + args: &'a [OpTy<'tcx>], + ) -> InterpResult<'tcx, (&'a [OpTy<'tcx>; N], &'a [OpTy<'tcx>])> + where + &'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>, + { + let this = self.eval_context_mut(); + check_shim_symbol_clash(this, link_name)?; + + if abi.conv != exp_abi { + throw_ub_format!( + r#"calling a function with calling convention "{exp_abi}" using caller calling convention "{}""#, + abi.conv + ); + } + if !abi.c_variadic { + throw_ub_format!( + "calling a variadic function with a non-variadic caller-side signature" + ); + } + if abi.fixed_count != u32::try_from(N).unwrap() { + throw_ub_format!( + "incorrect number of fixed arguments for variadic function `{}`: got {}, expected {N}", + link_name.as_str(), + abi.fixed_count + ) + } + if let Some(args) = args.split_first_chunk() { + return interp_ok(args); + } + panic!("mismatch between signature and `args` slice"); + } +} + +/// Check that the number of varargs is at least the minimum what we expect. +/// Fixed args should not be included. +pub fn check_min_vararg_count<'a, 'tcx, const N: usize>( + name: &'a str, + args: &'a [OpTy<'tcx>], +) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> { + if let Some((ops, _)) = args.split_first_chunk() { + return interp_ok(ops); + } + throw_ub_format!( + "not enough variadic arguments for `{name}`: got {}, expected at least {}", + args.len(), + N + ) +} diff --git a/src/tools/miri/src/shims/time.rs b/src/tools/miri/src/shims/time.rs index eb21abc2a455a..b5b35797fec2f 100644 --- a/src/tools/miri/src/shims/time.rs +++ b/src/tools/miri/src/shims/time.rs @@ -17,73 +17,71 @@ pub fn system_time_to_duration<'tcx>(time: &SystemTime) -> InterpResult<'tcx, Du impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { - fn clock_gettime( - &mut self, - clk_id_op: &OpTy<'tcx>, - tp_op: &OpTy<'tcx>, - dest: &MPlaceTy<'tcx>, - ) -> InterpResult<'tcx> { + fn parse_clockid(&self, clk_id: Scalar) -> Option { // This clock support is deliberately minimal because a lot of clock types have fiddly // properties (is it possible for Miri to be suspended independently of the host?). If you // have a use for another clock type, please open an issue. + let this = self.eval_context_ref(); - let this = self.eval_context_mut(); - - this.assert_target_os_is_unix("clock_gettime"); - let clockid_t_size = this.libc_ty_layout("clockid_t").size; - - let clk_id = this.read_scalar(clk_id_op)?.to_int(clockid_t_size)?; - let tp = this.deref_pointer_as(tp_op, this.libc_ty_layout("timespec"))?; - - let absolute_clocks; - let mut relative_clocks; + // Portable names that exist everywhere. + if clk_id == this.eval_libc("CLOCK_REALTIME") { + return Some(TimeoutClock::RealTime); + } else if clk_id == this.eval_libc("CLOCK_MONOTONIC") { + return Some(TimeoutClock::Monotonic); + } + // Some further platform-specific names we support. match this.tcx.sess.target.os.as_ref() { "linux" | "freebsd" | "android" => { - // Linux, Android, and FreeBSD have two main kinds of clocks. REALTIME clocks return the actual time since the - // Unix epoch, including effects which may cause time to move backwards such as NTP. // Linux further distinguishes regular and "coarse" clocks, but the "coarse" version - // is just specified to be "faster and less precise", so we implement both the same way. - absolute_clocks = vec![ - this.eval_libc("CLOCK_REALTIME").to_int(clockid_t_size)?, - this.eval_libc("CLOCK_REALTIME_COARSE").to_int(clockid_t_size)?, - ]; - // The second kind is MONOTONIC clocks for which 0 is an arbitrary time point, but they are - // never allowed to go backwards. We don't need to do any additional monotonicity - // enforcement because std::time::Instant already guarantees that it is monotonic. - relative_clocks = vec![ - this.eval_libc("CLOCK_MONOTONIC").to_int(clockid_t_size)?, - this.eval_libc("CLOCK_MONOTONIC_COARSE").to_int(clockid_t_size)?, - ]; + // is just specified to be "faster and less precise", so we treat it like normal + // clocks. + if clk_id == this.eval_libc("CLOCK_REALTIME_COARSE") { + return Some(TimeoutClock::RealTime); + } else if clk_id == this.eval_libc("CLOCK_MONOTONIC_COARSE") { + return Some(TimeoutClock::Monotonic); + } } "macos" => { - absolute_clocks = vec![this.eval_libc("CLOCK_REALTIME").to_int(clockid_t_size)?]; - relative_clocks = vec![this.eval_libc("CLOCK_MONOTONIC").to_int(clockid_t_size)?]; // `CLOCK_UPTIME_RAW` supposed to not increment while the system is asleep... but // that's not really something a program running inside Miri can tell, anyway. // We need to support it because std uses it. - relative_clocks.push(this.eval_libc("CLOCK_UPTIME_RAW").to_int(clockid_t_size)?); - } - "solaris" | "illumos" => { - // The REALTIME clock returns the actual time since the Unix epoch. - absolute_clocks = vec![this.eval_libc("CLOCK_REALTIME").to_int(clockid_t_size)?]; - // MONOTONIC, in the other hand, is the high resolution, non-adjustable - // clock from an arbitrary time in the past. - // Note that the man page mentions HIGHRES but it is just - // an alias of MONOTONIC and the libc crate does not expose it anyway. - // https://docs.oracle.com/cd/E23824_01/html/821-1465/clock-gettime-3c.html - relative_clocks = vec![this.eval_libc("CLOCK_MONOTONIC").to_int(clockid_t_size)?]; + if clk_id == this.eval_libc("CLOCK_UPTIME_RAW") { + return Some(TimeoutClock::Monotonic); + } } - target => throw_unsup_format!("`clock_gettime` is not supported on target OS {target}"), + _ => {} } - let duration = if absolute_clocks.contains(&clk_id) { - this.check_no_isolation("`clock_gettime` with `REALTIME` clocks")?; - system_time_to_duration(&SystemTime::now())? - } else if relative_clocks.contains(&clk_id) { - this.machine.monotonic_clock.now().duration_since(this.machine.monotonic_clock.epoch()) - } else { - return this.set_last_error_and_return(LibcError("EINVAL"), dest); + None + } + + fn clock_gettime( + &mut self, + clk_id_op: &OpTy<'tcx>, + tp_op: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + + this.assert_target_os_is_unix("clock_gettime"); + + let clk_id = this.read_scalar(clk_id_op)?; + let tp = this.deref_pointer_as(tp_op, this.libc_ty_layout("timespec"))?; + + let duration = match this.parse_clockid(clk_id) { + Some(TimeoutClock::RealTime) => { + this.check_no_isolation("`clock_gettime` with `REALTIME` clocks")?; + system_time_to_duration(&SystemTime::now())? + } + Some(TimeoutClock::Monotonic) => + this.machine + .monotonic_clock + .now() + .duration_since(this.machine.monotonic_clock.epoch()), + None => { + return this.set_last_error_and_return(LibcError("EINVAL"), dest); + } }; let tv_sec = duration.as_secs(); diff --git a/src/tools/miri/src/shims/unix/android/foreign_items.rs b/src/tools/miri/src/shims/unix/android/foreign_items.rs index 690b5295681d9..04c5d28838bbd 100644 --- a/src/tools/miri/src/shims/unix/android/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/android/foreign_items.rs @@ -26,29 +26,30 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match link_name.as_str() { // epoll, eventfd "epoll_create1" => { - let [flag] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [flag] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.epoll_create1(flag)?; this.write_scalar(result, dest)?; } "epoll_ctl" => { - let [epfd, op, fd, event] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [epfd, op, fd, event] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.epoll_ctl(epfd, op, fd, event)?; this.write_scalar(result, dest)?; } "epoll_wait" => { let [epfd, events, maxevents, timeout] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.epoll_wait(epfd, events, maxevents, timeout, dest)?; } "eventfd" => { - let [val, flag] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [val, flag] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.eventfd(val, flag)?; this.write_scalar(result, dest)?; } // Miscellaneous "__errno" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let errno_place = this.last_error_place()?; this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?; } diff --git a/src/tools/miri/src/shims/unix/android/thread.rs b/src/tools/miri/src/shims/unix/android/thread.rs index 5d17d6c8517b4..4e7b21d7d9443 100644 --- a/src/tools/miri/src/shims/unix/android/thread.rs +++ b/src/tools/miri/src/shims/unix/android/thread.rs @@ -3,7 +3,7 @@ use rustc_middle::ty::Ty; use rustc_span::Symbol; use rustc_target::callconv::FnAbi; -use crate::helpers::check_min_vararg_count; +use crate::shims::sig::check_min_vararg_count; use crate::shims::unix::thread::{EvalContextExt as _, ThreadNameResult}; use crate::*; @@ -16,7 +16,7 @@ pub fn prctl<'tcx>( args: &[OpTy<'tcx>], dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { - let ([op], varargs) = ecx.check_shim_variadic(abi, CanonAbi::C, link_name, args)?; + let ([op], varargs) = ecx.check_shim_sig_variadic_lenient(abi, CanonAbi::C, link_name, args)?; // FIXME: Use constants once https://github.com/rust-lang/libc/pull/3941 backported to the 0.2 branch. let pr_set_name = 15; diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index 71102d9f2f33f..e226a55d8b1ee 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -4,10 +4,11 @@ use std::io; use std::io::ErrorKind; +use rand::Rng; use rustc_abi::Size; -use crate::helpers::check_min_vararg_count; use crate::shims::files::FileDescription; +use crate::shims::sig::check_min_vararg_count; use crate::shims::unix::linux_like::epoll::EpollReadyEvents; use crate::shims::unix::*; use crate::*; @@ -263,9 +264,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_last_error_and_return(LibcError("EBADF"), dest); }; + // Non-deterministically decide to further reduce the count, simulating a partial read (but + // never to 0, that has different behavior). + let count = + if fd.nondet_short_accesses() && count >= 2 && this.machine.rng.get_mut().random() { + count / 2 + } else { + count + }; + trace!("read: FD mapped to {fd:?}"); // We want to read at most `count` bytes. We are sure that `count` is not negative - // because it was a target's `usize`. Also we are sure that its smaller than + // because it was a target's `usize`. Also we are sure that it's smaller than // `usize::MAX` because it is bounded by the host's `isize`. let finish = { @@ -328,6 +338,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_last_error_and_return(LibcError("EBADF"), dest); }; + // Non-deterministically decide to further reduce the count, simulating a partial write (but + // never to 0, that has different behavior). + let count = + if fd.nondet_short_accesses() && count >= 2 && this.machine.rng.get_mut().random() { + count / 2 + } else { + count + }; + let finish = { let dest = dest.clone(); callback!( diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 548eabb1b9fde..55906f4eb9548 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -1,7 +1,7 @@ use std::ffi::OsStr; use std::str; -use rustc_abi::{CanonAbi, ExternAbi, Size}; +use rustc_abi::{CanonAbi, Size}; use rustc_middle::ty::Ty; use rustc_span::Symbol; use rustc_target::callconv::FnAbi; @@ -14,7 +14,7 @@ use self::shims::unix::solarish::foreign_items as solarish; use crate::concurrency::cpu_affinity::CpuAffinityMask; use crate::shims::alloc::EvalContextExt as _; use crate::shims::unix::*; -use crate::*; +use crate::{shim_sig, *}; pub fn is_dyn_sym(name: &str, target_os: &str) -> bool { match name { @@ -111,40 +111,30 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match link_name.as_str() { // Environment related shims "getenv" => { - let [name] = this.check_shim_abi( + let [name] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _) -> *mut _), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.const_raw_ptr.ty], - this.machine.layouts.mut_raw_ptr.ty, args, )?; let result = this.getenv(name)?; this.write_pointer(result, dest)?; } "unsetenv" => { - let [name] = this.check_shim_abi( + let [name] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.const_raw_ptr.ty], - this.tcx.types.i32, args, )?; let result = this.unsetenv(name)?; this.write_scalar(result, dest)?; } "setenv" => { - let [name, value, overwrite] = this.check_shim_abi( + let [name, value, overwrite] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _, *const _, i32) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [ - this.machine.layouts.const_raw_ptr.ty, - this.machine.layouts.const_raw_ptr.ty, - this.tcx.types.i32, - ], - this.tcx.types.i32, args, )?; this.read_scalar(overwrite)?.to_i32()?; @@ -152,48 +142,40 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } "getcwd" => { - let [buf, size] = this.check_shim_abi( + let [buf, size] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _, usize) -> *mut _), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.mut_raw_ptr.ty, this.tcx.types.usize], - this.machine.layouts.mut_raw_ptr.ty, args, )?; let result = this.getcwd(buf, size)?; this.write_pointer(result, dest)?; } "chdir" => { - let [path] = this.check_shim_abi( + let [path] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.const_raw_ptr.ty], - this.tcx.types.i32, args, )?; let result = this.chdir(path)?; this.write_scalar(result, dest)?; } "getpid" => { - let [] = this.check_shim_abi( + let [] = this.check_shim_sig( + shim_sig!(extern "C" fn() -> libc::pid_t), link_name, abi, - ExternAbi::C { unwind: false }, - [], - this.libc_ty_layout("pid_t").ty, args, )?; let result = this.getpid()?; this.write_scalar(result, dest)?; } "sysconf" => { - let [val] = this.check_shim_abi( + let [val] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32) -> isize), link_name, abi, - ExternAbi::C { unwind: false }, - [this.tcx.types.i32], - this.tcx.types.isize, args, )?; let result = this.sysconf(val)?; @@ -201,12 +183,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } // File descriptors "read" => { - let [fd, buf, count] = this.check_shim_abi( + let [fd, buf, count] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *mut _, usize) -> isize), link_name, abi, - ExternAbi::C { unwind: false }, - [this.tcx.types.i32, this.machine.layouts.mut_raw_ptr.ty, this.tcx.types.usize], - this.tcx.types.isize, args, )?; let fd = this.read_scalar(fd)?.to_i32()?; @@ -215,16 +195,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.read(fd, buf, count, None, dest)?; } "write" => { - let [fd, buf, n] = this.check_shim_abi( + let [fd, buf, n] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *const _, usize) -> isize), link_name, abi, - ExternAbi::C { unwind: false }, - [ - this.tcx.types.i32, - this.machine.layouts.const_raw_ptr.ty, - this.tcx.types.usize, - ], - this.tcx.types.isize, args, )?; let fd = this.read_scalar(fd)?.to_i32()?; @@ -234,98 +208,64 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write(fd, buf, count, None, dest)?; } "pread" => { - let off_t = this.libc_ty_layout("off_t"); - let [fd, buf, count, offset] = this.check_shim_abi( + let [fd, buf, count, offset] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *mut _, usize, libc::off_t) -> isize), link_name, abi, - ExternAbi::C { unwind: false }, - [ - this.tcx.types.i32, - this.machine.layouts.mut_raw_ptr.ty, - this.tcx.types.usize, - off_t.ty, - ], - this.tcx.types.isize, args, )?; let fd = this.read_scalar(fd)?.to_i32()?; let buf = this.read_pointer(buf)?; let count = this.read_target_usize(count)?; - let offset = this.read_scalar(offset)?.to_int(off_t.size)?; + let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?; this.read(fd, buf, count, Some(offset), dest)?; } "pwrite" => { - let off_t = this.libc_ty_layout("off_t"); - let [fd, buf, n, offset] = this.check_shim_abi( + let [fd, buf, n, offset] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *const _, usize, libc::off_t) -> isize), link_name, abi, - ExternAbi::C { unwind: false }, - [ - this.tcx.types.i32, - this.machine.layouts.const_raw_ptr.ty, - this.tcx.types.usize, - off_t.ty, - ], - this.tcx.types.isize, args, )?; let fd = this.read_scalar(fd)?.to_i32()?; let buf = this.read_pointer(buf)?; let count = this.read_target_usize(n)?; - let offset = this.read_scalar(offset)?.to_int(off_t.size)?; + let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?; trace!("Called pwrite({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset); this.write(fd, buf, count, Some(offset), dest)?; } "pread64" => { - let off64_t = this.libc_ty_layout("off64_t"); - let [fd, buf, count, offset] = this.check_shim_abi( + let [fd, buf, count, offset] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *mut _, usize, libc::off64_t) -> isize), link_name, abi, - ExternAbi::C { unwind: false }, - [ - this.tcx.types.i32, - this.machine.layouts.mut_raw_ptr.ty, - this.tcx.types.usize, - off64_t.ty, - ], - this.tcx.types.isize, args, )?; let fd = this.read_scalar(fd)?.to_i32()?; let buf = this.read_pointer(buf)?; let count = this.read_target_usize(count)?; - let offset = this.read_scalar(offset)?.to_int(off64_t.size)?; + let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?; this.read(fd, buf, count, Some(offset), dest)?; } "pwrite64" => { - let off64_t = this.libc_ty_layout("off64_t"); - let [fd, buf, n, offset] = this.check_shim_abi( + let [fd, buf, n, offset] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *const _, usize, libc::off64_t) -> isize), link_name, abi, - ExternAbi::C { unwind: false }, - [ - this.tcx.types.i32, - this.machine.layouts.const_raw_ptr.ty, - this.tcx.types.usize, - off64_t.ty, - ], - this.tcx.types.isize, args, )?; let fd = this.read_scalar(fd)?.to_i32()?; let buf = this.read_pointer(buf)?; let count = this.read_target_usize(n)?; - let offset = this.read_scalar(offset)?.to_int(off64_t.size)?; + let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?; trace!("Called pwrite64({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset); this.write(fd, buf, count, Some(offset), dest)?; } "close" => { - let [fd] = this.check_shim_abi( + let [fd] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.tcx.types.i32], - this.tcx.types.i32, args, )?; let result = this.close(fd)?; @@ -333,17 +273,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "fcntl" => { let ([fd_num, cmd], varargs) = - this.check_shim_variadic(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_variadic_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.fcntl(fd_num, cmd, varargs)?; this.write_scalar(result, dest)?; } "dup" => { - let [old_fd] = this.check_shim_abi( + let [old_fd] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.tcx.types.i32], - this.tcx.types.i32, args, )?; let old_fd = this.read_scalar(old_fd)?.to_i32()?; @@ -351,12 +289,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(new_fd, dest)?; } "dup2" => { - let [old_fd, new_fd] = this.check_shim_abi( + let [old_fd, new_fd] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, i32) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.tcx.types.i32, this.tcx.types.i32], - this.tcx.types.i32, args, )?; let old_fd = this.read_scalar(old_fd)?.to_i32()?; @@ -367,12 +303,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "flock" => { // Currently this function does not exist on all Unixes, e.g. on Solaris. this.check_target_os(&["linux", "freebsd", "macos", "illumos"], link_name)?; - let [fd, op] = this.check_shim_abi( + let [fd, op] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, i32) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.tcx.types.i32, this.tcx.types.i32], - this.tcx.types.i32, args, )?; let fd = this.read_scalar(fd)?.to_i32()?; @@ -386,230 +320,187 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // `open` is variadic, the third argument is only present when the second argument // has O_CREAT (or on linux O_TMPFILE, but miri doesn't support that) set let ([path_raw, flag], varargs) = - this.check_shim_variadic(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_variadic_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.open(path_raw, flag, varargs)?; this.write_scalar(result, dest)?; } "unlink" => { - let [path] = this.check_shim_abi( + let [path] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.const_raw_ptr.ty], - this.tcx.types.i32, args, )?; let result = this.unlink(path)?; this.write_scalar(result, dest)?; } "symlink" => { - let [target, linkpath] = this.check_shim_abi( + let [target, linkpath] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _, *const _) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.const_raw_ptr.ty, this.machine.layouts.const_raw_ptr.ty], - this.tcx.types.i32, args, )?; let result = this.symlink(target, linkpath)?; this.write_scalar(result, dest)?; } "rename" => { - let [oldpath, newpath] = this.check_shim_abi( + let [oldpath, newpath] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _, *const _) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.const_raw_ptr.ty, this.machine.layouts.const_raw_ptr.ty], - this.tcx.types.i32, args, )?; let result = this.rename(oldpath, newpath)?; this.write_scalar(result, dest)?; } "mkdir" => { - let [path, mode] = this.check_shim_abi( + let [path, mode] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _, libc::mode_t) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.const_raw_ptr.ty, this.libc_ty_layout("mode_t").ty], - this.tcx.types.i32, args, )?; let result = this.mkdir(path, mode)?; this.write_scalar(result, dest)?; } "rmdir" => { - let [path] = this.check_shim_abi( + let [path] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.const_raw_ptr.ty], - this.tcx.types.i32, args, )?; let result = this.rmdir(path)?; this.write_scalar(result, dest)?; } "opendir" => { - let [name] = this.check_shim_abi( + let [name] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _) -> *mut _), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.const_raw_ptr.ty], - this.machine.layouts.mut_raw_ptr.ty, args, )?; let result = this.opendir(name)?; this.write_scalar(result, dest)?; } "closedir" => { - let [dirp] = this.check_shim_abi( + let [dirp] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.mut_raw_ptr.ty], - this.tcx.types.i32, args, )?; let result = this.closedir(dirp)?; this.write_scalar(result, dest)?; } "lseek64" => { - let off64_t = this.libc_ty_layout("off64_t"); - let [fd, offset, whence] = this.check_shim_abi( + let [fd, offset, whence] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, libc::off64_t, i32) -> libc::off64_t), link_name, abi, - ExternAbi::C { unwind: false }, - [this.tcx.types.i32, off64_t.ty, this.tcx.types.i32], - off64_t.ty, args, )?; let fd = this.read_scalar(fd)?.to_i32()?; - let offset = this.read_scalar(offset)?.to_int(off64_t.size)?; + let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?; let whence = this.read_scalar(whence)?.to_i32()?; this.lseek64(fd, offset, whence, dest)?; } "lseek" => { - let off_t = this.libc_ty_layout("off_t"); - let [fd, offset, whence] = this.check_shim_abi( + let [fd, offset, whence] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, libc::off_t, i32) -> libc::off_t), link_name, abi, - ExternAbi::C { unwind: false }, - [this.tcx.types.i32, off_t.ty, this.tcx.types.i32], - off_t.ty, args, )?; let fd = this.read_scalar(fd)?.to_i32()?; - let offset = this.read_scalar(offset)?.to_int(off_t.size)?; + let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?; let whence = this.read_scalar(whence)?.to_i32()?; this.lseek64(fd, offset, whence, dest)?; } "ftruncate64" => { - let off64_t = this.libc_ty_layout("off64_t"); - let [fd, length] = this.check_shim_abi( + let [fd, length] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, libc::off64_t) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.tcx.types.i32, off64_t.ty], - this.tcx.types.i32, args, )?; let fd = this.read_scalar(fd)?.to_i32()?; - let length = this.read_scalar(length)?.to_int(off64_t.size)?; + let length = this.read_scalar(length)?.to_int(length.layout.size)?; let result = this.ftruncate64(fd, length)?; this.write_scalar(result, dest)?; } "ftruncate" => { - let off_t = this.libc_ty_layout("off_t"); - let [fd, length] = this.check_shim_abi( + let [fd, length] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, libc::off_t) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.tcx.types.i32, off_t.ty], - this.tcx.types.i32, args, )?; let fd = this.read_scalar(fd)?.to_i32()?; - let length = this.read_scalar(length)?.to_int(off_t.size)?; + let length = this.read_scalar(length)?.to_int(length.layout.size)?; let result = this.ftruncate64(fd, length)?; this.write_scalar(result, dest)?; } "fsync" => { - let [fd] = this.check_shim_abi( + let [fd] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.tcx.types.i32], - this.tcx.types.i32, args, )?; let result = this.fsync(fd)?; this.write_scalar(result, dest)?; } "fdatasync" => { - let [fd] = this.check_shim_abi( + let [fd] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.tcx.types.i32], - this.tcx.types.i32, args, )?; let result = this.fdatasync(fd)?; this.write_scalar(result, dest)?; } "readlink" => { - let [pathname, buf, bufsize] = this.check_shim_abi( + let [pathname, buf, bufsize] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _, *mut _, usize) -> isize), link_name, abi, - ExternAbi::C { unwind: false }, - [ - this.machine.layouts.const_raw_ptr.ty, - this.machine.layouts.mut_raw_ptr.ty, - this.tcx.types.usize, - ], - this.tcx.types.isize, args, )?; let result = this.readlink(pathname, buf, bufsize)?; this.write_scalar(Scalar::from_target_isize(result, this), dest)?; } "posix_fadvise" => { - let off_t = this.libc_ty_layout("off_t"); - let [fd, offset, len, advice] = this.check_shim_abi( + let [fd, offset, len, advice] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, libc::off_t, libc::off_t, i32) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.tcx.types.i32, off_t.ty, off_t.ty, this.tcx.types.i32], - this.tcx.types.i32, args, )?; this.read_scalar(fd)?.to_i32()?; - this.read_scalar(offset)?.to_int(off_t.size)?; - this.read_scalar(len)?.to_int(off_t.size)?; + this.read_scalar(offset)?.to_int(offset.layout.size)?; + this.read_scalar(len)?.to_int(len.layout.size)?; this.read_scalar(advice)?.to_i32()?; // fadvise is only informational, we can ignore it. this.write_null(dest)?; } "realpath" => { - let [path, resolved_path] = this.check_shim_abi( + let [path, resolved_path] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _, *mut _) -> *mut _), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.const_raw_ptr.ty, this.machine.layouts.mut_raw_ptr.ty], - this.machine.layouts.mut_raw_ptr.ty, args, )?; let result = this.realpath(path, resolved_path)?; this.write_scalar(result, dest)?; } "mkstemp" => { - let [template] = this.check_shim_abi( + let [template] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.mut_raw_ptr.ty], - this.tcx.types.i32, args, )?; let result = this.mkstemp(template)?; @@ -618,29 +509,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Unnamed sockets and pipes "socketpair" => { - let [domain, type_, protocol, sv] = this.check_shim_abi( + let [domain, type_, protocol, sv] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, i32, i32, *mut _) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [ - this.tcx.types.i32, - this.tcx.types.i32, - this.tcx.types.i32, - this.machine.layouts.mut_raw_ptr.ty, - ], - this.tcx.types.i32, args, )?; let result = this.socketpair(domain, type_, protocol, sv)?; this.write_scalar(result, dest)?; } "pipe" => { - let [pipefd] = this.check_shim_abi( + let [pipefd] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.mut_raw_ptr.ty], - this.tcx.types.i32, args, )?; let result = this.pipe2(pipefd, /*flags*/ None)?; @@ -649,12 +531,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "pipe2" => { // Currently this function does not exist on all Unixes, e.g. on macOS. this.check_target_os(&["linux", "freebsd", "solaris", "illumos"], link_name)?; - let [pipefd, flags] = this.check_shim_abi( + let [pipefd, flags] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _, i32) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.mut_raw_ptr.ty, this.tcx.types.i32], - this.tcx.types.i32, args, )?; let result = this.pipe2(pipefd, Some(flags))?; @@ -663,36 +543,30 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Time "gettimeofday" => { - let [tv, tz] = this.check_shim_abi( + let [tv, tz] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _, *mut _) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.mut_raw_ptr.ty, this.machine.layouts.mut_raw_ptr.ty], - this.tcx.types.i32, args, )?; let result = this.gettimeofday(tv, tz)?; this.write_scalar(result, dest)?; } "localtime_r" => { - let [timep, result_op] = this.check_shim_abi( + let [timep, result_op] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _, *mut _) -> *mut _), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.const_raw_ptr.ty, this.machine.layouts.mut_raw_ptr.ty], - this.machine.layouts.mut_raw_ptr.ty, args, )?; let result = this.localtime_r(timep, result_op)?; this.write_pointer(result, dest)?; } "clock_gettime" => { - let [clk_id, tp] = this.check_shim_abi( + let [clk_id, tp] = this.check_shim_sig( + shim_sig!(extern "C" fn(libc::clockid_t, *mut _) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.libc_ty_layout("clockid_t").ty, this.machine.layouts.mut_raw_ptr.ty], - this.tcx.types.i32, args, )?; this.clock_gettime(clk_id, tp, dest)?; @@ -700,20 +574,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Allocation "posix_memalign" => { - let [memptr, align, size] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [memptr, align, size] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.posix_memalign(memptr, align, size)?; this.write_scalar(result, dest)?; } "mmap" => { let [addr, length, prot, flags, fd, offset] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?; let ptr = this.mmap(addr, length, prot, flags, fd, offset)?; this.write_scalar(ptr, dest)?; } "munmap" => { - let [addr, length] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [addr, length] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.munmap(addr, length)?; this.write_scalar(result, dest)?; } @@ -721,7 +597,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "reallocarray" => { // Currently this function does not exist on all Unixes, e.g. on macOS. this.check_target_os(&["linux", "freebsd", "android"], link_name)?; - let [ptr, nmemb, size] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [ptr, nmemb, size] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let ptr = this.read_pointer(ptr)?; let nmemb = this.read_target_usize(nmemb)?; let size = this.read_target_usize(size)?; @@ -744,14 +621,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "aligned_alloc" => { // This is a C11 function, we assume all Unixes have it. // (MSVC explicitly does not support this.) - let [align, size] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [align, size] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let res = this.aligned_alloc(align, size)?; this.write_pointer(res, dest)?; } // Dynamic symbol loading "dlsym" => { - let [handle, symbol] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [handle, symbol] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.read_target_usize(handle)?; let symbol = this.read_pointer(symbol)?; let name = this.read_c_str(symbol)?; @@ -767,7 +646,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Thread-local storage "pthread_key_create" => { - let [key, dtor] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [key, dtor] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let key_place = this.deref_pointer_as(key, this.libc_ty_layout("pthread_key_t"))?; let dtor = this.read_pointer(dtor)?; @@ -795,21 +674,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_null(dest)?; } "pthread_key_delete" => { - let [key] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [key] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let key = this.read_scalar(key)?.to_bits(key.layout.size)?; this.machine.tls.delete_tls_key(key)?; // Return success (0) this.write_null(dest)?; } "pthread_getspecific" => { - let [key] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [key] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let key = this.read_scalar(key)?.to_bits(key.layout.size)?; let active_thread = this.active_thread(); let ptr = this.machine.tls.load_tls(key, active_thread, this)?; this.write_scalar(ptr, dest)?; } "pthread_setspecific" => { - let [key, new_ptr] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [key, new_ptr] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let key = this.read_scalar(key)?.to_bits(key.layout.size)?; let active_thread = this.active_thread(); let new_data = this.read_scalar(new_ptr)?; @@ -821,117 +701,124 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Synchronization primitives "pthread_mutexattr_init" => { - let [attr] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [attr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_mutexattr_init(attr)?; this.write_null(dest)?; } "pthread_mutexattr_settype" => { - let [attr, kind] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [attr, kind] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.pthread_mutexattr_settype(attr, kind)?; this.write_scalar(result, dest)?; } "pthread_mutexattr_destroy" => { - let [attr] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [attr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_mutexattr_destroy(attr)?; this.write_null(dest)?; } "pthread_mutex_init" => { - let [mutex, attr] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [mutex, attr] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_mutex_init(mutex, attr)?; this.write_null(dest)?; } "pthread_mutex_lock" => { - let [mutex] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [mutex] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_mutex_lock(mutex, dest)?; } "pthread_mutex_trylock" => { - let [mutex] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [mutex] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.pthread_mutex_trylock(mutex)?; this.write_scalar(result, dest)?; } "pthread_mutex_unlock" => { - let [mutex] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [mutex] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.pthread_mutex_unlock(mutex)?; this.write_scalar(result, dest)?; } "pthread_mutex_destroy" => { - let [mutex] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [mutex] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_mutex_destroy(mutex)?; this.write_int(0, dest)?; } "pthread_rwlock_rdlock" => { - let [rwlock] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [rwlock] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_rwlock_rdlock(rwlock, dest)?; } "pthread_rwlock_tryrdlock" => { - let [rwlock] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [rwlock] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.pthread_rwlock_tryrdlock(rwlock)?; this.write_scalar(result, dest)?; } "pthread_rwlock_wrlock" => { - let [rwlock] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [rwlock] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_rwlock_wrlock(rwlock, dest)?; } "pthread_rwlock_trywrlock" => { - let [rwlock] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [rwlock] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.pthread_rwlock_trywrlock(rwlock)?; this.write_scalar(result, dest)?; } "pthread_rwlock_unlock" => { - let [rwlock] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [rwlock] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_rwlock_unlock(rwlock)?; this.write_null(dest)?; } "pthread_rwlock_destroy" => { - let [rwlock] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [rwlock] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_rwlock_destroy(rwlock)?; this.write_null(dest)?; } "pthread_condattr_init" => { - let [attr] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [attr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_condattr_init(attr)?; this.write_null(dest)?; } "pthread_condattr_setclock" => { - let [attr, clock_id] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [attr, clock_id] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.pthread_condattr_setclock(attr, clock_id)?; this.write_scalar(result, dest)?; } "pthread_condattr_getclock" => { - let [attr, clock_id] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [attr, clock_id] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_condattr_getclock(attr, clock_id)?; this.write_null(dest)?; } "pthread_condattr_destroy" => { - let [attr] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [attr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_condattr_destroy(attr)?; this.write_null(dest)?; } "pthread_cond_init" => { - let [cond, attr] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [cond, attr] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_cond_init(cond, attr)?; this.write_null(dest)?; } "pthread_cond_signal" => { - let [cond] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [cond] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_cond_signal(cond)?; this.write_null(dest)?; } "pthread_cond_broadcast" => { - let [cond] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [cond] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_cond_broadcast(cond)?; this.write_null(dest)?; } "pthread_cond_wait" => { - let [cond, mutex] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [cond, mutex] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_cond_wait(cond, mutex, dest)?; } "pthread_cond_timedwait" => { - let [cond, mutex, abstime] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [cond, mutex, abstime] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_cond_timedwait(cond, mutex, abstime, dest)?; } "pthread_cond_destroy" => { - let [cond] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [cond] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_cond_destroy(cond)?; this.write_null(dest)?; } @@ -939,31 +826,33 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Threading "pthread_create" => { let [thread, attr, start, arg] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_create(thread, attr, start, arg)?; this.write_null(dest)?; } "pthread_join" => { - let [thread, retval] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [thread, retval] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_join(thread, retval, dest)?; } "pthread_detach" => { - let [thread] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [thread] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let res = this.pthread_detach(thread)?; this.write_scalar(res, dest)?; } "pthread_self" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let res = this.pthread_self()?; this.write_scalar(res, dest)?; } "sched_yield" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.sched_yield()?; this.write_null(dest)?; } "nanosleep" => { - let [duration, rem] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [duration, rem] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.nanosleep(duration, rem)?; this.write_scalar(result, dest)?; } @@ -974,14 +863,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { link_name, )?; let [clock_id, flags, req, rem] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.clock_nanosleep(clock_id, flags, req, rem)?; this.write_scalar(result, dest)?; } "sched_getaffinity" => { // Currently this function does not exist on all Unixes, e.g. on macOS. this.check_target_os(&["linux", "freebsd", "android"], link_name)?; - let [pid, cpusetsize, mask] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [pid, cpusetsize, mask] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let pid = this.read_scalar(pid)?.to_u32()?; let cpusetsize = this.read_target_usize(cpusetsize)?; let mask = this.read_pointer(mask)?; @@ -1018,7 +908,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "sched_setaffinity" => { // Currently this function does not exist on all Unixes, e.g. on macOS. this.check_target_os(&["linux", "freebsd", "android"], link_name)?; - let [pid, cpusetsize, mask] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [pid, cpusetsize, mask] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let pid = this.read_scalar(pid)?.to_u32()?; let cpusetsize = this.read_target_usize(cpusetsize)?; let mask = this.read_pointer(mask)?; @@ -1058,13 +949,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Miscellaneous "isatty" => { - let [fd] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [fd] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.isatty(fd)?; this.write_scalar(result, dest)?; } "pthread_atfork" => { let [prepare, parent, child] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.read_pointer(prepare)?; this.read_pointer(parent)?; this.read_pointer(child)?; @@ -1078,7 +969,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &["linux", "macos", "freebsd", "illumos", "solaris", "android"], link_name, )?; - let [buf, bufsize] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [buf, bufsize] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let buf = this.read_pointer(buf)?; let bufsize = this.read_target_usize(bufsize)?; @@ -1096,7 +988,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "strerror_r" => { - let [errnum, buf, buflen] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [errnum, buf, buflen] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.strerror_r(errnum, buf, buflen)?; this.write_scalar(result, dest)?; } @@ -1108,7 +1001,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &["linux", "freebsd", "illumos", "solaris", "android"], link_name, )?; - let [ptr, len, flags] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [ptr, len, flags] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let ptr = this.read_pointer(ptr)?; let len = this.read_target_usize(len)?; let _flags = this.read_scalar(flags)?.to_i32()?; @@ -1120,7 +1014,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // This function is non-standard but exists with the same signature and // same behavior (eg never fails) on FreeBSD and Solaris/Illumos. this.check_target_os(&["freebsd", "illumos", "solaris"], link_name)?; - let [ptr, len] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [ptr, len] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let ptr = this.read_pointer(ptr)?; let len = this.read_target_usize(len)?; this.gen_random(ptr, len)?; @@ -1144,12 +1038,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { link_name, )?; // This function looks and behaves excatly like miri_start_unwind. - let [payload] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [payload] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.handle_miri_start_unwind(payload)?; return interp_ok(EmulateItemResult::NeedsUnwind); } "getuid" | "geteuid" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; // For now, just pretend we always have this fixed UID. this.write_int(UID, dest)?; } @@ -1157,7 +1051,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Incomplete shims that we "stub out" just to get pre-main initialization code to work. // These shims are enabled only when the caller is in the standard library. "pthread_attr_getguardsize" if this.frame_in_std() => { - let [_attr, guard_size] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [_attr, guard_size] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let guard_size_layout = this.machine.layouts.usize; let guard_size = this.deref_pointer_as(guard_size, guard_size_layout)?; this.write_scalar( @@ -1170,11 +1065,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "pthread_attr_init" | "pthread_attr_destroy" if this.frame_in_std() => { - let [_] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [_] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.write_null(dest)?; } "pthread_attr_setstacksize" if this.frame_in_std() => { - let [_, _] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [_, _] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.write_null(dest)?; } @@ -1182,7 +1077,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // We don't support "pthread_attr_setstack", so we just pretend all stacks have the same values here. // Hence we can mostly ignore the input `attr_place`. let [attr_place, addr_place, size_place] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let _attr_place = this.deref_pointer_as(attr_place, this.libc_ty_layout("pthread_attr_t"))?; let addr_place = this.deref_pointer_as(addr_place, this.machine.layouts.usize)?; @@ -1202,18 +1097,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "signal" | "sigaltstack" if this.frame_in_std() => { - let [_, _] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [_, _] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.write_null(dest)?; } "sigaction" | "mprotect" if this.frame_in_std() => { - let [_, _, _] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [_, _, _] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.write_null(dest)?; } "getpwuid_r" | "__posix_getpwuid_r" if this.frame_in_std() => { // getpwuid_r is the standard name, __posix_getpwuid_r is used on solarish let [uid, pwd, buf, buflen, result] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.check_no_isolation("`getpwuid_r`")?; let uid = this.read_scalar(uid)?.to_u32()?; diff --git a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs index 33564a2f84c78..9e247053fbcdb 100644 --- a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs @@ -24,7 +24,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match link_name.as_str() { // Threading "pthread_setname_np" => { - let [thread, name] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [thread, name] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let max_len = u64::MAX; // FreeBSD does not seem to have a limit. let res = match this.pthread_setname_np( this.read_scalar(thread)?, @@ -39,7 +40,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } "pthread_getname_np" => { - let [thread, name, len] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [thread, name, len] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; // FreeBSD's pthread_getname_np uses strlcpy, which truncates the resulting value, // but always adds a null terminator (except for zero-sized buffers). // https://github.com/freebsd/freebsd-src/blob/c2d93a803acef634bd0eede6673aeea59e90c277/lib/libthr/thread/thr_info.c#L119-L144 @@ -57,7 +59,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } "pthread_getthreadid_np" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.unix_gettid(link_name.as_str())?; this.write_scalar(result, dest)?; } @@ -65,7 +67,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "cpuset_getaffinity" => { // The "same" kind of api as `sched_getaffinity` but more fine grained control for FreeBSD specifically. let [level, which, id, set_size, mask] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let level = this.read_scalar(level)?.to_i32()?; let which = this.read_scalar(which)?.to_i32()?; @@ -129,7 +131,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Synchronization primitives "_umtx_op" => { let [obj, op, val, uaddr, uaddr2] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this._umtx_op(obj, op, val, uaddr, uaddr2, dest)?; } @@ -137,29 +139,30 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // For those, we both intercept `func` and `call@FBSD_1.0` symbols cases // since freebsd 12 the former form can be expected. "stat" | "stat@FBSD_1.0" => { - let [path, buf] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.macos_fbsd_solarish_stat(path, buf)?; this.write_scalar(result, dest)?; } "lstat" | "lstat@FBSD_1.0" => { - let [path, buf] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.macos_fbsd_solarish_lstat(path, buf)?; this.write_scalar(result, dest)?; } "fstat" | "fstat@FBSD_1.0" => { - let [fd, buf] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [fd, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.macos_fbsd_solarish_fstat(fd, buf)?; this.write_scalar(result, dest)?; } "readdir_r" | "readdir_r@FBSD_1.0" => { - let [dirp, entry, result] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [dirp, entry, result] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.macos_fbsd_readdir_r(dirp, entry, result)?; this.write_scalar(result, dest)?; } // Miscellaneous "__error" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let errno_place = this.last_error_place()?; this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?; } @@ -167,7 +170,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Incomplete shims that we "stub out" just to get pre-main initialization code to work. // These shims are enabled only when the caller is in the standard library. "pthread_attr_get_np" if this.frame_in_std() => { - let [_thread, _attr] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [_thread, _attr] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.write_null(dest)?; } diff --git a/src/tools/miri/src/shims/unix/freebsd/sync.rs b/src/tools/miri/src/shims/unix/freebsd/sync.rs index f4e7d9e58f9f8..13d30e05573af 100644 --- a/src/tools/miri/src/shims/unix/freebsd/sync.rs +++ b/src/tools/miri/src/shims/unix/freebsd/sync.rs @@ -228,26 +228,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let abs_time_flag = flags == abs_time; let clock_id_place = this.project_field(ut, FieldIdx::from_u32(2))?; - let clock_id = this.read_scalar(&clock_id_place)?.to_i32()?; - let timeout_clock = this.translate_umtx_time_clock_id(clock_id)?; + let clock_id = this.read_scalar(&clock_id_place)?; + let Some(timeout_clock) = this.parse_clockid(clock_id) else { + throw_unsup_format!("unsupported clock") + }; + if timeout_clock == TimeoutClock::RealTime { + this.check_no_isolation("`_umtx_op` with `CLOCK_REALTIME`")?; + } interp_ok(Some(UmtxTime { timeout: duration, abs_time: abs_time_flag, timeout_clock })) } - - /// Translate raw FreeBSD clockid to a Miri TimeoutClock. - /// FIXME: share this code with the pthread and clock_gettime shims. - fn translate_umtx_time_clock_id(&mut self, raw_id: i32) -> InterpResult<'tcx, TimeoutClock> { - let this = self.eval_context_mut(); - - let timeout = if raw_id == this.eval_libc_i32("CLOCK_REALTIME") { - // RealTime clock can't be used in isolation mode. - this.check_no_isolation("`_umtx_op` with `CLOCK_REALTIME` timeout")?; - TimeoutClock::RealTime - } else if raw_id == this.eval_libc_i32("CLOCK_MONOTONIC") { - TimeoutClock::Monotonic - } else { - throw_unsup_format!("unsupported clock id {raw_id}"); - }; - interp_ok(timeout) - } } diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 0f2878ad26c85..f9bcacf64c412 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -13,9 +13,9 @@ use rustc_abi::Size; use rustc_data_structures::fx::FxHashMap; use self::shims::time::system_time_to_duration; -use crate::helpers::check_min_vararg_count; use crate::shims::files::FileHandle; use crate::shims::os_str::bytes_to_os_str; +use crate::shims::sig::check_min_vararg_count; use crate::shims::unix::fd::{FlockOp, UnixFileDescription}; use crate::*; diff --git a/src/tools/miri/src/shims/unix/linux/foreign_items.rs b/src/tools/miri/src/shims/unix/linux/foreign_items.rs index b3e99e6cc68f7..e7e0c3b6ecd96 100644 --- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs @@ -37,48 +37,50 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match link_name.as_str() { // File related shims "readdir64" => { - let [dirp] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.linux_solarish_readdir64("dirent64", dirp)?; this.write_scalar(result, dest)?; } "sync_file_range" => { let [fd, offset, nbytes, flags] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.sync_file_range(fd, offset, nbytes, flags)?; this.write_scalar(result, dest)?; } "statx" => { let [dirfd, pathname, flags, mask, statxbuf] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.linux_statx(dirfd, pathname, flags, mask, statxbuf)?; this.write_scalar(result, dest)?; } // epoll, eventfd "epoll_create1" => { - let [flag] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [flag] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.epoll_create1(flag)?; this.write_scalar(result, dest)?; } "epoll_ctl" => { - let [epfd, op, fd, event] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [epfd, op, fd, event] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.epoll_ctl(epfd, op, fd, event)?; this.write_scalar(result, dest)?; } "epoll_wait" => { let [epfd, events, maxevents, timeout] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.epoll_wait(epfd, events, maxevents, timeout, dest)?; } "eventfd" => { - let [val, flag] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [val, flag] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.eventfd(val, flag)?; this.write_scalar(result, dest)?; } // Threading "pthread_setname_np" => { - let [thread, name] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [thread, name] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let res = match this.pthread_setname_np( this.read_scalar(thread)?, this.read_scalar(name)?, @@ -93,7 +95,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } "pthread_getname_np" => { - let [thread, name, len] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [thread, name, len] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; // The function's behavior isn't portable between platforms. // In case of glibc, the length of the output buffer must // be not shorter than TASK_COMM_LEN. @@ -116,7 +119,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } "gettid" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.unix_gettid(link_name.as_str())?; this.write_scalar(result, dest)?; } @@ -129,34 +132,35 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Miscellaneous "mmap64" => { let [addr, length, prot, flags, fd, offset] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let offset = this.read_scalar(offset)?.to_i64()?; let ptr = this.mmap(addr, length, prot, flags, fd, offset.into())?; this.write_scalar(ptr, dest)?; } "mremap" => { let ([old_address, old_size, new_size, flags], _) = - this.check_shim_variadic(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_variadic_lenient(abi, CanonAbi::C, link_name, args)?; let ptr = this.mremap(old_address, old_size, new_size, flags)?; this.write_scalar(ptr, dest)?; } "__xpg_strerror_r" => { - let [errnum, buf, buflen] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [errnum, buf, buflen] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.strerror_r(errnum, buf, buflen)?; this.write_scalar(result, dest)?; } "__errno_location" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let errno_place = this.last_error_place()?; this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?; } "__libc_current_sigrtmin" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.write_int(SIGRTMIN, dest)?; } "__libc_current_sigrtmax" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.write_int(SIGRTMAX, dest)?; } @@ -164,7 +168,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Incomplete shims that we "stub out" just to get pre-main initialization code to work. // These shims are enabled only when the caller is in the standard library. "pthread_getattr_np" if this.frame_in_std() => { - let [_thread, _attr] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [_thread, _attr] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.write_null(dest)?; } diff --git a/src/tools/miri/src/shims/unix/linux_like/eventfd.rs b/src/tools/miri/src/shims/unix/linux_like/eventfd.rs index ee7deb8d38308..2d35ef064db8b 100644 --- a/src/tools/miri/src/shims/unix/linux_like/eventfd.rs +++ b/src/tools/miri/src/shims/unix/linux_like/eventfd.rs @@ -37,6 +37,11 @@ impl FileDescription for EventFd { "event" } + fn nondet_short_accesses(&self) -> bool { + // We always read and write exactly one `u64`. + false + } + fn close<'tcx>( self, _communicate_allowed: bool, diff --git a/src/tools/miri/src/shims/unix/linux_like/sync.rs b/src/tools/miri/src/shims/unix/linux_like/sync.rs index 9fad74c0241e4..5f032c52deeb7 100644 --- a/src/tools/miri/src/shims/unix/linux_like/sync.rs +++ b/src/tools/miri/src/shims/unix/linux_like/sync.rs @@ -1,5 +1,5 @@ use crate::concurrency::sync::FutexRef; -use crate::helpers::check_min_vararg_count; +use crate::shims::sig::check_min_vararg_count; use crate::*; struct LinuxFutex { diff --git a/src/tools/miri/src/shims/unix/linux_like/syscall.rs b/src/tools/miri/src/shims/unix/linux_like/syscall.rs index d3534e6e1bc6e..106e6c448d069 100644 --- a/src/tools/miri/src/shims/unix/linux_like/syscall.rs +++ b/src/tools/miri/src/shims/unix/linux_like/syscall.rs @@ -3,7 +3,7 @@ use rustc_middle::ty::Ty; use rustc_span::Symbol; use rustc_target::callconv::FnAbi; -use crate::helpers::check_min_vararg_count; +use crate::shims::sig::check_min_vararg_count; use crate::shims::unix::env::EvalContextExt; use crate::shims::unix::linux_like::eventfd::EvalContextExt as _; use crate::shims::unix::linux_like::sync::futex; @@ -16,7 +16,7 @@ pub fn syscall<'tcx>( args: &[OpTy<'tcx>], dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { - let ([op], varargs) = ecx.check_shim_variadic(abi, CanonAbi::C, link_name, args)?; + let ([op], varargs) = ecx.check_shim_sig_variadic_lenient(abi, CanonAbi::C, link_name, args)?; // The syscall variadic function is legal to call with more arguments than needed, // extra arguments are simply ignored. The important check is that when we use an // argument, we have to also check all arguments *before* it to ensure that they diff --git a/src/tools/miri/src/shims/unix/macos/foreign_items.rs b/src/tools/miri/src/shims/unix/macos/foreign_items.rs index 2330371809104..297d903c6ba4e 100644 --- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs @@ -35,64 +35,67 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match link_name.as_str() { // errno "__error" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let errno_place = this.last_error_place()?; this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?; } // File related shims "close$NOCANCEL" => { - let [result] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [result] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.close(result)?; this.write_scalar(result, dest)?; } "stat" | "stat64" | "stat$INODE64" => { - let [path, buf] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.macos_fbsd_solarish_stat(path, buf)?; this.write_scalar(result, dest)?; } "lstat" | "lstat64" | "lstat$INODE64" => { - let [path, buf] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.macos_fbsd_solarish_lstat(path, buf)?; this.write_scalar(result, dest)?; } "fstat" | "fstat64" | "fstat$INODE64" => { - let [fd, buf] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [fd, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.macos_fbsd_solarish_fstat(fd, buf)?; this.write_scalar(result, dest)?; } "opendir$INODE64" => { - let [name] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [name] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.opendir(name)?; this.write_scalar(result, dest)?; } "readdir_r" | "readdir_r$INODE64" => { - let [dirp, entry, result] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [dirp, entry, result] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.macos_fbsd_readdir_r(dirp, entry, result)?; this.write_scalar(result, dest)?; } "realpath$DARWIN_EXTSN" => { - let [path, resolved_path] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [path, resolved_path] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.realpath(path, resolved_path)?; this.write_scalar(result, dest)?; } "ioctl" => { let ([fd_num, cmd], varargs) = - this.check_shim_variadic(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_variadic_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.ioctl(fd_num, cmd, varargs)?; this.write_scalar(result, dest)?; } // Environment related shims "_NSGetEnviron" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let environ = this.machine.env_vars.unix().environ(); this.write_pointer(environ, dest)?; } // Random data generation "CCRandomGenerateBytes" => { - let [bytes, count] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [bytes, count] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let bytes = this.read_pointer(bytes)?; let count = this.read_target_usize(count)?; let success = this.eval_libc_i32("kCCSuccess"); @@ -102,28 +105,29 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Time related shims "mach_absolute_time" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.mach_absolute_time()?; this.write_scalar(result, dest)?; } "mach_timebase_info" => { - let [info] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [info] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.mach_timebase_info(info)?; this.write_scalar(result, dest)?; } // Access to command-line arguments "_NSGetArgc" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.write_pointer(this.machine.argc.expect("machine must be initialized"), dest)?; } "_NSGetArgv" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.write_pointer(this.machine.argv.expect("machine must be initialized"), dest)?; } "_NSGetExecutablePath" => { - let [buf, bufsize] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [buf, bufsize] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.check_no_isolation("`_NSGetExecutablePath`")?; let buf_ptr = this.read_pointer(buf)?; @@ -148,7 +152,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Thread-local storage "_tlv_atexit" => { - let [dtor, data] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [dtor, data] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let dtor = this.read_pointer(dtor)?; let dtor = this.get_ptr_fn(dtor)?.as_instance()?; let data = this.read_scalar(data)?; @@ -158,13 +163,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Querying system information "pthread_get_stackaddr_np" => { - let [thread] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [thread] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.read_target_usize(thread)?; let stack_addr = Scalar::from_uint(this.machine.stack_addr, this.pointer_size()); this.write_scalar(stack_addr, dest)?; } "pthread_get_stacksize_np" => { - let [thread] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [thread] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.read_target_usize(thread)?; let stack_size = Scalar::from_uint(this.machine.stack_size, this.pointer_size()); this.write_scalar(stack_size, dest)?; @@ -172,7 +177,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Threading "pthread_setname_np" => { - let [name] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [name] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; // The real implementation has logic in two places: // * in userland at https://github.com/apple-oss-distributions/libpthread/blob/c032e0b076700a0a47db75528a282b8d3a06531a/src/pthread.c#L1178-L1200, @@ -199,7 +204,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } "pthread_getname_np" => { - let [thread, name, len] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [thread, name, len] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; // The function's behavior isn't portable between platforms. // In case of macOS, a truncated name (due to a too small buffer) @@ -223,7 +229,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } "pthread_threadid_np" => { - let [thread, tid_ptr] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [thread, tid_ptr] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let res = this.apple_pthread_threadip_np(thread, tid_ptr)?; this.write_scalar(res, dest)?; } @@ -231,7 +238,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Synchronization primitives "os_sync_wait_on_address" => { let [addr_op, value_op, size_op, flags_op] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.os_sync_wait_on_address( addr_op, value_op, @@ -243,7 +250,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "os_sync_wait_on_address_with_deadline" => { let [addr_op, value_op, size_op, flags_op, clock_op, timeout_op] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.os_sync_wait_on_address( addr_op, value_op, @@ -255,7 +262,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "os_sync_wait_on_address_with_timeout" => { let [addr_op, value_op, size_op, flags_op, clock_op, timeout_op] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.os_sync_wait_on_address( addr_op, value_op, @@ -267,36 +274,36 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "os_sync_wake_by_address_any" => { let [addr_op, size_op, flags_op] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.os_sync_wake_by_address( addr_op, size_op, flags_op, /* all */ false, dest, )?; } "os_sync_wake_by_address_all" => { let [addr_op, size_op, flags_op] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.os_sync_wake_by_address( addr_op, size_op, flags_op, /* all */ true, dest, )?; } "os_unfair_lock_lock" => { - let [lock_op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [lock_op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.os_unfair_lock_lock(lock_op)?; } "os_unfair_lock_trylock" => { - let [lock_op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [lock_op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.os_unfair_lock_trylock(lock_op, dest)?; } "os_unfair_lock_unlock" => { - let [lock_op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [lock_op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.os_unfair_lock_unlock(lock_op)?; } "os_unfair_lock_assert_owner" => { - let [lock_op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [lock_op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.os_unfair_lock_assert_owner(lock_op)?; } "os_unfair_lock_assert_not_owner" => { - let [lock_op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [lock_op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.os_unfair_lock_assert_not_owner(lock_op)?; } diff --git a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs index e3d15b89be6d6..d7033a65fe22b 100644 --- a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs @@ -27,32 +27,34 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // epoll, eventfd (NOT available on Solaris!) "epoll_create1" => { this.assert_target_os("illumos", "epoll_create1"); - let [flag] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [flag] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.epoll_create1(flag)?; this.write_scalar(result, dest)?; } "epoll_ctl" => { this.assert_target_os("illumos", "epoll_ctl"); - let [epfd, op, fd, event] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [epfd, op, fd, event] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.epoll_ctl(epfd, op, fd, event)?; this.write_scalar(result, dest)?; } "epoll_wait" => { this.assert_target_os("illumos", "epoll_wait"); let [epfd, events, maxevents, timeout] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.epoll_wait(epfd, events, maxevents, timeout, dest)?; } "eventfd" => { this.assert_target_os("illumos", "eventfd"); - let [val, flag] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [val, flag] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.eventfd(val, flag)?; this.write_scalar(result, dest)?; } // Threading "pthread_setname_np" => { - let [thread, name] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [thread, name] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; // THREAD_NAME_MAX allows a thread name of 31+1 length // https://github.com/illumos/illumos-gate/blob/7671517e13b8123748eda4ef1ee165c6d9dba7fe/usr/src/uts/common/sys/thread.h#L613 let max_len = 32; @@ -70,7 +72,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } "pthread_getname_np" => { - let [thread, name, len] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [thread, name, len] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; // See https://illumos.org/man/3C/pthread_getname_np for the error codes. let res = match this.pthread_getname_np( this.read_scalar(thread)?, @@ -87,22 +90,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // File related shims "stat" | "stat64" => { - let [path, buf] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.macos_fbsd_solarish_stat(path, buf)?; this.write_scalar(result, dest)?; } "lstat" | "lstat64" => { - let [path, buf] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.macos_fbsd_solarish_lstat(path, buf)?; this.write_scalar(result, dest)?; } "fstat" | "fstat64" => { - let [fd, buf] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [fd, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.macos_fbsd_solarish_fstat(fd, buf)?; this.write_scalar(result, dest)?; } "readdir" => { - let [dirp] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.linux_solarish_readdir64("dirent", dirp)?; this.write_scalar(result, dest)?; } @@ -110,20 +113,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Sockets and pipes "__xnet_socketpair" => { let [domain, type_, protocol, sv] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.socketpair(domain, type_, protocol, sv)?; this.write_scalar(result, dest)?; } // Miscellaneous "___errno" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let errno_place = this.last_error_place()?; this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?; } "stack_getbounds" => { - let [stack] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [stack] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let stack = this.deref_pointer_as(stack, this.libc_ty_layout("stack_t"))?; this.write_int_fields_named( @@ -141,7 +144,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "pset_info" => { - let [pset, tpe, cpus, list] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [pset, tpe, cpus, list] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; // We do not need to handle the current process cpu mask, available_parallelism // implementation pass null anyway. We only care for the number of // cpus. @@ -170,7 +174,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "__sysconf_xpg7" => { - let [val] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [val] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.sysconf(val)?; this.write_scalar(result, dest)?; } diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index e20e3b79c3bec..5ad4fd501a607 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -297,14 +297,13 @@ fn condattr_clock_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u fn condattr_get_clock_id<'tcx>( ecx: &MiriInterpCx<'tcx>, attr_ptr: &OpTy<'tcx>, -) -> InterpResult<'tcx, i32> { +) -> InterpResult<'tcx, Scalar> { ecx.deref_pointer_and_read( attr_ptr, condattr_clock_offset(ecx)?, ecx.libc_ty_layout("pthread_condattr_t"), ecx.machine.layouts.i32, - )? - .to_i32() + ) } fn condattr_set_clock_id<'tcx>( @@ -321,20 +320,6 @@ fn condattr_set_clock_id<'tcx>( ) } -/// Translates the clock from what is stored in pthread_condattr_t to our enum. -fn condattr_translate_clock_id<'tcx>( - ecx: &MiriInterpCx<'tcx>, - raw_id: i32, -) -> InterpResult<'tcx, ClockId> { - interp_ok(if raw_id == ecx.eval_libc_i32("CLOCK_REALTIME") { - ClockId::Realtime - } else if raw_id == ecx.eval_libc_i32("CLOCK_MONOTONIC") { - ClockId::Monotonic - } else { - throw_unsup_format!("unsupported clock id: {raw_id}"); - }) -} - // # pthread_cond_t // We store some data directly inside the type, ignoring the platform layout: // - init: u32 @@ -363,22 +348,16 @@ fn cond_init_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, Size> interp_ok(offset) } -#[derive(Debug, Clone, Copy)] -enum ClockId { - Realtime, - Monotonic, -} - #[derive(Debug, Clone)] struct PthreadCondvar { condvar_ref: CondvarRef, - clock: ClockId, + clock: TimeoutClock, } fn cond_create<'tcx>( ecx: &mut MiriInterpCx<'tcx>, cond_ptr: &OpTy<'tcx>, - clock: ClockId, + clock: TimeoutClock, ) -> InterpResult<'tcx, PthreadCondvar> { let cond = ecx.deref_pointer_as(cond_ptr, ecx.libc_ty_layout("pthread_cond_t"))?; let data = PthreadCondvar { condvar_ref: CondvarRef::new(), clock }; @@ -407,7 +386,10 @@ where throw_unsup_format!("unsupported static initializer used for `pthread_cond_t`"); } // This used the static initializer. The clock there is always CLOCK_REALTIME. - interp_ok(PthreadCondvar { condvar_ref: CondvarRef::new(), clock: ClockId::Realtime }) + interp_ok(PthreadCondvar { + condvar_ref: CondvarRef::new(), + clock: TimeoutClock::RealTime, + }) }, ) } @@ -742,11 +724,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let clock_id = this.read_scalar(clock_id_op)?.to_i32()?; - if clock_id == this.eval_libc_i32("CLOCK_REALTIME") - || clock_id == this.eval_libc_i32("CLOCK_MONOTONIC") - { - condattr_set_clock_id(this, attr_op, clock_id)?; + let clock_id = this.read_scalar(clock_id_op)?; + if this.parse_clockid(clock_id).is_some() { + condattr_set_clock_id(this, attr_op, clock_id.to_i32()?)?; } else { let einval = this.eval_libc_i32("EINVAL"); return interp_ok(Scalar::from_i32(einval)); @@ -764,7 +744,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let clock_id = condattr_get_clock_id(this, attr_op)?; this.write_scalar( - Scalar::from_i32(clock_id), + clock_id, &this.deref_pointer_as(clk_id_op, this.libc_ty_layout("clockid_t"))?, )?; @@ -799,13 +779,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let attr = this.read_pointer(attr_op)?; // Default clock if `attr` is null, and on macOS where there is no clock attribute. let clock_id = if this.ptr_is_null(attr)? || this.tcx.sess.target.os == "macos" { - this.eval_libc_i32("CLOCK_REALTIME") + this.eval_libc("CLOCK_REALTIME") } else { condattr_get_clock_id(this, attr_op)? }; - let clock_id = condattr_translate_clock_id(this, clock_id)?; + let Some(clock) = this.parse_clockid(clock_id) else { + // This is UB since this situation cannot arise when using pthread_condattr_setclock. + throw_ub_format!("pthread_cond_init: invalid attributes (unsupported clock)") + }; - cond_create(this, cond_op, clock_id)?; + cond_create(this, cond_op, clock)?; interp_ok(()) } @@ -870,18 +853,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return interp_ok(()); } }; - let timeout_clock = match data.clock { - ClockId::Realtime => { - this.check_no_isolation("`pthread_cond_timedwait` with `CLOCK_REALTIME`")?; - TimeoutClock::RealTime - } - ClockId::Monotonic => TimeoutClock::Monotonic, - }; + if data.clock == TimeoutClock::RealTime { + this.check_no_isolation("`pthread_cond_timedwait` with `CLOCK_REALTIME`")?; + } this.condvar_wait( data.condvar_ref, mutex_ref, - Some((timeout_clock, TimeoutAnchor::Absolute, duration)), + Some((data.clock, TimeoutAnchor::Absolute, duration)), Scalar::from_i32(0), this.eval_libc("ETIMEDOUT"), // retval_timeout dest.clone(), diff --git a/src/tools/miri/src/shims/unwind.rs b/src/tools/miri/src/shims/unwind.rs index ba0c50b54b47a..0dd2b20487d31 100644 --- a/src/tools/miri/src/shims/unwind.rs +++ b/src/tools/miri/src/shims/unwind.rs @@ -16,7 +16,6 @@ use rustc_abi::ExternAbi; use rustc_middle::mir; use rustc_target::spec::PanicStrategy; -use self::helpers::check_intrinsic_arg_count; use crate::*; /// Holds all of the relevant data for when unwinding hits a `try` frame. @@ -60,7 +59,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Handles the `catch_unwind` intrinsic. fn handle_catch_unwind( &mut self, - args: &[OpTy<'tcx>], + try_fn: &OpTy<'tcx>, + data: &OpTy<'tcx>, + catch_fn: &OpTy<'tcx>, dest: &MPlaceTy<'tcx>, ret: Option, ) -> InterpResult<'tcx> { @@ -78,7 +79,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // a pointer to `Box`. // Get all the arguments. - let [try_fn, data, catch_fn] = check_intrinsic_arg_count(args)?; let try_fn = this.read_pointer(try_fn)?; let data = this.read_immediate(data)?; let catch_fn = this.read_pointer(catch_fn)?; diff --git a/src/tools/miri/src/shims/wasi/foreign_items.rs b/src/tools/miri/src/shims/wasi/foreign_items.rs index 8d92d0f3381f7..bfcdbd8130d84 100644 --- a/src/tools/miri/src/shims/wasi/foreign_items.rs +++ b/src/tools/miri/src/shims/wasi/foreign_items.rs @@ -23,12 +23,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match link_name.as_str() { // Allocation "posix_memalign" => { - let [memptr, align, size] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [memptr, align, size] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.posix_memalign(memptr, align, size)?; this.write_scalar(result, dest)?; } "aligned_alloc" => { - let [align, size] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [align, size] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let res = this.aligned_alloc(align, size)?; this.write_pointer(res, dest)?; } diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index 959abc0bacaa6..7b13f1d908073 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -157,42 +157,44 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match link_name.as_str() { // Environment related shims "GetEnvironmentVariableW" => { - let [name, buf, size] = this.check_shim(abi, sys_conv, link_name, args)?; + let [name, buf, size] = + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let result = this.GetEnvironmentVariableW(name, buf, size)?; this.write_scalar(result, dest)?; } "SetEnvironmentVariableW" => { - let [name, value] = this.check_shim(abi, sys_conv, link_name, args)?; + let [name, value] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let result = this.SetEnvironmentVariableW(name, value)?; this.write_scalar(result, dest)?; } "GetEnvironmentStringsW" => { - let [] = this.check_shim(abi, sys_conv, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let result = this.GetEnvironmentStringsW()?; this.write_pointer(result, dest)?; } "FreeEnvironmentStringsW" => { - let [env_block] = this.check_shim(abi, sys_conv, link_name, args)?; + let [env_block] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let result = this.FreeEnvironmentStringsW(env_block)?; this.write_scalar(result, dest)?; } "GetCurrentDirectoryW" => { - let [size, buf] = this.check_shim(abi, sys_conv, link_name, args)?; + let [size, buf] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let result = this.GetCurrentDirectoryW(size, buf)?; this.write_scalar(result, dest)?; } "SetCurrentDirectoryW" => { - let [path] = this.check_shim(abi, sys_conv, link_name, args)?; + let [path] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let result = this.SetCurrentDirectoryW(path)?; this.write_scalar(result, dest)?; } "GetUserProfileDirectoryW" => { - let [token, buf, size] = this.check_shim(abi, sys_conv, link_name, args)?; + let [token, buf, size] = + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let result = this.GetUserProfileDirectoryW(token, buf, size)?; this.write_scalar(result, dest)?; } "GetCurrentProcessId" => { - let [] = this.check_shim(abi, sys_conv, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let result = this.GetCurrentProcessId()?; this.write_scalar(result, dest)?; } @@ -209,7 +211,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { n, byte_offset, key, - ] = this.check_shim(abi, sys_conv, link_name, args)?; + ] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.NtWriteFile( handle, event, @@ -234,7 +236,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { n, byte_offset, key, - ] = this.check_shim(abi, sys_conv, link_name, args)?; + ] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.NtReadFile( handle, event, @@ -250,7 +252,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "GetFullPathNameW" => { let [filename, size, buffer, filepart] = - this.check_shim(abi, sys_conv, link_name, args)?; + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.check_no_isolation("`GetFullPathNameW`")?; let filename = this.read_pointer(filename)?; @@ -287,7 +289,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { creation_disposition, flags_and_attributes, template_file, - ] = this.check_shim(abi, sys_conv, link_name, args)?; + ] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let handle = this.CreateFileW( file_name, desired_access, @@ -300,18 +302,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(handle.to_scalar(this), dest)?; } "GetFileInformationByHandle" => { - let [handle, info] = this.check_shim(abi, sys_conv, link_name, args)?; + let [handle, info] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let res = this.GetFileInformationByHandle(handle, info)?; this.write_scalar(res, dest)?; } "DeleteFileW" => { - let [file_name] = this.check_shim(abi, sys_conv, link_name, args)?; + let [file_name] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let res = this.DeleteFileW(file_name)?; this.write_scalar(res, dest)?; } "SetFilePointerEx" => { let [file, distance_to_move, new_file_pointer, move_method] = - this.check_shim(abi, sys_conv, link_name, args)?; + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let res = this.SetFilePointerEx(file, distance_to_move, new_file_pointer, move_method)?; this.write_scalar(res, dest)?; @@ -319,7 +321,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Allocation "HeapAlloc" => { - let [handle, flags, size] = this.check_shim(abi, sys_conv, link_name, args)?; + let [handle, flags, size] = + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.read_target_isize(handle)?; let flags = this.read_scalar(flags)?.to_u32()?; let size = this.read_target_usize(size)?; @@ -341,7 +344,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_pointer(ptr, dest)?; } "HeapFree" => { - let [handle, flags, ptr] = this.check_shim(abi, sys_conv, link_name, args)?; + let [handle, flags, ptr] = + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.read_target_isize(handle)?; this.read_scalar(flags)?.to_u32()?; let ptr = this.read_pointer(ptr)?; @@ -354,7 +358,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "HeapReAlloc" => { let [handle, flags, old_ptr, size] = - this.check_shim(abi, sys_conv, link_name, args)?; + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.read_target_isize(handle)?; this.read_scalar(flags)?.to_u32()?; let old_ptr = this.read_pointer(old_ptr)?; @@ -374,7 +378,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_pointer(new_ptr, dest)?; } "LocalFree" => { - let [ptr] = this.check_shim(abi, sys_conv, link_name, args)?; + let [ptr] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let ptr = this.read_pointer(ptr)?; // "If the hMem parameter is NULL, LocalFree ignores the parameter and returns NULL." // (https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-localfree) @@ -386,17 +390,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // errno "SetLastError" => { - let [error] = this.check_shim(abi, sys_conv, link_name, args)?; + let [error] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let error = this.read_scalar(error)?; this.set_last_error(error)?; } "GetLastError" => { - let [] = this.check_shim(abi, sys_conv, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let last_error = this.get_last_error()?; this.write_scalar(last_error, dest)?; } "RtlNtStatusToDosError" => { - let [status] = this.check_shim(abi, sys_conv, link_name, args)?; + let [status] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let status = this.read_scalar(status)?.to_u32()?; let err = match status { // STATUS_MEDIA_WRITE_PROTECTED => ERROR_WRITE_PROTECT @@ -418,7 +422,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Querying system information "GetSystemInfo" => { // Also called from `page_size` crate. - let [system_info] = this.check_shim(abi, sys_conv, link_name, args)?; + let [system_info] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let system_info = this.deref_pointer_as(system_info, this.windows_ty_layout("SYSTEM_INFO"))?; // Initialize with `0`. @@ -441,19 +445,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // This just creates a key; Windows does not natively support TLS destructors. // Create key and return it. - let [] = this.check_shim(abi, sys_conv, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let key = this.machine.tls.create_tls_key(None, dest.layout.size)?; this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?; } "TlsGetValue" => { - let [key] = this.check_shim(abi, sys_conv, link_name, args)?; + let [key] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let key = u128::from(this.read_scalar(key)?.to_u32()?); let active_thread = this.active_thread(); let ptr = this.machine.tls.load_tls(key, active_thread, this)?; this.write_scalar(ptr, dest)?; } "TlsSetValue" => { - let [key, new_ptr] = this.check_shim(abi, sys_conv, link_name, args)?; + let [key, new_ptr] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let key = u128::from(this.read_scalar(key)?.to_u32()?); let active_thread = this.active_thread(); let new_data = this.read_scalar(new_ptr)?; @@ -463,7 +467,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_int(1, dest)?; } "TlsFree" => { - let [key] = this.check_shim(abi, sys_conv, link_name, args)?; + let [key] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let key = u128::from(this.read_scalar(key)?.to_u32()?); this.machine.tls.delete_tls_key(key)?; @@ -473,7 +477,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Access to command-line arguments "GetCommandLineW" => { - let [] = this.check_shim(abi, sys_conv, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.write_pointer( this.machine.cmd_line.expect("machine must be initialized"), dest, @@ -483,29 +487,30 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Time related shims "GetSystemTimeAsFileTime" | "GetSystemTimePreciseAsFileTime" => { #[allow(non_snake_case)] - let [LPFILETIME] = this.check_shim(abi, sys_conv, link_name, args)?; + let [LPFILETIME] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.GetSystemTimeAsFileTime(link_name.as_str(), LPFILETIME)?; } "QueryPerformanceCounter" => { #[allow(non_snake_case)] - let [lpPerformanceCount] = this.check_shim(abi, sys_conv, link_name, args)?; + let [lpPerformanceCount] = + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let result = this.QueryPerformanceCounter(lpPerformanceCount)?; this.write_scalar(result, dest)?; } "QueryPerformanceFrequency" => { #[allow(non_snake_case)] - let [lpFrequency] = this.check_shim(abi, sys_conv, link_name, args)?; + let [lpFrequency] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let result = this.QueryPerformanceFrequency(lpFrequency)?; this.write_scalar(result, dest)?; } "Sleep" => { - let [timeout] = this.check_shim(abi, sys_conv, link_name, args)?; + let [timeout] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.Sleep(timeout)?; } "CreateWaitableTimerExW" => { let [attributes, name, flags, access] = - this.check_shim(abi, sys_conv, link_name, args)?; + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.read_pointer(attributes)?; this.read_pointer(name)?; this.read_scalar(flags)?.to_u32()?; @@ -519,27 +524,28 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Synchronization primitives "InitOnceBeginInitialize" => { let [ptr, flags, pending, context] = - this.check_shim(abi, sys_conv, link_name, args)?; + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.InitOnceBeginInitialize(ptr, flags, pending, context, dest)?; } "InitOnceComplete" => { - let [ptr, flags, context] = this.check_shim(abi, sys_conv, link_name, args)?; + let [ptr, flags, context] = + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let result = this.InitOnceComplete(ptr, flags, context)?; this.write_scalar(result, dest)?; } "WaitOnAddress" => { let [ptr_op, compare_op, size_op, timeout_op] = - this.check_shim(abi, sys_conv, link_name, args)?; + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.WaitOnAddress(ptr_op, compare_op, size_op, timeout_op, dest)?; } "WakeByAddressSingle" => { - let [ptr_op] = this.check_shim(abi, sys_conv, link_name, args)?; + let [ptr_op] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.WakeByAddressSingle(ptr_op)?; } "WakeByAddressAll" => { - let [ptr_op] = this.check_shim(abi, sys_conv, link_name, args)?; + let [ptr_op] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.WakeByAddressAll(ptr_op)?; } @@ -547,7 +553,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Dynamic symbol loading "GetProcAddress" => { #[allow(non_snake_case)] - let [hModule, lpProcName] = this.check_shim(abi, sys_conv, link_name, args)?; + let [hModule, lpProcName] = + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.read_target_isize(hModule)?; let name = this.read_c_str(this.read_pointer(lpProcName)?)?; if let Ok(name) = str::from_utf8(name) @@ -563,7 +570,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Threading "CreateThread" => { let [security, stacksize, start, arg, flags, thread] = - this.check_shim(abi, sys_conv, link_name, args)?; + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let thread_id = this.CreateThread(security, stacksize, start, arg, flags, thread)?; @@ -571,12 +578,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Handle::Thread(thread_id).to_scalar(this), dest)?; } "WaitForSingleObject" => { - let [handle, timeout] = this.check_shim(abi, sys_conv, link_name, args)?; + let [handle, timeout] = + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.WaitForSingleObject(handle, timeout, dest)?; } "GetCurrentProcess" => { - let [] = this.check_shim(abi, sys_conv, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.write_scalar( Handle::Pseudo(PseudoHandle::CurrentProcess).to_scalar(this), @@ -584,7 +592,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; } "GetCurrentThread" => { - let [] = this.check_shim(abi, sys_conv, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.write_scalar( Handle::Pseudo(PseudoHandle::CurrentThread).to_scalar(this), @@ -592,7 +600,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; } "SetThreadDescription" => { - let [handle, name] = this.check_shim(abi, sys_conv, link_name, args)?; + let [handle, name] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let handle = this.read_handle(handle, "SetThreadDescription")?; let name = this.read_wide_str(this.read_pointer(name)?)?; @@ -607,7 +615,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Scalar::from_u32(0), dest)?; } "GetThreadDescription" => { - let [handle, name_ptr] = this.check_shim(abi, sys_conv, link_name, args)?; + let [handle, name_ptr] = + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let handle = this.read_handle(handle, "GetThreadDescription")?; let name_ptr = this.deref_pointer_as(name_ptr, this.machine.layouts.mut_raw_ptr)?; // the pointer where we should store the ptr to the name @@ -630,7 +639,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } "GetThreadId" => { - let [handle] = this.check_shim(abi, sys_conv, link_name, args)?; + let [handle] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let handle = this.read_handle(handle, "GetThreadId")?; let thread = match handle { Handle::Thread(thread) => thread, @@ -641,7 +650,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Scalar::from_u32(tid), dest)?; } "GetCurrentThreadId" => { - let [] = this.check_shim(abi, sys_conv, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let thread = this.active_thread(); let tid = this.get_tid(thread); this.write_scalar(Scalar::from_u32(tid), dest)?; @@ -649,7 +658,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Miscellaneous "ExitProcess" => { - let [code] = this.check_shim(abi, sys_conv, link_name, args)?; + let [code] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; // Windows technically uses u32, but we unify everything to a Unix-style i32. let code = this.read_scalar(code)?.to_i32()?; throw_machine_stop!(TerminationInfo::Exit { code, leak_check: false }); @@ -657,7 +666,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "SystemFunction036" => { // used by getrandom 0.1 // This is really 'RtlGenRandom'. - let [ptr, len] = this.check_shim(abi, sys_conv, link_name, args)?; + let [ptr, len] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let ptr = this.read_pointer(ptr)?; let len = this.read_scalar(len)?.to_u32()?; this.gen_random(ptr, len.into())?; @@ -665,7 +674,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "ProcessPrng" => { // used by `std` - let [ptr, len] = this.check_shim(abi, sys_conv, link_name, args)?; + let [ptr, len] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let ptr = this.read_pointer(ptr)?; let len = this.read_target_usize(len)?; this.gen_random(ptr, len)?; @@ -674,7 +683,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "BCryptGenRandom" => { // used by getrandom 0.2 let [algorithm, ptr, len, flags] = - this.check_shim(abi, sys_conv, link_name, args)?; + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let algorithm = this.read_scalar(algorithm)?; let algorithm = algorithm.to_target_usize(this)?; let ptr = this.read_pointer(ptr)?; @@ -708,7 +717,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "GetConsoleScreenBufferInfo" => { // `term` needs this, so we fake it. - let [console, buffer_info] = this.check_shim(abi, sys_conv, link_name, args)?; + let [console, buffer_info] = + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.read_target_isize(console)?; // FIXME: this should use deref_pointer_as, but CONSOLE_SCREEN_BUFFER_INFO is not in std this.deref_pointer(buffer_info)?; @@ -717,13 +727,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_null(dest)?; } "GetStdHandle" => { - let [which] = this.check_shim(abi, sys_conv, link_name, args)?; + let [which] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let res = this.GetStdHandle(which)?; this.write_scalar(res, dest)?; } "DuplicateHandle" => { let [src_proc, src_handle, target_proc, target_handle, access, inherit, options] = - this.check_shim(abi, sys_conv, link_name, args)?; + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let res = this.DuplicateHandle( src_proc, src_handle, @@ -736,14 +746,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } "CloseHandle" => { - let [handle] = this.check_shim(abi, sys_conv, link_name, args)?; + let [handle] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let ret = this.CloseHandle(handle)?; this.write_scalar(ret, dest)?; } "GetModuleFileNameW" => { - let [handle, filename, size] = this.check_shim(abi, sys_conv, link_name, args)?; + let [handle, filename, size] = + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.check_no_isolation("`GetModuleFileNameW`")?; let handle = this.read_handle(handle, "GetModuleFileNameW")?; @@ -777,7 +788,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "FormatMessageW" => { let [flags, module, message_id, language_id, buffer, size, arguments] = - this.check_shim(abi, sys_conv, link_name, args)?; + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let flags = this.read_scalar(flags)?.to_u32()?; let _module = this.read_pointer(module)?; // seems to contain a module name @@ -812,26 +823,28 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Incomplete shims that we "stub out" just to get pre-main initialization code to work. // These shims are enabled only when the caller is in the standard library. "GetProcessHeap" if this.frame_in_std() => { - let [] = this.check_shim(abi, sys_conv, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; // Just fake a HANDLE // It's fine to not use the Handle type here because its a stub this.write_int(1, dest)?; } "GetModuleHandleA" if this.frame_in_std() => { #[allow(non_snake_case)] - let [_lpModuleName] = this.check_shim(abi, sys_conv, link_name, args)?; + let [_lpModuleName] = + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; // We need to return something non-null here to make `compat_fn!` work. this.write_int(1, dest)?; } "SetConsoleTextAttribute" if this.frame_in_std() => { #[allow(non_snake_case)] let [_hConsoleOutput, _wAttribute] = - this.check_shim(abi, sys_conv, link_name, args)?; + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; // Pretend these does not exist / nothing happened, by returning zero. this.write_null(dest)?; } "GetConsoleMode" if this.frame_in_std() => { - let [console, mode] = this.check_shim(abi, sys_conv, link_name, args)?; + let [console, mode] = + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.read_target_isize(console)?; this.deref_pointer_as(mode, this.machine.layouts.u32)?; // Indicate an error. @@ -839,25 +852,27 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "GetFileType" if this.frame_in_std() => { #[allow(non_snake_case)] - let [_hFile] = this.check_shim(abi, sys_conv, link_name, args)?; + let [_hFile] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; // Return unknown file type. this.write_null(dest)?; } "AddVectoredExceptionHandler" if this.frame_in_std() => { #[allow(non_snake_case)] - let [_First, _Handler] = this.check_shim(abi, sys_conv, link_name, args)?; + let [_First, _Handler] = + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; // Any non zero value works for the stdlib. This is just used for stack overflows anyway. this.write_int(1, dest)?; } "SetThreadStackGuarantee" if this.frame_in_std() => { #[allow(non_snake_case)] - let [_StackSizeInBytes] = this.check_shim(abi, sys_conv, link_name, args)?; + let [_StackSizeInBytes] = + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; // Any non zero value works for the stdlib. This is just used for stack overflows anyway. this.write_int(1, dest)?; } // this is only callable from std because we know that std ignores the return value "SwitchToThread" if this.frame_in_std() => { - let [] = this.check_shim(abi, sys_conv, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.yield_active_thread(); @@ -876,7 +891,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ); } // This function looks and behaves excatly like miri_start_unwind. - let [payload] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [payload] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.handle_miri_start_unwind(payload)?; return interp_ok(EmulateItemResult::NeedsUnwind); } diff --git a/src/tools/miri/src/shims/windows/fs.rs b/src/tools/miri/src/shims/windows/fs.rs index 72e016c12e944..e4ec1b0130c9d 100644 --- a/src/tools/miri/src/shims/windows/fs.rs +++ b/src/tools/miri/src/shims/windows/fs.rs @@ -462,6 +462,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; let io_status_info = this.project_field_named(&io_status_block, "Information")?; + // It seems like short writes are not a thing on Windows, so we don't truncate `count` here. + // FIXME: if we are on a Unix host, short host writes are still visible to the program! + let finish = { let io_status = io_status.clone(); let io_status_info = io_status_info.clone(); @@ -491,7 +494,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }} ) }; - desc.write(this.machine.communicate(), buf, count.try_into().unwrap(), this, finish)?; // Return status is written to `dest` and `io_status_block` on callback completion. @@ -556,6 +558,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; let io_status_info = this.project_field_named(&io_status_block, "Information")?; + let fd = match handle { + Handle::File(fd) => fd, + _ => this.invalid_handle("NtWriteFile")?, + }; + + let Some(desc) = this.machine.fds.get(fd) else { this.invalid_handle("NtReadFile")? }; + + // It seems like short reads are not a thing on Windows, so we don't truncate `count` here. + // FIXME: if we are on a Unix host, short host reads are still visible to the program! + let finish = { let io_status = io_status.clone(); let io_status_info = io_status_info.clone(); @@ -585,14 +597,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }} ) }; - - let fd = match handle { - Handle::File(fd) => fd, - _ => this.invalid_handle("NtWriteFile")?, - }; - - let Some(desc) = this.machine.fds.get(fd) else { this.invalid_handle("NtReadFile")? }; - desc.read(this.machine.communicate(), buf, count.try_into().unwrap(), this, finish)?; // See NtWriteFile for commentary on this diff --git a/src/tools/miri/src/shims/x86/aesni.rs b/src/tools/miri/src/shims/x86/aesni.rs index 058ca24e730f6..fdd3e78c61052 100644 --- a/src/tools/miri/src/shims/x86/aesni.rs +++ b/src/tools/miri/src/shims/x86/aesni.rs @@ -26,7 +26,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // `state` with the corresponding 128-bit key of `key`. // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesdec_si128 "aesdec" | "aesdec.256" | "aesdec.512" => { - let [state, key] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [state, key] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; aes_round(this, state, key, dest, |state, key| { let key = aes::Block::from(key.to_le_bytes()); let mut state = aes::Block::from(state.to_le_bytes()); @@ -42,7 +43,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // `state` with the corresponding 128-bit key of `key`. // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesdeclast_si128 "aesdeclast" | "aesdeclast.256" | "aesdeclast.512" => { - let [state, key] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [state, key] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; aes_round(this, state, key, dest, |state, key| { let mut state = aes::Block::from(state.to_le_bytes()); @@ -66,7 +68,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // `state` with the corresponding 128-bit key of `key`. // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesenc_si128 "aesenc" | "aesenc.256" | "aesenc.512" => { - let [state, key] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [state, key] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; aes_round(this, state, key, dest, |state, key| { let key = aes::Block::from(key.to_le_bytes()); let mut state = aes::Block::from(state.to_le_bytes()); @@ -82,7 +85,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // `state` with the corresponding 128-bit key of `key`. // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesenclast_si128 "aesenclast" | "aesenclast.256" | "aesenclast.512" => { - let [state, key] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [state, key] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; aes_round(this, state, key, dest, |state, key| { let mut state = aes::Block::from(state.to_le_bytes()); // `aes::hazmat::cipher_round` does the following operations: @@ -102,7 +106,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Used to implement the _mm_aesimc_si128 function. // Performs the AES InvMixColumns operation on `op` "aesimc" => { - let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; // Transmute to `u128` let op = op.transmute(this.machine.layouts.u128, this)?; let dest = dest.transmute(this.machine.layouts.u128, this)?; diff --git a/src/tools/miri/src/shims/x86/avx.rs b/src/tools/miri/src/shims/x86/avx.rs index 83d23d6ad3698..269ce3b51b93f 100644 --- a/src/tools/miri/src/shims/x86/avx.rs +++ b/src/tools/miri/src/shims/x86/avx.rs @@ -33,7 +33,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // matches the IEEE min/max operations, while x86 has different // semantics. "min.ps.256" | "max.ps.256" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = match unprefixed_name { "min.ps.256" => FloatBinOp::Min, @@ -45,7 +46,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } // Used to implement _mm256_min_pd and _mm256_max_pd functions. "min.pd.256" | "max.pd.256" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = match unprefixed_name { "min.pd.256" => FloatBinOp::Min, @@ -58,21 +60,23 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Used to implement the _mm256_round_ps function. // Rounds the elements of `op` according to `rounding`. "round.ps.256" => { - let [op, rounding] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op, rounding] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; round_all::(this, op, rounding, dest)?; } // Used to implement the _mm256_round_pd function. // Rounds the elements of `op` according to `rounding`. "round.pd.256" => { - let [op, rounding] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op, rounding] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; round_all::(this, op, rounding, dest)?; } // Used to implement _mm256_{rcp,rsqrt}_ps functions. // Performs the operations on all components of `op`. "rcp.ps.256" | "rsqrt.ps.256" => { - let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = match unprefixed_name { "rcp.ps.256" => FloatUnaryOp::Rcp, @@ -84,7 +88,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } // Used to implement the _mm256_dp_ps function. "dp.ps.256" => { - let [left, right, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; conditional_dot_product(this, left, right, imm, dest)?; } @@ -92,7 +97,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Horizontally add/subtract adjacent floating point values // in `left` and `right`. "hadd.ps.256" | "hadd.pd.256" | "hsub.ps.256" | "hsub.pd.256" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = match unprefixed_name { "hadd.ps.256" | "hadd.pd.256" => mir::BinOp::Add, @@ -107,7 +113,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // and `right`. For each component, returns 0 if false or u32::MAX // if true. "cmp.ps.256" => { - let [left, right, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?; @@ -119,7 +126,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // and `right`. For each component, returns 0 if false or u64::MAX // if true. "cmp.pd.256" => { - let [left, right, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?; @@ -130,7 +138,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // and _mm256_cvttpd_epi32 functions. // Converts packed f32/f64 to packed i32. "cvt.ps2dq.256" | "cvtt.ps2dq.256" | "cvt.pd2dq.256" | "cvtt.pd2dq.256" => { - let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let rnd = match unprefixed_name { // "current SSE rounding mode", assume nearest @@ -148,7 +156,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // sequence of 4-element arrays, and we shuffle each of these arrays, where // `control` determines which element of the current `data` array is written. "vpermilvar.ps" | "vpermilvar.ps.256" => { - let [data, control] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [data, control] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (data, data_len) = this.project_to_simd(data)?; let (control, control_len) = this.project_to_simd(control)?; @@ -181,7 +190,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // where `right` determines which element of the current `left` array is // written. "vpermilvar.pd" | "vpermilvar.pd.256" => { - let [data, control] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [data, control] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (data, data_len) = this.project_to_simd(data)?; let (control, control_len) = this.project_to_simd(control)?; @@ -213,7 +223,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // For each 128-bit element of `dest`, copies one from `left`, `right` or // zero, according to `imm`. "vperm2f128.ps.256" | "vperm2f128.pd.256" | "vperm2f128.si.256" => { - let [left, right, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; assert_eq!(dest.layout, left.layout); assert_eq!(dest.layout, right.layout); @@ -256,7 +267,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // is one, it is loaded from `ptr.wrapping_add(i)`, otherwise zero is // loaded. "maskload.ps" | "maskload.pd" | "maskload.ps.256" | "maskload.pd.256" => { - let [ptr, mask] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [ptr, mask] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; mask_load(this, ptr, mask, dest)?; } @@ -266,7 +277,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // is one, it is stored into `ptr.wapping_add(i)`. // Unlike SSE2's _mm_maskmoveu_si128, these are not non-temporal stores. "maskstore.ps" | "maskstore.pd" | "maskstore.ps.256" | "maskstore.pd.256" => { - let [ptr, mask, value] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [ptr, mask, value] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; mask_store(this, ptr, mask, value)?; } @@ -276,7 +288,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // the data crosses a cache line, but for Miri this is just a regular // unaligned read. "ldu.dq.256" => { - let [src_ptr] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [src_ptr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let src_ptr = this.read_pointer(src_ptr)?; let dest = dest.force_mplace(this)?; @@ -288,7 +300,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Tests `op & mask == 0`, `op & mask == mask` or // `op & mask != 0 && op & mask != mask` "ptestz.256" | "ptestc.256" | "ptestnzc.256" => { - let [op, mask] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op, mask] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (all_zero, masked_set) = test_bits_masked(this, op, mask)?; let res = match unprefixed_name { @@ -311,7 +323,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "vtestz.pd.256" | "vtestc.pd.256" | "vtestnzc.pd.256" | "vtestz.pd" | "vtestc.pd" | "vtestnzc.pd" | "vtestz.ps.256" | "vtestc.ps.256" | "vtestnzc.ps.256" | "vtestz.ps" | "vtestc.ps" | "vtestnzc.ps" => { - let [op, mask] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op, mask] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (direct, negated) = test_high_bits_masked(this, op, mask)?; let res = match unprefixed_name { @@ -333,7 +345,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // compiler, making these functions no-ops. // The only thing that needs to be ensured is the correct calling convention. - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; } _ => return interp_ok(EmulateItemResult::NotSupported), } diff --git a/src/tools/miri/src/shims/x86/avx2.rs b/src/tools/miri/src/shims/x86/avx2.rs index 49d5977078b03..ca80c0eba1e5c 100644 --- a/src/tools/miri/src/shims/x86/avx2.rs +++ b/src/tools/miri/src/shims/x86/avx2.rs @@ -28,7 +28,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Used to implement the _mm256_abs_epi{8,16,32} functions. // Calculates the absolute value of packed 8/16/32-bit integers. "pabs.b" | "pabs.w" | "pabs.d" => { - let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; int_abs(this, op, dest)?; } @@ -36,7 +36,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Horizontally add / add with saturation / subtract adjacent 16/32-bit // integer values in `left` and `right`. "phadd.w" | "phadd.sw" | "phadd.d" | "phsub.w" | "phsub.sw" | "phsub.d" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (which, saturating) = match unprefixed_name { "phadd.w" | "phadd.d" => (mir::BinOp::Add, false), @@ -57,7 +58,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | "gather.d.pd.256" | "gather.q.pd" | "gather.q.pd.256" | "gather.d.ps" | "gather.d.ps.256" | "gather.q.ps" | "gather.q.ps.256" => { let [src, slice, offsets, mask, scale] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; assert_eq!(dest.layout, src.layout); @@ -114,7 +115,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // intermediate signed 32-bit integers. Horizontally add adjacent pairs of // intermediate 32-bit integers, and pack the results in `dest`. "pmadd.wd" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (right, right_len) = this.project_to_simd(right)?; @@ -150,7 +152,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // the saturating sum of the products with indices `2*i` and `2*i+1` // produces the output at index `i`. "pmadd.ub.sw" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (right, right_len) = this.project_to_simd(right)?; @@ -184,7 +187,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // is one, it is loaded from `ptr.wrapping_add(i)`, otherwise zero is // loaded. "maskload.d" | "maskload.q" | "maskload.d.256" | "maskload.q.256" => { - let [ptr, mask] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [ptr, mask] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; mask_load(this, ptr, mask, dest)?; } @@ -194,7 +197,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // is one, it is stored into `ptr.wapping_add(i)`. // Unlike SSE2's _mm_maskmoveu_si128, these are not non-temporal stores. "maskstore.d" | "maskstore.q" | "maskstore.d.256" | "maskstore.q.256" => { - let [ptr, mask, value] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [ptr, mask, value] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; mask_store(this, ptr, mask, value)?; } @@ -205,7 +209,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // offsets specified in `imm`. // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_mpsadbw_epu8 "mpsadbw" => { - let [left, right, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; mpsadbw(this, left, right, imm, dest)?; } @@ -216,7 +221,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // 1 and then taking the bits `1..=16`. // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_mulhrs_epi16 "pmul.hr.sw" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; pmulhrsw(this, left, right, dest)?; } @@ -224,7 +230,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Converts two 16-bit integer vectors to a single 8-bit integer // vector with signed saturation. "packsswb" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; packsswb(this, left, right, dest)?; } @@ -232,7 +239,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Converts two 32-bit integer vectors to a single 16-bit integer // vector with signed saturation. "packssdw" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; packssdw(this, left, right, dest)?; } @@ -240,7 +248,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Converts two 16-bit signed integer vectors to a single 8-bit // unsigned integer vector with saturation. "packuswb" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; packuswb(this, left, right, dest)?; } @@ -248,7 +257,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Concatenates two 32-bit signed integer vectors and converts // the result to a 16-bit unsigned integer vector with saturation. "packusdw" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; packusdw(this, left, right, dest)?; } @@ -257,7 +267,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Shuffles `left` using the three low bits of each element of `right` // as indices. "permd" | "permps" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (right, right_len) = this.project_to_simd(right)?; @@ -277,7 +288,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Used to implement the _mm256_permute2x128_si256 function. // Shuffles 128-bit blocks of `a` and `b` using `imm` as pattern. "vperm2i128" => { - let [left, right, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; assert_eq!(left.layout.size.bits(), 256); assert_eq!(right.layout.size.bits(), 256); @@ -314,7 +326,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // in `dest`. // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_sad_epu8 "psad.bw" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (right, right_len) = this.project_to_simd(right)?; @@ -346,7 +359,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Shuffles bytes from `left` using `right` as pattern. // Each 128-bit block is shuffled independently. "pshuf.b" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (right, right_len) = this.project_to_simd(right)?; @@ -377,7 +391,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // is writen to the corresponding output element. // Basically, we multiply `left` with `right.signum()`. "psign.b" | "psign.w" | "psign.d" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; psign(this, left, right, dest)?; } @@ -391,7 +406,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // is copied to remaining bits. "psll.w" | "psrl.w" | "psra.w" | "psll.d" | "psrl.d" | "psra.d" | "psll.q" | "psrl.q" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = match unprefixed_name { "psll.w" | "psll.d" | "psll.q" => ShiftOp::Left, @@ -406,7 +422,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // (except _mm{,256}_srav_epi64, which are not available in AVX2). "psllv.d" | "psllv.d.256" | "psllv.q" | "psllv.q.256" | "psrlv.d" | "psrlv.d.256" | "psrlv.q" | "psrlv.q.256" | "psrav.d" | "psrav.d.256" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = match unprefixed_name { "psllv.d" | "psllv.d.256" | "psllv.q" | "psllv.q.256" => ShiftOp::Left, diff --git a/src/tools/miri/src/shims/x86/bmi.rs b/src/tools/miri/src/shims/x86/bmi.rs index 80b1b2e16e609..140e31cc51333 100644 --- a/src/tools/miri/src/shims/x86/bmi.rs +++ b/src/tools/miri/src/shims/x86/bmi.rs @@ -35,7 +35,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return interp_ok(EmulateItemResult::NotSupported); } - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let left = this.read_scalar(left)?; let right = this.read_scalar(right)?; diff --git a/src/tools/miri/src/shims/x86/gfni.rs b/src/tools/miri/src/shims/x86/gfni.rs index f83ce560c84e9..9a98a80d6dcc2 100644 --- a/src/tools/miri/src/shims/x86/gfni.rs +++ b/src/tools/miri/src/shims/x86/gfni.rs @@ -31,14 +31,16 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // See `affine_transform` for details. // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=gf2p8affine_ "vgf2p8affineqb.128" | "vgf2p8affineqb.256" | "vgf2p8affineqb.512" => { - let [left, right, imm8] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm8] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; affine_transform(this, left, right, imm8, dest, /* inverse */ false)?; } // Used to implement the `_mm{, 256, 512}_gf2p8affineinv_epi64_epi8` functions. // See `affine_transform` for details. // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=gf2p8affineinv "vgf2p8affineinvqb.128" | "vgf2p8affineinvqb.256" | "vgf2p8affineinvqb.512" => { - let [left, right, imm8] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm8] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; affine_transform(this, left, right, imm8, dest, /* inverse */ true)?; } // Used to implement the `_mm{, 256, 512}_gf2p8mul_epi8` functions. @@ -47,7 +49,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // polynomial representation with the reduction polynomial x^8 + x^4 + x^3 + x + 1. // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=gf2p8mul "vgf2p8mulb.128" | "vgf2p8mulb.256" | "vgf2p8mulb.512" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (right, right_len) = this.project_to_simd(right)?; let (dest, dest_len) = this.project_to_simd(dest)?; diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs index fbfe459711e0d..3324b7b024ace 100644 --- a/src/tools/miri/src/shims/x86/mod.rs +++ b/src/tools/miri/src/shims/x86/mod.rs @@ -45,7 +45,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return interp_ok(EmulateItemResult::NotSupported); } - let [cb_in, a, b] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [cb_in, a, b] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let op = if unprefixed_name.starts_with("add") { mir::BinOp::AddWithOverflow } else { @@ -67,7 +68,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if is_u64 && this.tcx.sess.target.arch != "x86_64" { return interp_ok(EmulateItemResult::NotSupported); } - let [c_in, a, b, out] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [c_in, a, b, out] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let out = this.deref_pointer_as( out, if is_u64 { this.machine.layouts.u64 } else { this.machine.layouts.u32 }, @@ -84,7 +86,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // the instruction behaves like a no-op, so it is always safe to call the // intrinsic. "sse2.pause" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; // Only exhibit the spin-loop hint behavior when SSE2 is enabled. if this.tcx.sess.unstable_target_features.contains(&Symbol::intern("sse2")) { this.yield_active_thread(); @@ -103,7 +105,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { len = 8; } - let [left, right, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; pclmulqdq(this, left, right, imm, dest, len)?; } diff --git a/src/tools/miri/src/shims/x86/sha.rs b/src/tools/miri/src/shims/x86/sha.rs index d37fad3e6c753..00fe58119e472 100644 --- a/src/tools/miri/src/shims/x86/sha.rs +++ b/src/tools/miri/src/shims/x86/sha.rs @@ -53,7 +53,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match unprefixed_name { // Used to implement the _mm_sha256rnds2_epu32 function. "256rnds2" => { - let [a, b, k] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [a, b, k] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (a_reg, a_len) = this.project_to_simd(a)?; let (b_reg, b_len) = this.project_to_simd(b)?; @@ -74,7 +74,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } // Used to implement the _mm_sha256msg1_epu32 function. "256msg1" => { - let [a, b] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [a, b] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (a_reg, a_len) = this.project_to_simd(a)?; let (b_reg, b_len) = this.project_to_simd(b)?; @@ -92,7 +92,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } // Used to implement the _mm_sha256msg2_epu32 function. "256msg2" => { - let [a, b] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [a, b] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (a_reg, a_len) = this.project_to_simd(a)?; let (b_reg, b_len) = this.project_to_simd(b)?; diff --git a/src/tools/miri/src/shims/x86/sse.rs b/src/tools/miri/src/shims/x86/sse.rs index 1ec15d609c687..6d8def5b53fca 100644 --- a/src/tools/miri/src/shims/x86/sse.rs +++ b/src/tools/miri/src/shims/x86/sse.rs @@ -34,7 +34,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Performs the operations on the first component of `left` and // `right` and copies the remaining components from `left`. "min.ss" | "max.ss" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = match unprefixed_name { "min.ss" => FloatBinOp::Min, @@ -50,7 +51,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // matches the IEEE min/max operations, while x86 has different // semantics. "min.ps" | "max.ps" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = match unprefixed_name { "min.ps" => FloatBinOp::Min, @@ -64,7 +66,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Performs the operations on the first component of `op` and // copies the remaining components from `op`. "rcp.ss" | "rsqrt.ss" => { - let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = match unprefixed_name { "rcp.ss" => FloatUnaryOp::Rcp, @@ -77,7 +79,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Used to implement _mm_{sqrt,rcp,rsqrt}_ps functions. // Performs the operations on all components of `op`. "rcp.ps" | "rsqrt.ps" => { - let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = match unprefixed_name { "rcp.ps" => FloatUnaryOp::Rcp, @@ -96,7 +98,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // _mm_cmp{eq,lt,le,gt,ge,neq,nlt,nle,ngt,nge,ord,unord}_ss are SSE functions // with hard-coded operations. "cmp.ss" => { - let [left, right, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?; @@ -112,7 +115,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // _mm_cmp{eq,lt,le,gt,ge,neq,nlt,nle,ngt,nge,ord,unord}_ps are SSE functions // with hard-coded operations. "cmp.ps" => { - let [left, right, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?; @@ -125,7 +129,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "comieq.ss" | "comilt.ss" | "comile.ss" | "comigt.ss" | "comige.ss" | "comineq.ss" | "ucomieq.ss" | "ucomilt.ss" | "ucomile.ss" | "ucomigt.ss" | "ucomige.ss" | "ucomineq.ss" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (right, right_len) = this.project_to_simd(right)?; @@ -153,7 +158,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // _mm_cvtss_si64 and _mm_cvttss_si64 functions. // Converts the first component of `op` from f32 to i32/i64. "cvtss2si" | "cvttss2si" | "cvtss2si64" | "cvttss2si64" => { - let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (op, _) = this.project_to_simd(op)?; let op = this.read_immediate(&this.project_index(&op, 0)?)?; @@ -181,7 +186,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // are copied from `left`. // https://www.felixcloutier.com/x86/cvtsi2ss "cvtsi2ss" | "cvtsi642ss" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (dest, dest_len) = this.project_to_simd(dest)?; diff --git a/src/tools/miri/src/shims/x86/sse2.rs b/src/tools/miri/src/shims/x86/sse2.rs index d6052f8307716..8f53adfb5ecf3 100644 --- a/src/tools/miri/src/shims/x86/sse2.rs +++ b/src/tools/miri/src/shims/x86/sse2.rs @@ -41,7 +41,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // intermediate signed 32-bit integers. Horizontally add adjacent pairs of // intermediate 32-bit integers, and pack the results in `dest`. "pmadd.wd" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (right, right_len) = this.project_to_simd(right)?; @@ -79,7 +80,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sad_epu8 "psad.bw" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (right, right_len) = this.project_to_simd(right)?; @@ -117,7 +119,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // is copied to remaining bits. "psll.w" | "psrl.w" | "psra.w" | "psll.d" | "psrl.d" | "psra.d" | "psll.q" | "psrl.q" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = match unprefixed_name { "psll.w" | "psll.d" | "psll.q" => ShiftOp::Left, @@ -132,7 +135,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // and _mm_cvttpd_epi32 functions. // Converts packed f32/f64 to packed i32. "cvtps2dq" | "cvttps2dq" | "cvtpd2dq" | "cvttpd2dq" => { - let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (op_len, _) = op.layout.ty.simd_size_and_type(*this.tcx); let (dest_len, _) = dest.layout.ty.simd_size_and_type(*this.tcx); @@ -169,7 +172,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Converts two 16-bit integer vectors to a single 8-bit integer // vector with signed saturation. "packsswb.128" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; packsswb(this, left, right, dest)?; } @@ -177,7 +181,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Converts two 16-bit signed integer vectors to a single 8-bit // unsigned integer vector with saturation. "packuswb.128" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; packuswb(this, left, right, dest)?; } @@ -185,7 +190,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Converts two 32-bit integer vectors to a single 16-bit integer // vector with signed saturation. "packssdw.128" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; packssdw(this, left, right, dest)?; } @@ -195,7 +201,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // matches the IEEE min/max operations, while x86 has different // semantics. "min.sd" | "max.sd" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = match unprefixed_name { "min.sd" => FloatBinOp::Min, @@ -211,7 +218,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // matches the IEEE min/max operations, while x86 has different // semantics. "min.pd" | "max.pd" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = match unprefixed_name { "min.pd" => FloatBinOp::Min, @@ -230,7 +238,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // _mm_cmp{eq,lt,le,gt,ge,neq,nlt,nle,ngt,nge,ord,unord}_sd are SSE2 functions // with hard-coded operations. "cmp.sd" => { - let [left, right, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?; @@ -246,7 +255,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // _mm_cmp{eq,lt,le,gt,ge,neq,nlt,nle,ngt,nge,ord,unord}_pd are SSE2 functions // with hard-coded operations. "cmp.pd" => { - let [left, right, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?; @@ -259,7 +269,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "comieq.sd" | "comilt.sd" | "comile.sd" | "comigt.sd" | "comige.sd" | "comineq.sd" | "ucomieq.sd" | "ucomilt.sd" | "ucomile.sd" | "ucomigt.sd" | "ucomige.sd" | "ucomineq.sd" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (right, right_len) = this.project_to_simd(right)?; @@ -287,7 +298,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // _mm_cvtsd_si64 and _mm_cvttsd_si64 functions. // Converts the first component of `op` from f64 to i32/i64. "cvtsd2si" | "cvttsd2si" | "cvtsd2si64" | "cvttsd2si64" => { - let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (op, _) = this.project_to_simd(op)?; let op = this.read_immediate(&this.project_index(&op, 0)?)?; @@ -313,7 +324,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Converts the first f64/f32 from `right` to f32/f64 and copies // the remaining elements from `left` "cvtsd2ss" | "cvtss2sd" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (right, _) = this.project_to_simd(right)?; diff --git a/src/tools/miri/src/shims/x86/sse3.rs b/src/tools/miri/src/shims/x86/sse3.rs index ebf3cb5c3ee0a..0fd8c3bc389b0 100644 --- a/src/tools/miri/src/shims/x86/sse3.rs +++ b/src/tools/miri/src/shims/x86/sse3.rs @@ -26,7 +26,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Horizontally add/subtract adjacent floating point values // in `left` and `right`. "hadd.ps" | "hadd.pd" | "hsub.ps" | "hsub.pd" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = match unprefixed_name { "hadd.ps" | "hadd.pd" => mir::BinOp::Add, @@ -42,7 +43,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // the data crosses a cache line, but for Miri this is just a regular // unaligned read. "ldu.dq" => { - let [src_ptr] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [src_ptr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let src_ptr = this.read_pointer(src_ptr)?; let dest = dest.force_mplace(this)?; diff --git a/src/tools/miri/src/shims/x86/sse41.rs b/src/tools/miri/src/shims/x86/sse41.rs index 6797039cf56f2..7736b5e443d0c 100644 --- a/src/tools/miri/src/shims/x86/sse41.rs +++ b/src/tools/miri/src/shims/x86/sse41.rs @@ -28,7 +28,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // bits `4..=5` if `imm`, and `i`th bit specifies whether element // `i` is zeroed. "insertps" => { - let [left, right, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (right, right_len) = this.project_to_simd(right)?; @@ -63,7 +64,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Concatenates two 32-bit signed integer vectors and converts // the result to a 16-bit unsigned integer vector with saturation. "packusdw" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; packusdw(this, left, right, dest)?; } @@ -73,7 +75,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // products, and conditionally stores the sum in `dest` using the low // 4 bits of `imm`. "dpps" | "dppd" => { - let [left, right, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; conditional_dot_product(this, left, right, imm, dest)?; } @@ -81,14 +84,16 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // functions. Rounds the first element of `right` according to `rounding` // and copies the remaining elements from `left`. "round.ss" => { - let [left, right, rounding] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, rounding] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; round_first::(this, left, right, rounding, dest)?; } // Used to implement the _mm_floor_ps, _mm_ceil_ps and _mm_round_ps // functions. Rounds the elements of `op` according to `rounding`. "round.ps" => { - let [op, rounding] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op, rounding] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; round_all::(this, op, rounding, dest)?; } @@ -96,14 +101,16 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // functions. Rounds the first element of `right` according to `rounding` // and copies the remaining elements from `left`. "round.sd" => { - let [left, right, rounding] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, rounding] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; round_first::(this, left, right, rounding, dest)?; } // Used to implement the _mm_floor_pd, _mm_ceil_pd and _mm_round_pd // functions. Rounds the elements of `op` according to `rounding`. "round.pd" => { - let [op, rounding] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op, rounding] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; round_all::(this, op, rounding, dest)?; } @@ -111,7 +118,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Find the minimum unsinged 16-bit integer in `op` and // returns its value and position. "phminposuw" => { - let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (op, op_len) = this.project_to_simd(op)?; let (dest, dest_len) = this.project_to_simd(dest)?; @@ -145,7 +152,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // offsets specified in `imm`. // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mpsadbw_epu8 "mpsadbw" => { - let [left, right, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; mpsadbw(this, left, right, imm, dest)?; } @@ -154,7 +162,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Tests `(op & mask) == 0`, `(op & mask) == mask` or // `(op & mask) != 0 && (op & mask) != mask` "ptestz" | "ptestc" | "ptestnzc" => { - let [op, mask] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op, mask] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (all_zero, masked_set) = test_bits_masked(this, op, mask)?; let res = match unprefixed_name { diff --git a/src/tools/miri/src/shims/x86/sse42.rs b/src/tools/miri/src/shims/x86/sse42.rs index 7e1e1482ef479..72c5039a12d3c 100644 --- a/src/tools/miri/src/shims/x86/sse42.rs +++ b/src/tools/miri/src/shims/x86/sse42.rs @@ -222,7 +222,8 @@ fn deconstruct_args<'tcx>( }; if is_explicit { - let [str1, len1, str2, len2, imm] = ecx.check_shim(abi, CanonAbi::C, link_name, args)?; + let [str1, len1, str2, len2, imm] = + ecx.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let imm = ecx.read_scalar(imm)?.to_u8()?; let default_len = default_len::(imm); @@ -235,7 +236,7 @@ fn deconstruct_args<'tcx>( interp_ok((str1, str2, Some((len1, len2)), imm)) } else { - let [str1, str2, imm] = ecx.check_shim(abi, CanonAbi::C, link_name, args)?; + let [str1, str2, imm] = ecx.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let imm = ecx.read_scalar(imm)?.to_u8()?; let array_layout = array_layout_fn(ecx, imm)?; @@ -385,7 +386,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // search for a null terminator (see `deconstruct_args` for more details). // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#ig_expand=924,925 "pcmpistriz128" | "pcmpistris128" => { - let [str1, str2, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [str1, str2, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let imm = this.read_scalar(imm)?.to_u8()?; let str = if unprefixed_name == "pcmpistris128" { str1 } else { str2 }; @@ -405,7 +407,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // than 16 for byte-sized operands or 8 for word-sized operands. // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#ig_expand=1046,1047 "pcmpestriz128" | "pcmpestris128" => { - let [_, len1, _, len2, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [_, len1, _, len2, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let len = if unprefixed_name == "pcmpestris128" { len1 } else { len2 }; let len = this.read_scalar(len)?.to_i32()?; let imm = this.read_scalar(imm)?.to_u8()?; @@ -432,7 +435,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return interp_ok(EmulateItemResult::NotSupported); } - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let left = this.read_scalar(left)?; let right = this.read_scalar(right)?; diff --git a/src/tools/miri/src/shims/x86/ssse3.rs b/src/tools/miri/src/shims/x86/ssse3.rs index 310d6b8f765ab..52ad6bd441992 100644 --- a/src/tools/miri/src/shims/x86/ssse3.rs +++ b/src/tools/miri/src/shims/x86/ssse3.rs @@ -25,7 +25,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Used to implement the _mm_abs_epi{8,16,32} functions. // Calculates the absolute value of packed 8/16/32-bit integers. "pabs.b.128" | "pabs.w.128" | "pabs.d.128" => { - let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; int_abs(this, op, dest)?; } @@ -33,7 +33,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Shuffles bytes from `left` using `right` as pattern. // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_shuffle_epi8 "pshuf.b.128" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (right, right_len) = this.project_to_simd(right)?; @@ -62,7 +63,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // integer values in `left` and `right`. "phadd.w.128" | "phadd.sw.128" | "phadd.d.128" | "phsub.w.128" | "phsub.sw.128" | "phsub.d.128" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (which, saturating) = match unprefixed_name { "phadd.w.128" | "phadd.d.128" => (mir::BinOp::Add, false), @@ -81,7 +83,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // produces the output at index `i`. // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_maddubs_epi16 "pmadd.ub.sw.128" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (right, right_len) = this.project_to_simd(right)?; @@ -116,7 +119,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // 1 and then taking the bits `1..=16`. // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mulhrs_epi16 "pmul.hr.sw.128" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; pmulhrsw(this, left, right, dest)?; } @@ -126,7 +130,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // is writen to the corresponding output element. // Basically, we multiply `left` with `right.signum()`. "psign.b.128" | "psign.w.128" | "psign.d.128" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; psign(this, left, right, dest)?; } diff --git a/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs b/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs index 314ce90cfb5d4..f6ec5be61bb60 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs +++ b/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs @@ -10,6 +10,9 @@ use std::convert::TryInto; use std::thread; use std::thread::spawn; +#[path = "../../utils/libc.rs"] +mod libc_utils; + #[track_caller] fn check_epoll_wait(epfd: i32, expected_notifications: &[(u32, u64)]) { let epoll_event = libc::epoll_event { events: 0, u64: 0 }; @@ -69,12 +72,12 @@ fn main() { unsafe { VAL_ONE = 41 }; let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds_a[0], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds_a[0], data as *const libc::c_void, 5) }; assert_eq!(res, 5); unsafe { VAL_TWO = 51 }; - let res = unsafe { libc::write(fds_b[0], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds_b[0], data as *const libc::c_void, 5) }; assert_eq!(res, 5); }); thread::yield_now(); diff --git a/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs b/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs index 1dc334486c3aa..e2fd6463a116d 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs +++ b/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs @@ -10,6 +10,9 @@ use std::mem::MaybeUninit; #[path = "../../utils/mod.rs"] mod utils; +#[path = "../../utils/libc.rs"] +mod libc_utils; + fn main() { let path = utils::prepare_with_content("fail-libc-read-and-uninit-premature-eof.txt", &[1u8, 2, 3]); @@ -18,8 +21,9 @@ fn main() { let fd = libc::open(cpath.as_ptr(), libc::O_RDONLY); assert_ne!(fd, -1); let mut buf: MaybeUninit<[u8; 4]> = std::mem::MaybeUninit::uninit(); - // Read 4 bytes from a 3-byte file. - assert_eq!(libc::read(fd, buf.as_mut_ptr().cast::(), 4), 3); + // Read as much as we can from a 3-byte file. + let res = libc_utils::read_all(fd, buf.as_mut_ptr().cast::(), 4); + assert!(res == 3); buf.assume_init(); //~ERROR: encountered uninitialized memory, but expected an integer assert_eq!(libc::close(fd), 0); } diff --git a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs index f6f2e2b93121b..054cb812d9e84 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs +++ b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs @@ -75,9 +75,10 @@ fn main() { }); let thread3 = spawn(move || { + // Just a single write, so we only wake up one of them. let data = "abcde".as_bytes().as_ptr(); let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) }; - assert_eq!(res, 5); + assert!(res > 0 && res <= 5); }); thread1.join().unwrap(); diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.rs b/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.rs index b383985950029..0fecfb8f663f9 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.rs +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.rs @@ -4,6 +4,7 @@ // test_race depends on a deterministic schedule. //@compile-flags: -Zmiri-deterministic-concurrency //@error-in-other-file: deadlock +//@require-annotations-for-level: error use std::thread; @@ -22,24 +23,26 @@ fn main() { assert_eq!(res, 0); let thread1 = thread::spawn(move || { // Let this thread block on read. - let mut buf: [u8; 3] = [0; 3]; + let mut buf: [u8; 1] = [0; 1]; let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; - assert_eq!(res, 3); - assert_eq!(&buf, "abc".as_bytes()); + assert_eq!(res, buf.len().cast_signed()); + assert_eq!(&buf, "a".as_bytes()); }); let thread2 = thread::spawn(move || { // Let this thread block on read. - let mut buf: [u8; 3] = [0; 3]; - let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; - //~^ERROR: deadlocked - assert_eq!(res, 3); - assert_eq!(&buf, "abc".as_bytes()); + let mut buf: [u8; 1] = [0; 1]; + let res = unsafe { + libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) + //~^ERROR: deadlock + }; + assert_eq!(res, buf.len().cast_signed()); + assert_eq!(&buf, "a".as_bytes()); }); let thread3 = thread::spawn(move || { // Unblock thread1 by writing something. - let data = "abc".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) }; - assert_eq!(res, 3); + let data = "a".as_bytes(); + let res = unsafe { libc::write(fds[0], data.as_ptr() as *const libc::c_void, data.len()) }; + assert_eq!(res, data.len().cast_signed()); }); thread1.join().unwrap(); thread2.join().unwrap(); diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr index 9f19a60e6ae2b..99d242ec7dafa 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr @@ -23,8 +23,8 @@ error: the evaluated program deadlocked error: the evaluated program deadlocked --> tests/fail-dep/libc/socketpair_block_read_twice.rs:LL:CC | -LL | let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; - | ^ this thread got stuck here +LL | libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) + | ^ this thread got stuck here | = note: BACKTRACE on thread `unnamed-ID`: = note: inside closure at tests/fail-dep/libc/socketpair_block_read_twice.rs:LL:CC diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.rs b/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.rs index 7d84d87ebbb14..048938c091e3a 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.rs +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.rs @@ -4,16 +4,20 @@ // test_race depends on a deterministic schedule. //@compile-flags: -Zmiri-deterministic-concurrency //@error-in-other-file: deadlock +//@require-annotations-for-level: error use std::thread; +#[path = "../../utils/libc.rs"] +mod libc_utils; + // Test the behaviour of a thread being blocked on write, get unblocked, then blocked again. // The expected execution is // 1. Thread 1 blocks. // 2. Thread 2 blocks. // 3. Thread 3 unblocks both thread 1 and thread 2. -// 4. Thread 1 reads. +// 4. Thread 1 writes. // 5. Thread 2's `write` can never complete -> deadlocked. fn main() { let mut fds = [-1, -1]; @@ -21,27 +25,28 @@ fn main() { assert_eq!(res, 0); let arr1: [u8; 212992] = [1; 212992]; // Exhaust the space in the buffer so the subsequent write will block. - let res = unsafe { libc::write(fds[0], arr1.as_ptr() as *const libc::c_void, 212992) }; + let res = + unsafe { libc_utils::write_all(fds[0], arr1.as_ptr() as *const libc::c_void, 212992) }; assert_eq!(res, 212992); let thread1 = thread::spawn(move || { - let data = "abc".as_bytes().as_ptr(); + let data = "a".as_bytes(); // The write below will be blocked because the buffer is already full. - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) }; - assert_eq!(res, 3); + let res = unsafe { libc::write(fds[0], data.as_ptr() as *const libc::c_void, data.len()) }; + assert_eq!(res, data.len().cast_signed()); }); let thread2 = thread::spawn(move || { - let data = "abc".as_bytes().as_ptr(); + let data = "a".as_bytes(); // The write below will be blocked because the buffer is already full. - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) }; - //~^ERROR: deadlocked - assert_eq!(res, 3); + let res = unsafe { libc::write(fds[0], data.as_ptr() as *const libc::c_void, data.len()) }; + //~^ERROR: deadlock + assert_eq!(res, data.len().cast_signed()); }); let thread3 = thread::spawn(move || { // Unblock thread1 by freeing up some space. - let mut buf: [u8; 3] = [0; 3]; + let mut buf: [u8; 1] = [0; 1]; let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; - assert_eq!(res, 3); - assert_eq!(buf, [1, 1, 1]); + assert_eq!(res, buf.len().cast_signed()); + assert_eq!(buf, [1]); }); thread1.join().unwrap(); thread2.join().unwrap(); diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr index b29cd70f35e4d..f766500d331da 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr @@ -23,8 +23,8 @@ error: the evaluated program deadlocked error: the evaluated program deadlocked --> tests/fail-dep/libc/socketpair_block_write_twice.rs:LL:CC | -LL | let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) }; - | ^ this thread got stuck here +LL | let res = unsafe { libc::write(fds[0], data.as_ptr() as *const libc::c_void, data.len()) }; + | ^ this thread got stuck here | = note: BACKTRACE on thread `unnamed-ID`: = note: inside closure at tests/fail-dep/libc/socketpair_block_write_twice.rs:LL:CC diff --git a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_return_type.stderr b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_return_type.stderr index 07e6561b53e7f..28c676ad4823a 100644 --- a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_return_type.stderr +++ b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_return_type.stderr @@ -6,6 +6,8 @@ LL | g() | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets + = help: if you think this code should be accepted anyway, please report an issue with Miri = note: BACKTRACE: = note: inside `main` at tests/fail/function_pointers/abi_mismatch_return_type.rs:LL:CC diff --git a/src/tools/miri/tests/fail/shims/ctor_wrong_ret_type.rs b/src/tools/miri/tests/fail/shims/ctor_wrong_ret_type.rs new file mode 100644 index 0000000000000..1e10f682e71e6 --- /dev/null +++ b/src/tools/miri/tests/fail/shims/ctor_wrong_ret_type.rs @@ -0,0 +1,39 @@ +unsafe extern "C" fn ctor() -> i32 { + //~^ERROR: calling a function with return type i32 passing return place of type () + 0 +} + +#[rustfmt::skip] +macro_rules! ctor { + ($ident:ident = $ctor:ident) => { + #[cfg_attr( + all(any( + target_os = "linux", + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "haiku", + target_os = "illumos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + target_os = "none", + target_family = "wasm", + )), + link_section = ".init_array" + )] + #[cfg_attr(windows, link_section = ".CRT$XCU")] + #[cfg_attr( + any(target_os = "macos", target_os = "ios"), + // We do not set the `mod_init_funcs` flag here since ctor/inventory also do not do + // that. See . + link_section = "__DATA,__mod_init_func" + )] + #[used] + static $ident: unsafe extern "C" fn() -> i32 = $ctor; + }; +} + +ctor! { CTOR = ctor } + +fn main() {} diff --git a/src/tools/miri/tests/fail/shims/ctor_wrong_ret_type.stderr b/src/tools/miri/tests/fail/shims/ctor_wrong_ret_type.stderr new file mode 100644 index 0000000000000..664bfbd32db2c --- /dev/null +++ b/src/tools/miri/tests/fail/shims/ctor_wrong_ret_type.stderr @@ -0,0 +1,12 @@ +error: Undefined Behavior: calling a function with return type i32 passing return place of type () + | + = note: Undefined Behavior occurred here + = note: (no span available) + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets + = help: if you think this code should be accepted anyway, please report an issue with Miri + = note: BACKTRACE: + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/shims/return_type_mismatch.stderr b/src/tools/miri/tests/fail/shims/return_type_mismatch.stderr index 080ee16a2eb5a..18ff3067fa031 100644 --- a/src/tools/miri/tests/fail/shims/return_type_mismatch.stderr +++ b/src/tools/miri/tests/fail/shims/return_type_mismatch.stderr @@ -6,6 +6,8 @@ LL | close(fd); | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets + = help: if you think this code should be accepted anyway, please report an issue with Miri = note: BACKTRACE: = note: inside `main` at tests/fail/shims/return_type_mismatch.rs:LL:CC diff --git a/src/tools/miri/tests/genmc/pass/test_cxx_build.rs b/src/tools/miri/tests/genmc/pass/test_cxx_build.rs new file mode 100644 index 0000000000000..f621bd9114fa4 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/test_cxx_build.rs @@ -0,0 +1,8 @@ +//@compile-flags: -Zmiri-genmc + +#![no_main] + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + 0 +} diff --git a/src/tools/miri/tests/genmc/pass/test_cxx_build.stderr b/src/tools/miri/tests/genmc/pass/test_cxx_build.stderr new file mode 100644 index 0000000000000..4b7aa824bd122 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/test_cxx_build.stderr @@ -0,0 +1,5 @@ +warning: borrow tracking has been disabled, it is not (yet) supported in GenMC mode. +C++: GenMC handle created! +Miri: GenMC handle creation successful! +C++: GenMC handle destroyed! +Miri: Dropping GenMC handle successful! diff --git a/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs b/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs index 54ebfa9d198d1..c97206487a101 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs @@ -6,6 +6,9 @@ use std::convert::TryInto; use std::thread; use std::thread::spawn; +#[path = "../../utils/libc.rs"] +mod libc_utils; + // This is a set of testcases for blocking epoll. fn main() { @@ -97,7 +100,7 @@ fn test_epoll_block_then_unblock() { let thread1 = spawn(move || { thread::yield_now(); let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) }; assert_eq!(res, 5); }); check_epoll_wait::<1>(epfd, &[(expected_event, expected_value)], 10); @@ -130,7 +133,7 @@ fn test_notification_after_timeout() { // Trigger epoll notification after timeout. let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) }; assert_eq!(res, 5); // Check the result of the notification. diff --git a/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs b/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs index dc3ab2828faa2..7130790b86d61 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs @@ -2,6 +2,9 @@ use std::convert::TryInto; +#[path = "../../utils/libc.rs"] +mod libc_utils; + fn main() { test_epoll_socketpair(); test_epoll_socketpair_both_sides(); @@ -64,7 +67,7 @@ fn test_epoll_socketpair() { // Write to fd[0] let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) }; assert_eq!(res, 5); // Register fd[1] with EPOLLIN|EPOLLOUT|EPOLLET|EPOLLRDHUP @@ -85,7 +88,7 @@ fn test_epoll_socketpair() { // Write some more to fd[0]. let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) }; assert_eq!(res, 5); // This did not change the readiness of fd[1]. And yet, we're seeing the event reported @@ -153,7 +156,7 @@ fn test_epoll_ctl_del() { // Write to fd[0] let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) }; assert_eq!(res, 5); // Register fd[1] with EPOLLIN|EPOLLOUT|EPOLLET @@ -182,7 +185,7 @@ fn test_two_epoll_instance() { // Write to the socketpair. let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) }; assert_eq!(res, 5); // Register one side of the socketpair with EPOLLIN | EPOLLOUT | EPOLLET. @@ -224,7 +227,7 @@ fn test_two_same_fd_in_same_epoll_instance() { // Write to the socketpair. let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) }; assert_eq!(res, 5); //Two notification should be received. @@ -243,7 +246,7 @@ fn test_epoll_eventfd() { // Write to the eventfd instance. let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes(); - let res = unsafe { libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) }; + let res = unsafe { libc_utils::write_all(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) }; assert_eq!(res, 8); // Create an epoll instance. @@ -282,7 +285,7 @@ fn test_epoll_socketpair_both_sides() { // Write to fds[1]. let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) }; assert_eq!(res, 5); //Two notification should be received. @@ -297,7 +300,8 @@ fn test_epoll_socketpair_both_sides() { // Read from fds[0]. let mut buf: [u8; 5] = [0; 5]; - let res = unsafe { libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; + let res = + unsafe { libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; assert_eq!(res, 5); assert_eq!(buf, "abcde".as_bytes()); @@ -325,7 +329,7 @@ fn test_closed_fd() { // Write to the eventfd instance. let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes(); - let res = unsafe { libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) }; + let res = unsafe { libc_utils::write_all(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) }; assert_eq!(res, 8); // Close the eventfd. @@ -371,7 +375,8 @@ fn test_not_fully_closed_fd() { // Write to the eventfd instance to produce notification. let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes(); - let res = unsafe { libc::write(newfd, sized_8_data.as_ptr() as *const libc::c_void, 8) }; + let res = + unsafe { libc_utils::write_all(newfd, sized_8_data.as_ptr() as *const libc::c_void, 8) }; assert_eq!(res, 8); // Close the dupped fd. @@ -391,7 +396,7 @@ fn test_event_overwrite() { // Write to the eventfd instance. let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes(); - let res = unsafe { libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) }; + let res = unsafe { libc_utils::write_all(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) }; assert_eq!(res, 8); // Create an epoll instance. @@ -445,7 +450,7 @@ fn test_socketpair_read() { // Write 5 bytes to fds[1]. let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) }; assert_eq!(res, 5); //Two notification should be received. @@ -460,7 +465,8 @@ fn test_socketpair_read() { // Read 3 bytes from fds[0]. let mut buf: [u8; 3] = [0; 3]; - let res = unsafe { libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; + let res = + unsafe { libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; assert_eq!(res, 3); assert_eq!(buf, "abc".as_bytes()); @@ -478,7 +484,8 @@ fn test_socketpair_read() { // Read until the buffer is empty. let mut buf: [u8; 2] = [0; 2]; - let res = unsafe { libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; + let res = + unsafe { libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; assert_eq!(res, 2); assert_eq!(buf, "de".as_bytes()); @@ -510,8 +517,9 @@ fn test_no_notification_for_unregister_flag() { // Write to fd[1]. let data = "abcde".as_bytes().as_ptr(); - let res: i32 = - unsafe { libc::write(fds[1], data as *const libc::c_void, 5).try_into().unwrap() }; + let res: i32 = unsafe { + libc_utils::write_all(fds[1], data as *const libc::c_void, 5).try_into().unwrap() + }; assert_eq!(res, 5); // Check result from epoll_wait. Since we didn't register EPOLLIN flag, the notification won't @@ -546,7 +554,7 @@ fn test_socketpair_epollerr() { // Write to fd[0] let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) }; assert_eq!(res, 5); // Close fds[1]. @@ -717,6 +725,6 @@ fn test_issue_3858() { // Write to the eventfd instance. let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes(); - let res = unsafe { libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) }; + let res = unsafe { libc_utils::write_all(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) }; assert_eq!(res, 8); } diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs index 0ff48c389e855..86cf2a041f067 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs @@ -14,6 +14,9 @@ use std::path::PathBuf; #[path = "../../utils/mod.rs"] mod utils; +#[path = "../../utils/libc.rs"] +mod libc_utils; + fn main() { test_dup(); test_dup_stdout_stderr(); @@ -74,8 +77,8 @@ fn test_dup_stdout_stderr() { unsafe { let new_stdout = libc::fcntl(1, libc::F_DUPFD, 0); let new_stderr = libc::fcntl(2, libc::F_DUPFD, 0); - libc::write(new_stdout, bytes.as_ptr() as *const libc::c_void, bytes.len()); - libc::write(new_stderr, bytes.as_ptr() as *const libc::c_void, bytes.len()); + libc_utils::write_all(new_stdout, bytes.as_ptr() as *const libc::c_void, bytes.len()); + libc_utils::write_all(new_stderr, bytes.as_ptr() as *const libc::c_void, bytes.len()); } } @@ -92,16 +95,24 @@ fn test_dup() { let new_fd2 = libc::dup2(fd, 8); let mut first_buf = [0u8; 4]; - libc::read(fd, first_buf.as_mut_ptr() as *mut libc::c_void, 4); - assert_eq!(&first_buf, b"dup "); + let first_len = libc::read(fd, first_buf.as_mut_ptr() as *mut libc::c_void, 4); + assert!(first_len > 0); + let first_len = first_len as usize; + assert_eq!(first_buf[..first_len], bytes[..first_len]); + let remaining_bytes = &bytes[first_len..]; let mut second_buf = [0u8; 4]; - libc::read(new_fd, second_buf.as_mut_ptr() as *mut libc::c_void, 4); - assert_eq!(&second_buf, b"and "); + let second_len = libc::read(new_fd, second_buf.as_mut_ptr() as *mut libc::c_void, 4); + assert!(second_len > 0); + let second_len = second_len as usize; + assert_eq!(second_buf[..second_len], remaining_bytes[..second_len]); + let remaining_bytes = &remaining_bytes[second_len..]; let mut third_buf = [0u8; 4]; - libc::read(new_fd2, third_buf.as_mut_ptr() as *mut libc::c_void, 4); - assert_eq!(&third_buf, b"dup2"); + let third_len = libc::read(new_fd2, third_buf.as_mut_ptr() as *mut libc::c_void, 4); + assert!(third_len > 0); + let third_len = third_len as usize; + assert_eq!(third_buf[..third_len], remaining_bytes[..third_len]); } } @@ -145,7 +156,7 @@ fn test_ftruncate>( let bytes = b"hello"; let path = utils::prepare("miri_test_libc_fs_ftruncate.txt"); let mut file = File::create(&path).unwrap(); - file.write(bytes).unwrap(); + file.write_all(bytes).unwrap(); file.sync_all().unwrap(); assert_eq!(file.metadata().unwrap().len(), 5); @@ -402,10 +413,10 @@ fn test_read_and_uninit() { unsafe { let fd = libc::open(cpath.as_ptr(), libc::O_RDONLY); assert_ne!(fd, -1); - let mut buf: MaybeUninit<[u8; 2]> = std::mem::MaybeUninit::uninit(); - assert_eq!(libc::read(fd, buf.as_mut_ptr().cast::(), 2), 2); + let mut buf: MaybeUninit = std::mem::MaybeUninit::uninit(); + assert_eq!(libc::read(fd, buf.as_mut_ptr().cast::(), 1), 1); let buf = buf.assume_init(); - assert_eq!(buf, [1, 2]); + assert_eq!(buf, 1); assert_eq!(libc::close(fd), 0); } remove_file(&path).unwrap(); @@ -413,14 +424,22 @@ fn test_read_and_uninit() { { // We test that if we requested to read 4 bytes, but actually read 3 bytes, then // 3 bytes (not 4) will be overwritten, and remaining byte will be left as-is. - let path = utils::prepare_with_content("pass-libc-read-and-uninit-2.txt", &[1u8, 2, 3]); + let data = [1u8, 2, 3]; + let path = utils::prepare_with_content("pass-libc-read-and-uninit-2.txt", &data); let cpath = CString::new(path.clone().into_os_string().into_encoded_bytes()).unwrap(); unsafe { let fd = libc::open(cpath.as_ptr(), libc::O_RDONLY); assert_ne!(fd, -1); let mut buf = [42u8; 5]; - assert_eq!(libc::read(fd, buf.as_mut_ptr().cast::(), 4), 3); - assert_eq!(buf, [1, 2, 3, 42, 42]); + let res = libc::read(fd, buf.as_mut_ptr().cast::(), 4); + assert!(res > 0 && res < 4); + for i in 0..buf.len() { + assert_eq!( + buf[i], + if i < res as usize { data[i] } else { 42 }, + "wrong result at pos {i}" + ); + } assert_eq!(libc::close(fd), 0); } remove_file(&path).unwrap(); diff --git a/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs b/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs index bc755af864c5e..ffbcf633b9873 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs @@ -2,6 +2,10 @@ // test_race depends on a deterministic schedule. //@compile-flags: -Zmiri-deterministic-concurrency use std::thread; + +#[path = "../../utils/libc.rs"] +mod libc_utils; + fn main() { test_pipe(); test_pipe_threaded(); @@ -26,21 +30,29 @@ fn test_pipe() { // Read size == data available in buffer. let data = "12345".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) }; assert_eq!(res, 5); let mut buf3: [u8; 5] = [0; 5]; - let res = unsafe { libc::read(fds[0], buf3.as_mut_ptr().cast(), buf3.len() as libc::size_t) }; + let res = unsafe { + libc_utils::read_all(fds[0], buf3.as_mut_ptr().cast(), buf3.len() as libc::size_t) + }; assert_eq!(res, 5); assert_eq!(buf3, "12345".as_bytes()); // Read size > data available in buffer. - let data = "123".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 3) }; + let data = "123".as_bytes(); + let res = unsafe { libc_utils::write_all(fds[1], data.as_ptr() as *const libc::c_void, 3) }; assert_eq!(res, 3); let mut buf4: [u8; 5] = [0; 5]; let res = unsafe { libc::read(fds[0], buf4.as_mut_ptr().cast(), buf4.len() as libc::size_t) }; - assert_eq!(res, 3); - assert_eq!(&buf4[0..3], "123".as_bytes()); + assert!(res > 0 && res <= 3); + let res = res as usize; + assert_eq!(buf4[..res], data[..res]); + if res < 3 { + // Drain the rest from the read end. + let res = unsafe { libc_utils::read_all(fds[0], buf4[res..].as_mut_ptr().cast(), 3 - res) }; + assert!(res > 0); + } } fn test_pipe_threaded() { @@ -51,7 +63,7 @@ fn test_pipe_threaded() { let thread1 = thread::spawn(move || { let mut buf: [u8; 5] = [0; 5]; let res: i64 = unsafe { - libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) + libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) .try_into() .unwrap() }; @@ -60,7 +72,7 @@ fn test_pipe_threaded() { }); thread::yield_now(); let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) }; assert_eq!(res, 5); thread1.join().unwrap(); @@ -68,11 +80,12 @@ fn test_pipe_threaded() { let thread2 = thread::spawn(move || { thread::yield_now(); let data = "12345".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) }; assert_eq!(res, 5); }); let mut buf: [u8; 5] = [0; 5]; - let res = unsafe { libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; + let res = + unsafe { libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; assert_eq!(res, 5); assert_eq!(buf, "12345".as_bytes()); thread2.join().unwrap(); @@ -90,7 +103,7 @@ fn test_race() { // write() from the main thread will occur before the read() here // because preemption is disabled and the main thread yields after write(). let res: i32 = unsafe { - libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) + libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) .try_into() .unwrap() }; @@ -101,7 +114,7 @@ fn test_race() { }); unsafe { VAL = 1 }; let data = "a".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 1) }; + let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 1) }; assert_eq!(res, 1); thread::yield_now(); thread1.join().unwrap(); @@ -186,11 +199,12 @@ fn test_pipe_fcntl_threaded() { // the socket is now "non-blocking", the shim needs to deal correctly // with threads that were blocked before the socket was made non-blocking. let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) }; assert_eq!(res, 5); }); // The `read` below will block. - let res = unsafe { libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; + let res = + unsafe { libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; thread1.join().unwrap(); assert_eq!(res, 5); } diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs b/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs index c36f6b112244e..9c211ffbdbe4a 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs @@ -6,6 +6,10 @@ #![allow(static_mut_refs)] use std::thread; + +#[path = "../../utils/libc.rs"] +mod libc_utils; + fn main() { test_socketpair(); test_socketpair_threaded(); @@ -22,54 +26,71 @@ fn test_socketpair() { // Read size == data available in buffer. let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) }; assert_eq!(res, 5); let mut buf: [u8; 5] = [0; 5]; - let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; + let res = + unsafe { libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; assert_eq!(res, 5); assert_eq!(buf, "abcde".as_bytes()); // Read size > data available in buffer. - let data = "abc".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) }; + let data = "abc".as_bytes(); + let res = unsafe { libc_utils::write_all(fds[0], data.as_ptr() as *const libc::c_void, 3) }; assert_eq!(res, 3); let mut buf2: [u8; 5] = [0; 5]; let res = unsafe { libc::read(fds[1], buf2.as_mut_ptr().cast(), buf2.len() as libc::size_t) }; - assert_eq!(res, 3); - assert_eq!(&buf2[0..3], "abc".as_bytes()); + assert!(res > 0 && res <= 3); + let res = res as usize; + assert_eq!(buf2[..res], data[..res]); + if res < 3 { + // Drain the rest from the read end. + let res = unsafe { libc_utils::read_all(fds[1], buf2[res..].as_mut_ptr().cast(), 3 - res) }; + assert!(res > 0); + } // Test read and write from another direction. // Read size == data available in buffer. let data = "12345".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) }; assert_eq!(res, 5); let mut buf3: [u8; 5] = [0; 5]; - let res = unsafe { libc::read(fds[0], buf3.as_mut_ptr().cast(), buf3.len() as libc::size_t) }; + let res = unsafe { + libc_utils::read_all(fds[0], buf3.as_mut_ptr().cast(), buf3.len() as libc::size_t) + }; assert_eq!(res, 5); assert_eq!(buf3, "12345".as_bytes()); // Read size > data available in buffer. - let data = "123".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 3) }; + let data = "123".as_bytes(); + let res = unsafe { libc_utils::write_all(fds[1], data.as_ptr() as *const libc::c_void, 3) }; assert_eq!(res, 3); let mut buf4: [u8; 5] = [0; 5]; let res = unsafe { libc::read(fds[0], buf4.as_mut_ptr().cast(), buf4.len() as libc::size_t) }; - assert_eq!(res, 3); - assert_eq!(&buf4[0..3], "123".as_bytes()); + assert!(res > 0 && res <= 3); + let res = res as usize; + assert_eq!(buf4[..res], data[..res]); + if res < 3 { + // Drain the rest from the read end. + let res = unsafe { libc_utils::read_all(fds[0], buf4[res..].as_mut_ptr().cast(), 3 - res) }; + assert!(res > 0); + } // Test when happens when we close one end, with some data in the buffer. - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) }; + let res = unsafe { libc_utils::write_all(fds[0], data.as_ptr() as *const libc::c_void, 3) }; assert_eq!(res, 3); unsafe { libc::close(fds[0]) }; // Reading the other end should return that data, then EOF. let mut buf: [u8; 5] = [0; 5]; - let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; + let res = + unsafe { libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; assert_eq!(res, 3); assert_eq!(&buf[0..3], "123".as_bytes()); - let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; + let res = + unsafe { libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; assert_eq!(res, 0); // 0-sized read: EOF. // Writing the other end should emit EPIPE. - let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 1) }; + let res = unsafe { libc_utils::write_all(fds[1], data.as_ptr() as *const libc::c_void, 1) }; assert_eq!(res, -1); assert_eq!(std::io::Error::last_os_error().raw_os_error(), Some(libc::EPIPE)); } @@ -82,7 +103,7 @@ fn test_socketpair_threaded() { let thread1 = thread::spawn(move || { let mut buf: [u8; 5] = [0; 5]; let res: i64 = unsafe { - libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) + libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) .try_into() .unwrap() }; @@ -91,7 +112,7 @@ fn test_socketpair_threaded() { }); thread::yield_now(); let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) }; assert_eq!(res, 5); thread1.join().unwrap(); @@ -99,11 +120,12 @@ fn test_socketpair_threaded() { let thread2 = thread::spawn(move || { thread::yield_now(); let data = "12345".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) }; assert_eq!(res, 5); }); let mut buf: [u8; 5] = [0; 5]; - let res = unsafe { libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; + let res = + unsafe { libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; assert_eq!(res, 5); assert_eq!(buf, "12345".as_bytes()); thread2.join().unwrap(); @@ -119,7 +141,7 @@ fn test_race() { // write() from the main thread will occur before the read() here // because preemption is disabled and the main thread yields after write(). let res: i32 = unsafe { - libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) + libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) .try_into() .unwrap() }; @@ -130,7 +152,7 @@ fn test_race() { }); unsafe { VAL = 1 }; let data = "a".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 1) }; + let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 1) }; assert_eq!(res, 1); thread::yield_now(); thread1.join().unwrap(); @@ -144,14 +166,16 @@ fn test_blocking_read() { let thread1 = thread::spawn(move || { // Let this thread block on read. let mut buf: [u8; 3] = [0; 3]; - let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; + let res = unsafe { + libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) + }; assert_eq!(res, 3); assert_eq!(&buf, "abc".as_bytes()); }); let thread2 = thread::spawn(move || { // Unblock thread1 by doing writing something. let data = "abc".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) }; + let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 3) }; assert_eq!(res, 3); }); thread1.join().unwrap(); @@ -165,18 +189,21 @@ fn test_blocking_write() { assert_eq!(res, 0); let arr1: [u8; 212992] = [1; 212992]; // Exhaust the space in the buffer so the subsequent write will block. - let res = unsafe { libc::write(fds[0], arr1.as_ptr() as *const libc::c_void, 212992) }; + let res = + unsafe { libc_utils::write_all(fds[0], arr1.as_ptr() as *const libc::c_void, 212992) }; assert_eq!(res, 212992); let thread1 = thread::spawn(move || { let data = "abc".as_bytes().as_ptr(); // The write below will be blocked because the buffer is already full. - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) }; + let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 3) }; assert_eq!(res, 3); }); let thread2 = thread::spawn(move || { // Unblock thread1 by freeing up some space. let mut buf: [u8; 3] = [0; 3]; - let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; + let res = unsafe { + libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) + }; assert_eq!(res, 3); assert_eq!(buf, [1, 1, 1]); }); diff --git a/src/tools/miri/tests/pass/shims/ctor.rs b/src/tools/miri/tests/pass/shims/ctor.rs index b997d2386b82c..a0fcdb1081e65 100644 --- a/src/tools/miri/tests/pass/shims/ctor.rs +++ b/src/tools/miri/tests/pass/shims/ctor.rs @@ -2,13 +2,13 @@ use std::sync::atomic::{AtomicUsize, Ordering}; static COUNT: AtomicUsize = AtomicUsize::new(0); -unsafe extern "C" fn ctor() { - COUNT.fetch_add(1, Ordering::Relaxed); +unsafe extern "C" fn ctor() { + COUNT.fetch_add(N, Ordering::Relaxed); } #[rustfmt::skip] macro_rules! ctor { - ($ident:ident = $ctor:ident) => { + ($ident:ident: $ty:ty = $ctor:expr) => { #[cfg_attr( all(any( target_os = "linux", @@ -33,14 +33,13 @@ macro_rules! ctor { link_section = "__DATA,__mod_init_func" )] #[used] - static $ident: unsafe extern "C" fn() = $ctor; + static $ident: $ty = $ctor; }; } -ctor! { CTOR1 = ctor } -ctor! { CTOR2 = ctor } -ctor! { CTOR3 = ctor } +ctor! { CTOR1: unsafe extern "C" fn() = ctor::<1> } +ctor! { CTOR2: [unsafe extern "C" fn(); 2] = [ctor::<2>, ctor::<3>] } fn main() { - assert_eq!(COUNT.load(Ordering::Relaxed), 3, "ctors did not run"); + assert_eq!(COUNT.load(Ordering::Relaxed), 6, "ctors did not run"); } diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs index 9d5725773e647..e7f11c54704d4 100644 --- a/src/tools/miri/tests/pass/shims/fs.rs +++ b/src/tools/miri/tests/pass/shims/fs.rs @@ -17,6 +17,10 @@ mod utils; fn main() { test_path_conversion(); test_file(); + // Partial reads/writes are apparently not a thing on Windows. + if cfg!(not(windows)) { + test_file_partial_reads_writes(); + } test_file_create_new(); test_metadata(); test_seek(); @@ -53,7 +57,7 @@ fn test_file() { file.write(&mut []).unwrap(); assert_eq!(file.metadata().unwrap().len(), 0); - file.write(bytes).unwrap(); + file.write_all(bytes).unwrap(); assert_eq!(file.metadata().unwrap().len(), bytes.len() as u64); // Test opening, reading and closing a file. let mut file = File::open(&path).unwrap(); @@ -66,10 +70,36 @@ fn test_file() { assert!(!file.is_terminal()); + // Writing to a file opened for reading should error (and not stop interpretation). std does not + // categorize the error so we don't check for details. + file.write(&[]).unwrap_err(); + // Removing file should succeed. remove_file(&path).unwrap(); } +fn test_file_partial_reads_writes() { + let path = utils::prepare_with_content("miri_test_fs_file.txt", b"abcdefg"); + + // Ensure we sometimes do incomplete writes. + let got_short_write = (0..16).any(|_| { + let _ = remove_file(&path); // FIXME(win, issue #4483): errors if the file already exists + let mut file = File::create(&path).unwrap(); + file.write(&[0; 4]).unwrap() != 4 + }); + assert!(got_short_write); + // Ensure we sometimes do incomplete reads. + let got_short_read = (0..16).any(|_| { + let mut file = File::open(&path).unwrap(); + let mut buf = [0; 4]; + file.read(&mut buf).unwrap() != 4 + }); + assert!(got_short_read); + + // Clean up + remove_file(&path).unwrap(); +} + fn test_file_clone() { let bytes = b"Hello, World!\n"; let path = utils::prepare_with_content("miri_test_fs_file_clone.txt", bytes); diff --git a/src/tools/miri/tests/pass/shims/pipe.rs b/src/tools/miri/tests/pass/shims/pipe.rs index c47feb8774ad1..4915e54c533f5 100644 --- a/src/tools/miri/tests/pass/shims/pipe.rs +++ b/src/tools/miri/tests/pass/shims/pipe.rs @@ -4,8 +4,8 @@ use std::io::{Read, Write, pipe}; fn main() { let (mut ping_rx, mut ping_tx) = pipe().unwrap(); - ping_tx.write(b"hello").unwrap(); + ping_tx.write_all(b"hello").unwrap(); let mut buf: [u8; 5] = [0; 5]; - ping_rx.read(&mut buf).unwrap(); + ping_rx.read_exact(&mut buf).unwrap(); assert_eq!(&buf, "hello".as_bytes()); } diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs index cb915b11b6797..73fbe2cc020fa 100644 --- a/src/tools/miri/tests/ui.rs +++ b/src/tools/miri/tests/ui.rs @@ -338,6 +338,20 @@ fn main() -> Result<()> { ui(Mode::Fail, "tests/native-lib/fail", &target, WithoutDependencies, tmpdir.path())?; } + // We only enable GenMC tests when the `genmc` feature is enabled, but also only on platforms we support: + // FIXME(genmc,macos): Add `target_os = "macos"` once `https://github.com/dtolnay/cxx/issues/1535` is fixed. + // FIXME(genmc,cross-platform): remove `host == target` check once cross-platform support with GenMC is possible. + if cfg!(all( + feature = "genmc", + target_os = "linux", + target_pointer_width = "64", + target_endian = "little" + )) && host == target + { + ui(Mode::Pass, "tests/genmc/pass", &target, WithDependencies, tmpdir.path())?; + ui(Mode::Fail, "tests/genmc/fail", &target, WithDependencies, tmpdir.path())?; + } + Ok(()) } diff --git a/src/tools/miri/tests/utils/fs.rs b/src/tools/miri/tests/utils/fs.rs index 7340908626fdf..7d75b3fced3c5 100644 --- a/src/tools/miri/tests/utils/fs.rs +++ b/src/tools/miri/tests/utils/fs.rs @@ -1,6 +1,6 @@ use std::ffi::OsString; -use std::fs; use std::path::PathBuf; +use std::{fs, io}; use super::miri_extern; diff --git a/src/tools/miri/tests/utils/libc.rs b/src/tools/miri/tests/utils/libc.rs new file mode 100644 index 0000000000000..1a3cd067c04bf --- /dev/null +++ b/src/tools/miri/tests/utils/libc.rs @@ -0,0 +1,44 @@ +//! Utils that need libc. +#![allow(dead_code)] + +pub unsafe fn read_all( + fd: libc::c_int, + buf: *mut libc::c_void, + count: libc::size_t, +) -> libc::ssize_t { + assert!(count > 0); + let mut read_so_far = 0; + while read_so_far < count { + let res = libc::read(fd, buf.add(read_so_far), count - read_so_far); + if res < 0 { + return res; + } + if res == 0 { + // EOF + break; + } + read_so_far += res as libc::size_t; + } + return read_so_far as libc::ssize_t; +} + +pub unsafe fn write_all( + fd: libc::c_int, + buf: *const libc::c_void, + count: libc::size_t, +) -> libc::ssize_t { + assert!(count > 0); + let mut written_so_far = 0; + while written_so_far < count { + let res = libc::write(fd, buf.add(written_so_far), count - written_so_far); + if res < 0 { + return res; + } + if res == 0 { + // EOF? + break; + } + written_so_far += res as libc::size_t; + } + return written_so_far as libc::ssize_t; +} diff --git a/src/tools/miri/tests/x86_64-unknown-kernel.json b/src/tools/miri/tests/x86_64-unknown-kernel.json index 8da67d3a1c6b2..a5eaceb4f68e7 100644 --- a/src/tools/miri/tests/x86_64-unknown-kernel.json +++ b/src/tools/miri/tests/x86_64-unknown-kernel.json @@ -2,7 +2,7 @@ "llvm-target": "x86_64-unknown-none", "target-endian": "little", "target-pointer-width": "64", - "target-c-int-width": "32", + "target-c-int-width": 32, "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", "arch": "x86_64", "os": "none", diff --git a/src/tools/tidy/src/extra_checks/mod.rs b/src/tools/tidy/src/extra_checks/mod.rs index 8121eb057db5b..f90f716cd9501 100644 --- a/src/tools/tidy/src/extra_checks/mod.rs +++ b/src/tools/tidy/src/extra_checks/mod.rs @@ -86,7 +86,7 @@ fn check_impl( std::env::var("TIDY_PRINT_DIFF").is_ok_and(|v| v.eq_ignore_ascii_case("true") || v == "1"); // Split comma-separated args up - let lint_args = match extra_checks { + let mut lint_args = match extra_checks { Some(s) => s .strip_prefix("--extra-checks=") .unwrap() @@ -99,11 +99,7 @@ fn check_impl( }) .filter_map(|(res, src)| match res { Ok(arg) => { - if arg.is_inactive_auto(ci_info) { - None - } else { - Some(arg) - } + Some(arg) } Err(err) => { // only warn because before bad extra checks would be silently ignored. @@ -114,6 +110,11 @@ fn check_impl( .collect(), None => vec![], }; + if lint_args.iter().any(|ck| ck.auto) { + crate::files_modified_batch_filter(ci_info, &mut lint_args, |ck, path| { + ck.is_non_auto_or_matches(path) + }); + } macro_rules! extra_check { ($lang:ident, $kind:ident) => { @@ -721,10 +722,10 @@ impl ExtraCheckArg { self.lang == lang && self.kind.map(|k| k == kind).unwrap_or(true) } - /// Returns `true` if this is an auto arg and the relevant files are not modified. - fn is_inactive_auto(&self, ci_info: &CiInfo) -> bool { + /// Returns `false` if this is an auto arg and the passed filename does not trigger the auto rule + fn is_non_auto_or_matches(&self, filepath: &str) -> bool { if !self.auto { - return false; + return true; } let ext = match self.lang { ExtraCheckLang::Py => ".py", @@ -732,12 +733,15 @@ impl ExtraCheckArg { ExtraCheckLang::Shell => ".sh", ExtraCheckLang::Js => ".js", ExtraCheckLang::Spellcheck => { - return !crate::files_modified(ci_info, |s| { - SPELLCHECK_DIRS.iter().any(|dir| Path::new(s).starts_with(dir)) - }); + for dir in SPELLCHECK_DIRS { + if Path::new(filepath).starts_with(dir) { + return true; + } + } + return false; } }; - !crate::files_modified(ci_info, |s| s.ends_with(ext)) + filepath.ends_with(ext) } fn has_supported_kind(&self) -> bool { diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index 4f9fb308a866d..4ea9d051ddb5e 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -125,40 +125,61 @@ pub fn git_diff>(base_commit: &str, extra_arg: S) -> Option bool) -> bool { +/// Similar to `files_modified`, but only involves a single call to `git`. +/// +/// removes all elements from `items` that do not cause any match when `pred` is called with the list of modifed files. +/// +/// if in CI, no elements will be removed. +pub fn files_modified_batch_filter( + ci_info: &CiInfo, + items: &mut Vec, + pred: impl Fn(&T, &str) -> bool, +) { if CiEnv::is_ci() { // assume everything is modified on CI because we really don't want false positives there. - return true; + return; } let Some(base_commit) = &ci_info.base_commit else { eprintln!("No base commit, assuming all files are modified"); - return true; + return; }; match crate::git_diff(base_commit, "--name-status") { Some(output) => { - let modified_files = output.lines().filter_map(|ln| { - let (status, name) = ln - .trim_end() - .split_once('\t') - .expect("bad format from `git diff --name-status`"); - if status == "M" { Some(name) } else { None } - }); - for modified_file in modified_files { - if pred(modified_file) { - return true; + let modified_files: Vec<_> = output + .lines() + .filter_map(|ln| { + let (status, name) = ln + .trim_end() + .split_once('\t') + .expect("bad format from `git diff --name-status`"); + if status == "M" { Some(name) } else { None } + }) + .collect(); + items.retain(|item| { + for modified_file in &modified_files { + if pred(item, modified_file) { + // at least one predicate matches, keep this item. + return true; + } } - } - false + // no predicates matched, remove this item. + false + }); } None => { eprintln!("warning: failed to run `git diff` to check for changes"); eprintln!("warning: assuming all files are modified"); - true } } } +/// Returns true if any modified file matches the predicate, if we are in CI, or if unable to list modified files. +pub fn files_modified(ci_info: &CiInfo, pred: impl Fn(&str) -> bool) -> bool { + let mut v = vec![()]; + files_modified_batch_filter(ci_info, &mut v, |_, p| pred(p)); + !v.is_empty() +} + pub mod alphabetical; pub mod bins; pub mod debug_artifacts; diff --git a/tests/codegen-llvm/debuginfo-cyclic-structure.rs b/tests/codegen-llvm/debuginfo-cyclic-structure.rs new file mode 100644 index 0000000000000..b8cc544774158 --- /dev/null +++ b/tests/codegen-llvm/debuginfo-cyclic-structure.rs @@ -0,0 +1,32 @@ +//@ compile-flags:-g -Copt-level=0 -C panic=abort + +// Check that debug information exists for structures containing loops (cyclic references). +// Previously it may incorrectly prune member information during recursive type inference check. + +// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "Arc { + buffer: Box>, +} +struct Shared { + shared: Arc>>, +} +struct Handle { + shared: Shared, +} +struct Core { + inner: Arc>>, +} + +#[no_mangle] +extern "C" fn test() { + let с = Core { inner: Arc::new(Inner { buffer: Box::new(MaybeUninit::uninit()) }) }; + std::hint::black_box(с); +} diff --git a/tests/run-make/crate-loading-crate-depends-on-itself/foo.stderr b/tests/run-make/crate-loading-crate-depends-on-itself/foo.stderr index 7f131153540bb..9433a0ba00e80 100644 --- a/tests/run-make/crate-loading-crate-depends-on-itself/foo.stderr +++ b/tests/run-make/crate-loading-crate-depends-on-itself/foo.stderr @@ -7,19 +7,19 @@ error[E0277]: the trait bound `foo::Struct: Trait` is not satisfied note: there are multiple different versions of crate `foo` in the dependency graph --> foo-current.rs:7:1 | -4 | extern crate foo; + 4 | extern crate foo; | ----------------- one version of crate `foo` used here, as a direct dependency of the current crate -5 | -6 | pub struct Struct; + 5 | + 6 | pub struct Struct; | ----------------- this type implements the required trait -7 | pub trait Trait {} + 7 | pub trait Trait {} | ^^^^^^^^^^^^^^^ this is the required trait | ::: foo-prev.rs:X:Y | -4 | pub struct Struct; + 4 | pub struct Struct; | ----------------- this type doesn't implement the required trait -5 | pub trait Trait {} + 5 | pub trait Trait {} | --------------- this is the found trait = note: two types coming from two different versions of the same crate are different types even if they look the same = help: you can use `cargo tree` to explore your dependency tree diff --git a/tests/run-make/rustdoc-dep-info/after.md b/tests/run-make/rustdoc-dep-info/after.md new file mode 100644 index 0000000000000..10d4b4c411640 --- /dev/null +++ b/tests/run-make/rustdoc-dep-info/after.md @@ -0,0 +1 @@ +meow! :3 diff --git a/tests/run-make/rustdoc-dep-info/before.html b/tests/run-make/rustdoc-dep-info/before.html new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/tests/run-make/rustdoc-dep-info/extend.css b/tests/run-make/rustdoc-dep-info/extend.css new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/tests/run-make/rustdoc-dep-info/rmake.rs b/tests/run-make/rustdoc-dep-info/rmake.rs index db7a00a5ce21c..625f81fd428ef 100644 --- a/tests/run-make/rustdoc-dep-info/rmake.rs +++ b/tests/run-make/rustdoc-dep-info/rmake.rs @@ -9,13 +9,26 @@ use run_make_support::{path, rfs, rustdoc}; fn main() { // We're only emitting dep info, so we shouldn't be running static analysis to // figure out that this program is erroneous. - rustdoc().input("lib.rs").arg("-Zunstable-options").emit("dep-info").run(); + // Ensure that all kinds of input reading flags end up in dep-info. + rustdoc() + .input("lib.rs") + .arg("-Zunstable-options") + .arg("--html-before-content=before.html") + .arg("--markdown-after-content=after.md") + .arg("--extend-css=extend.css") + .arg("--theme=theme.css") + .emit("dep-info") + .run(); let content = rfs::read_to_string("foo.d"); assert_contains(&content, "lib.rs:"); assert_contains(&content, "foo.rs:"); assert_contains(&content, "bar.rs:"); assert_contains(&content, "doc.md:"); + assert_contains(&content, "after.md:"); + assert_contains(&content, "before.html:"); + assert_contains(&content, "extend.css:"); + assert_contains(&content, "theme.css:"); // Now we check that we can provide a file name to the `dep-info` argument. rustdoc().input("lib.rs").arg("-Zunstable-options").emit("dep-info=bla.d").run(); diff --git a/tests/run-make/rustdoc-dep-info/theme.css b/tests/run-make/rustdoc-dep-info/theme.css new file mode 100644 index 0000000000000..459daffa9a19b --- /dev/null +++ b/tests/run-make/rustdoc-dep-info/theme.css @@ -0,0 +1 @@ +/* This is not a valid theme but that doesn't really matter */ diff --git a/tests/rustdoc-ui/doctest/standalone-warning-2024.stderr b/tests/rustdoc-ui/doctest/standalone-warning-2024.stderr index bfc1e91940453..ce65557c2c4a4 100644 --- a/tests/rustdoc-ui/doctest/standalone-warning-2024.stderr +++ b/tests/rustdoc-ui/doctest/standalone-warning-2024.stderr @@ -15,7 +15,7 @@ error: unknown attribute `standalone` note: the lint level is defined here --> $DIR/standalone-warning-2024.rs:9:9 | -9 | #![deny(warnings)] + 9 | #![deny(warnings)] | ^^^^^^^^ = note: `#[deny(rustdoc::invalid_codeblock_attributes)]` implied by `#[deny(warnings)]` diff --git a/tests/ui/async-await/issue-64130-non-send-future-diags.stderr b/tests/ui/async-await/issue-64130-non-send-future-diags.stderr index d28807e223bef..beaf8e9c96d85 100644 --- a/tests/ui/async-await/issue-64130-non-send-future-diags.stderr +++ b/tests/ui/async-await/issue-64130-non-send-future-diags.stderr @@ -4,12 +4,12 @@ error: future cannot be sent between threads safely LL | is_send(foo()); | ^^^^^ future returned by `foo` is not `Send` | - = help: within `impl Future`, the trait `Send` is not implemented for `MutexGuard<'_, u32>` + = help: within `impl Future`, the trait `Send` is not implemented for `std::sync::MutexGuard<'_, u32>` note: future is not `Send` as this value is used across an await --> $DIR/issue-64130-non-send-future-diags.rs:17:11 | LL | let g = x.lock().unwrap(); - | - has type `MutexGuard<'_, u32>` which is not `Send` + | - has type `std::sync::MutexGuard<'_, u32>` which is not `Send` LL | baz().await; | ^^^^^ await occurs here, with `g` maybe used later note: required by a bound in `is_send` diff --git a/tests/ui/async-await/issue-71137.stderr b/tests/ui/async-await/issue-71137.stderr index 8739c22a31048..d567e3f2063de 100644 --- a/tests/ui/async-await/issue-71137.stderr +++ b/tests/ui/async-await/issue-71137.stderr @@ -4,12 +4,12 @@ error: future cannot be sent between threads safely LL | fake_spawn(wrong_mutex()); | ^^^^^^^^^^^^^ future returned by `wrong_mutex` is not `Send` | - = help: within `impl Future`, the trait `Send` is not implemented for `MutexGuard<'_, i32>` + = help: within `impl Future`, the trait `Send` is not implemented for `std::sync::MutexGuard<'_, i32>` note: future is not `Send` as this value is used across an await --> $DIR/issue-71137.rs:14:26 | LL | let mut guard = m.lock().unwrap(); - | --------- has type `MutexGuard<'_, i32>` which is not `Send` + | --------- has type `std::sync::MutexGuard<'_, i32>` which is not `Send` LL | (async { "right"; }).await; | ^^^^^ await occurs here, with `mut guard` maybe used later note: required by a bound in `fake_spawn` diff --git a/tests/ui/async-await/issues/issue-67893.rs b/tests/ui/async-await/issues/issue-67893.rs index 73cce38c94a0a..2020abe7a5a90 100644 --- a/tests/ui/async-await/issues/issue-67893.rs +++ b/tests/ui/async-await/issues/issue-67893.rs @@ -7,5 +7,5 @@ fn g(_: impl Send) {} fn main() { g(issue_67893::run()) - //~^ ERROR `MutexGuard<'_, ()>` cannot be sent between threads safely + //~^ ERROR `std::sync::MutexGuard<'_, ()>` cannot be sent between threads safely } diff --git a/tests/ui/async-await/issues/issue-67893.stderr b/tests/ui/async-await/issues/issue-67893.stderr index c01237255b89b..34f28dd53c7b9 100644 --- a/tests/ui/async-await/issues/issue-67893.stderr +++ b/tests/ui/async-await/issues/issue-67893.stderr @@ -1,8 +1,8 @@ -error[E0277]: `MutexGuard<'_, ()>` cannot be sent between threads safely +error[E0277]: `std::sync::MutexGuard<'_, ()>` cannot be sent between threads safely --> $DIR/issue-67893.rs:9:7 | LL | g(issue_67893::run()) - | - ^^^^^^^^^^^^^^^^^^ `MutexGuard<'_, ()>` cannot be sent between threads safely + | - ^^^^^^^^^^^^^^^^^^ `std::sync::MutexGuard<'_, ()>` cannot be sent between threads safely | | | required by a bound introduced by this call | @@ -11,7 +11,7 @@ LL | g(issue_67893::run()) LL | pub async fn run() { | ------------------ within this `impl Future` | - = help: within `impl Future`, the trait `Send` is not implemented for `MutexGuard<'_, ()>` + = help: within `impl Future`, the trait `Send` is not implemented for `std::sync::MutexGuard<'_, ()>` note: required because it's used within this `async` fn body --> $DIR/auxiliary/issue_67893.rs:9:20 | diff --git a/tests/ui/compiletest-self-test/ui-testing-optout.stderr b/tests/ui/compiletest-self-test/ui-testing-optout.stderr index 652c472c0bcfe..f1d03eab14a9d 100644 --- a/tests/ui/compiletest-self-test/ui-testing-optout.stderr +++ b/tests/ui/compiletest-self-test/ui-testing-optout.stderr @@ -16,7 +16,7 @@ error[E0412]: cannot find type `D` in this scope error[E0412]: cannot find type `F` in this scope --> $DIR/ui-testing-optout.rs:92:10 | -4 | type A = B; + 4 | type A = B; | ----------- similarly named type alias `A` defined here ... 92 | type E = F; diff --git a/tests/ui/const-generics/generic_const_exprs/auxiliary/feature-attribute-missing-in-dependent-crate-ice-aux.rs b/tests/ui/const-generics/generic_const_exprs/auxiliary/feature-attribute-missing-in-dependent-crate-ice-aux.rs new file mode 100644 index 0000000000000..3902454c14cdb --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/auxiliary/feature-attribute-missing-in-dependent-crate-ice-aux.rs @@ -0,0 +1,9 @@ +#![feature(generic_const_exprs)] + +pub struct Error(()); + +pub trait FromSlice: Sized { + const SIZE: usize = std::mem::size_of::(); + + fn validate_slice(bytes: &[[u8; Self::SIZE]]) -> Result<(), Error>; +} diff --git a/tests/ui/const-generics/generic_const_exprs/feature-attribute-missing-in-dependent-crate-ice.rs b/tests/ui/const-generics/generic_const_exprs/feature-attribute-missing-in-dependent-crate-ice.rs new file mode 100644 index 0000000000000..b9537014767ea --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/feature-attribute-missing-in-dependent-crate-ice.rs @@ -0,0 +1,19 @@ +//! Regression test to ensure that using the `generic_const_exprs` feature in a library crate +//! without enabling it in a dependent crate does not lead to an ICE. +//! +//! Issue: + +//@ aux-build:feature-attribute-missing-in-dependent-crate-ice-aux.rs + +extern crate feature_attribute_missing_in_dependent_crate_ice_aux as aux; + +struct Wrapper(i64); + +impl aux::FromSlice for Wrapper { + fn validate_slice(_: &[[u8; Self::SIZE]]) -> Result<(), aux::Error> { + //~^ ERROR generic `Self` types are currently not permitted in anonymous constants + Ok(()) + } +} + +fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/feature-attribute-missing-in-dependent-crate-ice.stderr b/tests/ui/const-generics/generic_const_exprs/feature-attribute-missing-in-dependent-crate-ice.stderr new file mode 100644 index 0000000000000..5c3306651426b --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/feature-attribute-missing-in-dependent-crate-ice.stderr @@ -0,0 +1,14 @@ +error: generic `Self` types are currently not permitted in anonymous constants + --> $DIR/feature-attribute-missing-in-dependent-crate-ice.rs:13:33 + | +LL | fn validate_slice(_: &[[u8; Self::SIZE]]) -> Result<(), aux::Error> { + | ^^^^ + | +note: not a concrete type + --> $DIR/feature-attribute-missing-in-dependent-crate-ice.rs:12:41 + | +LL | impl aux::FromSlice for Wrapper { + | ^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/must_not_suspend/mutex.rs b/tests/ui/lint/must_not_suspend/mutex.rs index d14f7130b4cff..8dd4cc1761517 100644 --- a/tests/ui/lint/must_not_suspend/mutex.rs +++ b/tests/ui/lint/must_not_suspend/mutex.rs @@ -5,7 +5,7 @@ async fn other() {} pub async fn uhoh(m: std::sync::Mutex<()>) { - let _guard = m.lock().unwrap(); //~ ERROR `MutexGuard` held across + let _guard = m.lock().unwrap(); //~ ERROR `std::sync::MutexGuard` held across other().await; } diff --git a/tests/ui/lint/must_not_suspend/mutex.stderr b/tests/ui/lint/must_not_suspend/mutex.stderr index ca53a753150ec..0db1f2575b197 100644 --- a/tests/ui/lint/must_not_suspend/mutex.stderr +++ b/tests/ui/lint/must_not_suspend/mutex.stderr @@ -1,4 +1,4 @@ -error: `MutexGuard` held across a suspend point, but should not be +error: `std::sync::MutexGuard` held across a suspend point, but should not be --> $DIR/mutex.rs:8:9 | LL | let _guard = m.lock().unwrap(); diff --git a/tests/ui/loop-match/upvar-scrutinee.rs b/tests/ui/loop-match/upvar-scrutinee.rs new file mode 100644 index 0000000000000..a93e3a0e59ab1 --- /dev/null +++ b/tests/ui/loop-match/upvar-scrutinee.rs @@ -0,0 +1,81 @@ +#![allow(incomplete_features)] +#![feature(loop_match)] + +#[derive(Clone, Copy)] +enum State { + A, + B, +} + +fn main() { + let mut state = State::A; + + #[loop_match] + loop { + state = 'blk: { + match state { + State::A => { + #[const_continue] + break 'blk State::B; + } + State::B => { + return; + } + } + } + } + + || { + #[loop_match] + loop { + state = 'blk: { + match state { + //~^ ERROR invalid match on `#[loop_match]` state + State::A => { + #[const_continue] + break 'blk State::B; + } + State::B => { + return; + } + } + } + } + }; + + || { + let mut state = state; + #[loop_match] + loop { + state = 'blk: { + match state { + State::A => { + #[const_continue] + break 'blk State::B; + } + State::B => { + return; + } + } + } + } + }; + + move || { + #[loop_match] + loop { + state = 'blk: { + match state { + //~^ ERROR invalid match on `#[loop_match]` state + State::A => { + #[const_continue] + break 'blk State::B; + } + State::B => { + return; + } + } + } + } + }; +} diff --git a/tests/ui/loop-match/upvar-scrutinee.stderr b/tests/ui/loop-match/upvar-scrutinee.stderr new file mode 100644 index 0000000000000..b7a0a90193d70 --- /dev/null +++ b/tests/ui/loop-match/upvar-scrutinee.stderr @@ -0,0 +1,18 @@ +error: invalid match on `#[loop_match]` state + --> $DIR/upvar-scrutinee.rs:32:23 + | +LL | match state { + | ^^^^^ + | + = note: a local variable must be the scrutinee within a `#[loop_match]` + +error: invalid match on `#[loop_match]` state + --> $DIR/upvar-scrutinee.rs:68:23 + | +LL | match state { + | ^^^^^ + | + = note: a local variable must be the scrutinee within a `#[loop_match]` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/modules/issue-107649.stderr b/tests/ui/modules/issue-107649.stderr index 802ac669a10ea..49d7cb4e0aad6 100644 --- a/tests/ui/modules/issue-107649.stderr +++ b/tests/ui/modules/issue-107649.stderr @@ -9,8 +9,8 @@ error[E0277]: `Dummy` doesn't implement `Debug` help: consider annotating `Dummy` with `#[derive(Debug)]` --> $DIR/auxiliary/dummy_lib.rs:2:1 | -2 + #[derive(Debug)] -3 | pub struct Dummy; + 2 + #[derive(Debug)] + 3 | pub struct Dummy; | error: aborting due to 1 previous error diff --git a/tests/ui/privacy/private-field-access-in-mutex-54062.rs b/tests/ui/privacy/private-field-access-in-mutex-54062.rs index f145f9c3e529b..c957e0bc7e860 100644 --- a/tests/ui/privacy/private-field-access-in-mutex-54062.rs +++ b/tests/ui/privacy/private-field-access-in-mutex-54062.rs @@ -8,7 +8,7 @@ fn main() {} fn testing(test: Test) { let _ = test.comps.inner.try_lock(); - //~^ ERROR: field `inner` of struct `Mutex` is private + //~^ ERROR: field `inner` of struct `std::sync::Mutex` is private } // https://github.com/rust-lang/rust/issues/54062 diff --git a/tests/ui/privacy/private-field-access-in-mutex-54062.stderr b/tests/ui/privacy/private-field-access-in-mutex-54062.stderr index 1424410759746..f7f846406485e 100644 --- a/tests/ui/privacy/private-field-access-in-mutex-54062.stderr +++ b/tests/ui/privacy/private-field-access-in-mutex-54062.stderr @@ -1,4 +1,4 @@ -error[E0616]: field `inner` of struct `Mutex` is private +error[E0616]: field `inner` of struct `std::sync::Mutex` is private --> $DIR/private-field-access-in-mutex-54062.rs:10:24 | LL | let _ = test.comps.inner.try_lock(); diff --git a/tests/ui/runtime/out-of-stack.rs b/tests/ui/runtime/out-of-stack.rs index 6be34afb56035..913d3637c8f2b 100644 --- a/tests/ui/runtime/out-of-stack.rs +++ b/tests/ui/runtime/out-of-stack.rs @@ -19,7 +19,7 @@ extern crate libc; use std::env; use std::hint::black_box; use std::process::Command; -use std::thread; +use std::thread::Builder; fn silent_recurse() { let buf = [0u8; 1000]; @@ -56,9 +56,9 @@ fn main() { } else if args.len() > 1 && args[1] == "loud" { loud_recurse(); } else if args.len() > 1 && args[1] == "silent-thread" { - thread::spawn(silent_recurse).join(); + Builder::new().name("ferris".to_string()).spawn(silent_recurse).unwrap().join(); } else if args.len() > 1 && args[1] == "loud-thread" { - thread::spawn(loud_recurse).join(); + Builder::new().name("ferris".to_string()).spawn(loud_recurse).unwrap().join(); } else { let mut modes = vec![ "silent-thread", @@ -82,6 +82,12 @@ fn main() { let error = String::from_utf8_lossy(&silent.stderr); assert!(error.contains("has overflowed its stack"), "missing overflow message: {}", error); + + if mode.contains("thread") { + assert!(error.contains("ferris"), "missing thread name: {}", error); + } else { + assert!(error.contains("main"), "missing thread name: {}", error); + } } } } diff --git a/tests/ui/suggestions/inner_type.fixed b/tests/ui/suggestions/inner_type.fixed index 3dc939d6b5c4f..175a2a02acdf8 100644 --- a/tests/ui/suggestions/inner_type.fixed +++ b/tests/ui/suggestions/inner_type.fixed @@ -25,7 +25,7 @@ fn main() { let another_item = std::sync::Mutex::new(Struct { p: 42_u32 }); another_item.lock().unwrap().method(); - //~^ ERROR no method named `method` found for struct `Mutex` in the current scope [E0599] + //~^ ERROR no method named `method` found for struct `std::sync::Mutex` in the current scope [E0599] //~| HELP use `.lock().unwrap()` to borrow the `Struct`, blocking the current thread until it can be acquired let another_item = std::sync::RwLock::new(Struct { p: 42_u32 }); diff --git a/tests/ui/suggestions/inner_type.rs b/tests/ui/suggestions/inner_type.rs index 81a05c2531197..ab021414f56ce 100644 --- a/tests/ui/suggestions/inner_type.rs +++ b/tests/ui/suggestions/inner_type.rs @@ -25,7 +25,7 @@ fn main() { let another_item = std::sync::Mutex::new(Struct { p: 42_u32 }); another_item.method(); - //~^ ERROR no method named `method` found for struct `Mutex` in the current scope [E0599] + //~^ ERROR no method named `method` found for struct `std::sync::Mutex` in the current scope [E0599] //~| HELP use `.lock().unwrap()` to borrow the `Struct`, blocking the current thread until it can be acquired let another_item = std::sync::RwLock::new(Struct { p: 42_u32 }); diff --git a/tests/ui/suggestions/inner_type.stderr b/tests/ui/suggestions/inner_type.stderr index 5ac3d04f10414..67ebb5789b70c 100644 --- a/tests/ui/suggestions/inner_type.stderr +++ b/tests/ui/suggestions/inner_type.stderr @@ -30,11 +30,11 @@ help: use `.borrow_mut()` to mutably borrow the `Struct`, panicking if any LL | other_item.borrow_mut().some_mutable_method(); | +++++++++++++ -error[E0599]: no method named `method` found for struct `Mutex` in the current scope +error[E0599]: no method named `method` found for struct `std::sync::Mutex` in the current scope --> $DIR/inner_type.rs:27:18 | LL | another_item.method(); - | ^^^^^^ method not found in `Mutex>` + | ^^^^^^ method not found in `std::sync::Mutex>` | note: the method `method` exists on the type `Struct` --> $DIR/inner_type.rs:9:5 diff --git a/tests/ui/sync/mutexguard-sync.stderr b/tests/ui/sync/mutexguard-sync.stderr index 1501a793d5e8b..ab9983c1f2c1c 100644 --- a/tests/ui/sync/mutexguard-sync.stderr +++ b/tests/ui/sync/mutexguard-sync.stderr @@ -8,7 +8,7 @@ LL | test_sync(guard); | = help: the trait `Sync` is not implemented for `Cell` = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead - = note: required for `MutexGuard<'_, Cell>` to implement `Sync` + = note: required for `std::sync::MutexGuard<'_, Cell>` to implement `Sync` note: required by a bound in `test_sync` --> $DIR/mutexguard-sync.rs:5:17 | diff --git a/tests/ui/traits/const-traits/span-bug-issue-121418.stderr b/tests/ui/traits/const-traits/span-bug-issue-121418.stderr index 92cfecd054049..f31129d9cb7c7 100644 --- a/tests/ui/traits/const-traits/span-bug-issue-121418.stderr +++ b/tests/ui/traits/const-traits/span-bug-issue-121418.stderr @@ -14,8 +14,8 @@ error[E0277]: the size for values of type `(dyn T + 'static)` cannot be known at LL | pub const fn new() -> std::sync::Mutex {} | ^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - = help: within `Mutex<(dyn T + 'static)>`, the trait `Sized` is not implemented for `(dyn T + 'static)` -note: required because it appears within the type `Mutex<(dyn T + 'static)>` + = help: within `std::sync::Mutex<(dyn T + 'static)>`, the trait `Sized` is not implemented for `(dyn T + 'static)` +note: required because it appears within the type `std::sync::Mutex<(dyn T + 'static)>` --> $SRC_DIR/std/src/sync/poison/mutex.rs:LL:COL = note: the return type of a function must have a statically known size @@ -27,7 +27,7 @@ LL | pub const fn new() -> std::sync::Mutex {} | | | implicitly returns `()` as its body has no tail or `return` expression | - = note: expected struct `Mutex<(dyn T + 'static)>` + = note: expected struct `std::sync::Mutex<(dyn T + 'static)>` found unit type `()` error: aborting due to 3 previous errors diff --git a/tests/ui/typeck/assign-non-lval-derefmut.fixed b/tests/ui/typeck/assign-non-lval-derefmut.fixed index 6ecec574f2ece..e6f97a9e86c68 100644 --- a/tests/ui/typeck/assign-non-lval-derefmut.fixed +++ b/tests/ui/typeck/assign-non-lval-derefmut.fixed @@ -5,11 +5,11 @@ fn main() { *x.lock().unwrap() = 2; //~^ ERROR invalid left-hand side of assignment *x.lock().unwrap() += 1; - //~^ ERROR binary assignment operation `+=` cannot be applied to type `MutexGuard<'_, usize>` + //~^ ERROR binary assignment operation `+=` cannot be applied to type `std::sync::MutexGuard<'_, usize>` let mut y = x.lock().unwrap(); *y = 2; //~^ ERROR mismatched types *y += 1; - //~^ ERROR binary assignment operation `+=` cannot be applied to type `MutexGuard<'_, usize>` + //~^ ERROR binary assignment operation `+=` cannot be applied to type `std::sync::MutexGuard<'_, usize>` } diff --git a/tests/ui/typeck/assign-non-lval-derefmut.rs b/tests/ui/typeck/assign-non-lval-derefmut.rs index ac1be913e2a9d..a53a52c7e4dd9 100644 --- a/tests/ui/typeck/assign-non-lval-derefmut.rs +++ b/tests/ui/typeck/assign-non-lval-derefmut.rs @@ -5,11 +5,11 @@ fn main() { x.lock().unwrap() = 2; //~^ ERROR invalid left-hand side of assignment x.lock().unwrap() += 1; - //~^ ERROR binary assignment operation `+=` cannot be applied to type `MutexGuard<'_, usize>` + //~^ ERROR binary assignment operation `+=` cannot be applied to type `std::sync::MutexGuard<'_, usize>` let mut y = x.lock().unwrap(); y = 2; //~^ ERROR mismatched types y += 1; - //~^ ERROR binary assignment operation `+=` cannot be applied to type `MutexGuard<'_, usize>` + //~^ ERROR binary assignment operation `+=` cannot be applied to type `std::sync::MutexGuard<'_, usize>` } diff --git a/tests/ui/typeck/assign-non-lval-derefmut.stderr b/tests/ui/typeck/assign-non-lval-derefmut.stderr index 16fb1e9c5c3a4..f57b5abe2eedc 100644 --- a/tests/ui/typeck/assign-non-lval-derefmut.stderr +++ b/tests/ui/typeck/assign-non-lval-derefmut.stderr @@ -11,15 +11,15 @@ help: consider dereferencing here to assign to the mutably borrowed value LL | *x.lock().unwrap() = 2; | + -error[E0368]: binary assignment operation `+=` cannot be applied to type `MutexGuard<'_, usize>` +error[E0368]: binary assignment operation `+=` cannot be applied to type `std::sync::MutexGuard<'_, usize>` --> $DIR/assign-non-lval-derefmut.rs:7:5 | LL | x.lock().unwrap() += 1; | -----------------^^^^^ | | - | cannot use `+=` on type `MutexGuard<'_, usize>` + | cannot use `+=` on type `std::sync::MutexGuard<'_, usize>` | -note: the foreign item type `MutexGuard<'_, usize>` doesn't implement `AddAssign<{integer}>` +note: the foreign item type `std::sync::MutexGuard<'_, usize>` doesn't implement `AddAssign<{integer}>` --> $SRC_DIR/std/src/sync/poison/mutex.rs:LL:COL | = note: not implement `AddAssign<{integer}>` @@ -36,22 +36,22 @@ LL | let mut y = x.lock().unwrap(); LL | y = 2; | ^ expected `MutexGuard<'_, usize>`, found integer | - = note: expected struct `MutexGuard<'_, usize>` + = note: expected struct `std::sync::MutexGuard<'_, usize>` found type `{integer}` help: consider dereferencing here to assign to the mutably borrowed value | LL | *y = 2; | + -error[E0368]: binary assignment operation `+=` cannot be applied to type `MutexGuard<'_, usize>` +error[E0368]: binary assignment operation `+=` cannot be applied to type `std::sync::MutexGuard<'_, usize>` --> $DIR/assign-non-lval-derefmut.rs:13:5 | LL | y += 1; | -^^^^^ | | - | cannot use `+=` on type `MutexGuard<'_, usize>` + | cannot use `+=` on type `std::sync::MutexGuard<'_, usize>` | -note: the foreign item type `MutexGuard<'_, usize>` doesn't implement `AddAssign<{integer}>` +note: the foreign item type `std::sync::MutexGuard<'_, usize>` doesn't implement `AddAssign<{integer}>` --> $SRC_DIR/std/src/sync/poison/mutex.rs:LL:COL | = note: not implement `AddAssign<{integer}>` diff --git a/tests/ui/typeck/deref-multi.stderr b/tests/ui/typeck/deref-multi.stderr index 02513853c486d..c4fa49e43ef27 100644 --- a/tests/ui/typeck/deref-multi.stderr +++ b/tests/ui/typeck/deref-multi.stderr @@ -63,7 +63,7 @@ LL | x.lock().unwrap() | ^^^^^^^^^^^^^^^^^ expected `i32`, found `MutexGuard<'_, &i32>` | = note: expected type `i32` - found struct `MutexGuard<'_, &i32>` + found struct `std::sync::MutexGuard<'_, &i32>` help: consider dereferencing the type | LL | **x.lock().unwrap()