Skip to content

Commit beb8a81

Browse files
committed
Auto merge of #3453 - RalfJung:rustup, r=RalfJung
Rustup
2 parents 11a9d7e + aba29d8 commit beb8a81

File tree

6 files changed

+129
-35
lines changed

6 files changed

+129
-35
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,8 @@ binaries, and as such worth documenting:
507507
crate currently being compiled.
508508
* `MIRI_ORIG_RUSTDOC` is set and read by different phases of `cargo-miri` to remember the
509509
value of `RUSTDOC` from before it was overwritten.
510+
* `MIRI_REPLACE_LIBRS_IF_NOT_TEST` when set to any value enables a hack that helps bootstrap
511+
run the standard library tests in Miri.
510512
* `MIRI_VERBOSE` when set to any value tells the various `cargo-miri` phases to
511513
perform verbose logging.
512514
* `MIRI_HOST_SYSROOT` is set by bootstrap to tell `cargo-miri` which sysroot to use for *host*

cargo-miri/src/phases.rs

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use std::env;
44
use std::fs::{self, File};
55
use std::io::BufReader;
6-
use std::path::PathBuf;
6+
use std::path::{Path, PathBuf};
77
use std::process::Command;
88

99
use rustc_version::VersionMeta;
@@ -412,9 +412,25 @@ pub fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
412412
// Arguments are treated very differently depending on whether this crate is
413413
// for interpretation by Miri, or for use by a build script / proc macro.
414414
if target_crate {
415-
// Forward arguments, but remove "link" from "--emit" to make this a check-only build.
415+
// Forward arguments, but patched.
416416
let emit_flag = "--emit";
417+
// This hack helps bootstrap run standard library tests in Miri. The issue is as follows:
418+
// when running `cargo miri test` on libcore, cargo builds a local copy of core and makes it
419+
// a dependency of the integration test crate. This copy duplicates all the lang items, so
420+
// the build fails. (Regular testing avoids this because the sysroot is a literal copy of
421+
// what `cargo build` produces, but since Miri builds its own sysroot this does not work for
422+
// us.) So we need to make it so that the locally built libcore contains all the items from
423+
// `core`, but does not re-define them -- we want to replace the entire crate but a
424+
// re-export of the sysroot crate. We do this by swapping out the source file: if
425+
// `MIRI_REPLACE_LIBRS_IF_NOT_TEST` is set and we are building a `lib.rs` file, and a
426+
// `lib.miri.rs` file exists in the same folder, we build that instead. But crucially we
427+
// only do that for the library, not the unit test crate (which would be runnable) or
428+
// rustdoc (which would have a different `phase`).
429+
let replace_librs = env::var_os("MIRI_REPLACE_LIBRS_IF_NOT_TEST").is_some()
430+
&& !runnable_crate
431+
&& phase == RustcPhase::Build;
417432
while let Some(arg) = args.next() {
433+
// Patch `--emit`: remove "link" from "--emit" to make this a check-only build.
418434
if let Some(val) = arg.strip_prefix(emit_flag) {
419435
// Patch this argument. First, extract its value.
420436
let val =
@@ -429,13 +445,36 @@ pub fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
429445
}
430446
}
431447
cmd.arg(format!("{emit_flag}={}", val.join(",")));
432-
} else if arg == "--extern" {
433-
// Patch `--extern` filenames, since Cargo sometimes passes stub `.rlib` files:
434-
// https://github.com/rust-lang/miri/issues/1705
448+
continue;
449+
}
450+
// Patch `--extern` filenames, since Cargo sometimes passes stub `.rlib` files:
451+
// https://github.com/rust-lang/miri/issues/1705
452+
if arg == "--extern" {
435453
forward_patched_extern_arg(&mut args, &mut cmd);
436-
} else {
437-
cmd.arg(arg);
454+
continue;
438455
}
456+
// If the REPLACE_LIBRS hack is enabled and we are building a `lib.rs` file, and a
457+
// `lib.miri.rs` file exists, then build that instead. We only consider relative paths
458+
// as cargo uses those for files in the workspace; dependencies from crates.io get
459+
// absolute paths.
460+
if replace_librs {
461+
let path = Path::new(&arg);
462+
if path.is_relative()
463+
&& path.file_name().is_some_and(|f| f == "lib.rs")
464+
&& path.is_file()
465+
{
466+
let miri_rs = Path::new(&arg).with_extension("miri.rs");
467+
if miri_rs.is_file() {
468+
if verbose > 0 {
469+
eprintln!("Performing REPLACE_LIBRS hack: {arg:?} -> {miri_rs:?}");
470+
}
471+
cmd.arg(miri_rs);
472+
continue;
473+
}
474+
}
475+
}
476+
// Fallback: just propagate the argument.
477+
cmd.arg(arg);
439478
}
440479

441480
// During setup, patch the panic runtime for `libpanic_abort` (mirroring what bootstrap usually does).

rust-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0accf4ec4c07d23aa86f6a97aeb8797941abc30e
1+
23d47dba319331d4418827cfbb8c1af283497d3c

src/helpers.rs

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -105,24 +105,41 @@ fn try_resolve_did(tcx: TyCtxt<'_>, path: &[&str], namespace: Option<Namespace>)
105105
(path, None)
106106
};
107107

108-
// First find the crate.
109-
let krate =
110-
tcx.crates(()).iter().find(|&&krate| tcx.crate_name(krate).as_str() == crate_name)?;
111-
let mut cur_item = DefId { krate: *krate, index: CRATE_DEF_INDEX };
112-
// Then go over the modules.
113-
for &segment in modules {
114-
cur_item = find_children(tcx, cur_item, segment)
115-
.find(|item| tcx.def_kind(item) == DefKind::Mod)?;
116-
}
117-
// Finally, look up the desired item in this module, if any.
118-
match item {
119-
Some((item_name, namespace)) =>
120-
Some(
121-
find_children(tcx, cur_item, item_name)
122-
.find(|item| tcx.def_kind(item).ns() == Some(namespace))?,
123-
),
124-
None => Some(cur_item),
108+
// There may be more than one crate with this name. We try them all.
109+
// (This is particularly relevant when running `std` tests as then there are two `std` crates:
110+
// the one in the sysroot and the one locally built by `cargo test`.)
111+
// FIXME: can we prefer the one from the sysroot?
112+
'crates: for krate in
113+
tcx.crates(()).iter().filter(|&&krate| tcx.crate_name(krate).as_str() == crate_name)
114+
{
115+
let mut cur_item = DefId { krate: *krate, index: CRATE_DEF_INDEX };
116+
// Go over the modules.
117+
for &segment in modules {
118+
let Some(next_item) = find_children(tcx, cur_item, segment)
119+
.find(|item| tcx.def_kind(item) == DefKind::Mod)
120+
else {
121+
continue 'crates;
122+
};
123+
cur_item = next_item;
124+
}
125+
// Finally, look up the desired item in this module, if any.
126+
match item {
127+
Some((item_name, namespace)) => {
128+
let Some(item) = find_children(tcx, cur_item, item_name)
129+
.find(|item| tcx.def_kind(item).ns() == Some(namespace))
130+
else {
131+
continue 'crates;
132+
};
133+
return Some(item);
134+
}
135+
None => {
136+
// Just return the module.
137+
return Some(cur_item);
138+
}
139+
}
125140
}
141+
// Item not found in any of the crates with the right name.
142+
None
126143
}
127144

128145
/// Convert a softfloat type to its corresponding hostfloat type.
@@ -968,10 +985,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
968985

969986
fn frame_in_std(&self) -> bool {
970987
let this = self.eval_context_ref();
971-
let Some(start_fn) = this.tcx.lang_items().start_fn() else {
972-
// no_std situations
973-
return false;
974-
};
975988
let frame = this.frame();
976989
// Make an attempt to get at the instance of the function this is inlined from.
977990
let instance: Option<_> = try {
@@ -982,13 +995,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
982995
};
983996
// Fall back to the instance of the function itself.
984997
let instance = instance.unwrap_or(frame.instance);
985-
// Now check if this is in the same crate as start_fn.
986-
// As a special exception we also allow unit tests from
987-
// <https://github.com/rust-lang/miri-test-libstd/tree/master/std_miri_test> to call these
988-
// shims.
998+
// Now check the crate it is in. We could try to be clever here and e.g. check if this is
999+
// the same crate as `start_fn`, but that would not work for running std tests in Miri, so
1000+
// we'd need some more hacks anyway. So we just check the name of the crate. If someone
1001+
// calls their crate `std` then we'll just let them keep the pieces.
9891002
let frame_crate = this.tcx.def_path(instance.def_id()).krate;
990-
frame_crate == this.tcx.def_path(start_fn).krate
991-
|| this.tcx.crate_name(frame_crate).as_str() == "std_miri_test"
1003+
let crate_name = this.tcx.crate_name(frame_crate);
1004+
let crate_name = crate_name.as_str();
1005+
// On miri-test-libstd, the name of the crate is different.
1006+
crate_name == "std" || crate_name == "std_miri_test"
9921007
}
9931008

9941009
/// Handler that should be called when unsupported functionality is encountered.

tests/pass/async-closure-captures.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,38 @@ async fn async_main() {
8888
};
8989
call_once(c).await;
9090
}
91+
92+
fn force_fnonce<T>(f: impl async FnOnce() -> T) -> impl async FnOnce() -> T {
93+
f
94+
}
95+
96+
// Capture something with `move`, but infer to `AsyncFnOnce`
97+
{
98+
let x = Hello(6);
99+
let c = force_fnonce(async move || {
100+
println!("{x:?}");
101+
});
102+
call_once(c).await;
103+
104+
let x = &Hello(7);
105+
let c = force_fnonce(async move || {
106+
println!("{x:?}");
107+
});
108+
call_once(c).await;
109+
}
110+
111+
// Capture something by-ref, but infer to `AsyncFnOnce`
112+
{
113+
let x = Hello(8);
114+
let c = force_fnonce(async || {
115+
println!("{x:?}");
116+
});
117+
call_once(c).await;
118+
119+
let x = &Hello(9);
120+
let c = force_fnonce(async || {
121+
println!("{x:?}");
122+
});
123+
call_once(c).await;
124+
}
91125
}

tests/pass/async-closure-captures.stdout

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,7 @@ Hello(3)
88
Hello(4)
99
Hello(4)
1010
Hello(5)
11+
Hello(6)
12+
Hello(7)
13+
Hello(8)
14+
Hello(9)

0 commit comments

Comments
 (0)