about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-11-26 15:15:22 +0000
committerbors <bors@rust-lang.org>2022-11-26 15:15:22 +0000
commit144b4859ad2c9fcee453f3a1000357e20ecad535 (patch)
treeee32e3a0171eb40b4f67e78e5cd237960d6077d3
parentb3e44029cf0c2a43a6326666fa8e0d7c60ac5000 (diff)
parent3158a8d476d6f35664502fa50aae9f3e873a263e (diff)
downloadrust-144b4859ad2c9fcee453f3a1000357e20ecad535.tar.gz
rust-144b4859ad2c9fcee453f3a1000357e20ecad535.zip
Auto merge of #2696 - RalfJung:no-std-windows, r=RalfJung
make no_std work on Windows

Also fixes https://github.com/rust-lang/miri/issues/1123 by cherry-picking a patch by `@DrMeepster.`
-rwxr-xr-xsrc/tools/miri/ci.sh1
-rw-r--r--src/tools/miri/src/eval.rs3
-rw-r--r--src/tools/miri/src/helpers.rs93
-rw-r--r--src/tools/miri/src/shims/tls.rs5
-rw-r--r--src/tools/miri/src/shims/unix/fs.rs8
-rw-r--r--src/tools/miri/tests/pass/no_std.rs4
6 files changed, 71 insertions, 43 deletions
diff --git a/src/tools/miri/ci.sh b/src/tools/miri/ci.sh
index 01925e39da9..e528be8b037 100755
--- a/src/tools/miri/ci.sh
+++ b/src/tools/miri/ci.sh
@@ -88,7 +88,6 @@ function run_tests_minimal {
   ./miri test -- "$@"
 
   # Ensure that a small smoke test of cargo-miri works.
-  # Note: This doesn't work on windows because of TLS.
   cargo miri run --manifest-path test-cargo-miri/no-std-smoke/Cargo.toml
 
   endgroup
diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs
index 81132db94cf..363b647d6c6 100644
--- a/src/tools/miri/src/eval.rs
+++ b/src/tools/miri/src/eval.rs
@@ -9,6 +9,7 @@ use std::thread;
 use log::info;
 
 use rustc_data_structures::fx::FxHashSet;
+use rustc_hir::def::Namespace;
 use rustc_hir::def_id::DefId;
 use rustc_middle::ty::{
     self,
@@ -195,7 +196,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
     MiriMachine::late_init(&mut ecx, config)?;
 
     // Make sure we have MIR. We check MIR for some stable monomorphic function in libcore.
-    let sentinel = ecx.try_resolve_path(&["core", "ascii", "escape_default"]);
+    let sentinel = ecx.try_resolve_path(&["core", "ascii", "escape_default"], Namespace::ValueNS);
     if !matches!(sentinel, Some(s) if tcx.is_mir_available(s.def.def_id())) {
         tcx.sess.fatal(
             "the current sysroot was built without `-Zalways-encode-mir`, or libcore seems missing. \
diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs
index 8c7bc9eff00..bf086a7c623 100644
--- a/src/tools/miri/src/helpers.rs
+++ b/src/tools/miri/src/helpers.rs
@@ -2,12 +2,12 @@ pub mod convert;
 
 use std::cmp;
 use std::iter;
-use std::mem;
 use std::num::NonZeroUsize;
 use std::time::Duration;
 
 use log::trace;
 
+use rustc_hir::def::{DefKind, Namespace};
 use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
 use rustc_middle::mir;
 use rustc_middle::ty::{
@@ -74,40 +74,71 @@ const UNIX_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = {
 };
 
 /// Gets an instance for a path.
-fn try_resolve_did<'tcx>(tcx: TyCtxt<'tcx>, path: &[&str]) -> Option<DefId> {
-    tcx.crates(()).iter().find(|&&krate| tcx.crate_name(krate).as_str() == path[0]).and_then(
-        |krate| {
-            let krate = DefId { krate: *krate, index: CRATE_DEF_INDEX };
-            let mut items = tcx.module_children(krate);
-            let mut path_it = path.iter().skip(1).peekable();
-
-            while let Some(segment) = path_it.next() {
-                for item in mem::take(&mut items).iter() {
-                    if item.ident.name.as_str() == *segment {
-                        if path_it.peek().is_none() {
-                            return Some(item.res.def_id());
-                        }
+///
+/// A `None` namespace indicates we are looking for a module.
+fn try_resolve_did<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    path: &[&str],
+    namespace: Option<Namespace>,
+) -> Option<DefId> {
+    /// Yield all children of the given item, that have the given name.
+    fn find_children<'tcx: 'a, 'a>(
+        tcx: TyCtxt<'tcx>,
+        item: DefId,
+        name: &'a str,
+    ) -> impl Iterator<Item = DefId> + 'a {
+        tcx.module_children(item)
+            .iter()
+            .filter(move |item| item.ident.name.as_str() == name)
+            .map(move |item| item.res.def_id())
+    }
 
-                        items = tcx.module_children(item.res.def_id());
-                        break;
-                    }
-                }
-            }
-            None
-        },
-    )
+    // Take apart the path: leading crate, a sequence of modules, and potentially a final item.
+    let (&crate_name, path) = path.split_first().expect("paths must have at least one segment");
+    let (modules, item) = if let Some(namespace) = namespace {
+        let (&item_name, modules) =
+            path.split_last().expect("non-module paths must have at least 2 segments");
+        (modules, Some((item_name, namespace)))
+    } else {
+        (path, None)
+    };
+
+    // First find the crate.
+    let krate =
+        tcx.crates(()).iter().find(|&&krate| tcx.crate_name(krate).as_str() == crate_name)?;
+    let mut cur_item = DefId { krate: *krate, index: CRATE_DEF_INDEX };
+    // Then go over the modules.
+    for &segment in modules {
+        cur_item = find_children(tcx, cur_item, segment)
+            .find(|item| tcx.def_kind(item) == DefKind::Mod)?;
+    }
+    // Finally, look up the desired item in this module, if any.
+    match item {
+        Some((item_name, namespace)) =>
+            Some(
+                find_children(tcx, cur_item, item_name)
+                    .find(|item| tcx.def_kind(item).ns() == Some(namespace))?,
+            ),
+        None => Some(cur_item),
+    }
 }
 
 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
+    /// Checks if the given crate/module exists.
+    fn have_module(&self, path: &[&str]) -> bool {
+        try_resolve_did(*self.eval_context_ref().tcx, path, None).is_some()
+    }
+
     /// Gets an instance for a path; fails gracefully if the path does not exist.
-    fn try_resolve_path(&self, path: &[&str]) -> Option<ty::Instance<'tcx>> {
-        let did = try_resolve_did(self.eval_context_ref().tcx.tcx, path)?;
-        Some(ty::Instance::mono(self.eval_context_ref().tcx.tcx, did))
+    fn try_resolve_path(&self, path: &[&str], namespace: Namespace) -> Option<ty::Instance<'tcx>> {
+        let tcx = self.eval_context_ref().tcx.tcx;
+        let did = try_resolve_did(tcx, path, Some(namespace))?;
+        Some(ty::Instance::mono(tcx, did))
     }
 
     /// Gets an instance for a path.
-    fn resolve_path(&self, path: &[&str]) -> ty::Instance<'tcx> {
-        self.try_resolve_path(path)
+    fn resolve_path(&self, path: &[&str], namespace: Namespace) -> ty::Instance<'tcx> {
+        self.try_resolve_path(path, namespace)
             .unwrap_or_else(|| panic!("failed to find required Rust item: {path:?}"))
     }
 
