Auto merge of #144633 - Zalathar:rollup-h984m13, r=Zalathar
Rollup of 13 pull requests Successful merges: - rust-lang/rust#144022 (Implementation: `#[feature(sync_nonpoison)]`, `#[feature(nonpoison_mutex)]`) - rust-lang/rust#144167 (Document why `Range*<&T> as RangeBounds<T>` impls are not `T: ?Sized`, and give an alternative.) - rust-lang/rust#144407 (fix(debuginfo): disable overflow check for recursive non-enum types) - rust-lang/rust#144451 (fix: Reject upvar scrutinees for `loop_match`) - rust-lang/rust#144482 (Add explicit download methods to download module in bootstrap) - rust-lang/rust#144500 (thread name in stack overflow message) - rust-lang/rust#144511 (tidy: increase performance of auto extra checks feature) - rust-lang/rust#144599 (bootstrap: enable tidy auto extra checks on tools profile) - rust-lang/rust#144600 (Ensure external paths passed via flags end up in rustdoc depinfo) - rust-lang/rust#144609 (feat: Right align line numbers) - rust-lang/rust#144623 (miri subtree update) - rust-lang/rust#144626 (cc dependencies: clarify comment) - rust-lang/rust#144627 (Add a test case for the issue rust-lang/rust#129882) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
7278554d82
175 changed files with 4986 additions and 2336 deletions
139
Cargo.lock
139
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"
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -955,9 +955,13 @@ impl<'tcx> ThirBuildCx<'tcx> {
|
|||
dcx.emit_fatal(LoopMatchBadRhs { span: block_body_expr.span })
|
||||
};
|
||||
|
||||
fn local(expr: &rustc_hir::Expr<'_>) -> Option<hir::HirId> {
|
||||
fn local(
|
||||
cx: &mut ThirBuildCx<'_>,
|
||||
expr: &rustc_hir::Expr<'_>,
|
||||
) -> Option<hir::HirId> {
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1141,6 +1141,12 @@ impl<'a, T: ?Sized + 'a> RangeBounds<T> 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<T> RangeBounds<T> for RangeFrom<&T> {
|
||||
fn start_bound(&self) -> Bound<&T> {
|
||||
|
|
@ -1151,6 +1157,12 @@ impl<T> RangeBounds<T> 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<T> RangeBounds<T> for RangeTo<&T> {
|
||||
fn start_bound(&self) -> Bound<&T> {
|
||||
|
|
@ -1161,6 +1173,12 @@ impl<T> RangeBounds<T> 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<T> RangeBounds<T> for Range<&T> {
|
||||
fn start_bound(&self) -> Bound<&T> {
|
||||
|
|
@ -1171,6 +1189,12 @@ impl<T> RangeBounds<T> 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<T> RangeBounds<T> for RangeInclusive<&T> {
|
||||
fn start_bound(&self) -> Bound<&T> {
|
||||
|
|
@ -1181,6 +1205,12 @@ impl<T> RangeBounds<T> 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<T> RangeBounds<T> for RangeToInclusive<&T> {
|
||||
fn start_bound(&self) -> Bound<&T> {
|
||||
|
|
|
|||
|
|
@ -167,6 +167,12 @@ impl<T> RangeBounds<T> 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::Excluded(end))`.
|
||||
#[unstable(feature = "new_range_api", issue = "125687")]
|
||||
impl<T> RangeBounds<T> for Range<&T> {
|
||||
fn start_bound(&self) -> Bound<&T> {
|
||||
|
|
@ -346,6 +352,12 @@ impl<T> RangeBounds<T> 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 `start..=end` with `(Bound::Included(start), Bound::Included(end))`.
|
||||
#[unstable(feature = "new_range_api", issue = "125687")]
|
||||
impl<T> RangeBounds<T> for RangeInclusive<&T> {
|
||||
fn start_bound(&self) -> Bound<&T> {
|
||||
|
|
@ -491,6 +503,12 @@ impl<T> RangeBounds<T> 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 `start..` with `(Bound::Included(start), Bound::Unbounded)`.
|
||||
#[unstable(feature = "new_range_api", issue = "125687")]
|
||||
impl<T> RangeBounds<T> for RangeFrom<&T> {
|
||||
fn start_bound(&self) -> Bound<&T> {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
37
library/std/src/sync/nonpoison.rs
Normal file
37
library/std/src/sync/nonpoison.rs
Normal file
|
|
@ -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<Guard> = Result<Guard, WouldBlock>;
|
||||
|
||||
/// 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;
|
||||
611
library/std/src/sync/nonpoison/mutex.rs
Normal file
611
library/std/src/sync/nonpoison/mutex.rs
Normal file
|
|
@ -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<T: ?Sized> {
|
||||
inner: sys::Mutex,
|
||||
data: UnsafeCell<T>,
|
||||
}
|
||||
|
||||
/// `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<T: ?Sized + Send> Send for Mutex<T> {}
|
||||
|
||||
/// `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<T>`] 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<Rc<_>>`, 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<T: ?Sized + Send> Sync for Mutex<T> {}
|
||||
|
||||
/// 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<T>,
|
||||
}
|
||||
|
||||
/// 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<T: ?Sized> !Send for MutexGuard<'_, T> {}
|
||||
|
||||
/// `T` must be `Sync` for a [`MutexGuard<T>`] to be `Sync`
|
||||
/// because it is possible to get a `&T` from `&MutexGuard` (via `Deref`).
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
unsafe impl<T: ?Sized + Sync> 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<T>,
|
||||
inner: &'a sys::Mutex,
|
||||
_variance: PhantomData<&'a mut T>,
|
||||
}
|
||||
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
// #[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
impl<T: ?Sized> !Send for MappedMutexGuard<'_, T> {}
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
// #[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
unsafe impl<T: ?Sized + Sync> Sync for MappedMutexGuard<'_, T> {}
|
||||
|
||||
impl<T> Mutex<T> {
|
||||
/// 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<T> {
|
||||
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::<T>() {
|
||||
// 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<T: ?Sized> Mutex<T> {
|
||||
/// 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<MutexGuard<'_, T>> {
|
||||
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<T> From<T> for Mutex<T> {
|
||||
/// 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<T: ?Sized + Default> Default for Mutex<T> {
|
||||
/// Creates a `Mutex<T>`, with the `Default` value for T.
|
||||
fn default() -> Mutex<T> {
|
||||
Mutex::new(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
impl<T: ?Sized + fmt::Debug> fmt::Debug for Mutex<T> {
|
||||
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", &"<locked>");
|
||||
}
|
||||
}
|
||||
d.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'mutex, T: ?Sized> MutexGuard<'mutex, T> {
|
||||
unsafe fn new(lock: &'mutex Mutex<T>) -> MutexGuard<'mutex, T> {
|
||||
return MutexGuard { lock };
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
impl<T: ?Sized> Deref for MutexGuard<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*self.lock.data.get() }
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
impl<T: ?Sized> DerefMut for MutexGuard<'_, T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
unsafe { &mut *self.lock.data.get() }
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
impl<T: ?Sized> Drop for MutexGuard<'_, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.lock.inner.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
impl<T: ?Sized + fmt::Debug> 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<T: ?Sized + fmt::Display> 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<U, F>(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<U, F>(orig: Self, f: F) -> Result<MappedMutexGuard<'a, U>, 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<T: ?Sized> Deref for MappedMutexGuard<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { self.data.as_ref() }
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
impl<T: ?Sized> DerefMut for MappedMutexGuard<'_, T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
unsafe { self.data.as_mut() }
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
impl<T: ?Sized> Drop for MappedMutexGuard<'_, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.inner.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
impl<T: ?Sized + fmt::Debug> 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<T: ?Sized + fmt::Display> 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<U, F>(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<U, F>(mut orig: Self, f: F) -> Result<MappedMutexGuard<'a, U>, 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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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")]
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -650,7 +650,7 @@ impl<T: ?Sized + fmt::Debug> fmt::Debug for Mutex<T> {
|
|||
d.field("data", &&**err.get_ref());
|
||||
}
|
||||
Err(TryLockError::WouldBlock) => {
|
||||
d.field("data", &format_args!("<locked>"));
|
||||
d.field("data", &"<locked>");
|
||||
}
|
||||
}
|
||||
d.field("poisoned", &self.poison.get());
|
||||
|
|
|
|||
|
|
@ -58,7 +58,11 @@ impl Thread {
|
|||
}
|
||||
}
|
||||
|
||||
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
|
||||
pub unsafe fn new(
|
||||
stack: usize,
|
||||
_name: Option<&str>,
|
||||
p: Box<dyn FnOnce()>,
|
||||
) -> io::Result<Thread> {
|
||||
unsafe {
|
||||
Thread::new_with_coreid(stack, p, -1 /* = no specific core */)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,7 +86,11 @@ impl Thread {
|
|||
/// # Safety
|
||||
///
|
||||
/// See `thread::Builder::spawn_unchecked` for safety requirements.
|
||||
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
|
||||
pub unsafe fn new(
|
||||
stack: usize,
|
||||
_name: Option<&str>,
|
||||
p: Box<dyn FnOnce()>,
|
||||
) -> io::Result<Thread> {
|
||||
let inner = Box::new(ThreadInner {
|
||||
start: UnsafeCell::new(ManuallyDrop::new(p)),
|
||||
lifecycle: AtomicUsize::new(LIFECYCLE_INIT),
|
||||
|
|
|
|||
|
|
@ -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<dyn FnOnce() + Send>) -> io::Result<Thread> {
|
||||
pub unsafe fn new(
|
||||
_stack: usize,
|
||||
_name: Option<&str>,
|
||||
p: Box<dyn FnOnce() + Send>,
|
||||
) -> io::Result<Thread> {
|
||||
let mut queue_lock = task_queue::lock();
|
||||
unsafe { usercalls::launch_thread()? };
|
||||
let (task, handle) = task_queue::Task::new(p);
|
||||
|
|
|
|||
|
|
@ -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<dyn FnOnce()>) -> io::Result<Thread> {
|
||||
pub unsafe fn new(
|
||||
stack: usize,
|
||||
_name: Option<&str>,
|
||||
p: Box<dyn FnOnce()>,
|
||||
) -> io::Result<Thread> {
|
||||
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() };
|
||||
|
|
|
|||
|
|
@ -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<dyn FnOnce()>) -> io::Result<Thread> {
|
||||
pub unsafe fn new(
|
||||
_stack: usize,
|
||||
_name: Option<&str>,
|
||||
_p: Box<dyn FnOnce()>,
|
||||
) -> io::Result<Thread> {
|
||||
unsupported()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ pub struct Handler {
|
|||
}
|
||||
|
||||
impl Handler {
|
||||
pub unsafe fn new() -> Handler {
|
||||
make_handler(false)
|
||||
pub unsafe fn new(thread_name: Option<Box<str>>) -> 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<Box<str>>) -> 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<Box<str>>,
|
||||
) -> 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<Box<str>>,
|
||||
) -> super::Handler {
|
||||
if !main_thread {
|
||||
reserve_stack();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Box<str>>,
|
||||
f: Box<dyn FnOnce()>,
|
||||
}
|
||||
|
||||
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<dyn FnOnce()>) -> io::Result<Thread> {
|
||||
let p = Box::into_raw(Box::new(p));
|
||||
pub unsafe fn new(
|
||||
stack: usize,
|
||||
name: Option<&str>,
|
||||
f: Box<dyn FnOnce()>,
|
||||
) -> io::Result<Thread> {
|
||||
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<libc::pthread_attr_t> = 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<dyn FnOnce()>)();
|
||||
(data.f)();
|
||||
}
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<dyn FnOnce()>) -> io::Result<Thread> {
|
||||
pub unsafe fn new(
|
||||
_stack: usize,
|
||||
_name: Option<&str>,
|
||||
_p: Box<dyn FnOnce()>,
|
||||
) -> io::Result<Thread> {
|
||||
unsupported()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<dyn FnOnce()>) -> io::Result<Thread> {
|
||||
pub unsafe fn new(stack: usize, _name: Option<&str>, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
|
||||
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<dyn FnOnce()>) -> io::Result<Thread> {
|
||||
pub unsafe fn new(_stack: usize, _name: Option<&str>, _p: Box<dyn FnOnce()>) -> io::Result<Thread> {
|
||||
crate::sys::unsupported()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<dyn FnOnce()>) -> io::Result<Thread> {
|
||||
pub unsafe fn new(
|
||||
_stack: usize,
|
||||
_name: Option<&str>,
|
||||
_p: Box<dyn FnOnce()>,
|
||||
) -> io::Result<Thread> {
|
||||
unsupported()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<dyn FnOnce()>) -> io::Result<Thread> {
|
||||
pub unsafe fn new(
|
||||
stack: usize,
|
||||
_name: Option<&str>,
|
||||
p: Box<dyn FnOnce()>,
|
||||
) -> io::Result<Thread> {
|
||||
let p = Box::into_raw(Box::new(p));
|
||||
|
||||
// CreateThread rounds up values for the stack size to the nearest page size (at least 4kb).
|
||||
|
|
|
|||
|
|
@ -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<dyn FnOnce()>) -> io::Result<Thread> {
|
||||
pub unsafe fn new(
|
||||
stack: usize,
|
||||
_name: Option<&str>,
|
||||
p: Box<dyn FnOnce()>,
|
||||
) -> io::Result<Thread> {
|
||||
let p = Box::into_raw(Box::new(p));
|
||||
let mut stack_size = crate::cmp::max(stack, MIN_STACK_SIZE);
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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<T, E: std::fmt::Debug>(x: Result<T, E>) -> 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;
|
||||
|
|
|
|||
|
|
@ -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<T>(Arc<(Mutex<T>, 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<u32>) {
|
||||
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::<NonCopyNeedsDrop>());
|
||||
}
|
||||
|
||||
#[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());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lots_and_lots() {
|
||||
const J: u32 = 1000;
|
||||
const K: u32 = 3;
|
||||
|
||||
let m = Arc::new(Mutex::new(0));
|
||||
|
||||
fn inc(m: &Mutex<u32>) {
|
||||
for _ in 0..J {
|
||||
*m.lock().unwrap() += 1;
|
||||
}
|
||||
let m = Mutex::new(NonCopy(10));
|
||||
assert_eq!(maybe_unwrap(m.into_inner()), NonCopy(10));
|
||||
}
|
||||
);
|
||||
|
||||
nonpoison_and_poison_unwrap_test!(
|
||||
name: test_into_inner_drop,
|
||||
test_body: {
|
||||
use locks::Mutex;
|
||||
|
||||
struct Foo(Arc<AtomicUsize>);
|
||||
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 = maybe_unwrap(m.into_inner());
|
||||
assert_eq!(num_drops.load(Ordering::SeqCst), 0);
|
||||
}
|
||||
assert_eq!(num_drops.load(Ordering::SeqCst), 1);
|
||||
}
|
||||
);
|
||||
|
||||
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));
|
||||
}
|
||||
);
|
||||
|
||||
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));
|
||||
}
|
||||
);
|
||||
|
||||
nonpoison_and_poison_unwrap_test!(
|
||||
name: test_set,
|
||||
test_body: {
|
||||
use locks::Mutex;
|
||||
|
||||
fn inner<T>(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<AtomicUsize>);
|
||||
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<T>(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 test_mutex_arc_condvar() {
|
||||
struct Packet<T>(Arc<(Mutex<T>, Condvar)>);
|
||||
|
||||
let packet = Packet(Arc::new((Mutex::new(false), Condvar::new())));
|
||||
let packet2 = Packet(packet.0.clone());
|
||||
|
||||
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 {
|
||||
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();
|
||||
}
|
||||
assert_eq!(*m.lock().unwrap(), J * K * 2);
|
||||
}
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn try_lock() {
|
||||
let m = Mutex::new(());
|
||||
*m.try_lock().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<Mutex<i32>>,
|
||||
}
|
||||
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<T>(value: T) -> Mutex<T> {
|
||||
let mutex = Mutex::new(value);
|
||||
|
||||
|
|
@ -93,30 +377,6 @@ fn new_poisoned_mutex<T>(value: T) -> Mutex<T> {
|
|||
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<AtomicUsize>);
|
||||
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<T>(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<T>(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<T>(Arc<(Mutex<T>, 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<Mutex<i32>>,
|
||||
}
|
||||
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() {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -3,6 +3,7 @@
|
|||
/// See <https://github.com/rust-lang/rust/issues/134863>
|
||||
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",
|
||||
|
|
|
|||
|
|
@ -380,7 +380,7 @@ impl Options {
|
|||
early_dcx: &mut EarlyDiagCtxt,
|
||||
matches: &getopts::Matches,
|
||||
args: Vec<String>,
|
||||
) -> Option<(InputMode, Options, RenderOptions)> {
|
||||
) -> Option<(InputMode, Options, RenderOptions, Vec<PathBuf>)> {
|
||||
// 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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Playground>,
|
||||
loaded_paths: &mut Vec<PathBuf>,
|
||||
) -> Option<ExternalHtml> {
|
||||
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<P: AsRef<Path>>(
|
||||
file_path: P,
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
loaded_paths: &mut Vec<PathBuf>,
|
||||
) -> Result<String, LoadStringError> {
|
||||
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<P: AsRef<Path>>(
|
|||
}
|
||||
}
|
||||
|
||||
fn load_external_files(names: &[String], dcx: DiagCtxtHandle<'_>) -> Option<String> {
|
||||
fn load_external_files(
|
||||
names: &[String],
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
loaded_paths: &mut Vec<PathBuf>,
|
||||
) -> Option<String> {
|
||||
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');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -333,9 +333,11 @@ pub(crate) fn run(
|
|||
pub(crate) fn load_call_locations(
|
||||
with_examples: Vec<String>,
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
loaded_paths: &mut Vec<PathBuf>,
|
||||
) -> 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}")),
|
||||
|
|
|
|||
53
src/tools/miri/.github/workflows/ci.yml
vendored
53
src/tools/miri/.github/workflows/ci.yml
vendored
|
|
@ -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)
|
||||
run: |
|
||||
./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.
|
||||
# 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")
|
||||
|
||||
# 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?
|
||||
|
||||
|
|
|
|||
1
src/tools/miri/.gitignore
vendored
1
src/tools/miri/.gitignore
vendored
|
|
@ -1,5 +1,4 @@
|
|||
target
|
||||
/doc
|
||||
tex/*/out
|
||||
*.dot
|
||||
*.out
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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<Item = String>) {
|
|||
// 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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
62
src/tools/miri/doc/genmc.md
Normal file
62
src/tools/miri/doc/genmc.md
Normal file
|
|
@ -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.**
|
||||
|
||||
<!-- FIXME(genmc): add explanation. -->
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
<!-- FIXME(genmc): explain options. -->
|
||||
|
||||
<!-- FIXME(genmc): explain Miri-GenMC specific functions. -->
|
||||
|
||||
## Tips
|
||||
|
||||
<!-- FIXME(genmc): add tips for using Miri-GenMC more efficiently. -->
|
||||
|
||||
## 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.
|
||||
|
||||
<!-- FIXME(genmc): document remaining limitations -->
|
||||
|
||||
## 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.
|
||||
<!-- FIXME(genmc,llvm): remove once LLVM dependency is no longer required. -->
|
||||
|
||||
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.
|
||||
|
||||
<!-- FIXME(genmc): explain how submitting code to GenMC should be handled. -->
|
||||
|
||||
<!-- FIXME(genmc): explain development. -->
|
||||
|
|
@ -5,6 +5,7 @@ source = "discover"
|
|||
linkedProjects = [
|
||||
"Cargo.toml",
|
||||
"cargo-miri/Cargo.toml",
|
||||
"genmc-sys/Cargo.toml",
|
||||
"miri-script/Cargo.toml",
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
1
src/tools/miri/genmc-sys/.gitignore
vendored
Normal file
1
src/tools/miri/genmc-sys/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
genmc-src*/
|
||||
17
src/tools/miri/genmc-sys/Cargo.toml
Normal file
17
src/tools/miri/genmc-sys/Cargo.toml
Normal file
|
|
@ -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"] }
|
||||
269
src/tools/miri/genmc-sys/build.rs
Normal file
269
src/tools/miri/genmc-sys/build.rs
Normal file
|
|
@ -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");
|
||||
}
|
||||
30
src/tools/miri/genmc-sys/src/lib.rs
Normal file
30
src/tools/miri/genmc-sys/src/lib.rs
Normal file
|
|
@ -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<MiriGenMCShim>;
|
||||
}
|
||||
}
|
||||
50
src/tools/miri/genmc-sys/src_cpp/MiriInterface.cpp
Normal file
50
src/tools/miri/genmc-sys/src_cpp/MiriInterface.cpp
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
#include "MiriInterface.hpp"
|
||||
|
||||
#include "genmc-sys/src/lib.rs.h"
|
||||
|
||||
auto MiriGenMCShim::createHandle(const GenmcParams &config)
|
||||
-> std::unique_ptr<MiriGenMCShim>
|
||||
{
|
||||
auto conf = std::make_shared<Config>();
|
||||
|
||||
// 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<MiriGenMCShim>(std::move(conf), mode);
|
||||
return driver;
|
||||
}
|
||||
44
src/tools/miri/genmc-sys/src_cpp/MiriInterface.hpp
Normal file
44
src/tools/miri/genmc-sys/src_cpp/MiriInterface.hpp
Normal file
|
|
@ -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 <iostream>
|
||||
|
||||
/**** 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<const Config> 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<MiriGenMCShim> 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<MiriGenMCShim>
|
||||
{
|
||||
return MiriGenMCShim::createHandle(config);
|
||||
}
|
||||
|
||||
#endif /* GENMC_MIRI_INTERFACE_HPP */
|
||||
2
src/tools/miri/josh-sync.toml
Normal file
2
src/tools/miri/josh-sync.toml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
repo = "miri"
|
||||
filter = ":rev(75dd959a3a40eb5b4574f8d2e23aa6efbeb33573:prefix=src/tools/miri):/src/tools/miri"
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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<impl Drop> {
|
||||
// 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<String>) -> 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<u32> {
|
||||
Ok(cmd!(sh, "git rev-list HEAD --max-parents=0 --count")
|
||||
.read()
|
||||
.context("failed to determine the number of root commits")?
|
||||
.parse::<u32>()?)
|
||||
};
|
||||
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())
|
||||
|
|
|
|||
|
|
@ -142,25 +142,6 @@ pub enum Command {
|
|||
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
|
||||
flags: Vec<String>,
|
||||
},
|
||||
/// 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<String>,
|
||||
},
|
||||
/// 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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
6707bf0f59485cf054ac1095725df43220e4be20
|
||||
733dab558992d902d6d17576de1da768094e2cf3
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<u64>),
|
||||
Enter(f64, Callsite, Option<i64>),
|
||||
Event(f64, Callsite),
|
||||
Exit(f64, Callsite, Option<u64>),
|
||||
Exit(f64, Callsite, Option<i64>),
|
||||
NewThread(usize, String),
|
||||
Flush,
|
||||
Drop,
|
||||
|
|
@ -519,14 +520,17 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn get_root_id(&self, span: SpanRef<S>) -> Option<u64> {
|
||||
fn get_root_id(&self, span: SpanRef<S>) -> Option<i64> {
|
||||
// Returns `Option<i64>` instead of `Option<u64>` 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
|
||||
),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,8 +67,6 @@ use crate::log::setup::{deinit_loggers, init_early_loggers, init_late_loggers};
|
|||
struct MiriCompilerCalls {
|
||||
miri_config: Option<MiriConfig>,
|
||||
many_seeds: Option<ManySeedsConfig>,
|
||||
/// Settings for using GenMC with Miri.
|
||||
genmc_config: Option<GenmcConfig>,
|
||||
}
|
||||
|
||||
struct ManySeedsConfig {
|
||||
|
|
@ -77,12 +75,8 @@ struct ManySeedsConfig {
|
|||
}
|
||||
|
||||
impl MiriCompilerCalls {
|
||||
fn new(
|
||||
miri_config: MiriConfig,
|
||||
many_seeds: Option<ManySeedsConfig>,
|
||||
genmc_config: Option<GenmcConfig>,
|
||||
) -> Self {
|
||||
Self { miri_config: Some(miri_config), many_seeds, genmc_config }
|
||||
fn new(miri_config: MiriConfig, many_seeds: Option<ManySeedsConfig>) -> 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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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".
|
||||
///
|
||||
/// `trimmed_arg` should be the argument to be parsed, with the suffix "-Zmiri-genmc" removed
|
||||
pub fn parse_arg(genmc_config: &mut Option<GenmcConfig>, trimmed_arg: &str) {
|
||||
/// 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<GenmcConfig>,
|
||||
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!();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<GenmcConfig>, 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<GenmcConfig>,
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<GenmcConfig>,
|
||||
/// 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."
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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::<InterpResult<'tcx, Vec<bool>>>()?
|
||||
.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)*) => {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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 _};
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
266
src/tools/miri/src/shims/sig.rs
Normal file
266
src/tools/miri/src/shims/sig.rs
Normal file
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
|
@ -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 parse_clockid(&self, clk_id: Scalar) -> Option<TimeoutClock> {
|
||||
// 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();
|
||||
|
||||
// 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 further distinguishes regular and "coarse" clocks, but the "coarse" version
|
||||
// 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" => {
|
||||
// `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.
|
||||
if clk_id == this.eval_libc("CLOCK_UPTIME_RAW") {
|
||||
return Some(TimeoutClock::Monotonic);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn clock_gettime(
|
||||
&mut self,
|
||||
clk_id_op: &OpTy<'tcx>,
|
||||
tp_op: &OpTy<'tcx>,
|
||||
dest: &MPlaceTy<'tcx>,
|
||||
) -> InterpResult<'tcx> {
|
||||
// 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_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 clk_id = this.read_scalar(clk_id_op)?;
|
||||
let tp = this.deref_pointer_as(tp_op, this.libc_ty_layout("timespec"))?;
|
||||
|
||||
let absolute_clocks;
|
||||
let mut relative_clocks;
|
||||
|
||||
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)?,
|
||||
];
|
||||
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())?
|
||||
}
|
||||
"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)?);
|
||||
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);
|
||||
}
|
||||
"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)?];
|
||||
}
|
||||
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);
|
||||
};
|
||||
|
||||
let tv_sec = duration.as_secs();
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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!(
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -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)?;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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::*;
|
||||
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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<mir::BasicBlock>,
|
||||
) -> InterpResult<'tcx> {
|
||||
|
|
@ -78,7 +79,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
// a pointer to `Box<dyn Any + Send + 'static>`.
|
||||
|
||||
// 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)?;
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue