Auto merge of #130133 - workingjubilee:rollup-t5o827k, r=workingjubilee

Rollup of 14 pull requests

Successful merges:

 - #119229 (Update mingw-w64 + GNU toolchain)
 - #128345 (added support for GNU/Hurd on x86_64)
 - #128667 (rustdoc: normalise type/field names)
 - #129876 (Use sysroot crates maximally in `rustc_codegen_gcc`.)
 - #130034 ( Fix enabling wasm-component-ld to match other tools )
 - #130048 (run-make-support: Add llvm-pdbutil)
 - #130068 (Test codegen when setting deployment target)
 - #130070 (Rename variant `AddrOfRegion` of `RegionVariableOrigin` to `BorrowRegion`)
 - #130087 (remove 'const' from 'Option::iter')
 - #130090 (make Result::copied unstably const)
 - #130092 (Fixes typo in wasm32-wasip2 doc comment)
 - #130107 (const: make ptr.is_null() stop execution on ambiguity)
 - #130115 (Remove needless returns detected by clippy in libraries)
 - #130130 (Miri subtree update)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2024-09-09 09:24:11 +00:00
commit 1f44f0a66f
144 changed files with 1265 additions and 980 deletions

View File

@ -11,63 +11,12 @@ dependencies = [
"memchr",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
[[package]]
name = "boml"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85fdb93f04c73bff54305fa437ffea5449c41edcaadfe882f35836206b166ac5"
[[package]]
name = "cc"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "errno"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
dependencies = [
"errno-dragonfly",
"libc",
"windows-sys",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "fastrand"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764"
[[package]]
name = "fm"
version = "0.2.2"
@ -132,12 +81,6 @@ version = "0.2.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
[[package]]
name = "linux-raw-sys"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
[[package]]
name = "memchr"
version = "2.5.0"
@ -154,24 +97,6 @@ dependencies = [
"libc",
]
[[package]]
name = "object"
version = "0.30.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385"
dependencies = [
"memchr",
]
[[package]]
name = "redox_syscall"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "regex"
version = "1.8.4"
@ -196,22 +121,6 @@ dependencies = [
"boml",
"gccjit",
"lang_tester",
"object",
"smallvec",
"tempfile",
]
[[package]]
name = "rustix"
version = "0.38.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f"
dependencies = [
"bitflags 2.4.0",
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
@ -223,25 +132,6 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "smallvec"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
[[package]]
name = "tempfile"
version = "3.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc02fddf48964c42031a0b3fe0428320ecf3a73c401040fc0096f97794310651"
dependencies = [
"cfg-if",
"fastrand",
"redox_syscall",
"rustix",
"windows-sys",
]
[[package]]
name = "termcolor"
version = "1.2.0"
@ -315,69 +205,3 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.48.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_i686_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
name = "windows_i686_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"

View File

@ -23,21 +23,11 @@ default = ["master"]
[dependencies]
gccjit = "2.1"
# Local copy.
#gccjit = { path = "../gccjit.rs" }
object = { version = "0.30.1", default-features = false, features = [
"std",
"read",
] }
smallvec = { version = "1.6.1", features = ["union", "may_dangle"] }
# TODO(antoyo): make tempfile optional.
tempfile = "3.7.1"
[dev-dependencies]
lang_tester = "0.8.0"
tempfile = "3.1.0"
boml = "0.3.1"
[profile.dev]

View File

@ -24,6 +24,14 @@
#![deny(clippy::pattern_type_mismatch)]
#![allow(clippy::needless_lifetimes)]
// Some "regular" crates we want to share with rustc
extern crate object;
extern crate smallvec;
extern crate tempfile;
#[macro_use]
extern crate tracing;
// The rustc crates we need
extern crate rustc_apfloat;
extern crate rustc_ast;
extern crate rustc_attr;
@ -42,8 +50,6 @@ extern crate rustc_middle;
extern crate rustc_session;
extern crate rustc_span;
extern crate rustc_target;
#[macro_use]
extern crate tracing;
// This prevents duplicating functions and statics that are already part of the host rustc process.
#[allow(unused_extern_crates)]

View File

@ -447,7 +447,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// this time with enough precision to check that the value
// whose address was taken can actually be made to live as long
// as it needs to live.
let region = self.next_region_var(infer::AddrOfRegion(expr.span));
let region = self.next_region_var(infer::BorrowRegion(expr.span));
Ty::new_ref(self.tcx, region, ty, mutbl)
}
}

View File

@ -458,8 +458,8 @@ pub enum RegionVariableOrigin {
PatternRegion(Span),
/// Regions created by `&` operator.
///
AddrOfRegion(Span),
BorrowRegion(Span),
/// Regions created as part of an autoref of a method receiver.
Autoref(Span),
@ -1741,7 +1741,7 @@ impl RegionVariableOrigin {
match *self {
MiscVariable(a)
| PatternRegion(a)
| AddrOfRegion(a)
| BorrowRegion(a)
| Autoref(a)
| Coercion(a)
| RegionParameterDefinition(a, ..)

View File

@ -1645,6 +1645,7 @@ supported_targets! {
("x86_64-unknown-haiku", x86_64_unknown_haiku),
("i686-unknown-hurd-gnu", i686_unknown_hurd_gnu),
("x86_64-unknown-hurd-gnu", x86_64_unknown_hurd_gnu),
("aarch64-apple-darwin", aarch64_apple_darwin),
("arm64e-apple-darwin", arm64e_apple_darwin),

View File

@ -1,6 +1,6 @@
//! The `wasm32-wasip2` target is the next evolution of the
//! wasm32-wasi target. While the wasi specification is still under
//! active development, the {review 2 iteration is considered an "island
//! active development, the preview 2 iteration is considered an "island
//! of stability" that should allow users to rely on it indefinitely.
//!
//! The `wasi` target is a proposal to define a standardized set of WebAssembly

View File

@ -0,0 +1,26 @@
use crate::spec::{base, Cc, LinkerFlavor, Lld, StackProbeType, Target};
pub(crate) fn target() -> Target {
let mut base = base::hurd_gnu::opts();
base.cpu = "x86-64".into();
base.plt_by_default = false;
base.max_atomic_width = Some(64);
base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]);
base.stack_probes = StackProbeType::Inline;
base.supports_xray = true;
Target {
llvm_target: "x86_64-unknown-hurd-gnu".into(),
metadata: crate::spec::TargetMetadata {
description: Some("64-bit GNU/Hurd".into()),
tier: Some(3),
host_tools: Some(true),
std: Some(true),
},
pointer_width: 64,
data_layout:
"e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(),
arch: "x86_64".into(),
options: base,
}
}

View File

@ -1018,7 +1018,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
let var_description = match var_origin {
infer::MiscVariable(_) => String::new(),
infer::PatternRegion(_) => " for pattern".to_string(),
infer::AddrOfRegion(_) => " for borrow expression".to_string(),
infer::BorrowRegion(_) => " for borrow expression".to_string(),
infer::Autoref(_) => " for autoref".to_string(),
infer::Coercion(_) => " for automatic coercion".to_string(),
infer::BoundRegion(_, br, infer::FnCall) => {

View File

@ -73,7 +73,7 @@ impl<'a, T> Iterator for Iter<'a, T> {
fn advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
let remaining = self.i1.advance_by(n);
match remaining {
Ok(()) => return Ok(()),
Ok(()) => Ok(()),
Err(n) => {
mem::swap(&mut self.i1, &mut self.i2);
self.i1.advance_by(n.get())
@ -144,7 +144,7 @@ impl<'a, T> DoubleEndedIterator for Iter<'a, T> {
fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
match self.i2.advance_back_by(n) {
Ok(()) => return Ok(()),
Ok(()) => Ok(()),
Err(n) => {
mem::swap(&mut self.i1, &mut self.i2);
self.i2.advance_back_by(n.get())

View File

@ -64,7 +64,7 @@ impl<'a, T> Iterator for IterMut<'a, T> {
fn advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
match self.i1.advance_by(n) {
Ok(()) => return Ok(()),
Ok(()) => Ok(()),
Err(remaining) => {
mem::swap(&mut self.i1, &mut self.i2);
self.i1.advance_by(remaining.get())
@ -135,7 +135,7 @@ impl<'a, T> DoubleEndedIterator for IterMut<'a, T> {
fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
match self.i2.advance_back_by(n) {
Ok(()) => return Ok(()),
Ok(()) => Ok(()),
Err(remaining) => {
mem::swap(&mut self.i1, &mut self.i2);
self.i2.advance_back_by(remaining.get())

View File

@ -208,7 +208,7 @@ const fn needs_realloc<SRC, DEST>(src_cap: usize, dst_cap: usize) -> bool {
// type layouts don't guarantee a fit, so do a runtime check to see if
// the allocations happen to match
return src_cap > 0 && src_cap * mem::size_of::<SRC>() != dst_cap * mem::size_of::<DEST>();
src_cap > 0 && src_cap * mem::size_of::<SRC>() != dst_cap * mem::size_of::<DEST>()
}
/// This provides a shorthand for the source type since local type aliases aren't a thing.

View File

@ -288,11 +288,11 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> {
// Safety: `len` is larger than the array size. Copy a fixed amount here to fully initialize
// the array.
return unsafe {
unsafe {
ptr::copy_nonoverlapping(self.ptr.as_ptr(), raw_ary.as_mut_ptr() as *mut T, N);
self.ptr = self.ptr.add(N);
Ok(raw_ary.transpose().assume_init())
};
}
}
fn fold<B, F>(mut self, mut accum: B, mut f: F) -> B

View File

@ -1338,9 +1338,8 @@ impl<T> Option<T> {
/// assert_eq!(x.iter().next(), None);
/// ```
#[inline]
#[rustc_const_unstable(feature = "const_option", issue = "67441")]
#[stable(feature = "rust1", since = "1.0.0")]
pub const fn iter(&self) -> Iter<'_, T> {
pub fn iter(&self) -> Iter<'_, T> {
Iter { inner: Item { opt: self.as_ref() } }
}
@ -1894,7 +1893,7 @@ impl<T> Option<&T> {
where
T: Copy,
{
// FIXME: this implementation, which sidesteps using `Option::map` since it's not const
// FIXME(const-hack): this implementation, which sidesteps using `Option::map` since it's not const
// ready yet, should be reverted when possible to avoid code repetition
match self {
Some(&v) => Some(v),
@ -1942,7 +1941,7 @@ impl<T> Option<&mut T> {
/// ```
#[must_use = "`self` will be dropped if the result is not used"]
#[stable(feature = "copied", since = "1.35.0")]
#[rustc_const_unstable(feature = "const_option_ext", issue = "91930")]
#[rustc_const_unstable(feature = "const_option", issue = "67441")]
pub const fn copied(self) -> Option<T>
where
T: Copy,

View File

@ -40,15 +40,17 @@ impl<T: ?Sized> *const T {
#[inline]
const fn const_impl(ptr: *const u8) -> bool {
// Compare via a cast to a thin pointer, so fat pointers are only
// considering their "data" part for null-ness.
match (ptr).guaranteed_eq(null_mut()) {
None => false,
Some(res) => res,
// To remain maximally convervative, we stop execution when we don't
// know whether the pointer is null or not.
// We can *not* return `false` here, that would be unsound in `NonNull::new`!
None => panic!("null-ness of this pointer cannot be determined in const context"),
}
}
#[allow(unused_unsafe)]
// Compare via a cast to a thin pointer, so fat pointers are only
// considering their "data" part for null-ness.
const_eval_select((self as *const u8,), const_impl, runtime_impl)
}

View File

@ -187,14 +187,14 @@ impl<Dyn: ?Sized> DynMetadata<Dyn> {
// Consider a reference like `&(i32, dyn Send)`: the vtable will only store the size of the
// `Send` part!
// SAFETY: DynMetadata always contains a valid vtable pointer
return unsafe { crate::intrinsics::vtable_size(self.vtable_ptr() as *const ()) };
unsafe { crate::intrinsics::vtable_size(self.vtable_ptr() as *const ()) }
}
/// Returns the alignment of the type associated with this vtable.
#[inline]
pub fn align_of(self) -> usize {
// SAFETY: DynMetadata always contains a valid vtable pointer
return unsafe { crate::intrinsics::vtable_align(self.vtable_ptr() as *const ()) };
unsafe { crate::intrinsics::vtable_align(self.vtable_ptr() as *const ()) }
}
/// Returns the size and alignment together as a `Layout`

View File

@ -33,22 +33,7 @@ impl<T: ?Sized> *mut T {
#[rustc_diagnostic_item = "ptr_is_null"]
#[inline]
pub const fn is_null(self) -> bool {
#[inline]
fn runtime_impl(ptr: *mut u8) -> bool {
ptr.addr() == 0
}
#[inline]
const fn const_impl(ptr: *mut u8) -> bool {
// Compare via a cast to a thin pointer, so fat pointers are only
// considering their "data" part for null-ness.
match (ptr).guaranteed_eq(null_mut()) {
None => false,
Some(res) => res,
}
}
const_eval_select((self as *mut u8,), const_impl, runtime_impl)
self.cast_const().is_null()
}
/// Casts to a pointer of another type.

View File

@ -1535,11 +1535,17 @@ impl<T, E> Result<&T, E> {
/// ```
#[inline]
#[stable(feature = "result_copied", since = "1.59.0")]
pub fn copied(self) -> Result<T, E>
#[rustc_const_unstable(feature = "const_result", issue = "82814")]
pub const fn copied(self) -> Result<T, E>
where
T: Copy,
{
self.map(|&t| t)
// FIXME(const-hack): this implementation, which sidesteps using `Result::map` since it's not const
// ready yet, should be reverted when possible to avoid code repetition
match self {
Ok(&v) => Ok(v),
Err(e) => Err(e),
}
}
/// Maps a `Result<&T, E>` to a `Result<T, E>` by cloning the contents of the
@ -1579,11 +1585,17 @@ impl<T, E> Result<&mut T, E> {
/// ```
#[inline]
#[stable(feature = "result_copied", since = "1.59.0")]
pub fn copied(self) -> Result<T, E>
#[rustc_const_unstable(feature = "const_result", issue = "82814")]
pub const fn copied(self) -> Result<T, E>
where
T: Copy,
{
self.map(|&mut t| t)
// FIXME(const-hack): this implementation, which sidesteps using `Result::map` since it's not const
// ready yet, should be reverted when possible to avoid code repetition
match self {
Ok(&mut v) => Ok(v),
Err(e) => Err(e),
}
}
/// Maps a `Result<&mut T, E>` to a `Result<T, E>` by cloning the contents of the

View File

@ -1814,7 +1814,7 @@ fn simd_contains(needle: &str, haystack: &str) -> Option<bool> {
}
mask &= !(1 << trailing);
}
return false;
false
};
let test_chunk = |idx| -> u16 {
@ -1830,7 +1830,7 @@ fn simd_contains(needle: &str, haystack: &str) -> Option<bool> {
let both = eq_first.bitand(eq_last);
let mask = both.to_bitmask() as u16;
return mask;
mask
};
let mut i = 0;

View File

@ -13,7 +13,7 @@ pub(crate) struct PidFd(FileDesc);
impl PidFd {
pub fn kill(&self) -> io::Result<()> {
return cvt(unsafe {
cvt(unsafe {
libc::syscall(
libc::SYS_pidfd_send_signal,
self.0.as_raw_fd(),
@ -22,7 +22,7 @@ impl PidFd {
0,
)
})
.map(drop);
.map(drop)
}
pub fn wait(&self) -> io::Result<ExitStatus> {
@ -30,7 +30,7 @@ impl PidFd {
cvt(unsafe {
libc::waitid(libc::P_PIDFD, self.0.as_raw_fd() as u32, &mut siginfo, libc::WEXITED)
})?;
return Ok(ExitStatus::from_waitid_siginfo(siginfo));
Ok(ExitStatus::from_waitid_siginfo(siginfo))
}
pub fn try_wait(&self) -> io::Result<Option<ExitStatus>> {
@ -45,9 +45,10 @@ impl PidFd {
)
})?;
if unsafe { siginfo.si_pid() } == 0 {
return Ok(None);
Ok(None)
} else {
Ok(Some(ExitStatus::from_waitid_siginfo(siginfo)))
}
return Ok(Some(ExitStatus::from_waitid_siginfo(siginfo)));
}
}

View File

@ -265,7 +265,7 @@ impl OpenOptions {
pub fn new() -> OpenOptions {
let mut base = OpenOptions::default();
base.dirflags = wasi::LOOKUPFLAGS_SYMLINK_FOLLOW;
return base;
base
}
pub fn read(&mut self, read: bool) {
@ -382,7 +382,7 @@ impl OpenOptions {
base |= wasi::RIGHTS_PATH_UNLINK_FILE;
base |= wasi::RIGHTS_POLL_FD_READWRITE;
return base;
base
}
fn rights_inheriting(&self) -> wasi::Rights {

View File

@ -115,7 +115,7 @@ pub fn hashmap_random_keys() -> (u64, u64) {
let len = mem::size_of_val(&ret);
wasi::random_get(base, len).expect("random_get failure");
}
return ret;
ret
}
#[inline]

View File

@ -1912,7 +1912,7 @@ impl Step for Assemble {
// delegates to the `rust-lld` binary for linking and then runs
// logic to create the final binary. This is used by the
// `wasm32-wasip2` target of Rust.
if builder.build_wasm_component_ld() {
if builder.tool_enabled("wasm-component-ld") {
let wasm_component_ld_exe =
builder.ensure(crate::core::build_steps::tool::WasmComponentLd {
compiler: build_compiler,

View File

@ -473,7 +473,7 @@ impl Step for Rustc {
);
}
}
if builder.build_wasm_component_ld() {
if builder.tool_enabled("wasm-component-ld") {
let src_dir = builder.sysroot_libdir(compiler, host).parent().unwrap().join("bin");
let ld = exe("wasm-component-ld", compiler.host);
builder.copy_link(&src_dir.join(&ld), &dst_dir.join(&ld));

View File

@ -693,14 +693,7 @@ impl Step for Cargo {
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
let builder = run.builder;
run.path("src/tools/cargo").default_condition(
builder.config.extended
&& builder.config.tools.as_ref().map_or(
true,
// If `tools` is set, search list for this tool.
|tools| tools.iter().any(|tool| tool == "cargo"),
),
)
run.path("src/tools/cargo").default_condition(builder.tool_enabled("cargo"))
}
fn make_run(run: RunConfig<'_>) {
@ -772,14 +765,7 @@ impl Step for RustAnalyzer {
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
let builder = run.builder;
run.path("src/tools/rust-analyzer").default_condition(
builder.config.extended
&& builder
.config
.tools
.as_ref()
.map_or(true, |tools| tools.iter().any(|tool| tool == "rust-analyzer")),
)
run.path("src/tools/rust-analyzer").default_condition(builder.tool_enabled("rust-analyzer"))
}
fn make_run(run: RunConfig<'_>) {
@ -821,12 +807,8 @@ impl Step for RustAnalyzerProcMacroSrv {
run.path("src/tools/rust-analyzer")
.path("src/tools/rust-analyzer/crates/proc-macro-srv-cli")
.default_condition(
builder.config.extended
&& builder.config.tools.as_ref().map_or(true, |tools| {
tools.iter().any(|tool| {
tool == "rust-analyzer" || tool == "rust-analyzer-proc-macro-srv"
})
}),
builder.tool_enabled("rust-analyzer")
|| builder.tool_enabled("rust-analyzer-proc-macro-srv"),
)
}
@ -874,16 +856,8 @@ impl Step for LlvmBitcodeLinker {
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
let builder = run.builder;
run.path("src/tools/llvm-bitcode-linker").default_condition(
builder.config.extended
&& builder
.config
.tools
.as_ref()
.map_or(builder.build.unstable_features(), |tools| {
tools.iter().any(|tool| tool == "llvm-bitcode-linker")
}),
)
run.path("src/tools/llvm-bitcode-linker")
.default_condition(builder.tool_enabled("llvm-bitcode-linker"))
}
fn make_run(run: RunConfig<'_>) {

View File

@ -1407,16 +1407,17 @@ Executed at: {executed_at}"#,
None
}
/// Returns whether it's requested that `wasm-component-ld` is built as part
/// of the sysroot. This is done either with the `extended` key in
/// `config.toml` or with the `tools` set.
fn build_wasm_component_ld(&self) -> bool {
if self.config.extended {
return true;
/// Returns whether the specified tool is configured as part of this build.
///
/// This requires that both the `extended` key is set and the `tools` key is
/// either unset or specifically contains the specified tool.
fn tool_enabled(&self, tool: &str) -> bool {
if !self.config.extended {
return false;
}
match &self.config.tools {
Some(set) => set.contains("wasm-component-ld"),
None => false,
Some(set) => set.contains(tool),
None => true,
}
}

View File

@ -6,8 +6,8 @@ IFS=$'\n\t'
source "$(cd "$(dirname "$0")" && pwd)/../shared.sh"
MINGW_ARCHIVE_32="i686-12.2.0-release-posix-dwarf-rt_v10-rev0.7z"
MINGW_ARCHIVE_64="x86_64-12.2.0-release-posix-seh-rt_v10-rev0.7z"
MINGW_ARCHIVE_32="i686-14.1.0-release-posix-dwarf-msvcrt-rt_v12-rev0.7z"
MINGW_ARCHIVE_64="x86_64-14.1.0-release-posix-seh-msvcrt-rt_v12-rev0.7z"
if isWindows && isKnownToBeMingwBuild; then
case "${CI_JOB_NAME}" in

View File

@ -382,6 +382,7 @@ target | std | host | notes
[`x86_64-unikraft-linux-musl`](platform-support/unikraft-linux-musl.md) | ✓ | | 64-bit Unikraft with musl 1.2.3
`x86_64-unknown-dragonfly` | ✓ | ✓ | 64-bit DragonFlyBSD
`x86_64-unknown-haiku` | ✓ | ✓ | 64-bit Haiku
[`x86_64-unknown-hurd-gnu`](platform-support/hurd.md) | ✓ | ✓ | 64-bit GNU/Hurd
[`x86_64-unknown-hermit`](platform-support/hermit.md) | ✓ | | x86_64 Hermit
`x86_64-unknown-l4re-uclibc` | ? | |
[`x86_64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | 64-bit OpenBSD

View File

@ -1,4 +1,4 @@
# `i686-unknown-hurd-gnu`
# `i686-unknown-hurd-gnu` and `x86_64-unknown-hurd-gnu`
**Tier: 3**
@ -16,7 +16,8 @@ The GNU/Hurd target supports `std` and uses the standard ELF file format.
## Building the target
This target can be built by adding `i686-unknown-hurd-gnu` as target in the rustc list.
This target can be built by adding `i686-unknown-hurd-gnu` and
`x86_64-unknown-hurd-gnu` as targets in the rustc list.
## Building Rust programs
@ -32,4 +33,4 @@ Tests can be run in the same way as a regular binary.
## Cross-compilation toolchains and C code
The target supports C code, the GNU toolchain calls the target
`i686-unknown-gnu`.
`i686-unknown-gnu` and `x86_64-unknown-gnu`.

View File

@ -156,7 +156,7 @@ impl FromWithTcx<clean::GenericArgs> for GenericArgs {
match args {
AngleBracketed { args, constraints } => GenericArgs::AngleBracketed {
args: args.into_vec().into_tcx(tcx),
bindings: constraints.into_tcx(tcx),
constraints: constraints.into_tcx(tcx),
},
Parenthesized { inputs, output } => GenericArgs::Parenthesized {
inputs: inputs.into_vec().into_tcx(tcx),
@ -198,9 +198,9 @@ impl FromWithTcx<clean::ConstantKind> for Constant {
}
}
impl FromWithTcx<clean::AssocItemConstraint> for TypeBinding {
impl FromWithTcx<clean::AssocItemConstraint> for AssocItemConstraint {
fn from_tcx(constraint: clean::AssocItemConstraint, tcx: TyCtxt<'_>) -> Self {
TypeBinding {
AssocItemConstraint {
name: constraint.assoc.name.to_string(),
args: constraint.assoc.args.into_tcx(tcx),
binding: constraint.kind.into_tcx(tcx),
@ -208,12 +208,12 @@ impl FromWithTcx<clean::AssocItemConstraint> for TypeBinding {
}
}
impl FromWithTcx<clean::AssocItemConstraintKind> for TypeBindingKind {
impl FromWithTcx<clean::AssocItemConstraintKind> for AssocItemConstraintKind {
fn from_tcx(kind: clean::AssocItemConstraintKind, tcx: TyCtxt<'_>) -> Self {
use clean::AssocItemConstraintKind::*;
match kind {
Equality { term } => TypeBindingKind::Equality(term.into_tcx(tcx)),
Bound { bounds } => TypeBindingKind::Constraint(bounds.into_tcx(tcx)),
Equality { term } => AssocItemConstraintKind::Equality(term.into_tcx(tcx)),
Bound { bounds } => AssocItemConstraintKind::Constraint(bounds.into_tcx(tcx)),
}
}
}
@ -314,7 +314,7 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
ModuleItem(m) => {
ItemEnum::Module(Module { is_crate, items: ids(m.items, tcx), is_stripped: false })
}
ImportItem(i) => ItemEnum::Import(i.into_tcx(tcx)),
ImportItem(i) => ItemEnum::Use(i.into_tcx(tcx)),
StructItem(s) => ItemEnum::Struct(s.into_tcx(tcx)),
UnionItem(u) => ItemEnum::Union(u.into_tcx(tcx)),
StructFieldItem(f) => ItemEnum::StructField(f.into_tcx(tcx)),
@ -331,7 +331,7 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
ImplItem(i) => ItemEnum::Impl((*i).into_tcx(tcx)),
StaticItem(s) => ItemEnum::Static(s.into_tcx(tcx)),
ForeignStaticItem(s, _) => ItemEnum::Static(s.into_tcx(tcx)),
ForeignTypeItem => ItemEnum::ForeignType,
ForeignTypeItem => ItemEnum::ExternType,
TypeAliasItem(t) => ItemEnum::TypeAlias(t.into_tcx(tcx)),
// FIXME(generic_const_items): Add support for generic free consts
ConstantItem(ci) => {
@ -347,21 +347,19 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
}
// FIXME(generic_const_items): Add support for generic associated consts.
TyAssocConstItem(_generics, ty) => {
ItemEnum::AssocConst { type_: (*ty).into_tcx(tcx), default: None }
ItemEnum::AssocConst { type_: (*ty).into_tcx(tcx), value: None }
}
// FIXME(generic_const_items): Add support for generic associated consts.
AssocConstItem(ci) => {
ItemEnum::AssocConst { type_: ci.type_.into_tcx(tcx), default: Some(ci.kind.expr(tcx)) }
ItemEnum::AssocConst { type_: ci.type_.into_tcx(tcx), value: Some(ci.kind.expr(tcx)) }
}
TyAssocTypeItem(g, b) => {
ItemEnum::AssocType { generics: g.into_tcx(tcx), bounds: b.into_tcx(tcx), type_: None }
}
TyAssocTypeItem(g, b) => ItemEnum::AssocType {
generics: g.into_tcx(tcx),
bounds: b.into_tcx(tcx),
default: None,
},
AssocTypeItem(t, b) => ItemEnum::AssocType {
generics: t.generics.into_tcx(tcx),
bounds: b.into_tcx(tcx),
default: Some(t.item_type.unwrap_or(t.type_).into_tcx(tcx)),
type_: Some(t.item_type.unwrap_or(t.type_).into_tcx(tcx)),
},
// `convert_item` early returns `None` for stripped items and keywords.
KeywordItem => unreachable!(),
@ -385,7 +383,7 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
impl FromWithTcx<clean::Struct> for Struct {
fn from_tcx(struct_: clean::Struct, tcx: TyCtxt<'_>) -> Self {
let fields_stripped = struct_.has_stripped_entries();
let has_stripped_fields = struct_.has_stripped_entries();
let clean::Struct { ctor_kind, generics, fields } = struct_;
let kind = match ctor_kind {
@ -394,7 +392,7 @@ impl FromWithTcx<clean::Struct> for Struct {
assert!(fields.is_empty());
StructKind::Unit
}
None => StructKind::Plain { fields: ids(fields, tcx), fields_stripped },
None => StructKind::Plain { fields: ids(fields, tcx), has_stripped_fields },
};
Struct {
@ -407,22 +405,22 @@ impl FromWithTcx<clean::Struct> for Struct {
impl FromWithTcx<clean::Union> for Union {
fn from_tcx(union_: clean::Union, tcx: TyCtxt<'_>) -> Self {
let fields_stripped = union_.has_stripped_entries();
let has_stripped_fields = union_.has_stripped_entries();
let clean::Union { generics, fields } = union_;
Union {
generics: generics.into_tcx(tcx),
fields_stripped,
has_stripped_fields,
fields: ids(fields, tcx),
impls: Vec::new(), // Added in JsonRenderer::item
}
}
}
pub(crate) fn from_fn_header(header: &rustc_hir::FnHeader) -> Header {
Header {
async_: header.is_async(),
const_: header.is_const(),
unsafe_: header.is_unsafe(),
pub(crate) fn from_fn_header(header: &rustc_hir::FnHeader) -> FunctionHeader {
FunctionHeader {
is_async: header.is_async(),
is_const: header.is_const(),
is_unsafe: header.is_unsafe(),
abi: convert_abi(header.abi),
}
}
@ -474,7 +472,7 @@ impl FromWithTcx<clean::GenericParamDefKind> for GenericParamDefKind {
Type { bounds, default, synthetic } => GenericParamDefKind::Type {
bounds: bounds.into_tcx(tcx),
default: default.map(|x| (*x).into_tcx(tcx)),
synthetic,
is_synthetic: synthetic,
},
Const { ty, default, synthetic: _ } => GenericParamDefKind::Const {
type_: (*ty).into_tcx(tcx),
@ -508,7 +506,7 @@ impl FromWithTcx<clean::WherePredicate> for WherePredicate {
.map(|bound| bound.into_tcx(tcx))
.collect(),
default: default.map(|ty| (*ty).into_tcx(tcx)),
synthetic,
is_synthetic: synthetic,
}
}
clean::GenericParamDefKind::Const { ty, default, synthetic: _ } => {
@ -602,12 +600,12 @@ impl FromWithTcx<clean::Type> for Type {
ImplTrait(g) => Type::ImplTrait(g.into_tcx(tcx)),
Infer => Type::Infer,
RawPointer(mutability, type_) => Type::RawPointer {
mutable: mutability == ast::Mutability::Mut,
is_mutable: mutability == ast::Mutability::Mut,
type_: Box::new((*type_).into_tcx(tcx)),
},
BorrowedRef { lifetime, mutability, type_ } => Type::BorrowedRef {
lifetime: lifetime.map(convert_lifetime),
mutable: mutability == ast::Mutability::Mut,
is_mutable: mutability == ast::Mutability::Mut,
type_: Box::new((*type_).into_tcx(tcx)),
},
QPath(box clean::QPathData { assoc, self_type, trait_, .. }) => Type::QualifiedPath {
@ -643,29 +641,29 @@ impl FromWithTcx<clean::BareFunctionDecl> for FunctionPointer {
fn from_tcx(bare_decl: clean::BareFunctionDecl, tcx: TyCtxt<'_>) -> Self {
let clean::BareFunctionDecl { safety, generic_params, decl, abi } = bare_decl;
FunctionPointer {
header: Header {
unsafe_: matches!(safety, rustc_hir::Safety::Unsafe),
const_: false,
async_: false,
header: FunctionHeader {
is_unsafe: matches!(safety, rustc_hir::Safety::Unsafe),
is_const: false,
is_async: false,
abi: convert_abi(abi),
},
generic_params: generic_params.into_tcx(tcx),
decl: decl.into_tcx(tcx),
sig: decl.into_tcx(tcx),
}
}
}
impl FromWithTcx<clean::FnDecl> for FnDecl {
impl FromWithTcx<clean::FnDecl> for FunctionSignature {
fn from_tcx(decl: clean::FnDecl, tcx: TyCtxt<'_>) -> Self {
let clean::FnDecl { inputs, output, c_variadic } = decl;
FnDecl {
FunctionSignature {
inputs: inputs
.values
.into_iter()
.map(|arg| (arg.name.to_string(), arg.type_.into_tcx(tcx)))
.collect(),
output: if output.is_unit() { None } else { Some(output.into_tcx(tcx)) },
c_variadic,
is_c_variadic: c_variadic,
}
}
}
@ -702,12 +700,12 @@ impl FromWithTcx<clean::Impl> for Impl {
let provided_trait_methods = impl_.provided_trait_methods(tcx);
let clean::Impl { safety, generics, trait_, for_, items, polarity, kind } = impl_;
// FIXME: use something like ImplKind in JSON?
let (synthetic, blanket_impl) = match kind {
let (is_synthetic, blanket_impl) = match kind {
clean::ImplKind::Normal | clean::ImplKind::FakeVariadic => (false, None),
clean::ImplKind::Auto => (true, None),
clean::ImplKind::Blanket(ty) => (false, Some(*ty)),
};
let negative_polarity = match polarity {
let is_negative = match polarity {
ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => false,
ty::ImplPolarity::Negative => true,
};
@ -721,8 +719,8 @@ impl FromWithTcx<clean::Impl> for Impl {
trait_: trait_.map(|path| path.into_tcx(tcx)),
for_: for_.into_tcx(tcx),
items: ids(items, tcx),
negative: negative_polarity,
synthetic,
is_negative,
is_synthetic,
blanket_impl: blanket_impl.map(|x| x.into_tcx(tcx)),
}
}
@ -736,7 +734,7 @@ pub(crate) fn from_function(
) -> Function {
let clean::Function { decl, generics } = *function;
Function {
decl: decl.into_tcx(tcx),
sig: decl.into_tcx(tcx),
generics: generics.into_tcx(tcx),
header: from_fn_header(&header),
has_body,
@ -745,11 +743,11 @@ pub(crate) fn from_function(
impl FromWithTcx<clean::Enum> for Enum {
fn from_tcx(enum_: clean::Enum, tcx: TyCtxt<'_>) -> Self {
let variants_stripped = enum_.has_stripped_entries();
let has_stripped_variants = enum_.has_stripped_entries();
let clean::Enum { variants, generics } = enum_;
Enum {
generics: generics.into_tcx(tcx),
variants_stripped,
has_stripped_variants,
variants: ids(variants, tcx),
impls: Vec::new(), // Added in JsonRenderer::item
}
@ -766,7 +764,7 @@ impl FromWithTcx<clean::Variant> for Variant {
CLike => VariantKind::Plain,
Tuple(fields) => VariantKind::Tuple(ids_keeping_stripped(fields, tcx)),
Struct(s) => VariantKind::Struct {
fields_stripped: s.has_stripped_entries(),
has_stripped_fields: s.has_stripped_entries(),
fields: ids(s.fields, tcx),
},
};
@ -787,21 +785,21 @@ impl FromWithTcx<clean::Discriminant> for Discriminant {
}
}
impl FromWithTcx<clean::Import> for Import {
impl FromWithTcx<clean::Import> for Use {
fn from_tcx(import: clean::Import, tcx: TyCtxt<'_>) -> Self {
use clean::ImportKind::*;
let (name, glob) = match import.kind {
let (name, is_glob) = match import.kind {
Simple(s) => (s.to_string(), false),
Glob => (
import.source.path.last_opt().unwrap_or_else(|| Symbol::intern("*")).to_string(),
true,
),
};
Import {
Use {
source: import.source.path.whole_name(),
name,
id: import.source.did.map(ItemId::from).map(|i| id_from_item_default(i, tcx)),
glob,
is_glob,
}
}
}
@ -835,7 +833,7 @@ impl FromWithTcx<clean::Static> for Static {
fn from_tcx(stat: clean::Static, tcx: TyCtxt<'_>) -> Self {
Static {
type_: (*stat.type_).into_tcx(tcx),
mutable: stat.mutability == ast::Mutability::Mut,
is_mutable: stat.mutability == ast::Mutability::Mut,
expr: stat
.expr
.map(|e| rendered_const(tcx, tcx.hir().body(e), tcx.hir().body_owner_def_id(e)))
@ -856,7 +854,7 @@ impl FromWithTcx<ItemType> for ItemKind {
match kind {
Module => ItemKind::Module,
ExternCrate => ItemKind::ExternCrate,
Import => ItemKind::Import,
Import => ItemKind::Use,
Struct => ItemKind::Struct,
Union => ItemKind::Union,
Enum => ItemKind::Enum,
@ -872,7 +870,7 @@ impl FromWithTcx<ItemType> for ItemKind {
Primitive => ItemKind::Primitive,
AssocConst => ItemKind::AssocConst,
AssocType => ItemKind::AssocType,
ForeignType => ItemKind::ForeignType,
ForeignType => ItemKind::ExternType,
Keyword => ItemKind::Keyword,
TraitAlias => ItemKind::TraitAlias,
ProcAttribute => ItemKind::ProcAttribute,

View File

@ -197,7 +197,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
types::ItemEnum::Function(_)
| types::ItemEnum::Module(_)
| types::ItemEnum::Import(_)
| types::ItemEnum::Use(_)
| types::ItemEnum::AssocConst { .. }
| types::ItemEnum::AssocType { .. } => true,
types::ItemEnum::ExternCrate { .. }
@ -208,7 +208,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
| types::ItemEnum::TypeAlias(_)
| types::ItemEnum::Constant { .. }
| types::ItemEnum::Static(_)
| types::ItemEnum::ForeignType
| types::ItemEnum::ExternType
| types::ItemEnum::Macro(_)
| types::ItemEnum::ProcMacro(_) => false,
};

View File

@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize};
/// This integer is incremented with every breaking change to the API,
/// and is returned along with the JSON blob as [`Crate::format_version`].
/// Consuming code should assert that this value matches the format version(s) that it supports.
pub const FORMAT_VERSION: u32 = 33;
pub const FORMAT_VERSION: u32 = 34;
/// The root of the emitted JSON blob.
///
@ -194,7 +194,7 @@ pub enum GenericArgs {
/// ```
args: Vec<GenericArg>,
/// Associated type or constant bindings (e.g. `Item=i32` or `Item: Clone`) for this type.
bindings: Vec<TypeBinding>,
constraints: Vec<AssocItemConstraint>,
},
/// `Fn(A, B) -> C`
Parenthesized {
@ -258,19 +258,19 @@ pub struct Constant {
/// ^^^^^^^^^^ ^^^^^^^^^^^^^^^
/// ```
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct TypeBinding {
pub struct AssocItemConstraint {
/// The name of the associated type/constant.
pub name: String,
/// Arguments provided to the associated type/constant.
pub args: GenericArgs,
/// The kind of bound applied to the associated type/constant.
pub binding: TypeBindingKind,
pub binding: AssocItemConstraintKind,
}
/// The way in which an associate type/constant is bound.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TypeBindingKind {
pub enum AssocItemConstraintKind {
/// The required value/type is specified exactly. e.g.
/// ```text
/// Iterator<Item = u32, IntoIter: DoubleEndedIterator>
@ -311,7 +311,7 @@ pub enum ItemKind {
/// A crate imported via the `extern crate` syntax.
ExternCrate,
/// An import of 1 or more items into scope, using the `use` keyword.
Import,
Use,
/// A `struct` declaration.
Struct,
/// A field of a struct.
@ -341,7 +341,7 @@ pub enum ItemKind {
/// `type`s from an `extern` block.
///
/// See [the tracking issue](https://github.com/rust-lang/rust/issues/43467)
ForeignType,
ExternType,
/// A macro declaration.
///
/// Corresponds to either `ItemEnum::Macro(_)`
@ -386,7 +386,7 @@ pub enum ItemEnum {
rename: Option<String>,
},
/// An import of 1 or more items into scope, using the `use` keyword.
Import(Import),
Use(Use),
/// A `union` declaration.
Union(Union),
@ -429,7 +429,7 @@ pub enum ItemEnum {
/// `type`s from an `extern` block.
///
/// See [the tracking issue](https://github.com/rust-lang/rust/issues/43467)
ForeignType,
ExternType,
/// A macro_rules! declarative macro. Contains a single string with the source
/// representation of the macro with the patterns stripped.
@ -447,12 +447,19 @@ pub enum ItemEnum {
/// The type of the constant.
#[serde(rename = "type")]
type_: Type,
/// The stringified expression for the default value, if provided, e.g.
/// Inside a trait declaration, this is the default value for the associated constant,
/// if provided.
/// Inside an `impl` block, this is the value assigned to the associated constant,
/// and will always be present.
///
/// The representation is implementation-defined and not guaranteed to be representative of
/// either the resulting value or of the source code.
///
/// ```rust
/// const X: usize = 640 * 1024;
/// // ^^^^^^^^^^
/// ```
default: Option<String>,
value: Option<String>,
},
/// An associated type of a trait or a type.
AssocType {
@ -467,12 +474,16 @@ pub enum ItemEnum {
/// }
/// ```
bounds: Vec<GenericBound>,
/// The default for this type, if provided, e.g.
/// Inside a trait declaration, this is the default for the associated type, if provided.
/// Inside an impl block, this is the type assigned to the associated type, and will always
/// be present.
///
/// ```rust
/// type X = usize;
/// // ^^^^^
/// ```
default: Option<Type>,
#[serde(rename = "type")]
type_: Option<Type>,
},
}
@ -497,7 +508,7 @@ pub struct Union {
/// The generic parameters and where clauses on this union.
pub generics: Generics,
/// Whether any fields have been removed from the result, due to being private or hidden.
pub fields_stripped: bool,
pub has_stripped_fields: bool,
/// The list of fields in the union.
///
/// All of the corresponding [`Item`]s are of kind [`ItemEnum::StructField`].
@ -554,7 +565,7 @@ pub enum StructKind {
/// All of the corresponding [`Item`]s are of kind [`ItemEnum::StructField`].
fields: Vec<Id>,
/// Whether any fields have been removed from the result, due to being private or hidden.
fields_stripped: bool,
has_stripped_fields: bool,
},
}
@ -564,7 +575,7 @@ pub struct Enum {
/// Information about the type parameters and `where` clauses of the enum.
pub generics: Generics,
/// Whether any variants have been removed from the result, due to being private or hidden.
pub variants_stripped: bool,
pub has_stripped_variants: bool,
/// The list of variants in the enum.
///
/// All of the corresponding [`Item`]s are of kind [`ItemEnum::Variant`]
@ -621,7 +632,7 @@ pub enum VariantKind {
/// All of the corresponding [`Item`]s are of kind [`ItemEnum::Variant`].
fields: Vec<Id>,
/// Whether any variants have been removed from the result, due to being private or hidden.
fields_stripped: bool,
has_stripped_fields: bool,
},
}
@ -645,16 +656,13 @@ pub struct Discriminant {
/// A set of fundamental properties of a function.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Header {
pub struct FunctionHeader {
/// Is this function marked as `const`?
#[serde(rename = "const")]
pub const_: bool,
pub is_const: bool,
/// Is this function unsafe?
#[serde(rename = "unsafe")]
pub unsafe_: bool,
pub is_unsafe: bool,
/// Is this function async?
#[serde(rename = "async")]
pub async_: bool,
pub is_async: bool,
/// The ABI used by the function.
pub abi: Abi,
}
@ -697,11 +705,11 @@ pub enum Abi {
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Function {
/// Information about the function signature, or declaration.
pub decl: FnDecl,
pub sig: FunctionSignature,
/// Information about the functions type parameters and `where` clauses.
pub generics: Generics,
/// Information about core properties of the function, e.g. whether it's `const`, its ABI, etc.
pub header: Header,
pub header: FunctionHeader,
/// Whether the function has a body, i.e. an implementation.
pub has_body: bool,
}
@ -784,7 +792,7 @@ pub enum GenericParamDefKind {
/// In this example, the generic parameter named `impl Trait` (and which
/// is bound by `Trait`) is synthetic, because it was not originally in
/// the Rust source text.
synthetic: bool,
is_synthetic: bool,
},
/// Denotes a constant parameter.
@ -894,7 +902,7 @@ pub enum TraitBoundModifier {
}
/// Either a type or a constant, usually stored as the right-hand side of an equation in places like
/// [`TypeBinding`]
/// [`AssocItemConstraint`]
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Term {
@ -963,7 +971,7 @@ pub enum Type {
/// A raw pointer type, e.g. `*mut u32`, `*const u8`, etc.
RawPointer {
/// This is `true` for `*mut _` and `false` for `*const _`.
mutable: bool,
is_mutable: bool,
/// The type of the pointee.
#[serde(rename = "type")]
type_: Box<Type>,
@ -973,7 +981,7 @@ pub enum Type {
/// The name of the lifetime of the reference, if provided.
lifetime: Option<String>,
/// This is `true` for `&mut i32` and `false` for `&i32`
mutable: bool,
is_mutable: bool,
/// The type of the pointee, e.g. the `i32` in `&'a mut i32`
#[serde(rename = "type")]
type_: Box<Type>,
@ -1036,7 +1044,7 @@ pub struct Path {
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct FunctionPointer {
/// The signature of the function.
pub decl: FnDecl,
pub sig: FunctionSignature,
/// Used for Higher-Rank Trait Bounds (HRTBs)
///
/// ```ignore (incomplete expression)
@ -1045,12 +1053,12 @@ pub struct FunctionPointer {
/// ```
pub generic_params: Vec<GenericParamDef>,
/// The core properties of the function, such as the ABI it conforms to, whether it's unsafe, etc.
pub header: Header,
pub header: FunctionHeader,
}
/// The signature of a function.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct FnDecl {
pub struct FunctionSignature {
/// List of argument names and their type.
///
/// Note that not all names will be valid identifiers, as some of
@ -1063,7 +1071,7 @@ pub struct FnDecl {
/// ```ignore (incomplete code)
/// fn printf(fmt: &str, ...);
/// ```
pub c_variadic: bool,
pub is_c_variadic: bool,
}
/// A `trait` declaration.
@ -1127,10 +1135,10 @@ pub struct Impl {
/// The list of associated items contained in this impl block.
pub items: Vec<Id>,
/// Whether this is a negative impl (e.g. `!Sized` or `!Send`).
pub negative: bool,
pub is_negative: bool,
/// Whether this is an impl thats implied by the compiler
/// (for autotraits, e.g. `Send` or `Sync`).
pub synthetic: bool,
pub is_synthetic: bool,
// FIXME: document this
pub blanket_impl: Option<Type>,
}
@ -1138,7 +1146,7 @@ pub struct Impl {
/// A `use` statement.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct Import {
pub struct Use {
/// The full path being imported.
pub source: String,
/// May be different from the last segment of `source` when renaming imports:
@ -1150,7 +1158,7 @@ pub struct Import {
/// ```
pub id: Option<Id>,
/// Whether this statement is a wildcard `use`, e.g. `use source::*;`
pub glob: bool,
pub is_glob: bool,
}
/// A procedural macro.
@ -1205,7 +1213,7 @@ pub struct Static {
#[serde(rename = "type")]
pub type_: Type,
/// This is `true` for mutable statics, declared as `static mut X: T = f();`
pub mutable: bool,
pub is_mutable: bool,
/// The stringified expression for the initial value.
///
/// It's not guaranteed that it'll match the actual source code for the initial value.

View File

@ -4,7 +4,7 @@ use super::*;
fn test_struct_info_roundtrip() {
let s = ItemEnum::Struct(Struct {
generics: Generics { params: vec![], where_predicates: vec![] },
kind: StructKind::Plain { fields: vec![], fields_stripped: false },
kind: StructKind::Plain { fields: vec![], has_stripped_fields: false },
impls: vec![],
});
@ -23,7 +23,7 @@ fn test_struct_info_roundtrip() {
fn test_union_info_roundtrip() {
let u = ItemEnum::Union(Union {
generics: Generics { params: vec![], where_predicates: vec![] },
fields_stripped: false,
has_stripped_fields: false,
fields: vec![],
impls: vec![],
});

View File

@ -5,7 +5,7 @@ use rustdoc_json_types::{Item, ItemEnum, ItemKind, ItemSummary};
pub(crate) enum Kind {
Module,
ExternCrate,
Import,
Use,
Struct,
StructField,
Union,
@ -18,7 +18,7 @@ pub(crate) enum Kind {
TraitAlias,
Impl,
Static,
ForeignType,
ExternType,
Macro,
ProcAttribute,
ProcDerive,
@ -36,7 +36,7 @@ impl Kind {
match self {
Module => true,
ExternCrate => true,
Import => true,
Use => true,
Union => true,
Struct => true,
Enum => true,
@ -50,7 +50,7 @@ impl Kind {
Macro => true,
ProcMacro => true,
Primitive => true,
ForeignType => true,
ExternType => true,
// FIXME(adotinthevoid): I'm not sure if these are correct
Keyword => false,
@ -69,7 +69,7 @@ impl Kind {
pub fn can_appear_in_import(self) -> bool {
match self {
Kind::Variant => true,
Kind::Import => false,
Kind::Use => false,
other => other.can_appear_in_mod(),
}
}
@ -90,7 +90,7 @@ impl Kind {
Kind::Module => false,
Kind::ExternCrate => false,
Kind::Import => false,
Kind::Use => false,
Kind::Struct => false,
Kind::StructField => false,
Kind::Union => false,
@ -102,7 +102,7 @@ impl Kind {
Kind::TraitAlias => false,
Kind::Impl => false,
Kind::Static => false,
Kind::ForeignType => false,
Kind::ExternType => false,
Kind::Macro => false,
Kind::ProcAttribute => false,
Kind::ProcDerive => false,
@ -135,7 +135,7 @@ impl Kind {
use Kind::*;
match i.inner {
ItemEnum::Module(_) => Module,
ItemEnum::Import(_) => Import,
ItemEnum::Use(_) => Use,
ItemEnum::Union(_) => Union,
ItemEnum::Struct(_) => Struct,
ItemEnum::StructField(_) => StructField,
@ -151,7 +151,7 @@ impl Kind {
ItemEnum::Macro(_) => Macro,
ItemEnum::ProcMacro(_) => ProcMacro,
ItemEnum::Primitive(_) => Primitive,
ItemEnum::ForeignType => ForeignType,
ItemEnum::ExternType => ExternType,
ItemEnum::ExternCrate { .. } => ExternCrate,
ItemEnum::AssocConst { .. } => AssocConst,
ItemEnum::AssocType { .. } => AssocType,
@ -166,10 +166,10 @@ impl Kind {
ItemKind::Constant => Constant,
ItemKind::Enum => Enum,
ItemKind::ExternCrate => ExternCrate,
ItemKind::ForeignType => ForeignType,
ItemKind::ExternType => ExternType,
ItemKind::Function => Function,
ItemKind::Impl => Impl,
ItemKind::Import => Import,
ItemKind::Use => Use,
ItemKind::Keyword => Keyword,
ItemKind::Macro => Macro,
ItemKind::Module => Module,

View File

@ -2,10 +2,11 @@ use std::collections::HashSet;
use std::hash::Hash;
use rustdoc_json_types::{
Constant, Crate, DynTrait, Enum, FnDecl, Function, FunctionPointer, GenericArg, GenericArgs,
GenericBound, GenericParamDef, Generics, Id, Impl, Import, ItemEnum, ItemSummary, Module, Path,
Primitive, ProcMacro, Static, Struct, StructKind, Term, Trait, TraitAlias, Type, TypeAlias,
TypeBinding, TypeBindingKind, Union, Variant, VariantKind, WherePredicate,
AssocItemConstraint, AssocItemConstraintKind, Constant, Crate, DynTrait, Enum, Function,
FunctionPointer, FunctionSignature, GenericArg, GenericArgs, GenericBound, GenericParamDef,
Generics, Id, Impl, ItemEnum, ItemSummary, Module, Path, Primitive, ProcMacro, Static, Struct,
StructKind, Term, Trait, TraitAlias, Type, TypeAlias, Union, Use, Variant, VariantKind,
WherePredicate,
};
use serde_json::Value;
@ -90,7 +91,7 @@ impl<'a> Validator<'a> {
item.links.values().for_each(|id| self.add_any_id(id));
match &item.inner {
ItemEnum::Import(x) => self.check_import(x),
ItemEnum::Use(x) => self.check_use(x),
ItemEnum::Union(x) => self.check_union(x),
ItemEnum::Struct(x) => self.check_struct(x),
ItemEnum::StructField(x) => self.check_struct_field(x),
@ -106,18 +107,18 @@ impl<'a> Validator<'a> {
self.check_constant(const_);
}
ItemEnum::Static(x) => self.check_static(x),
ItemEnum::ForeignType => {} // nop
ItemEnum::ExternType => {} // nop
ItemEnum::Macro(x) => self.check_macro(x),
ItemEnum::ProcMacro(x) => self.check_proc_macro(x),
ItemEnum::Primitive(x) => self.check_primitive_type(x),
ItemEnum::Module(x) => self.check_module(x, id),
// FIXME: Why don't these have their own structs?
ItemEnum::ExternCrate { .. } => {}
ItemEnum::AssocConst { type_, default: _ } => self.check_type(type_),
ItemEnum::AssocType { generics, bounds, default } => {
ItemEnum::AssocConst { type_, value: _ } => self.check_type(type_),
ItemEnum::AssocType { generics, bounds, type_ } => {
self.check_generics(generics);
bounds.iter().for_each(|b| self.check_generic_bound(b));
if let Some(ty) = default {
if let Some(ty) = type_ {
self.check_type(ty);
}
}
@ -133,8 +134,8 @@ impl<'a> Validator<'a> {
module.items.iter().for_each(|i| self.add_mod_item_id(i));
}
fn check_import(&mut self, x: &'a Import) {
if x.glob {
fn check_use(&mut self, x: &'a Use) {
if x.is_glob {
self.add_glob_import_item_id(x.id.as_ref().unwrap());
} else if let Some(id) = &x.id {
self.add_import_item_id(id);
@ -152,7 +153,7 @@ impl<'a> Validator<'a> {
match &x.kind {
StructKind::Unit => {}
StructKind::Tuple(fields) => fields.iter().flatten().for_each(|f| self.add_field_id(f)),
StructKind::Plain { fields, fields_stripped: _ } => {
StructKind::Plain { fields, has_stripped_fields: _ } => {
fields.iter().for_each(|f| self.add_field_id(f))
}
}
@ -187,7 +188,7 @@ impl<'a> Validator<'a> {
match kind {
VariantKind::Plain => {}
VariantKind::Tuple(tys) => tys.iter().flatten().for_each(|t| self.add_field_id(t)),
VariantKind::Struct { fields, fields_stripped: _ } => {
VariantKind::Struct { fields, has_stripped_fields: _ } => {
fields.iter().for_each(|f| self.add_field_id(f))
}
}
@ -195,7 +196,7 @@ impl<'a> Validator<'a> {
fn check_function(&mut self, x: &'a Function) {
self.check_generics(&x.generics);
self.check_fn_decl(&x.decl);
self.check_function_signature(&x.sig);
}
fn check_trait(&mut self, x: &'a Trait, id: &Id) {
@ -267,8 +268,8 @@ impl<'a> Validator<'a> {
Type::Array { type_, len: _ } => self.check_type(&**type_),
Type::ImplTrait(bounds) => bounds.iter().for_each(|b| self.check_generic_bound(b)),
Type::Infer => {}
Type::RawPointer { mutable: _, type_ } => self.check_type(&**type_),
Type::BorrowedRef { lifetime: _, mutable: _, type_ } => self.check_type(&**type_),
Type::RawPointer { is_mutable: _, type_ } => self.check_type(&**type_),
Type::BorrowedRef { lifetime: _, is_mutable: _, type_ } => self.check_type(&**type_),
Type::QualifiedPath { name: _, args, self_type, trait_ } => {
self.check_generic_args(&**args);
self.check_type(&**self_type);
@ -279,7 +280,7 @@ impl<'a> Validator<'a> {
}
}
fn check_fn_decl(&mut self, x: &'a FnDecl) {
fn check_function_signature(&mut self, x: &'a FunctionSignature) {
x.inputs.iter().for_each(|(_name, ty)| self.check_type(ty));
if let Some(output) = &x.output {
self.check_type(output);
@ -309,9 +310,9 @@ impl<'a> Validator<'a> {
fn check_generic_args(&mut self, x: &'a GenericArgs) {
match x {
GenericArgs::AngleBracketed { args, bindings } => {
GenericArgs::AngleBracketed { args, constraints } => {
args.iter().for_each(|arg| self.check_generic_arg(arg));
bindings.iter().for_each(|bind| self.check_type_binding(bind));
constraints.iter().for_each(|bind| self.check_assoc_item_constraint(bind));
}
GenericArgs::Parenthesized { inputs, output } => {
inputs.iter().for_each(|ty| self.check_type(ty));
@ -325,7 +326,7 @@ impl<'a> Validator<'a> {
fn check_generic_param_def(&mut self, gpd: &'a GenericParamDef) {
match &gpd.kind {
rustdoc_json_types::GenericParamDefKind::Lifetime { outlives: _ } => {}
rustdoc_json_types::GenericParamDefKind::Type { bounds, default, synthetic: _ } => {
rustdoc_json_types::GenericParamDefKind::Type { bounds, default, is_synthetic: _ } => {
bounds.iter().for_each(|b| self.check_generic_bound(b));
if let Some(ty) = default {
self.check_type(ty);
@ -346,11 +347,11 @@ impl<'a> Validator<'a> {
}
}
fn check_type_binding(&mut self, bind: &'a TypeBinding) {
fn check_assoc_item_constraint(&mut self, bind: &'a AssocItemConstraint) {
self.check_generic_args(&bind.args);
match &bind.binding {
TypeBindingKind::Equality(term) => self.check_term(term),
TypeBindingKind::Constraint(bounds) => {
AssocItemConstraintKind::Equality(term) => self.check_term(term),
AssocItemConstraintKind::Constraint(bounds) => {
bounds.iter().for_each(|b| self.check_generic_bound(b))
}
}
@ -388,7 +389,7 @@ impl<'a> Validator<'a> {
}
fn check_function_pointer(&mut self, fp: &'a FunctionPointer) {
self.check_fn_decl(&fp.decl);
self.check_function_signature(&fp.sig);
fp.generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd));
}

View File

@ -37,8 +37,6 @@ features = ['unprefixed_malloc_on_supported_platforms']
[target.'cfg(unix)'.dependencies]
libc = "0.2"
[target.'cfg(target_os = "linux")'.dependencies]
libffi = "3.2.0"
libloading = "0.8"

View File

@ -383,7 +383,7 @@ to Miri failing to detect cases of undefined behavior in a program.
file descriptors will be mixed up.
This is **work in progress**; currently, only integer arguments and return values are
supported (and no, pointer/integer casts to work around this limitation will not work;
they will fail horribly). It also only works on Linux hosts for now.
they will fail horribly). It also only works on Unix hosts for now.
* `-Zmiri-measureme=<name>` enables `measureme` profiling for the interpreted program.
This can be used to find which parts of your program are executing slowly under Miri.
The profile is written out to a file inside a directory called `<name>`, and can be processed

View File

@ -5,8 +5,8 @@ set MIRI_SCRIPT_TARGET_DIR=%0\..\miri-script\target
:: If any other steps are added, the "|| exit /b" must be appended to early
:: return from the script. If not, it will continue execution.
cargo +stable build %CARGO_EXTRA_FLAGS% -q --target-dir %MIRI_SCRIPT_TARGET_DIR% --manifest-path %0\..\miri-script\Cargo.toml ^
|| (echo Failed to build miri-script. Is the 'stable' toolchain installed? & exit /b)
cargo +nightly build %CARGO_EXTRA_FLAGS% -q --target-dir %MIRI_SCRIPT_TARGET_DIR% --manifest-path %0\..\miri-script\Cargo.toml ^
|| (echo Failed to build miri-script. Is the 'nightly' toolchain installed? & exit /b)
:: Forwards all arguments to this file to the executable.
:: We invoke the binary directly to avoid going through rustup, which would set some extra

View File

@ -1 +1 @@
0d634185dfddefe09047881175f35c65d68dcff1
54fdef7799d9ff9470bb5cabd29fde9471a99eaa

View File

@ -5,7 +5,6 @@ mod reuse_pool;
use std::cell::RefCell;
use std::cmp::max;
use std::collections::hash_map::Entry;
use rand::Rng;
@ -151,18 +150,13 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
fn addr_from_alloc_id(
fn addr_from_alloc_id_uncached(
&self,
global_state: &mut GlobalStateInner,
alloc_id: AllocId,
memory_kind: MemoryKind,
) -> InterpResult<'tcx, u64> {
let ecx = self.eval_context_ref();
let mut global_state = ecx.machine.alloc_addresses.borrow_mut();
let global_state = &mut *global_state;
Ok(match global_state.base_addr.entry(alloc_id) {
Entry::Occupied(entry) => *entry.get(),
Entry::Vacant(entry) => {
let mut rng = ecx.machine.rng.borrow_mut();
let (size, align, kind) = ecx.get_alloc_info(alloc_id);
// This is either called immediately after allocation (and then cached), or when
@ -173,12 +167,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
assert!(!matches!(kind, AllocKind::Dead));
// This allocation does not have a base address yet, pick or reuse one.
let base_addr = if ecx.machine.native_lib.is_some() {
if ecx.machine.native_lib.is_some() {
// In native lib mode, we use the "real" address of the bytes for this allocation.
// This ensures the interpreted program and native code have the same view of memory.
match kind {
let base_ptr = match kind {
AllocKind::LiveData => {
let ptr = if ecx.tcx.try_get_global_alloc(alloc_id).is_some() {
if ecx.tcx.try_get_global_alloc(alloc_id).is_some() {
// For new global allocations, we always pre-allocate the memory to be able use the machine address directly.
let prepared_bytes = MiriAllocBytes::zeroed(size, align)
.unwrap_or_else(|| {
@ -186,36 +180,37 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
});
let ptr = prepared_bytes.as_ptr();
// Store prepared allocation space to be picked up for use later.
global_state.prepared_alloc_bytes.try_insert(alloc_id, prepared_bytes).unwrap();
global_state
.prepared_alloc_bytes
.try_insert(alloc_id, prepared_bytes)
.unwrap();
ptr
} else {
ecx.get_alloc_bytes_unchecked_raw(alloc_id)?
};
// Ensure this pointer's provenance is exposed, so that it can be used by FFI code.
ptr.expose_provenance().try_into().unwrap()
}
}
AllocKind::Function | AllocKind::VTable => {
// Allocate some dummy memory to get a unique address for this function/vtable.
let alloc_bytes = MiriAllocBytes::from_bytes(&[0u8; 1], Align::from_bytes(1).unwrap());
// We don't need to expose these bytes as nobody is allowed to access them.
let addr = alloc_bytes.as_ptr().addr().try_into().unwrap();
let alloc_bytes =
MiriAllocBytes::from_bytes(&[0u8; 1], Align::from_bytes(1).unwrap());
let ptr = alloc_bytes.as_ptr();
// Leak the underlying memory to ensure it remains unique.
std::mem::forget(alloc_bytes);
addr
ptr
}
AllocKind::Dead => unreachable!()
AllocKind::Dead => unreachable!(),
};
// Ensure this pointer's provenance is exposed, so that it can be used by FFI code.
return Ok(base_ptr.expose_provenance().try_into().unwrap());
}
} else if let Some((reuse_addr, clock)) = global_state.reuse.take_addr(
&mut *rng,
size,
align,
memory_kind,
ecx.active_thread(),
) {
// We are not in native lib mode, so we control the addresses ourselves.
if let Some((reuse_addr, clock)) =
global_state.reuse.take_addr(&mut *rng, size, align, memory_kind, ecx.active_thread())
{
if let Some(clock) = clock {
ecx.acquire_clock(&clock);
}
reuse_addr
Ok(reuse_addr)
} else {
// We have to pick a fresh address.
// Leave some space to the previous allocation, to give it some chance to be less aligned.
@ -228,10 +223,10 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
.ok_or_else(|| err_exhaust!(AddressSpaceFull))?;
let base_addr = align_addr(base_addr, align.bytes());
// Remember next base address. If this allocation is zero-sized, leave a gap
// of at least 1 to avoid two allocations having the same base address.
// (The logic in `alloc_id_from_addr` assumes unique addresses, and different
// function/vtable pointers need to be distinguishable!)
// Remember next base address. If this allocation is zero-sized, leave a gap of at
// least 1 to avoid two allocations having the same base address. (The logic in
// `alloc_id_from_addr` assumes unique addresses, and different function/vtable pointers
// need to be distinguishable!)
global_state.next_base_addr = base_addr
.checked_add(max(size.bytes(), 1))
.ok_or_else(|| err_exhaust!(AddressSpaceFull))?;
@ -240,18 +235,29 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
throw_exhaust!(AddressSpaceFull);
}
base_addr
};
trace!(
"Assigning base address {:#x} to allocation {:?} (size: {}, align: {})",
base_addr,
alloc_id,
size.bytes(),
align.bytes(),
);
Ok(base_addr)
}
}
fn addr_from_alloc_id(
&self,
alloc_id: AllocId,
memory_kind: MemoryKind,
) -> InterpResult<'tcx, u64> {
let ecx = self.eval_context_ref();
let mut global_state = ecx.machine.alloc_addresses.borrow_mut();
let global_state = &mut *global_state;
match global_state.base_addr.get(&alloc_id) {
Some(&addr) => Ok(addr),
None => {
// First time we're looking for the absolute address of this allocation.
let base_addr =
self.addr_from_alloc_id_uncached(global_state, alloc_id, memory_kind)?;
trace!("Assigning base address {:#x} to allocation {:?}", base_addr, alloc_id);
// Store address in cache.
entry.insert(base_addr);
global_state.base_addr.try_insert(alloc_id, base_addr).unwrap();
// Also maintain the opposite mapping in `int_to_ptr_map`, ensuring we keep it sorted.
// We have a fast-path for the common case that this address is bigger than all previous ones.
@ -269,9 +275,9 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
};
global_state.int_to_ptr_map.insert(pos, (base_addr, alloc_id));
base_addr
Ok(base_addr)
}
}
})
}
}
@ -359,16 +365,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// This returns some prepared `MiriAllocBytes`, either because `addr_from_alloc_id` reserved
// memory space in the past, or by doing the pre-allocation right upon being called.
fn get_global_alloc_bytes(&self, id: AllocId, kind: MemoryKind, bytes: &[u8], align: Align) -> InterpResult<'tcx, MiriAllocBytes> {
fn get_global_alloc_bytes(
&self,
id: AllocId,
kind: MemoryKind,
bytes: &[u8],
align: Align,
) -> InterpResult<'tcx, MiriAllocBytes> {
let ecx = self.eval_context_ref();
Ok(if ecx.machine.native_lib.is_some() {
if ecx.machine.native_lib.is_some() {
// In native lib mode, MiriAllocBytes for global allocations are handled via `prepared_alloc_bytes`.
// This additional call ensures that some `MiriAllocBytes` are always prepared.
// This additional call ensures that some `MiriAllocBytes` are always prepared, just in case
// this function gets called before the first time `addr_from_alloc_id` gets called.
ecx.addr_from_alloc_id(id, kind)?;
let mut global_state = ecx.machine.alloc_addresses.borrow_mut();
// The memory we need here will have already been allocated during an earlier call to
// `addr_from_alloc_id` for this allocation. So don't create a new `MiriAllocBytes` here, instead
// fetch the previously prepared bytes from `prepared_alloc_bytes`.
let mut global_state = ecx.machine.alloc_addresses.borrow_mut();
let mut prepared_alloc_bytes = global_state
.prepared_alloc_bytes
.remove(&id)
@ -378,10 +391,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
assert_eq!(prepared_alloc_bytes.len(), bytes.len());
// Copy allocation contents into prepared memory.
prepared_alloc_bytes.copy_from_slice(bytes);
prepared_alloc_bytes
Ok(prepared_alloc_bytes)
} else {
MiriAllocBytes::from_bytes(std::borrow::Cow::Borrowed(&*bytes), align)
})
Ok(MiriAllocBytes::from_bytes(std::borrow::Cow::Borrowed(bytes), align))
}
}
/// When a pointer is used for a memory access, this computes where in which allocation the

View File

@ -35,7 +35,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
offset: u64,
) -> InterpResult<'tcx, InitOnceId> {
let this = self.eval_context_mut();
this.get_or_create_id(lock_op, lock_layout, offset, |ecx| &mut ecx.machine.sync.init_onces)?
this.get_or_create_id(
lock_op,
lock_layout,
offset,
|ecx| &mut ecx.machine.sync.init_onces,
|_| Ok(Default::default()),
)?
.ok_or_else(|| err_ub_format!("init_once has invalid ID").into())
}

View File

@ -66,6 +66,27 @@ pub(super) use declare_id;
declare_id!(MutexId);
/// The mutex kind.
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub enum MutexKind {
Invalid,
Normal,
Default,
Recursive,
ErrorCheck,
}
#[derive(Debug)]
/// Additional data that may be used by shim implementations.
pub struct AdditionalMutexData {
/// The mutex kind, used by some mutex implementations like pthreads mutexes.
pub kind: MutexKind,
/// The address of the mutex.
pub address: u64,
}
/// The mutex state.
#[derive(Default, Debug)]
struct Mutex {
@ -77,6 +98,9 @@ struct Mutex {
queue: VecDeque<ThreadId>,
/// Mutex clock. This tracks the moment of the last unlock.
clock: VClock,
/// Additional data that can be set by shim implementations.
data: Option<AdditionalMutexData>,
}
declare_id!(RwLockId);
@ -168,13 +192,15 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// Returns `None` if memory stores a non-zero invalid ID.
///
/// `get_objs` must return the `IndexVec` that stores all the objects of this type.
/// `create_obj` must create the new object if initialization is needed.
#[inline]
fn get_or_create_id<Id: SyncId + Idx, T: Default>(
fn get_or_create_id<Id: SyncId + Idx, T>(
&mut self,
lock_op: &OpTy<'tcx>,
lock_layout: TyAndLayout<'tcx>,
offset: u64,
get_objs: impl for<'a> Fn(&'a mut MiriInterpCx<'tcx>) -> &'a mut IndexVec<Id, T>,
create_obj: impl for<'a> FnOnce(&'a mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, T>,
) -> InterpResult<'tcx, Option<Id>> {
let this = self.eval_context_mut();
let value_place =
@ -196,7 +222,8 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
Ok(if success.to_bool().expect("compare_exchange's second return value is a bool") {
// We set the in-memory ID to `next_index`, now also create this object in the machine
// state.
let new_index = get_objs(this).push(T::default());
let obj = create_obj(this)?;
let new_index = get_objs(this).push(obj);
assert_eq!(next_index, new_index);
Some(new_index)
} else {
@ -210,6 +237,32 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
})
}
/// Eagerly creates a Miri sync structure.
///
/// `create_id` will store the index of the sync_structure in the memory pointed to by
/// `lock_op`, so that future calls to `get_or_create_id` will see it as initialized.
/// - `lock_op` must hold a pointer to the sync structure.
/// - `lock_layout` must be the memory layout of the sync structure.
/// - `offset` must be the offset inside the sync structure where its miri id will be stored.
/// - `get_objs` is described in `get_or_create_id`.
/// - `obj` must be the new sync object.
fn create_id<Id: SyncId + Idx, T>(
&mut self,
lock_op: &OpTy<'tcx>,
lock_layout: TyAndLayout<'tcx>,
offset: u64,
get_objs: impl for<'a> Fn(&'a mut MiriInterpCx<'tcx>) -> &'a mut IndexVec<Id, T>,
obj: T,
) -> InterpResult<'tcx, Id> {
let this = self.eval_context_mut();
let value_place =
this.deref_pointer_and_offset(lock_op, offset, lock_layout, this.machine.layouts.u32)?;
let new_index = get_objs(this).push(obj);
this.write_scalar(Scalar::from_u32(new_index.to_u32()), &value_place)?;
Ok(new_index)
}
fn condvar_reacquire_mutex(
&mut self,
mutex: MutexId,
@ -236,17 +289,55 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// situations.
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// Eagerly create and initialize a new mutex.
fn mutex_create(
&mut self,
lock_op: &OpTy<'tcx>,
lock_layout: TyAndLayout<'tcx>,
offset: u64,
data: Option<AdditionalMutexData>,
) -> InterpResult<'tcx, MutexId> {
let this = self.eval_context_mut();
this.create_id(
lock_op,
lock_layout,
offset,
|ecx| &mut ecx.machine.sync.mutexes,
Mutex { data, ..Default::default() },
)
}
/// Lazily create a new mutex.
/// `initialize_data` must return any additional data that a user wants to associate with the mutex.
fn mutex_get_or_create_id(
&mut self,
lock_op: &OpTy<'tcx>,
lock_layout: TyAndLayout<'tcx>,
offset: u64,
initialize_data: impl for<'a> FnOnce(
&'a mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, Option<AdditionalMutexData>>,
) -> InterpResult<'tcx, MutexId> {
let this = self.eval_context_mut();
this.get_or_create_id(lock_op, lock_layout, offset, |ecx| &mut ecx.machine.sync.mutexes)?
this.get_or_create_id(
lock_op,
lock_layout,
offset,
|ecx| &mut ecx.machine.sync.mutexes,
|ecx| initialize_data(ecx).map(|data| Mutex { data, ..Default::default() }),
)?
.ok_or_else(|| err_ub_format!("mutex has invalid ID").into())
}
/// Retrieve the additional data stored for a mutex.
fn mutex_get_data<'a>(&'a mut self, id: MutexId) -> Option<&'a AdditionalMutexData>
where
'tcx: 'a,
{
let this = self.eval_context_ref();
this.machine.sync.mutexes[id].data.as_ref()
}
fn rwlock_get_or_create_id(
&mut self,
lock_op: &OpTy<'tcx>,
@ -254,7 +345,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
offset: u64,
) -> InterpResult<'tcx, RwLockId> {
let this = self.eval_context_mut();
this.get_or_create_id(lock_op, lock_layout, offset, |ecx| &mut ecx.machine.sync.rwlocks)?
this.get_or_create_id(
lock_op,
lock_layout,
offset,
|ecx| &mut ecx.machine.sync.rwlocks,
|_| Ok(Default::default()),
)?
.ok_or_else(|| err_ub_format!("rwlock has invalid ID").into())
}
@ -265,7 +362,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
offset: u64,
) -> InterpResult<'tcx, CondvarId> {
let this = self.eval_context_mut();
this.get_or_create_id(lock_op, lock_layout, offset, |ecx| &mut ecx.machine.sync.condvars)?
this.get_or_create_id(
lock_op,
lock_layout,
offset,
|ecx| &mut ecx.machine.sync.condvars,
|_| Ok(Default::default()),
)?
.ok_or_else(|| err_ub_format!("condvar has invalid ID").into())
}

View File

@ -888,8 +888,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
let alloc = this.ctfe_query(|tcx| tcx.eval_static_initializer(def_id))?;
// We make a full copy of this allocation.
let mut alloc =
alloc.inner().adjust_from_tcx(&this.tcx, |bytes, align| Ok(MiriAllocBytes::from_bytes(std::borrow::Cow::Borrowed(bytes), align)), |ptr| this.global_root_pointer(ptr))?;
let mut alloc = alloc.inner().adjust_from_tcx(
&this.tcx,
|bytes, align| {
Ok(MiriAllocBytes::from_bytes(std::borrow::Cow::Borrowed(bytes), align))
},
|ptr| this.global_root_pointer(ptr),
)?;
// This allocation will be deallocated when the thread dies, so it is not in read-only memory.
alloc.mutability = Mutability::Mut;
// Create a fresh allocation with this content.

View File

@ -133,7 +133,10 @@ pub use crate::concurrency::{
cpu_affinity::MAX_CPUS,
data_race::{AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, EvalContextExt as _},
init_once::{EvalContextExt as _, InitOnceId},
sync::{CondvarId, EvalContextExt as _, MutexId, RwLockId, SynchronizationObjects},
sync::{
AdditionalMutexData, CondvarId, EvalContextExt as _, MutexId, MutexKind, RwLockId,
SynchronizationObjects,
},
thread::{
BlockReason, EvalContextExt as _, StackEmptyCallback, ThreadId, ThreadManager,
TimeoutAnchor, TimeoutClock, UnblockCallback,

View File

@ -535,9 +535,9 @@ pub struct MiriMachine<'tcx> {
pub(crate) basic_block_count: u64,
/// Handle of the optional shared object file for native functions.
#[cfg(target_os = "linux")]
#[cfg(unix)]
pub native_lib: Option<(libloading::Library, std::path::PathBuf)>,
#[cfg(not(target_os = "linux"))]
#[cfg(not(unix))]
pub native_lib: Option<!>,
/// Run a garbage collector for BorTags every N basic blocks.
@ -678,7 +678,7 @@ impl<'tcx> MiriMachine<'tcx> {
report_progress: config.report_progress,
basic_block_count: 0,
clock: Clock::new(config.isolated_op == IsolatedOp::Allow),
#[cfg(target_os = "linux")]
#[cfg(unix)]
native_lib: config.native_lib.as_ref().map(|lib_file_path| {
let target_triple = layout_cx.tcx.sess.opts.target_triple.triple();
// Check if host target == the session target.
@ -700,9 +700,9 @@ impl<'tcx> MiriMachine<'tcx> {
lib_file_path.clone(),
)
}),
#[cfg(not(target_os = "linux"))]
#[cfg(not(unix))]
native_lib: config.native_lib.as_ref().map(|_| {
panic!("loading external .so files is only supported on Linux")
panic!("calling functions from native libraries via FFI is only supported on Unix")
}),
gc_interval: config.gc_interval,
since_gc: 0,
@ -1277,12 +1277,12 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra, Self::Bytes>>>
{
let kind = Self::GLOBAL_KIND.unwrap().into();
let alloc = alloc.adjust_from_tcx(&ecx.tcx,
let alloc = alloc.adjust_from_tcx(
&ecx.tcx,
|bytes, align| ecx.get_global_alloc_bytes(id, kind, bytes, align),
|ptr| ecx.global_root_pointer(ptr),
)?;
let extra =
Self::init_alloc_extra(ecx, id, kind, alloc.size(), alloc.align)?;
let extra = Self::init_alloc_extra(ecx, id, kind, alloc.size(), alloc.align)?;
Ok(Cow::Owned(alloc.with_extra(extra)))
}

View File

@ -226,7 +226,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
let this = self.eval_context_mut();
// First deal with any external C functions in linked .so file.
#[cfg(target_os = "linux")]
#[cfg(unix)]
if this.machine.native_lib.as_ref().is_some() {
use crate::shims::native_lib::EvalContextExt as _;
// An Ok(false) here means that the function being called was not exported

View File

@ -2,7 +2,7 @@
mod alloc;
mod backtrace;
#[cfg(target_os = "linux")]
#[cfg(unix)]
mod native_lib;
mod unix;
mod wasi;

View File

@ -240,13 +240,16 @@ fn imm_to_carg<'tcx>(v: ImmTy<'tcx>, cx: &impl HasDataLayout) -> InterpResult<'t
ty::RawPtr(_, mutability) => {
// Arbitrary mutable pointer accesses are not currently supported in Miri.
if mutability.is_mut() {
throw_unsup_format!("unsupported mutable pointer type for native call: {}", v.layout.ty);
throw_unsup_format!(
"unsupported mutable pointer type for native call: {}",
v.layout.ty
);
} else {
let s = v.to_scalar().to_pointer(cx)?.addr();
// This relies on the `expose_provenance` in `addr_from_alloc_id`.
CArg::RawPtr(std::ptr::with_exposed_provenance_mut(s.bytes_usize()))
}
},
}
_ => throw_unsup_format!("unsupported argument type for native call: {}", v.layout.ty),
})
}

View File

@ -19,7 +19,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// os_unfair_lock holds a 32-bit value, is initialized with zero and
// must be assumed to be opaque. Therefore, we can just store our
// internal mutex ID in the structure without anyone noticing.
this.mutex_get_or_create_id(lock_op, this.libc_ty_layout("os_unfair_lock"), 0)
this.mutex_get_or_create_id(lock_op, this.libc_ty_layout("os_unfair_lock"), 0, |_| Ok(None))
}
}

View File

@ -62,7 +62,6 @@ fn is_mutex_kind_normal<'tcx>(ecx: &MiriInterpCx<'tcx>, kind: i32) -> InterpResu
// pthread_mutex_t is between 24 and 48 bytes, depending on the platform.
// We ignore the platform layout and store our own fields:
// - id: u32
// - kind: i32
fn mutex_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
let offset = match &*ecx.tcx.sess.target.os {
@ -74,6 +73,8 @@ fn mutex_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
// Sanity-check this against PTHREAD_MUTEX_INITIALIZER (but only once):
// the id must start out as 0.
// FIXME on some platforms (e.g linux) there are more static initializers for
// recursive or error checking mutexes. We should also add thme in this sanity check.
static SANITY: AtomicBool = AtomicBool::new(false);
if !SANITY.swap(true, Ordering::Relaxed) {
let static_initializer = ecx.eval_path(&["libc", "PTHREAD_MUTEX_INITIALIZER"]);
@ -90,79 +91,92 @@ fn mutex_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
Ok(offset)
}
fn mutex_kind_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> u64 {
// These offsets are picked for compatibility with Linux's static initializer
// macros, e.g. PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP.)
let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 };
// Sanity-check this against PTHREAD_MUTEX_INITIALIZER (but only once):
// the kind must start out as PTHREAD_MUTEX_DEFAULT.
static SANITY: AtomicBool = AtomicBool::new(false);
if !SANITY.swap(true, Ordering::Relaxed) {
let static_initializer = ecx.eval_path(&["libc", "PTHREAD_MUTEX_INITIALIZER"]);
let kind_field = static_initializer
.offset(Size::from_bytes(mutex_kind_offset(ecx)), ecx.machine.layouts.i32, ecx)
.unwrap();
let kind = ecx.read_scalar(&kind_field).unwrap().to_i32().unwrap();
assert_eq!(
kind,
ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT"),
"PTHREAD_MUTEX_INITIALIZER is incompatible with our pthread_mutex layout: kind is not PTHREAD_MUTEX_DEFAULT"
);
}
offset
/// Eagerly create and initialize a new mutex.
fn mutex_create<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
mutex_op: &OpTy<'tcx>,
kind: i32,
) -> InterpResult<'tcx> {
// FIXME: might be worth changing mutex_create to take the mplace
// rather than the `OpTy`.
let address = ecx.read_pointer(mutex_op)?.addr().bytes();
let kind = translate_kind(ecx, kind)?;
let data = Some(AdditionalMutexData { address, kind });
ecx.mutex_create(mutex_op, ecx.libc_ty_layout("pthread_mutex_t"), mutex_id_offset(ecx)?, data)?;
Ok(())
}
/// Returns the `MutexId` of the mutex stored at `mutex_op`.
///
/// `mutex_get_id` will also check if the mutex has been moved since its first use and
/// return an error if it has.
fn mutex_get_id<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
mutex_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, MutexId> {
ecx.mutex_get_or_create_id(
let address = ecx.read_pointer(mutex_op)?.addr().bytes();
// FIXME: might be worth changing mutex_get_or_create_id to take the mplace
// rather than the `OpTy`.
let id = ecx.mutex_get_or_create_id(
mutex_op,
ecx.libc_ty_layout("pthread_mutex_t"),
mutex_id_offset(ecx)?,
)
|ecx| {
// This is called if a static initializer was used and the lock has not been assigned
// an ID yet. We have to determine the mutex kind from the static initializer.
let kind = kind_from_static_initializer(ecx, mutex_op)?;
Ok(Some(AdditionalMutexData { kind, address }))
},
)?;
// Check that the mutex has not been moved since last use.
let data = ecx.mutex_get_data(id).expect("data should be always exist for pthreads");
if data.address != address {
throw_ub_format!("pthread_mutex_t can't be moved after first use")
}
Ok(id)
}
fn mutex_reset_id<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
mutex_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, ()> {
ecx.deref_pointer_and_write(
mutex_op,
mutex_id_offset(ecx)?,
Scalar::from_u32(0),
ecx.libc_ty_layout("pthread_mutex_t"),
ecx.machine.layouts.u32,
)
}
fn mutex_get_kind<'tcx>(
/// Returns the kind of a static initializer.
fn kind_from_static_initializer<'tcx>(
ecx: &MiriInterpCx<'tcx>,
mutex_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, i32> {
) -> InterpResult<'tcx, MutexKind> {
// Only linux has static initializers other than PTHREAD_MUTEX_DEFAULT.
let kind = match &*ecx.tcx.sess.target.os {
"linux" => {
let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 };
ecx.deref_pointer_and_read(
mutex_op,
mutex_kind_offset(ecx),
offset,
ecx.libc_ty_layout("pthread_mutex_t"),
ecx.machine.layouts.i32,
)?
.to_i32()
.to_i32()?
}
| "illumos" | "solaris" | "macos" => ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT"),
os => throw_unsup_format!("`pthread_mutex` is not supported on {os}"),
};
translate_kind(ecx, kind)
}
fn mutex_set_kind<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
mutex_op: &OpTy<'tcx>,
kind: i32,
) -> InterpResult<'tcx, ()> {
ecx.deref_pointer_and_write(
mutex_op,
mutex_kind_offset(ecx),
Scalar::from_i32(kind),
ecx.libc_ty_layout("pthread_mutex_t"),
ecx.machine.layouts.i32,
)
fn translate_kind<'tcx>(ecx: &MiriInterpCx<'tcx>, kind: i32) -> InterpResult<'tcx, MutexKind> {
Ok(if is_mutex_kind_default(ecx, kind)? {
MutexKind::Default
} else if is_mutex_kind_normal(ecx, kind)? {
MutexKind::Normal
} else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK") {
MutexKind::ErrorCheck
} else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE") {
MutexKind::Recursive
} else {
throw_unsup_format!("unsupported type of mutex: {kind}");
})
}
// pthread_rwlock_t is between 32 and 56 bytes, depending on the platform.
@ -452,10 +466,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
mutexattr_get_kind(this, attr_op)?
};
// Write 0 to use the same code path as the static initializers.
mutex_reset_id(this, mutex_op)?;
mutex_set_kind(this, mutex_op, kind)?;
mutex_create(this, mutex_op, kind)?;
Ok(())
}
@ -467,8 +478,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let kind = mutex_get_kind(this, mutex_op)?;
let id = mutex_get_id(this, mutex_op)?;
let kind =
this.mutex_get_data(id).expect("data should always exist for pthread mutexes").kind;
let ret = if this.mutex_is_locked(id) {
let owner_thread = this.mutex_get_owner(id);
@ -477,19 +489,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
return Ok(());
} else {
// Trying to acquire the same mutex again.
if is_mutex_kind_default(this, kind)? {
throw_ub_format!("trying to acquire already locked default mutex");
} else if is_mutex_kind_normal(this, kind)? {
throw_machine_stop!(TerminationInfo::Deadlock);
} else if kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK") {
this.eval_libc_i32("EDEADLK")
} else if kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE") {
match kind {
MutexKind::Default =>
throw_ub_format!("trying to acquire already locked default mutex"),
MutexKind::Normal => throw_machine_stop!(TerminationInfo::Deadlock),
MutexKind::ErrorCheck => this.eval_libc_i32("EDEADLK"),
MutexKind::Recursive => {
this.mutex_lock(id);
0
} else {
}
_ =>
throw_unsup_format!(
"called pthread_mutex_lock on an unsupported type of mutex"
);
),
}
}
} else {
@ -504,26 +516,26 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn pthread_mutex_trylock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let kind = mutex_get_kind(this, mutex_op)?;
let id = mutex_get_id(this, mutex_op)?;
let kind =
this.mutex_get_data(id).expect("data should always exist for pthread mutexes").kind;
Ok(Scalar::from_i32(if this.mutex_is_locked(id) {
let owner_thread = this.mutex_get_owner(id);
if owner_thread != this.active_thread() {
this.eval_libc_i32("EBUSY")
} else {
if is_mutex_kind_default(this, kind)?
|| is_mutex_kind_normal(this, kind)?
|| kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK")
{
this.eval_libc_i32("EBUSY")
} else if kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE") {
match kind {
MutexKind::Default | MutexKind::Normal | MutexKind::ErrorCheck =>
this.eval_libc_i32("EBUSY"),
MutexKind::Recursive => {
this.mutex_lock(id);
0
} else {
}
_ =>
throw_unsup_format!(
"called pthread_mutex_trylock on an unsupported type of mutex"
);
),
}
}
} else {
@ -536,8 +548,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn pthread_mutex_unlock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let kind = mutex_get_kind(this, mutex_op)?;
let id = mutex_get_id(this, mutex_op)?;
let kind =
this.mutex_get_data(id).expect("data should always exist for pthread mutexes").kind;
if let Some(_old_locked_count) = this.mutex_unlock(id)? {
// The mutex was locked by the current thread.
@ -546,20 +559,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// The mutex was locked by another thread or not locked at all. See
// the “Unlock When Not Owner” column in
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_unlock.html.
if is_mutex_kind_default(this, kind)? {
match kind {
MutexKind::Default =>
throw_ub_format!(
"unlocked a default mutex that was not locked by the current thread"
);
} else if is_mutex_kind_normal(this, kind)? {
),
MutexKind::Normal =>
throw_ub_format!(
"unlocked a PTHREAD_MUTEX_NORMAL mutex that was not locked by the current thread"
);
} else if kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK")
|| kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE")
{
Ok(Scalar::from_i32(this.eval_libc_i32("EPERM")))
} else {
throw_unsup_format!("called pthread_mutex_unlock on an unsupported type of mutex");
),
MutexKind::ErrorCheck | MutexKind::Recursive =>
Ok(Scalar::from_i32(this.eval_libc_i32("EPERM"))),
_ =>
throw_unsup_format!(
"called pthread_mutex_unlock on an unsupported type of mutex"
),
}
}
}
@ -574,7 +588,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
// Destroying an uninit pthread_mutex is UB, so check to make sure it's not uninit.
mutex_get_kind(this, mutex_op)?;
mutex_get_id(this, mutex_op)?;
// This might lead to false positives, see comment in pthread_mutexattr_destroy

View File

@ -0,0 +1,20 @@
error: Undefined Behavior: pthread_mutex_t can't be moved after first use
--> $DIR/libc_pthread_mutex_move.rs:LL:CC
|
LL | libc::pthread_mutex_lock(&mut m2 as *mut _);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pthread_mutex_t can't be moved after first use
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `check` at $DIR/libc_pthread_mutex_move.rs:LL:CC
note: inside `main`
--> $DIR/libc_pthread_mutex_move.rs:LL:CC
|
LL | check();
| ^^^^^^^
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View File

@ -0,0 +1,28 @@
//@ignore-target-windows: No pthreads on Windows
//@revisions: static_initializer init
fn main() {
check();
}
#[cfg(init)]
fn check() {
unsafe {
let mut m: libc::pthread_mutex_t = std::mem::zeroed();
assert_eq!(libc::pthread_mutex_init(&mut m as *mut _, std::ptr::null()), 0);
let mut m2 = m; // move the mutex
libc::pthread_mutex_lock(&mut m2 as *mut _); //~[init] ERROR: pthread_mutex_t can't be moved after first use
}
}
#[cfg(static_initializer)]
fn check() {
unsafe {
let mut m: libc::pthread_mutex_t = libc::PTHREAD_MUTEX_INITIALIZER;
libc::pthread_mutex_lock(&mut m as *mut _);
let mut m2 = m; // move the mutex
libc::pthread_mutex_unlock(&mut m2 as *mut _); //~[static_initializer] ERROR: pthread_mutex_t can't be moved after first use
}
}

View File

@ -0,0 +1,20 @@
error: Undefined Behavior: pthread_mutex_t can't be moved after first use
--> $DIR/libc_pthread_mutex_move.rs:LL:CC
|
LL | libc::pthread_mutex_unlock(&mut m2 as *mut _);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pthread_mutex_t can't be moved after first use
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `check` at $DIR/libc_pthread_mutex_move.rs:LL:CC
note: inside `main`
--> $DIR/libc_pthread_mutex_move.rs:LL:CC
|
LL | check();
| ^^^^^^^
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View File

@ -1,4 +1,6 @@
//@only-target-linux
// Only works on Unix targets
//@ignore-target-windows
//@ignore-target-wasm
//@only-on-host
//@normalize-stderr-test: "OS `.*`" -> "$$OS"

View File

@ -0,0 +1,15 @@
// Only works on Unix targets
//@ignore-target-windows
//@ignore-target-wasm
//@only-on-host
//@normalize-stderr-test: "OS `.*`" -> "$$OS"
extern "C" {
fn not_exported();
}
fn main() {
unsafe {
not_exported(); //~ ERROR: unsupported operation: can't call foreign function `not_exported`
}
}

View File

@ -0,0 +1,15 @@
error: unsupported operation: can't call foreign function `not_exported` on $OS
--> $DIR/private_function.rs:LL:CC
|
LL | not_exported();
| ^^^^^^^^^^^^^^ can't call foreign function `not_exported` on $OS
|
= help: if this is a basic API commonly used on this target, please report an issue with Miri
= help: however, note that Miri does not aim to support every FFI function out there; for instance, we will not support APIs for things such as GUIs, scripting languages, or databases
= note: BACKTRACE:
= note: inside `main` at $DIR/private_function.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View File

@ -1,20 +0,0 @@
CODEABI_1.0 {
# Define which symbols to export.
global:
# scalar_arguments.c
add_one_int;
printer;
test_stack_spill;
get_unsigned_int;
add_int16;
add_short_to_long;
# ptr_read_access.c
print_pointer;
access_simple;
access_nested;
access_static;
# The rest remains private.
local: *;
};

View File

@ -1,4 +1,6 @@
//@only-target-linux
// Only works on Unix targets
//@ignore-target-windows
//@ignore-target-wasm
//@only-on-host
fn main() {
@ -26,7 +28,7 @@ fn test_pointer() {
fn test_simple() {
#[repr(C)]
struct Simple {
field: i32
field: i32,
}
extern "C" {
@ -62,7 +64,6 @@ fn test_nested() {
// Test function that dereferences static struct pointers and accesses fields.
fn test_static() {
#[repr(C)]
struct Static {
value: i32,
@ -73,10 +74,7 @@ fn test_static() {
fn access_static(n_ptr: *const Static) -> i32;
}
static STATIC: Static = Static {
value: 9001,
recurse: &STATIC,
};
static STATIC: Static = Static { value: 9001, recurse: &STATIC };
assert_eq!(unsafe { access_static(&STATIC) }, 9001);
}

View File

@ -1,4 +1,6 @@
//@only-target-linux
// Only works on Unix targets
//@ignore-target-windows
//@ignore-target-wasm
//@only-on-host
extern "C" {

View File

@ -1,8 +1,11 @@
#include <stdio.h>
// See comments in build_native_lib()
#define EXPORT __attribute__((visibility("default")))
/* Test: test_pointer */
void print_pointer(const int *ptr) {
EXPORT void print_pointer(const int *ptr) {
printf("printing pointer dereference from C: %d\n", *ptr);
}
@ -12,7 +15,7 @@ typedef struct Simple {
int field;
} Simple;
int access_simple(const Simple *s_ptr) {
EXPORT int access_simple(const Simple *s_ptr) {
return s_ptr->field;
}
@ -24,7 +27,7 @@ typedef struct Nested {
} Nested;
// Returns the innermost/last value of a Nested pointer chain.
int access_nested(const Nested *n_ptr) {
EXPORT int access_nested(const Nested *n_ptr) {
// Edge case: `n_ptr == NULL` (i.e. first Nested is None).
if (!n_ptr) { return 0; }
@ -42,6 +45,6 @@ typedef struct Static {
struct Static *recurse;
} Static;
int access_static(const Static *s_ptr) {
EXPORT int access_static(const Static *s_ptr) {
return s_ptr->recurse->recurse->value;
}

View File

@ -1,27 +1,35 @@
#include <stdio.h>
int add_one_int(int x) {
// See comments in build_native_lib()
#define EXPORT __attribute__((visibility("default")))
EXPORT int add_one_int(int x) {
return 2 + x;
}
void printer() {
EXPORT void printer(void) {
printf("printing from C\n");
}
// function with many arguments, to test functionality when some args are stored
// on the stack
int test_stack_spill(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l) {
EXPORT int test_stack_spill(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l) {
return a+b+c+d+e+f+g+h+i+j+k+l;
}
unsigned int get_unsigned_int() {
EXPORT unsigned int get_unsigned_int(void) {
return -10;
}
short add_int16(short x) {
EXPORT short add_int16(short x) {
return x + 3;
}
long add_short_to_long(short x, long y) {
EXPORT long add_short_to_long(short x, long y) {
return x + y;
}
// To test that functions not marked with EXPORT cannot be called by Miri.
int not_exported(void) {
return 0;
}

View File

@ -36,20 +36,21 @@ fn build_native_lib() -> PathBuf {
// Create the directory if it does not already exist.
std::fs::create_dir_all(&so_target_dir)
.expect("Failed to create directory for shared object file");
let so_file_path = so_target_dir.join("native-lib.so");
// We use a platform-neutral file extension to avoid having to hard-code alternatives.
let native_lib_path = so_target_dir.join("native-lib.module");
let cc_output = Command::new(cc)
.args([
"-shared",
"-fPIC",
// We hide all symbols by default and export just the ones we need
// This is to future-proof against a more complex shared object which eg defines its own malloc
// (but we wouldn't want miri to call that, as it would if it was exported).
"-fvisibility=hidden",
"-o",
so_file_path.to_str().unwrap(),
native_lib_path.to_str().unwrap(),
// FIXME: Automate gathering of all relevant C source files in the directory.
"tests/native-lib/scalar_arguments.c",
"tests/native-lib/ptr_read_access.c",
// Only add the functions specified in libcode.version to the shared object file.
// This is to avoid automatically adding `malloc`, etc.
// Source: https://anadoxin.org/blog/control-over-symbol-exports-in-gcc.html/
"-fPIC",
"-Wl,--version-script=tests/native-lib/native-lib.map",
// Ensure we notice serious problems in the C code.
"-Wall",
"-Wextra",
@ -64,7 +65,7 @@ fn build_native_lib() -> PathBuf {
String::from_utf8_lossy(&cc_output.stderr),
);
}
so_file_path
native_lib_path
}
/// Does *not* set any args or env vars, since it is shared between the test runner and
@ -300,7 +301,7 @@ fn main() -> Result<()> {
WithDependencies,
tmpdir.path(),
)?;
if cfg!(target_os = "linux") {
if cfg!(unix) {
ui(Mode::Pass, "tests/native-lib/pass", &target, WithoutDependencies, tmpdir.path())?;
ui(
Mode::Fail { require_patterns: true, rustfix: RustfixMode::Disabled },

View File

@ -110,6 +110,13 @@ pub struct LlvmDwarfdump {
cmd: Command,
}
/// A `llvm-pdbutil` invocation builder.
#[derive(Debug)]
#[must_use]
pub struct LlvmPdbutil {
cmd: Command,
}
crate::macros::impl_common_helpers!(LlvmReadobj);
crate::macros::impl_common_helpers!(LlvmProfdata);
crate::macros::impl_common_helpers!(LlvmFilecheck);
@ -118,6 +125,7 @@ crate::macros::impl_common_helpers!(LlvmAr);
crate::macros::impl_common_helpers!(LlvmNm);
crate::macros::impl_common_helpers!(LlvmBcanalyzer);
crate::macros::impl_common_helpers!(LlvmDwarfdump);
crate::macros::impl_common_helpers!(LlvmPdbutil);
/// Generate the path to the bin directory of LLVM.
#[must_use]
@ -360,3 +368,19 @@ impl LlvmDwarfdump {
self
}
}
impl LlvmPdbutil {
/// Construct a new `llvm-pdbutil` invocation. This assumes that `llvm-pdbutil` is available
/// at `$LLVM_BIN_DIR/llvm-pdbutil`.
pub fn new() -> Self {
let llvm_pdbutil = llvm_bin_dir().join("llvm-pdbutil");
let cmd = Command::new(llvm_pdbutil);
Self { cmd }
}
/// Provide an input file.
pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
self.cmd.arg(path.as_ref());
self
}
}

View File

@ -73,7 +73,7 @@ pub use env::{env_var, env_var_os, set_current_dir};
pub use run::{cmd, run, run_fail, run_with_args};
/// Helpers for checking target information.
pub use targets::{is_darwin, is_msvc, is_windows, llvm_components_contain, target, uname};
pub use targets::{is_darwin, is_msvc, is_windows, llvm_components_contain, target, uname, apple_os};
/// Helpers for building names of output artifacts that are potentially target-specific.
pub use artifact_names::{

View File

@ -28,6 +28,24 @@ pub fn is_darwin() -> bool {
target().contains("darwin")
}
/// Get the target OS on Apple operating systems.
#[must_use]
pub fn apple_os() -> &'static str {
if target().contains("darwin") {
"macos"
} else if target().contains("ios") {
"ios"
} else if target().contains("tvos") {
"tvos"
} else if target().contains("watchos") {
"watchos"
} else if target().contains("visionos") {
"visionos"
} else {
panic!("not an Apple OS")
}
}
/// Check if `component` is within `LLVM_COMPONENTS`
#[must_use]
pub fn llvm_components_contain(component: &str) -> bool {

View File

@ -6,7 +6,6 @@ run-make/incr-add-rust-src-component/Makefile
run-make/issue-84395-lto-embed-bitcode/Makefile
run-make/jobserver-error/Makefile
run-make/libs-through-symlinks/Makefile
run-make/macos-deployment-target/Makefile
run-make/split-debuginfo/Makefile
run-make/symbol-mangling-hashed/Makefile
run-make/translation/Makefile

View File

@ -1,7 +1,7 @@
# `wasm-component-ld`
This wrapper is a wrapper around the [`wasm-component-ld`] crates.io crate. That
crate. That crate is itself a thin wrapper around two pieces:
This wrapper is a wrapper around the [`wasm-component-ld`] crates.io crate.
That crate is itself a thin wrapper around two pieces:
* `wasm-ld` - the LLVM-based linker distributed as part of LLD and packaged in
Rust as `rust-lld`.

View File

@ -552,6 +552,9 @@
//@ revisions: x86_64_unknown_haiku
//@ [x86_64_unknown_haiku] compile-flags: --target x86_64-unknown-haiku
//@ [x86_64_unknown_haiku] needs-llvm-components: x86
//@ revisions: x86_64_unknown_hurd_gnu
//@ [x86_64_unknown_hurd_gnu] compile-flags: --target x86_64-unknown-hurd-gnu
//@ [x86_64_unknown_hurd_gnu] needs-llvm-components: x86
//@ revisions: x86_64_unknown_hermit
//@ [x86_64_unknown_hermit] compile-flags: --target x86_64-unknown-hermit
//@ [x86_64_unknown_hermit] needs-llvm-components: x86

View File

@ -1,6 +1,7 @@
//@ min-lldb-version: 1800
//@ min-gdb-version: 13.0
//@ compile-flags:-g
//@ ignore-windows-gnu: #128973
// === GDB TESTS ===================================================================================

View File

@ -3,6 +3,8 @@
//@ compile-flags:-g
//@ ignore-windows-gnu: #128973
// === GDB TESTS ===================================================================================
// gdb-command:run

View File

@ -0,0 +1 @@
fn main() {}

View File

@ -0,0 +1,157 @@
//! Test codegen when setting deployment targets on Apple platforms.
//!
//! This is important since its a compatibility hazard. The linker will
//! generate load commands differently based on what minimum OS it can assume.
//!
//! See https://github.com/rust-lang/rust/pull/105123.
//@ only-apple
use run_make_support::{apple_os, cmd, run_in_tmpdir, rustc, target};
/// Run vtool to check the `minos` field in LC_BUILD_VERSION.
///
/// On lower deployment targets, LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS and similar
/// are used instead of LC_BUILD_VERSION - these have a `version` field, so also check that.
#[track_caller]
fn minos(file: &str, version: &str) {
cmd("vtool")
.arg("-show-build")
.arg(file)
.run()
.assert_stdout_contains_regex(format!("(minos|version) {version}"));
}
fn main() {
// These versions should generally be higher than the default versions
let (env_var, example_version, higher_example_version) = match apple_os() {
"macos" => ("MACOSX_DEPLOYMENT_TARGET", "12.0", "13.0"),
// armv7s-apple-ios and i386-apple-ios only supports iOS 10.0
"ios" if target() == "armv7s-apple-ios" || target() == "i386-apple-ios" => {
("IPHONEOS_DEPLOYMENT_TARGET", "10.0", "10.0")
}
"ios" => ("IPHONEOS_DEPLOYMENT_TARGET", "15.0", "16.0"),
"watchos" => ("WATCHOS_DEPLOYMENT_TARGET", "7.0", "9.0"),
"tvos" => ("TVOS_DEPLOYMENT_TARGET", "14.0", "15.0"),
"visionos" => ("XROS_DEPLOYMENT_TARGET", "1.1", "1.2"),
_ => unreachable!(),
};
let default_version =
rustc().target(target()).env_remove(env_var).print("deployment-target").run().stdout_utf8();
let default_version = default_version.strip_prefix("deployment_target=").unwrap().trim();
// Test that version makes it to the object file.
run_in_tmpdir(|| {
let rustc = || {
let mut rustc = rustc();
rustc.target(target());
rustc.crate_type("lib");
rustc.emit("obj");
rustc.input("foo.rs");
rustc.output("foo.o");
rustc
};
rustc().env(env_var, example_version).run();
minos("foo.o", example_version);
// FIXME(madsmtm): Doesn't work on Mac Catalyst and the simulator.
if !target().contains("macabi") && !target().contains("sim") {
rustc().env_remove(env_var).run();
minos("foo.o", default_version);
}
});
// Test that version makes it to the linker when linking dylibs.
run_in_tmpdir(|| {
// Certain watchOS targets don't support dynamic linking, so we disable the test on those.
if apple_os() == "watchos" {
return;
}
let rustc = || {
let mut rustc = rustc();
rustc.target(target());
rustc.crate_type("dylib");
rustc.input("foo.rs");
rustc.output("libfoo.dylib");
rustc
};
rustc().env(env_var, example_version).run();
minos("libfoo.dylib", example_version);
// FIXME(madsmtm): Deployment target is not currently passed properly to linker
// rustc().env_remove(env_var).run();
// minos("libfoo.dylib", default_version);
// Test with ld64 instead
rustc().arg("-Clinker-flavor=ld").env(env_var, example_version).run();
minos("libfoo.dylib", example_version);
rustc().arg("-Clinker-flavor=ld").env_remove(env_var).run();
minos("libfoo.dylib", default_version);
});
// Test that version makes it to the linker when linking executables.
run_in_tmpdir(|| {
let rustc = || {
let mut rustc = rustc();
rustc.target(target());
rustc.crate_type("bin");
rustc.input("foo.rs");
rustc.output("foo");
rustc
};
// FIXME(madsmtm): Doesn't work on watchOS for some reason?
if !target().contains("watchos") {
rustc().env(env_var, example_version).run();
minos("foo", example_version);
// FIXME(madsmtm): Deployment target is not currently passed properly to linker
// rustc().env_remove(env_var).run();
// minos("foo", default_version);
}
// Test with ld64 instead
rustc().arg("-Clinker-flavor=ld").env(env_var, example_version).run();
minos("foo", example_version);
rustc().arg("-Clinker-flavor=ld").env_remove(env_var).run();
minos("foo", default_version);
});
// Test that changing the deployment target busts the incremental cache.
run_in_tmpdir(|| {
let rustc = || {
let mut rustc = rustc();
rustc.target(target());
rustc.incremental("incremental");
rustc.crate_type("lib");
rustc.emit("obj");
rustc.input("foo.rs");
rustc.output("foo.o");
rustc
};
// FIXME(madsmtm): Incremental cache is not yet busted
// https://github.com/rust-lang/rust/issues/118204
let higher_example_version = example_version;
let default_version = example_version;
rustc().env(env_var, example_version).run();
minos("foo.o", example_version);
rustc().env(env_var, higher_example_version).run();
minos("foo.o", higher_example_version);
// FIXME(madsmtm): Doesn't work on Mac Catalyst and the simulator.
if !target().contains("macabi") && !target().contains("sim") {
rustc().env_remove(env_var).run();
minos("foo.o", default_version);
}
});
}

View File

@ -1,21 +0,0 @@
# only-macos
#
# Check that a set deployment target actually makes it to the linker.
# This is important since its a compatibility hazard. The linker will
# generate load commands differently based on what minimum OS it can assume.
include ../tools.mk
ifeq ($(strip $(shell uname -m)),arm64)
GREP_PATTERN = "minos 11.0"
else
GREP_PATTERN = "version 10.13"
endif
OUT_FILE=$(TMPDIR)/with_deployment_target.dylib
all:
env MACOSX_DEPLOYMENT_TARGET=10.13 $(RUSTC) with_deployment_target.rs -o $(OUT_FILE)
# XXX: The check is for either the x86_64 minimum OR the aarch64 minimum (M1 starts at macOS 11).
# They also use different load commands, so we let that change with each too. The aarch64 check
# isn't as robust as the x86 one, but testing both seems unneeded.
vtool -show-build $(OUT_FILE) | $(CGREP) -e $(GREP_PATTERN)

View File

@ -1,4 +0,0 @@
#![crate_type = "cdylib"]
#[allow(dead_code)]
fn something_and_nothing() {}

View File

@ -9,12 +9,12 @@ impl Simple {
pub trait EasyToImpl {
//@ has "$.index[*][?(@.docs=='ToDeclare trait')].inner.assoc_type"
//@ is "$.index[*][?(@.docs=='ToDeclare trait')].inner.assoc_type.default" null
//@ is "$.index[*][?(@.docs=='ToDeclare trait')].inner.assoc_type.type" null
//@ is "$.index[*][?(@.docs=='ToDeclare trait')].inner.assoc_type.bounds" []
/// ToDeclare trait
type ToDeclare;
//@ has "$.index[*][?(@.docs=='AN_ATTRIBUTE trait')].inner.assoc_const"
//@ is "$.index[*][?(@.docs=='AN_ATTRIBUTE trait')].inner.assoc_const.default" null
//@ is "$.index[*][?(@.docs=='AN_ATTRIBUTE trait')].inner.assoc_const.value" null
//@ is "$.index[*][?(@.docs=='AN_ATTRIBUTE trait')].inner.assoc_const.type.primitive" '"usize"'
/// AN_ATTRIBUTE trait
const AN_ATTRIBUTE: usize;
@ -22,13 +22,13 @@ pub trait EasyToImpl {
impl EasyToImpl for Simple {
//@ has "$.index[*][?(@.docs=='ToDeclare impl')].inner.assoc_type"
//@ is "$.index[*][?(@.docs=='ToDeclare impl')].inner.assoc_type.default.primitive" \"usize\"
//@ is "$.index[*][?(@.docs=='ToDeclare impl')].inner.assoc_type.type.primitive" \"usize\"
/// ToDeclare impl
type ToDeclare = usize;
//@ has "$.index[*][?(@.docs=='AN_ATTRIBUTE impl')].inner.assoc_const"
//@ is "$.index[*][?(@.docs=='AN_ATTRIBUTE impl')].inner.assoc_const.type.primitive" \"usize\"
//@ is "$.index[*][?(@.docs=='AN_ATTRIBUTE impl')].inner.assoc_const.default" \"12\"
//@ is "$.index[*][?(@.docs=='AN_ATTRIBUTE impl')].inner.assoc_const.value" \"12\"
/// AN_ATTRIBUTE impl
const AN_ATTRIBUTE: usize = 12;
}

View File

@ -3,6 +3,6 @@
#![no_std]
//@ has "$.index[*][?(@.name=='Error')].inner.assoc_type"
//@ has "$.index[*][?(@.name=='Error')].inner.assoc_type.default.resolved_path"
//@ has "$.index[*][?(@.name=='Error')].inner.assoc_type.default.resolved_path.name" \"Infallible\"
//@ has "$.index[*][?(@.name=='Error')].inner.assoc_type.type.resolved_path"
//@ has "$.index[*][?(@.name=='Error')].inner.assoc_type.type.resolved_path.name" \"Infallible\"
pub struct ForBlanketTryFromImpl;

View File

@ -5,7 +5,7 @@ pub enum Foo {
//@ is "$.index[*][?(@.name=='Unit')].inner.variant.kind" '"plain"'
Unit,
//@ set Named = "$.index[*][?(@.name=='Named')].id"
//@ is "$.index[*][?(@.name=='Named')].inner.variant.kind.struct" '{"fields": [], "fields_stripped": false}'
//@ is "$.index[*][?(@.name=='Named')].inner.variant.kind.struct" '{"fields": [], "has_stripped_fields": false}'
Named {},
//@ set Tuple = "$.index[*][?(@.name=='Tuple')].id"
//@ is "$.index[*][?(@.name=='Tuple')].inner.variant.kind.tuple" []
@ -13,7 +13,7 @@ pub enum Foo {
//@ set NamedField = "$.index[*][?(@.name=='NamedField')].id"
//@ set x = "$.index[*][?(@.name=='x' && @.inner.struct_field)].id"
//@ is "$.index[*][?(@.name=='NamedField')].inner.variant.kind.struct.fields[*]" $x
//@ is "$.index[*][?(@.name=='NamedField')].inner.variant.kind.struct.fields_stripped" false
//@ is "$.index[*][?(@.name=='NamedField')].inner.variant.kind.struct.has_stripped_fields" false
NamedField { x: i32 },
//@ set TupleField = "$.index[*][?(@.name=='TupleField')].id"
//@ set tup_field = "$.index[*][?(@.name=='0' && @.inner.struct_field)].id"

View File

@ -9,7 +9,7 @@ pub enum Foo {
//@ set y = "$.index[*][?(@.name=='y')].id"
y: i32,
},
//@ is "$.index[*][?(@.name=='Variant')].inner.variant.kind.struct.fields_stripped" true
//@ is "$.index[*][?(@.name=='Variant')].inner.variant.kind.struct.has_stripped_fields" true
//@ is "$.index[*][?(@.name=='Variant')].inner.variant.kind.struct.fields[0]" $b
//@ is "$.index[*][?(@.name=='Variant')].inner.variant.kind.struct.fields[1]" $y
//@ count "$.index[*][?(@.name=='Variant')].inner.variant.kind.struct.fields[*]" 2

View File

@ -7,9 +7,9 @@ pub enum Color {
Blue,
}
//@ set use_Color = "$.index[*][?(@.inner.import)].id"
//@ is "$.index[*][?(@.inner.import)].inner.import.id" $Color
//@ is "$.index[*][?(@.inner.import)].inner.import.glob" true
//@ set use_Color = "$.index[*][?(@.inner.use)].id"
//@ is "$.index[*][?(@.inner.use)].inner.use.id" $Color
//@ is "$.index[*][?(@.inner.use)].inner.use.is_glob" true
pub use Color::*;
//@ ismany "$.index[*][?(@.name == 'use_glob')].inner.module.items[*]" $Color $use_Color

View File

@ -5,8 +5,8 @@ pub enum AlwaysNone {
}
//@ is "$.index[*][?(@.name == 'AlwaysNone')].inner.enum.variants[*]" $None
//@ set use_None = "$.index[*][?(@.inner.import)].id"
//@ is "$.index[*][?(@.inner.import)].inner.import.id" $None
//@ set use_None = "$.index[*][?(@.inner.use)].id"
//@ is "$.index[*][?(@.inner.use)].inner.use.id" $None
pub use AlwaysNone::None;
//@ ismany "$.index[*][?(@.name == 'use_variant')].inner.module.items[*]" $AlwaysNone $use_None

View File

@ -2,7 +2,7 @@
extern crate color;
//@ has "$.index[*].inner.import[?(@.name == 'Red')]"
//@ has "$.index[*].inner.use[?(@.name == 'Red')]"
pub use color::Color::Red;
//@ !has "$.index[*][?(@.name == 'Red')]"

View File

@ -1,9 +1,9 @@
// ignore-tidy-linelength
//@ count "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type_alias.type.function_pointer.decl.inputs[*]" 1
//@ is "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type_alias.type.function_pointer.decl.inputs[0][0]" '"val"'
//@ is "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type_alias.type.function_pointer.decl.inputs[0][1].borrowed_ref.lifetime" \"\'c\"
//@ is "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type_alias.type.function_pointer.decl.output.primitive" \"i32\"
//@ count "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type_alias.type.function_pointer.sig.inputs[*]" 1
//@ is "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type_alias.type.function_pointer.sig.inputs[0][0]" '"val"'
//@ is "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type_alias.type.function_pointer.sig.inputs[0][1].borrowed_ref.lifetime" \"\'c\"
//@ is "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type_alias.type.function_pointer.sig.output.primitive" \"i32\"
//@ count "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type_alias.type.function_pointer.generic_params[*]" 1
//@ is "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type_alias.type.function_pointer.generic_params[0].name" \"\'c\"
//@ is "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type_alias.type.function_pointer.generic_params[0].kind" '{ "lifetime": { "outlives": [] } }'

View File

@ -1,11 +1,11 @@
// ignore-tidy-linelength
//@ is "$.index[*][?(@.name=='FnPointer')].inner.type_alias.type.function_pointer.header.unsafe" false
//@ is "$.index[*][?(@.name=='FnPointer')].inner.type_alias.type.function_pointer.header.const" false
//@ is "$.index[*][?(@.name=='FnPointer')].inner.type_alias.type.function_pointer.header.async" false
//@ is "$.index[*][?(@.name=='FnPointer')].inner.type_alias.type.function_pointer.header.is_unsafe" false
//@ is "$.index[*][?(@.name=='FnPointer')].inner.type_alias.type.function_pointer.header.is_const" false
//@ is "$.index[*][?(@.name=='FnPointer')].inner.type_alias.type.function_pointer.header.is_async" false
pub type FnPointer = fn();
//@ is "$.index[*][?(@.name=='UnsafePointer')].inner.type_alias.type.function_pointer.header.unsafe" true
//@ is "$.index[*][?(@.name=='UnsafePointer')].inner.type_alias.type.function_pointer.header.const" false
//@ is "$.index[*][?(@.name=='UnsafePointer')].inner.type_alias.type.function_pointer.header.async" false
//@ is "$.index[*][?(@.name=='UnsafePointer')].inner.type_alias.type.function_pointer.header.is_unsafe" true
//@ is "$.index[*][?(@.name=='UnsafePointer')].inner.type_alias.type.function_pointer.header.is_const" false
//@ is "$.index[*][?(@.name=='UnsafePointer')].inner.type_alias.type.function_pointer.header.is_async" false
pub type UnsafePointer = unsafe fn();

View File

@ -5,30 +5,30 @@
use std::future::Future;
//@ is "$.index[*][?(@.name=='get_int')].inner.function.decl.output.primitive" \"i32\"
//@ is "$.index[*][?(@.name=='get_int')].inner.function.header.async" false
//@ is "$.index[*][?(@.name=='get_int')].inner.function.sig.output.primitive" \"i32\"
//@ is "$.index[*][?(@.name=='get_int')].inner.function.header.is_async" false
pub fn get_int() -> i32 {
42
}
//@ is "$.index[*][?(@.name=='get_int_async')].inner.function.decl.output.primitive" \"i32\"
//@ is "$.index[*][?(@.name=='get_int_async')].inner.function.header.async" true
//@ is "$.index[*][?(@.name=='get_int_async')].inner.function.sig.output.primitive" \"i32\"
//@ is "$.index[*][?(@.name=='get_int_async')].inner.function.header.is_async" true
pub async fn get_int_async() -> i32 {
42
}
//@ is "$.index[*][?(@.name=='get_int_future')].inner.function.decl.output.impl_trait[0].trait_bound.trait.name" '"Future"'
//@ is "$.index[*][?(@.name=='get_int_future')].inner.function.decl.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.bindings[0].name" '"Output"'
//@ is "$.index[*][?(@.name=='get_int_future')].inner.function.decl.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.bindings[0].binding.equality.type.primitive" \"i32\"
//@ is "$.index[*][?(@.name=='get_int_future')].inner.function.header.async" false
//@ is "$.index[*][?(@.name=='get_int_future')].inner.function.sig.output.impl_trait[0].trait_bound.trait.name" '"Future"'
//@ is "$.index[*][?(@.name=='get_int_future')].inner.function.sig.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[0].name" '"Output"'
//@ is "$.index[*][?(@.name=='get_int_future')].inner.function.sig.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[0].binding.equality.type.primitive" \"i32\"
//@ is "$.index[*][?(@.name=='get_int_future')].inner.function.header.is_async" false
pub fn get_int_future() -> impl Future<Output = i32> {
async { 42 }
}
//@ is "$.index[*][?(@.name=='get_int_future_async')].inner.function.decl.output.impl_trait[0].trait_bound.trait.name" '"Future"'
//@ is "$.index[*][?(@.name=='get_int_future_async')].inner.function.decl.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.bindings[0].name" '"Output"'
//@ is "$.index[*][?(@.name=='get_int_future_async')].inner.function.decl.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.bindings[0].binding.equality.type.primitive" \"i32\"
//@ is "$.index[*][?(@.name=='get_int_future_async')].inner.function.header.async" true
//@ is "$.index[*][?(@.name=='get_int_future_async')].inner.function.sig.output.impl_trait[0].trait_bound.trait.name" '"Future"'
//@ is "$.index[*][?(@.name=='get_int_future_async')].inner.function.sig.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[0].name" '"Output"'
//@ is "$.index[*][?(@.name=='get_int_future_async')].inner.function.sig.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[0].binding.equality.type.primitive" \"i32\"
//@ is "$.index[*][?(@.name=='get_int_future_async')].inner.function.header.is_async" true
pub async fn get_int_future_async() -> impl Future<Output = i32> {
async { 42 }
}

View File

@ -1,6 +1,6 @@
extern "C" {
//@ is "$.index[*][?(@.name == 'not_variadic')].inner.function.decl.c_variadic" false
//@ is "$.index[*][?(@.name == 'not_variadic')].inner.function.sig.is_c_variadic" false
pub fn not_variadic(_: i32);
//@ is "$.index[*][?(@.name == 'variadic')].inner.function.decl.c_variadic" true
//@ is "$.index[*][?(@.name == 'variadic')].inner.function.sig.is_c_variadic" true
pub fn variadic(_: i32, ...);
}

View File

@ -12,27 +12,27 @@ pub trait GenericFoo<'a> {}
//@ is "$.index[*][?(@.name=='generics')].inner.function.generics.params[0].kind.type.default" 'null'
//@ count "$.index[*][?(@.name=='generics')].inner.function.generics.params[0].kind.type.bounds[*]" 1
//@ is "$.index[*][?(@.name=='generics')].inner.function.generics.params[0].kind.type.bounds[0].trait_bound.trait.id" '$foo'
//@ count "$.index[*][?(@.name=='generics')].inner.function.decl.inputs[*]" 1
//@ is "$.index[*][?(@.name=='generics')].inner.function.decl.inputs[0][0]" '"f"'
//@ is "$.index[*][?(@.name=='generics')].inner.function.decl.inputs[0][1].generic" '"F"'
//@ count "$.index[*][?(@.name=='generics')].inner.function.sig.inputs[*]" 1
//@ is "$.index[*][?(@.name=='generics')].inner.function.sig.inputs[0][0]" '"f"'
//@ is "$.index[*][?(@.name=='generics')].inner.function.sig.inputs[0][1].generic" '"F"'
pub fn generics<F: Foo>(f: F) {}
//@ is "$.index[*][?(@.name=='impl_trait')].inner.function.generics.where_predicates" "[]"
//@ count "$.index[*][?(@.name=='impl_trait')].inner.function.generics.params[*]" 1
//@ is "$.index[*][?(@.name=='impl_trait')].inner.function.generics.params[0].name" '"impl Foo"'
//@ is "$.index[*][?(@.name=='impl_trait')].inner.function.generics.params[0].kind.type.bounds[0].trait_bound.trait.id" $foo
//@ count "$.index[*][?(@.name=='impl_trait')].inner.function.decl.inputs[*]" 1
//@ is "$.index[*][?(@.name=='impl_trait')].inner.function.decl.inputs[0][0]" '"f"'
//@ count "$.index[*][?(@.name=='impl_trait')].inner.function.decl.inputs[0][1].impl_trait[*]" 1
//@ is "$.index[*][?(@.name=='impl_trait')].inner.function.decl.inputs[0][1].impl_trait[0].trait_bound.trait.id" $foo
//@ count "$.index[*][?(@.name=='impl_trait')].inner.function.sig.inputs[*]" 1
//@ is "$.index[*][?(@.name=='impl_trait')].inner.function.sig.inputs[0][0]" '"f"'
//@ count "$.index[*][?(@.name=='impl_trait')].inner.function.sig.inputs[0][1].impl_trait[*]" 1
//@ is "$.index[*][?(@.name=='impl_trait')].inner.function.sig.inputs[0][1].impl_trait[0].trait_bound.trait.id" $foo
pub fn impl_trait(f: impl Foo) {}
//@ count "$.index[*][?(@.name=='where_clase')].inner.function.generics.params[*]" 3
//@ is "$.index[*][?(@.name=='where_clase')].inner.function.generics.params[0].name" '"F"'
//@ is "$.index[*][?(@.name=='where_clase')].inner.function.generics.params[0].kind" '{"type": {"bounds": [], "default": null, "synthetic": false}}'
//@ count "$.index[*][?(@.name=='where_clase')].inner.function.decl.inputs[*]" 3
//@ is "$.index[*][?(@.name=='where_clase')].inner.function.decl.inputs[0][0]" '"f"'
//@ is "$.index[*][?(@.name=='where_clase')].inner.function.decl.inputs[0][1].generic" '"F"'
//@ is "$.index[*][?(@.name=='where_clase')].inner.function.generics.params[0].kind" '{"type": {"bounds": [], "default": null, "is_synthetic": false}}'
//@ count "$.index[*][?(@.name=='where_clase')].inner.function.sig.inputs[*]" 3
//@ is "$.index[*][?(@.name=='where_clase')].inner.function.sig.inputs[0][0]" '"f"'
//@ is "$.index[*][?(@.name=='where_clase')].inner.function.sig.inputs[0][1].generic" '"F"'
//@ count "$.index[*][?(@.name=='where_clase')].inner.function.generics.where_predicates[*]" 3
//@ is "$.index[*][?(@.name=='where_clase')].inner.function.generics.where_predicates[0].bound_predicate.type.generic" \"F\"

View File

@ -5,9 +5,9 @@
//@ set foo = "$.index[*][?(@.name=='Foo')].id"
pub trait Foo {}
//@ is "$.index[*][?(@.name=='get_foo')].inner.function.decl.inputs" []
//@ count "$.index[*][?(@.name=='get_foo')].inner.function.decl.output.impl_trait[*]" 1
//@ is "$.index[*][?(@.name=='get_foo')].inner.function.decl.output.impl_trait[0].trait_bound.trait.id" $foo
//@ is "$.index[*][?(@.name=='get_foo')].inner.function.sig.inputs" []
//@ count "$.index[*][?(@.name=='get_foo')].inner.function.sig.output.impl_trait[*]" 1
//@ is "$.index[*][?(@.name=='get_foo')].inner.function.sig.output.impl_trait[0].trait_bound.trait.id" $foo
pub fn get_foo() -> impl Foo {
Fooer {}
}

View File

@ -6,17 +6,17 @@ pub trait Wham {}
//@ is "$.index[*][?(@.name=='one_generic_param_fn')].inner.function.generics.where_predicates" []
//@ count "$.index[*][?(@.name=='one_generic_param_fn')].inner.function.generics.params[*]" 1
//@ is "$.index[*][?(@.name=='one_generic_param_fn')].inner.function.generics.params[0].name" '"T"'
//@ is "$.index[*][?(@.name=='one_generic_param_fn')].inner.function.generics.params[0].kind.type.synthetic" false
//@ is "$.index[*][?(@.name=='one_generic_param_fn')].inner.function.generics.params[0].kind.type.is_synthetic" false
//@ is "$.index[*][?(@.name=='one_generic_param_fn')].inner.function.generics.params[0].kind.type.bounds[0].trait_bound.trait.id" $wham_id
//@ is "$.index[*][?(@.name=='one_generic_param_fn')].inner.function.decl.inputs" '[["w", {"generic": "T"}]]'
//@ is "$.index[*][?(@.name=='one_generic_param_fn')].inner.function.sig.inputs" '[["w", {"generic": "T"}]]'
pub fn one_generic_param_fn<T: Wham>(w: T) {}
//@ is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.generics.where_predicates" []
//@ count "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.generics.params[*]" 1
//@ is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.generics.params[0].name" '"impl Wham"'
//@ is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.generics.params[0].kind.type.synthetic" true
//@ is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.generics.params[0].kind.type.is_synthetic" true
//@ is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.generics.params[0].kind.type.bounds[0].trait_bound.trait.id" $wham_id
//@ count "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.decl.inputs[*]" 1
//@ is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.decl.inputs[0][0]" '"w"'
//@ is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.decl.inputs[0][1].impl_trait[0].trait_bound.trait.id" $wham_id
//@ count "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.sig.inputs[*]" 1
//@ is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.sig.inputs[0][0]" '"w"'
//@ is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.sig.inputs[0][1].impl_trait[0].trait_bound.trait.id" $wham_id
pub fn one_synthetic_generic_param_fn(w: impl Wham) {}

View File

@ -1,7 +1,7 @@
//@ is "$.index[*][?(@.name=='fst')].inner.function.decl.inputs[0][0]" '"(x, _)"'
//@ is "$.index[*][?(@.name=='fst')].inner.function.sig.inputs[0][0]" '"(x, _)"'
pub fn fst<X, Y>((x, _): (X, Y)) -> X {
x
}
//@ is "$.index[*][?(@.name=='drop_int')].inner.function.decl.inputs[0][0]" '"_"'
//@ is "$.index[*][?(@.name=='drop_int')].inner.function.sig.inputs[0][0]" '"_"'
pub fn drop_int(_: i32) {}

View File

@ -1,33 +1,33 @@
//@ edition:2018
//@ is "$.index[*][?(@.name=='nothing_fn')].inner.function.header.async" false
//@ is "$.index[*][?(@.name=='nothing_fn')].inner.function.header.const" false
//@ is "$.index[*][?(@.name=='nothing_fn')].inner.function.header.unsafe" false
//@ is "$.index[*][?(@.name=='nothing_fn')].inner.function.header.is_async" false
//@ is "$.index[*][?(@.name=='nothing_fn')].inner.function.header.is_const" false
//@ is "$.index[*][?(@.name=='nothing_fn')].inner.function.header.is_unsafe" false
pub fn nothing_fn() {}
//@ is "$.index[*][?(@.name=='unsafe_fn')].inner.function.header.async" false
//@ is "$.index[*][?(@.name=='unsafe_fn')].inner.function.header.const" false
//@ is "$.index[*][?(@.name=='unsafe_fn')].inner.function.header.unsafe" true
//@ is "$.index[*][?(@.name=='unsafe_fn')].inner.function.header.is_async" false
//@ is "$.index[*][?(@.name=='unsafe_fn')].inner.function.header.is_const" false
//@ is "$.index[*][?(@.name=='unsafe_fn')].inner.function.header.is_unsafe" true
pub unsafe fn unsafe_fn() {}
//@ is "$.index[*][?(@.name=='const_fn')].inner.function.header.async" false
//@ is "$.index[*][?(@.name=='const_fn')].inner.function.header.const" true
//@ is "$.index[*][?(@.name=='const_fn')].inner.function.header.unsafe" false
//@ is "$.index[*][?(@.name=='const_fn')].inner.function.header.is_async" false
//@ is "$.index[*][?(@.name=='const_fn')].inner.function.header.is_const" true
//@ is "$.index[*][?(@.name=='const_fn')].inner.function.header.is_unsafe" false
pub const fn const_fn() {}
//@ is "$.index[*][?(@.name=='async_fn')].inner.function.header.async" true
//@ is "$.index[*][?(@.name=='async_fn')].inner.function.header.const" false
//@ is "$.index[*][?(@.name=='async_fn')].inner.function.header.unsafe" false
//@ is "$.index[*][?(@.name=='async_fn')].inner.function.header.is_async" true
//@ is "$.index[*][?(@.name=='async_fn')].inner.function.header.is_const" false
//@ is "$.index[*][?(@.name=='async_fn')].inner.function.header.is_unsafe" false
pub async fn async_fn() {}
//@ is "$.index[*][?(@.name=='async_unsafe_fn')].inner.function.header.async" true
//@ is "$.index[*][?(@.name=='async_unsafe_fn')].inner.function.header.const" false
//@ is "$.index[*][?(@.name=='async_unsafe_fn')].inner.function.header.unsafe" true
//@ is "$.index[*][?(@.name=='async_unsafe_fn')].inner.function.header.is_async" true
//@ is "$.index[*][?(@.name=='async_unsafe_fn')].inner.function.header.is_const" false
//@ is "$.index[*][?(@.name=='async_unsafe_fn')].inner.function.header.is_unsafe" true
pub async unsafe fn async_unsafe_fn() {}
//@ is "$.index[*][?(@.name=='const_unsafe_fn')].inner.function.header.async" false
//@ is "$.index[*][?(@.name=='const_unsafe_fn')].inner.function.header.const" true
//@ is "$.index[*][?(@.name=='const_unsafe_fn')].inner.function.header.unsafe" true
//@ is "$.index[*][?(@.name=='const_unsafe_fn')].inner.function.header.is_async" false
//@ is "$.index[*][?(@.name=='const_unsafe_fn')].inner.function.header.is_const" true
//@ is "$.index[*][?(@.name=='const_unsafe_fn')].inner.function.header.is_unsafe" true
pub const unsafe fn const_unsafe_fn() {}
// It's impossible for a function to be both const and async, so no test for that

View File

@ -3,7 +3,7 @@
///@ set foo = "$.index[*][?(@.name=='Foo')].id"
pub type Foo = i32;
//@ is "$.index[*][?(@.name=='demo')].inner.function.decl.output.resolved_path.id" $foo
//@ is "$.index[*][?(@.name=='demo')].inner.function.sig.output.resolved_path.id" $foo
pub fn demo() -> Foo {
42
}

View File

@ -13,10 +13,10 @@ pub trait LendingIterator {
where
Self: 'a;
//@ count "$.index[*][?(@.name=='lending_next')].inner.function.decl.output.qualified_path.args.angle_bracketed.args[*]" 1
//@ count "$.index[*][?(@.name=='lending_next')].inner.function.decl.output.qualified_path.args.angle_bracketed.bindings[*]" 0
//@ is "$.index[*][?(@.name=='lending_next')].inner.function.decl.output.qualified_path.self_type.generic" \"Self\"
//@ is "$.index[*][?(@.name=='lending_next')].inner.function.decl.output.qualified_path.name" \"LendingItem\"
//@ count "$.index[*][?(@.name=='lending_next')].inner.function.sig.output.qualified_path.args.angle_bracketed.args[*]" 1
//@ count "$.index[*][?(@.name=='lending_next')].inner.function.sig.output.qualified_path.args.angle_bracketed.bindings[*]" 0
//@ is "$.index[*][?(@.name=='lending_next')].inner.function.sig.output.qualified_path.self_type.generic" \"Self\"
//@ is "$.index[*][?(@.name=='lending_next')].inner.function.sig.output.qualified_path.name" \"LendingItem\"
fn lending_next<'a>(&'a self) -> Self::LendingItem<'a>;
}
@ -26,9 +26,9 @@ pub trait Iterator {
//@ count "$.index[*][?(@.name=='Item')].inner.assoc_type.bounds[*]" 1
type Item: Display;
//@ count "$.index[*][?(@.name=='next')].inner.function.decl.output.qualified_path.args.angle_bracketed.args[*]" 0
//@ count "$.index[*][?(@.name=='next')].inner.function.decl.output.qualified_path.args.angle_bracketed.bindings[*]" 0
//@ is "$.index[*][?(@.name=='next')].inner.function.decl.output.qualified_path.self_type.generic" \"Self\"
//@ is "$.index[*][?(@.name=='next')].inner.function.decl.output.qualified_path.name" \"Item\"
//@ count "$.index[*][?(@.name=='next')].inner.function.sig.output.qualified_path.args.angle_bracketed.args[*]" 0
//@ count "$.index[*][?(@.name=='next')].inner.function.sig.output.qualified_path.args.angle_bracketed.bindings[*]" 0
//@ is "$.index[*][?(@.name=='next')].inner.function.sig.output.qualified_path.self_type.generic" \"Self\"
//@ is "$.index[*][?(@.name=='next')].inner.function.sig.output.qualified_path.name" \"Item\"
fn next<'a>(&'a self) -> Self::Item;
}

View File

@ -3,7 +3,7 @@
#![no_std]
//@ has "$.index[*][?(@.name=='glob')]"
//@ has "$.index[*][?(@.inner.import)].inner.import.name" \"*\"
//@ has "$.index[*][?(@.inner.use)].inner.use.name" \"*\"
mod m1 {
pub fn f() {}

View File

@ -9,11 +9,11 @@ impl IntoIterator for AlwaysTrue {
/// type Item
type Item = bool;
//@ count '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.default.impl_trait[*]' 1
//@ is '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.default.impl_trait[0].trait_bound.trait.name' '"Iterator"'
//@ count '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.default.impl_trait[0].trait_bound.trait.args.angle_bracketed.bindings[*]' 1
//@ is '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.default.impl_trait[0].trait_bound.trait.args.angle_bracketed.bindings[0].name' '"Item"'
//@ is '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.default.impl_trait[0].trait_bound.trait.args.angle_bracketed.bindings[0].binding.equality.type.primitive' '"bool"'
//@ count '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait[*]' 1
//@ is '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait[0].trait_bound.trait.name' '"Iterator"'
//@ count '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[*]' 1
//@ is '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[0].name' '"Item"'
//@ is '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[0].binding.equality.type.primitive' '"bool"'
//@ set IntoIter = '$.index[*][?(@.docs=="type IntoIter")].id'
/// type IntoIter

View File

@ -1,4 +1,4 @@
//@ is "$.index[*][?(@.name=='hello')].inner.function.decl.output.impl_trait[1].use[0]" \"\'a\"
//@ is "$.index[*][?(@.name=='hello')].inner.function.decl.output.impl_trait[1].use[1]" \"T\"
//@ is "$.index[*][?(@.name=='hello')].inner.function.decl.output.impl_trait[1].use[2]" \"N\"
//@ is "$.index[*][?(@.name=='hello')].inner.function.sig.output.impl_trait[1].use[0]" \"\'a\"
//@ is "$.index[*][?(@.name=='hello')].inner.function.sig.output.impl_trait[1].use[1]" \"T\"
//@ is "$.index[*][?(@.name=='hello')].inner.function.sig.output.impl_trait[1].use[2]" \"N\"
pub fn hello<'a, T, const N: usize>() -> impl Sized + use<'a, T, N> {}

View File

@ -18,5 +18,5 @@ impl Foo {
//@ is "$.index[*][?(@.docs=='has span')].span.begin" "[13, 0]"
//@ is "$.index[*][?(@.docs=='has span')].span.end" "[15, 1]"
// FIXME: this doesn't work due to https://github.com/freestrings/jsonpath/issues/91
// is "$.index[*][?(@.inner.impl.synthetic==true)].span" null
// is "$.index[*][?(@.inner.impl.is_synthetic==true)].span" null
pub struct Foo;

View File

@ -3,7 +3,7 @@ extern crate foreign_trait;
/// ForeignTrait id hack
pub use foreign_trait::ForeignTrait as _;
//@ set ForeignTrait = "$.index[*][?(@.docs=='ForeignTrait id hack')].inner.import.id"
//@ set ForeignTrait = "$.index[*][?(@.docs=='ForeignTrait id hack')].inner.use.id"
pub struct LocalStruct;
//@ set LocalStruct = "$.index[*][?(@.name=='LocalStruct')].id"

View File

@ -11,10 +11,10 @@ mod bar {
}
}
//@ set import = "$.index[*][?(@.inner.import)].id"
//@ set import = "$.index[*][?(@.inner.use)].id"
pub use bar::Baz;
//@ is "$.index[*].inner.module.items[*]" $import
//@ is "$.index[*].inner.import.id" $baz
//@ is "$.index[*].inner.use.id" $baz
//@ has "$.index[*][?(@.name == 'Baz')].inner.struct.impls[*]" $impl
//@ is "$.index[*][?(@.docs=='impl')].inner.impl.items[*]" $doit

View File

@ -3,7 +3,7 @@ extern crate foreign_struct;
/// ForeignStruct id hack
pub use foreign_struct::ForeignStruct as _;
//@ set ForeignStruct = "$.index[*][?(@.docs=='ForeignStruct id hack')].inner.import.id"
//@ set ForeignStruct = "$.index[*][?(@.docs=='ForeignStruct id hack')].inner.use.id"
pub trait LocalTrait {}
//@ set LocalTrait = "$.index[*][?(@.name=='LocalTrait')].id"

View File

@ -6,21 +6,21 @@
//@ count "$.index[*][?(@.name=='longest')].inner.function.generics.params[*]" 1
//@ is "$.index[*][?(@.name=='longest')].inner.function.generics.where_predicates" []
//@ count "$.index[*][?(@.name=='longest')].inner.function.decl.inputs[*]" 2
//@ is "$.index[*][?(@.name=='longest')].inner.function.decl.inputs[0][0]" '"l"'
//@ is "$.index[*][?(@.name=='longest')].inner.function.decl.inputs[1][0]" '"r"'
//@ count "$.index[*][?(@.name=='longest')].inner.function.sig.inputs[*]" 2
//@ is "$.index[*][?(@.name=='longest')].inner.function.sig.inputs[0][0]" '"l"'
//@ is "$.index[*][?(@.name=='longest')].inner.function.sig.inputs[1][0]" '"r"'
//@ is "$.index[*][?(@.name=='longest')].inner.function.decl.inputs[0][1].borrowed_ref.lifetime" \"\'a\"
//@ is "$.index[*][?(@.name=='longest')].inner.function.decl.inputs[0][1].borrowed_ref.mutable" false
//@ is "$.index[*][?(@.name=='longest')].inner.function.decl.inputs[0][1].borrowed_ref.type.primitive" \"str\"
//@ is "$.index[*][?(@.name=='longest')].inner.function.sig.inputs[0][1].borrowed_ref.lifetime" \"\'a\"
//@ is "$.index[*][?(@.name=='longest')].inner.function.sig.inputs[0][1].borrowed_ref.is_mutable" false
//@ is "$.index[*][?(@.name=='longest')].inner.function.sig.inputs[0][1].borrowed_ref.type.primitive" \"str\"
//@ is "$.index[*][?(@.name=='longest')].inner.function.decl.inputs[1][1].borrowed_ref.lifetime" \"\'a\"
//@ is "$.index[*][?(@.name=='longest')].inner.function.decl.inputs[1][1].borrowed_ref.mutable" false
//@ is "$.index[*][?(@.name=='longest')].inner.function.decl.inputs[1][1].borrowed_ref.type.primitive" \"str\"
//@ is "$.index[*][?(@.name=='longest')].inner.function.sig.inputs[1][1].borrowed_ref.lifetime" \"\'a\"
//@ is "$.index[*][?(@.name=='longest')].inner.function.sig.inputs[1][1].borrowed_ref.is_mutable" false
//@ is "$.index[*][?(@.name=='longest')].inner.function.sig.inputs[1][1].borrowed_ref.type.primitive" \"str\"
//@ is "$.index[*][?(@.name=='longest')].inner.function.decl.output.borrowed_ref.lifetime" \"\'a\"
//@ is "$.index[*][?(@.name=='longest')].inner.function.decl.output.borrowed_ref.mutable" false
//@ is "$.index[*][?(@.name=='longest')].inner.function.decl.output.borrowed_ref.type.primitive" \"str\"
//@ is "$.index[*][?(@.name=='longest')].inner.function.sig.output.borrowed_ref.lifetime" \"\'a\"
//@ is "$.index[*][?(@.name=='longest')].inner.function.sig.output.borrowed_ref.is_mutable" false
//@ is "$.index[*][?(@.name=='longest')].inner.function.sig.output.borrowed_ref.type.primitive" \"str\"
pub fn longest<'a>(l: &'a str, r: &'a str) -> &'a str {
if l.len() > r.len() { l } else { r }

Some files were not shown because too many files have changed in this diff Show More