@@ -115,7 +146,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
     /// if the path could be resolved, and None otherwise
     fn eval_path_scalar(&self, path: &[&str]) -> InterpResult<'tcx, Scalar<Provenance>> {
         let this = self.eval_context_ref();
-        let instance = this.resolve_path(path);
+        let instance = this.resolve_path(path, Namespace::ValueNS);
         let cid = GlobalId { instance, promoted: None };
         // We don't give a span -- this isn't actually used directly by the program anyway.
         let const_val = this.eval_global(cid, None)?;
@@ -147,7 +178,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
     /// Helper function to get the `TyAndLayout` of a `libc` type
     fn libc_ty_layout(&self, name: &str) -> InterpResult<'tcx, TyAndLayout<'tcx>> {
         let this = self.eval_context_ref();
-        let ty = this.resolve_path(&["libc", name]).ty(*this.tcx, ty::ParamEnv::reveal_all());
+        let ty = this
+            .resolve_path(&["libc", name], Namespace::TypeNS)
+            .ty(*this.tcx, ty::ParamEnv::reveal_all());
         this.layout_of(ty)
     }
 
@@ -155,7 +188,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
     fn windows_ty_layout(&self, name: &str) -> InterpResult<'tcx, TyAndLayout<'tcx>> {
         let this = self.eval_context_ref();
         let ty = this
-            .resolve_path(&["std", "sys", "windows", "c", name])
+            .resolve_path(&["std", "sys", "windows", "c", name], Namespace::TypeNS)
             .ty(*this.tcx, ty::ParamEnv::reveal_all());
         this.layout_of(ty)
     }
diff --git a/src/tools/miri/src/shims/tls.rs b/src/tools/miri/src/shims/tls.rs
index 430dedbc170..5fda8bd7b7d 100644
--- a/src/tools/miri/src/shims/tls.rs
+++ b/src/tools/miri/src/shims/tls.rs
@@ -261,6 +261,11 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         // (that would be basically https://github.com/rust-lang/miri/issues/450),
         // we specifically look up the static in libstd that we know is placed
         // in that section.
+        if !this.have_module(&["std"]) {
+            // Looks like we are running in a `no_std` crate.
+            // That also means no TLS dtors callback to call.
+            return Ok(());
+        }
         let thread_callback =
             this.eval_windows("thread_local_key", "p_thread_callback")?.to_pointer(this)?;
         let thread_callback = this.get_ptr_fn(thread_callback)?.as_instance()?;
diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs
index b152082b4de..816e7d87e85 100644
--- a/src/tools/miri/src/shims/unix/fs.rs
+++ b/src/tools/miri/src/shims/unix/fs.rs
@@ -11,7 +11,6 @@ use std::time::SystemTime;
 use log::trace;
 
 use rustc_data_structures::fx::FxHashMap;
-use rustc_middle::ty::{self, layout::LayoutOf};
 use rustc_target::abi::{Align, Size};
 
 use crate::shims::os_str::bytes_to_os_str;
@@ -1006,12 +1005,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         // as `isize`s instead of having the proper types. Thus, we have to recover the layout of
         // `statxbuf_op` by using the `libc::statx` struct type.
         let statxbuf = {
-            // FIXME: This long path is required because `libc::statx` is an struct and also a
-            // function and `resolve_path` is returning the latter.
-            let statx_ty = this
-                .resolve_path(&["libc", "unix", "linux_like", "linux", "gnu", "statx"])
-                .ty(*this.tcx, ty::ParamEnv::reveal_all());
-            let statx_layout = this.layout_of(statx_ty)?;
+            let statx_layout = this.libc_ty_layout("statx")?;
             MPlaceTy::from_aligned_ptr(statxbuf_ptr, statx_layout)
         };
 
diff --git a/src/tools/miri/tests/pass/no_std.rs b/src/tools/miri/tests/pass/no_std.rs
index eb0e860e68e..3bece7783f7 100644
--- a/src/tools/miri/tests/pass/no_std.rs
+++ b/src/tools/miri/tests/pass/no_std.rs
@@ -1,9 +1,5 @@
 #![feature(lang_items, start)]
 #![no_std]
-// windows tls dtors go through libstd right now, thus this test
-// cannot pass. When windows tls dtors go through the special magic
-// windows linker section, we can run this test on windows again.
-//@ignore-target-windows: no-std not supported on Windows
 
 // Plumbing to let us use `writeln!` to host stdout: