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", "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]] [[package]]
name = "boml" name = "boml"
version = "0.3.1" version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85fdb93f04c73bff54305fa437ffea5449c41edcaadfe882f35836206b166ac5" 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]] [[package]]
name = "fm" name = "fm"
version = "0.2.2" version = "0.2.2"
@ -132,12 +81,6 @@ version = "0.2.150"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
[[package]]
name = "linux-raw-sys"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.5.0" version = "2.5.0"
@ -154,24 +97,6 @@ dependencies = [
"libc", "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]] [[package]]
name = "regex" name = "regex"
version = "1.8.4" version = "1.8.4"
@ -196,22 +121,6 @@ dependencies = [
"boml", "boml",
"gccjit", "gccjit",
"lang_tester", "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]] [[package]]
@ -223,25 +132,6 @@ dependencies = [
"winapi-util", "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]] [[package]]
name = "termcolor" name = "termcolor"
version = "1.2.0" version = "1.2.0"
@ -315,69 +205,3 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 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] [dependencies]
gccjit = "2.1" gccjit = "2.1"
# Local copy. # Local copy.
#gccjit = { path = "../gccjit.rs" } #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] [dev-dependencies]
lang_tester = "0.8.0" lang_tester = "0.8.0"
tempfile = "3.1.0"
boml = "0.3.1" boml = "0.3.1"
[profile.dev] [profile.dev]

View File

@ -24,6 +24,14 @@
#![deny(clippy::pattern_type_mismatch)] #![deny(clippy::pattern_type_mismatch)]
#![allow(clippy::needless_lifetimes)] #![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_apfloat;
extern crate rustc_ast; extern crate rustc_ast;
extern crate rustc_attr; extern crate rustc_attr;
@ -42,8 +50,6 @@ extern crate rustc_middle;
extern crate rustc_session; extern crate rustc_session;
extern crate rustc_span; extern crate rustc_span;
extern crate rustc_target; extern crate rustc_target;
#[macro_use]
extern crate tracing;
// This prevents duplicating functions and statics that are already part of the host rustc process. // This prevents duplicating functions and statics that are already part of the host rustc process.
#[allow(unused_extern_crates)] #[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 // this time with enough precision to check that the value
// whose address was taken can actually be made to live as long // whose address was taken can actually be made to live as long
// as it needs to live. // 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) Ty::new_ref(self.tcx, region, ty, mutbl)
} }
} }

View File

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

View File

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

View File

@ -1,6 +1,6 @@
//! The `wasm32-wasip2` target is the next evolution of the //! The `wasm32-wasip2` target is the next evolution of the
//! wasm32-wasi target. While the wasi specification is still under //! 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. //! of stability" that should allow users to rely on it indefinitely.
//! //!
//! The `wasi` target is a proposal to define a standardized set of WebAssembly //! 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 { let var_description = match var_origin {
infer::MiscVariable(_) => String::new(), infer::MiscVariable(_) => String::new(),
infer::PatternRegion(_) => " for pattern".to_string(), 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::Autoref(_) => " for autoref".to_string(),
infer::Coercion(_) => " for automatic coercion".to_string(), infer::Coercion(_) => " for automatic coercion".to_string(),
infer::BoundRegion(_, br, infer::FnCall) => { 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>> { fn advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
let remaining = self.i1.advance_by(n); let remaining = self.i1.advance_by(n);
match remaining { match remaining {
Ok(()) => return Ok(()), Ok(()) => Ok(()),
Err(n) => { Err(n) => {
mem::swap(&mut self.i1, &mut self.i2); mem::swap(&mut self.i1, &mut self.i2);
self.i1.advance_by(n.get()) 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>> { fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
match self.i2.advance_back_by(n) { match self.i2.advance_back_by(n) {
Ok(()) => return Ok(()), Ok(()) => Ok(()),
Err(n) => { Err(n) => {
mem::swap(&mut self.i1, &mut self.i2); mem::swap(&mut self.i1, &mut self.i2);
self.i2.advance_back_by(n.get()) 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>> { fn advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
match self.i1.advance_by(n) { match self.i1.advance_by(n) {
Ok(()) => return Ok(()), Ok(()) => Ok(()),
Err(remaining) => { Err(remaining) => {
mem::swap(&mut self.i1, &mut self.i2); mem::swap(&mut self.i1, &mut self.i2);
self.i1.advance_by(remaining.get()) 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>> { fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
match self.i2.advance_back_by(n) { match self.i2.advance_back_by(n) {
Ok(()) => return Ok(()), Ok(()) => Ok(()),
Err(remaining) => { Err(remaining) => {
mem::swap(&mut self.i1, &mut self.i2); mem::swap(&mut self.i1, &mut self.i2);
self.i2.advance_back_by(remaining.get()) 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 // type layouts don't guarantee a fit, so do a runtime check to see if
// the allocations happen to match // 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. /// 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 // Safety: `len` is larger than the array size. Copy a fixed amount here to fully initialize
// the array. // the array.
return unsafe { unsafe {
ptr::copy_nonoverlapping(self.ptr.as_ptr(), raw_ary.as_mut_ptr() as *mut T, N); ptr::copy_nonoverlapping(self.ptr.as_ptr(), raw_ary.as_mut_ptr() as *mut T, N);
self.ptr = self.ptr.add(N); self.ptr = self.ptr.add(N);
Ok(raw_ary.transpose().assume_init()) Ok(raw_ary.transpose().assume_init())
}; }
} }
fn fold<B, F>(mut self, mut accum: B, mut f: F) -> B 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); /// assert_eq!(x.iter().next(), None);
/// ``` /// ```
#[inline] #[inline]
#[rustc_const_unstable(feature = "const_option", issue = "67441")]
#[stable(feature = "rust1", since = "1.0.0")] #[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() } } Iter { inner: Item { opt: self.as_ref() } }
} }
@ -1894,7 +1893,7 @@ impl<T> Option<&T> {
where where
T: Copy, 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 // ready yet, should be reverted when possible to avoid code repetition
match self { match self {
Some(&v) => Some(v), 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"] #[must_use = "`self` will be dropped if the result is not used"]
#[stable(feature = "copied", since = "1.35.0")] #[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> pub const fn copied(self) -> Option<T>
where where
T: Copy, T: Copy,

View File

@ -40,15 +40,17 @@ impl<T: ?Sized> *const T {
#[inline] #[inline]
const fn const_impl(ptr: *const u8) -> bool { 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()) { match (ptr).guaranteed_eq(null_mut()) {
None => false,
Some(res) => res, 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) 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 // Consider a reference like `&(i32, dyn Send)`: the vtable will only store the size of the
// `Send` part! // `Send` part!
// SAFETY: DynMetadata always contains a valid vtable pointer // 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. /// Returns the alignment of the type associated with this vtable.
#[inline] #[inline]
pub fn align_of(self) -> usize { pub fn align_of(self) -> usize {
// SAFETY: DynMetadata always contains a valid vtable pointer // 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` /// 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"] #[rustc_diagnostic_item = "ptr_is_null"]
#[inline] #[inline]
pub const fn is_null(self) -> bool { pub const fn is_null(self) -> bool {
#[inline] self.cast_const().is_null()
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)
} }
/// Casts to a pointer of another type. /// Casts to a pointer of another type.

View File

@ -1535,11 +1535,17 @@ impl<T, E> Result<&T, E> {
/// ``` /// ```
#[inline] #[inline]
#[stable(feature = "result_copied", since = "1.59.0")] #[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 where
T: Copy, 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 /// 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] #[inline]
#[stable(feature = "result_copied", since = "1.59.0")] #[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 where
T: Copy, 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 /// 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); mask &= !(1 << trailing);
} }
return false; false
}; };
let test_chunk = |idx| -> u16 { 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 both = eq_first.bitand(eq_last);
let mask = both.to_bitmask() as u16; let mask = both.to_bitmask() as u16;
return mask; mask
}; };
let mut i = 0; let mut i = 0;

View File

@ -13,7 +13,7 @@ pub(crate) struct PidFd(FileDesc);
impl PidFd { impl PidFd {
pub fn kill(&self) -> io::Result<()> { pub fn kill(&self) -> io::Result<()> {
return cvt(unsafe { cvt(unsafe {
libc::syscall( libc::syscall(
libc::SYS_pidfd_send_signal, libc::SYS_pidfd_send_signal,
self.0.as_raw_fd(), self.0.as_raw_fd(),
@ -22,7 +22,7 @@ impl PidFd {
0, 0,
) )
}) })
.map(drop); .map(drop)
} }
pub fn wait(&self) -> io::Result<ExitStatus> { pub fn wait(&self) -> io::Result<ExitStatus> {
@ -30,7 +30,7 @@ impl PidFd {
cvt(unsafe { cvt(unsafe {
libc::waitid(libc::P_PIDFD, self.0.as_raw_fd() as u32, &mut siginfo, libc::WEXITED) 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>> { pub fn try_wait(&self) -> io::Result<Option<ExitStatus>> {
@ -45,9 +45,10 @@ impl PidFd {
) )
})?; })?;
if unsafe { siginfo.si_pid() } == 0 { 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 { pub fn new() -> OpenOptions {
let mut base = OpenOptions::default(); let mut base = OpenOptions::default();
base.dirflags = wasi::LOOKUPFLAGS_SYMLINK_FOLLOW; base.dirflags = wasi::LOOKUPFLAGS_SYMLINK_FOLLOW;
return base; base
} }
pub fn read(&mut self, read: bool) { pub fn read(&mut self, read: bool) {
@ -382,7 +382,7 @@ impl OpenOptions {
base |= wasi::RIGHTS_PATH_UNLINK_FILE; base |= wasi::RIGHTS_PATH_UNLINK_FILE;
base |= wasi::RIGHTS_POLL_FD_READWRITE; base |= wasi::RIGHTS_POLL_FD_READWRITE;
return base; base
} }
fn rights_inheriting(&self) -> wasi::Rights { 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); let len = mem::size_of_val(&ret);
wasi::random_get(base, len).expect("random_get failure"); wasi::random_get(base, len).expect("random_get failure");
} }
return ret; ret
} }
#[inline] #[inline]

View File

@ -1912,7 +1912,7 @@ impl Step for Assemble {
// delegates to the `rust-lld` binary for linking and then runs // delegates to the `rust-lld` binary for linking and then runs
// logic to create the final binary. This is used by the // logic to create the final binary. This is used by the
// `wasm32-wasip2` target of Rust. // `wasm32-wasip2` target of Rust.
if builder.build_wasm_component_ld() { if builder.tool_enabled("wasm-component-ld") {
let wasm_component_ld_exe = let wasm_component_ld_exe =
builder.ensure(crate::core::build_steps::tool::WasmComponentLd { builder.ensure(crate::core::build_steps::tool::WasmComponentLd {
compiler: build_compiler, 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 src_dir = builder.sysroot_libdir(compiler, host).parent().unwrap().join("bin");
let ld = exe("wasm-component-ld", compiler.host); let ld = exe("wasm-component-ld", compiler.host);
builder.copy_link(&src_dir.join(&ld), &dst_dir.join(&ld)); 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<'_> { fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
let builder = run.builder; let builder = run.builder;
run.path("src/tools/cargo").default_condition( run.path("src/tools/cargo").default_condition(builder.tool_enabled("cargo"))
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"),
),
)
} }
fn make_run(run: RunConfig<'_>) { fn make_run(run: RunConfig<'_>) {
@ -772,14 +765,7 @@ impl Step for RustAnalyzer {
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
let builder = run.builder; let builder = run.builder;
run.path("src/tools/rust-analyzer").default_condition( run.path("src/tools/rust-analyzer").default_condition(builder.tool_enabled("rust-analyzer"))
builder.config.extended
&& builder
.config
.tools
.as_ref()
.map_or(true, |tools| tools.iter().any(|tool| tool == "rust-analyzer")),
)
} }
fn make_run(run: RunConfig<'_>) { fn make_run(run: RunConfig<'_>) {
@ -821,12 +807,8 @@ impl Step for RustAnalyzerProcMacroSrv {
run.path("src/tools/rust-analyzer") run.path("src/tools/rust-analyzer")
.path("src/tools/rust-analyzer/crates/proc-macro-srv-cli") .path("src/tools/rust-analyzer/crates/proc-macro-srv-cli")
.default_condition( .default_condition(
builder.config.extended builder.tool_enabled("rust-analyzer")
&& builder.config.tools.as_ref().map_or(true, |tools| { || builder.tool_enabled("rust-analyzer-proc-macro-srv"),
tools.iter().any(|tool| {
tool == "rust-analyzer" || tool == "rust-analyzer-proc-macro-srv"
})
}),
) )
} }
@ -874,16 +856,8 @@ impl Step for LlvmBitcodeLinker {
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
let builder = run.builder; let builder = run.builder;
run.path("src/tools/llvm-bitcode-linker").default_condition( run.path("src/tools/llvm-bitcode-linker")
builder.config.extended .default_condition(builder.tool_enabled("llvm-bitcode-linker"))
&& builder
.config
.tools
.as_ref()
.map_or(builder.build.unstable_features(), |tools| {
tools.iter().any(|tool| tool == "llvm-bitcode-linker")
}),
)
} }
fn make_run(run: RunConfig<'_>) { fn make_run(run: RunConfig<'_>) {

View File

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

View File

@ -6,8 +6,8 @@ IFS=$'\n\t'
source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" source "$(cd "$(dirname "$0")" && pwd)/../shared.sh"
MINGW_ARCHIVE_32="i686-12.2.0-release-posix-dwarf-rt_v10-rev0.7z" MINGW_ARCHIVE_32="i686-14.1.0-release-posix-dwarf-msvcrt-rt_v12-rev0.7z"
MINGW_ARCHIVE_64="x86_64-12.2.0-release-posix-seh-rt_v10-rev0.7z" MINGW_ARCHIVE_64="x86_64-14.1.0-release-posix-seh-msvcrt-rt_v12-rev0.7z"
if isWindows && isKnownToBeMingwBuild; then if isWindows && isKnownToBeMingwBuild; then
case "${CI_JOB_NAME}" in 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-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-dragonfly` | ✓ | ✓ | 64-bit DragonFlyBSD
`x86_64-unknown-haiku` | ✓ | ✓ | 64-bit Haiku `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-hermit`](platform-support/hermit.md) | ✓ | | x86_64 Hermit
`x86_64-unknown-l4re-uclibc` | ? | | `x86_64-unknown-l4re-uclibc` | ? | |
[`x86_64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | 64-bit OpenBSD [`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** **Tier: 3**
@ -16,7 +16,8 @@ The GNU/Hurd target supports `std` and uses the standard ELF file format.
## Building the target ## 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 ## 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 ## Cross-compilation toolchains and C code
The target supports C code, the GNU toolchain calls the target 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 { match args {
AngleBracketed { args, constraints } => GenericArgs::AngleBracketed { AngleBracketed { args, constraints } => GenericArgs::AngleBracketed {
args: args.into_vec().into_tcx(tcx), args: args.into_vec().into_tcx(tcx),
bindings: constraints.into_tcx(tcx), constraints: constraints.into_tcx(tcx),
}, },
Parenthesized { inputs, output } => GenericArgs::Parenthesized { Parenthesized { inputs, output } => GenericArgs::Parenthesized {
inputs: inputs.into_vec().into_tcx(tcx), 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 { fn from_tcx(constraint: clean::AssocItemConstraint, tcx: TyCtxt<'_>) -> Self {
TypeBinding { AssocItemConstraint {
name: constraint.assoc.name.to_string(), name: constraint.assoc.name.to_string(),
args: constraint.assoc.args.into_tcx(tcx), args: constraint.assoc.args.into_tcx(tcx),
binding: constraint.kind.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 { fn from_tcx(kind: clean::AssocItemConstraintKind, tcx: TyCtxt<'_>) -> Self {
use clean::AssocItemConstraintKind::*; use clean::AssocItemConstraintKind::*;
match kind { match kind {
Equality { term } => TypeBindingKind::Equality(term.into_tcx(tcx)), Equality { term } => AssocItemConstraintKind::Equality(term.into_tcx(tcx)),
Bound { bounds } => TypeBindingKind::Constraint(bounds.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) => { ModuleItem(m) => {
ItemEnum::Module(Module { is_crate, items: ids(m.items, tcx), is_stripped: false }) 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)), StructItem(s) => ItemEnum::Struct(s.into_tcx(tcx)),
UnionItem(u) => ItemEnum::Union(u.into_tcx(tcx)), UnionItem(u) => ItemEnum::Union(u.into_tcx(tcx)),
StructFieldItem(f) => ItemEnum::StructField(f.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)), ImplItem(i) => ItemEnum::Impl((*i).into_tcx(tcx)),
StaticItem(s) => ItemEnum::Static(s.into_tcx(tcx)), StaticItem(s) => ItemEnum::Static(s.into_tcx(tcx)),
ForeignStaticItem(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)), TypeAliasItem(t) => ItemEnum::TypeAlias(t.into_tcx(tcx)),
// FIXME(generic_const_items): Add support for generic free consts // FIXME(generic_const_items): Add support for generic free consts
ConstantItem(ci) => { 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. // FIXME(generic_const_items): Add support for generic associated consts.
TyAssocConstItem(_generics, ty) => { 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. // FIXME(generic_const_items): Add support for generic associated consts.
AssocConstItem(ci) => { 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 { AssocTypeItem(t, b) => ItemEnum::AssocType {
generics: t.generics.into_tcx(tcx), generics: t.generics.into_tcx(tcx),
bounds: b.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. // `convert_item` early returns `None` for stripped items and keywords.
KeywordItem => unreachable!(), KeywordItem => unreachable!(),
@ -385,7 +383,7 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
impl FromWithTcx<clean::Struct> for Struct { impl FromWithTcx<clean::Struct> for Struct {
fn from_tcx(struct_: clean::Struct, tcx: TyCtxt<'_>) -> Self { 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 clean::Struct { ctor_kind, generics, fields } = struct_;
let kind = match ctor_kind { let kind = match ctor_kind {
@ -394,7 +392,7 @@ impl FromWithTcx<clean::Struct> for Struct {
assert!(fields.is_empty()); assert!(fields.is_empty());
StructKind::Unit StructKind::Unit
} }
None => StructKind::Plain { fields: ids(fields, tcx), fields_stripped }, None => StructKind::Plain { fields: ids(fields, tcx), has_stripped_fields },
}; };
Struct { Struct {
@ -407,22 +405,22 @@ impl FromWithTcx<clean::Struct> for Struct {
impl FromWithTcx<clean::Union> for Union { impl FromWithTcx<clean::Union> for Union {
fn from_tcx(union_: clean::Union, tcx: TyCtxt<'_>) -> Self { 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_; let clean::Union { generics, fields } = union_;
Union { Union {
generics: generics.into_tcx(tcx), generics: generics.into_tcx(tcx),
fields_stripped, has_stripped_fields,
fields: ids(fields, tcx), fields: ids(fields, tcx),
impls: Vec::new(), // Added in JsonRenderer::item impls: Vec::new(), // Added in JsonRenderer::item
} }
} }
} }
pub(crate) fn from_fn_header(header: &rustc_hir::FnHeader) -> Header { pub(crate) fn from_fn_header(header: &rustc_hir::FnHeader) -> FunctionHeader {
Header { FunctionHeader {
async_: header.is_async(), is_async: header.is_async(),
const_: header.is_const(), is_const: header.is_const(),
unsafe_: header.is_unsafe(), is_unsafe: header.is_unsafe(),
abi: convert_abi(header.abi), abi: convert_abi(header.abi),
} }
} }
@ -474,7 +472,7 @@ impl FromWithTcx<clean::GenericParamDefKind> for GenericParamDefKind {
Type { bounds, default, synthetic } => GenericParamDefKind::Type { Type { bounds, default, synthetic } => GenericParamDefKind::Type {
bounds: bounds.into_tcx(tcx), bounds: bounds.into_tcx(tcx),
default: default.map(|x| (*x).into_tcx(tcx)), default: default.map(|x| (*x).into_tcx(tcx)),
synthetic, is_synthetic: synthetic,
}, },
Const { ty, default, synthetic: _ } => GenericParamDefKind::Const { Const { ty, default, synthetic: _ } => GenericParamDefKind::Const {
type_: (*ty).into_tcx(tcx), type_: (*ty).into_tcx(tcx),
@ -508,7 +506,7 @@ impl FromWithTcx<clean::WherePredicate> for WherePredicate {
.map(|bound| bound.into_tcx(tcx)) .map(|bound| bound.into_tcx(tcx))
.collect(), .collect(),
default: default.map(|ty| (*ty).into_tcx(tcx)), default: default.map(|ty| (*ty).into_tcx(tcx)),
synthetic, is_synthetic: synthetic,
} }
} }
clean::GenericParamDefKind::Const { ty, default, 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)), ImplTrait(g) => Type::ImplTrait(g.into_tcx(tcx)),
Infer => Type::Infer, Infer => Type::Infer,
RawPointer(mutability, type_) => Type::RawPointer { RawPointer(mutability, type_) => Type::RawPointer {
mutable: mutability == ast::Mutability::Mut, is_mutable: mutability == ast::Mutability::Mut,
type_: Box::new((*type_).into_tcx(tcx)), type_: Box::new((*type_).into_tcx(tcx)),
}, },
BorrowedRef { lifetime, mutability, type_ } => Type::BorrowedRef { BorrowedRef { lifetime, mutability, type_ } => Type::BorrowedRef {
lifetime: lifetime.map(convert_lifetime), lifetime: lifetime.map(convert_lifetime),
mutable: mutability == ast::Mutability::Mut, is_mutable: mutability == ast::Mutability::Mut,
type_: Box::new((*type_).into_tcx(tcx)), type_: Box::new((*type_).into_tcx(tcx)),
}, },
QPath(box clean::QPathData { assoc, self_type, trait_, .. }) => Type::QualifiedPath { 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 { fn from_tcx(bare_decl: clean::BareFunctionDecl, tcx: TyCtxt<'_>) -> Self {
let clean::BareFunctionDecl { safety, generic_params, decl, abi } = bare_decl; let clean::BareFunctionDecl { safety, generic_params, decl, abi } = bare_decl;
FunctionPointer { FunctionPointer {
header: Header { header: FunctionHeader {
unsafe_: matches!(safety, rustc_hir::Safety::Unsafe), is_unsafe: matches!(safety, rustc_hir::Safety::Unsafe),
const_: false, is_const: false,
async_: false, is_async: false,
abi: convert_abi(abi), abi: convert_abi(abi),
}, },
generic_params: generic_params.into_tcx(tcx), 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 { fn from_tcx(decl: clean::FnDecl, tcx: TyCtxt<'_>) -> Self {
let clean::FnDecl { inputs, output, c_variadic } = decl; let clean::FnDecl { inputs, output, c_variadic } = decl;
FnDecl { FunctionSignature {
inputs: inputs inputs: inputs
.values .values
.into_iter() .into_iter()
.map(|arg| (arg.name.to_string(), arg.type_.into_tcx(tcx))) .map(|arg| (arg.name.to_string(), arg.type_.into_tcx(tcx)))
.collect(), .collect(),
output: if output.is_unit() { None } else { Some(output.into_tcx(tcx)) }, 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 provided_trait_methods = impl_.provided_trait_methods(tcx);
let clean::Impl { safety, generics, trait_, for_, items, polarity, kind } = impl_; let clean::Impl { safety, generics, trait_, for_, items, polarity, kind } = impl_;
// FIXME: use something like ImplKind in JSON? // 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::Normal | clean::ImplKind::FakeVariadic => (false, None),
clean::ImplKind::Auto => (true, None), clean::ImplKind::Auto => (true, None),
clean::ImplKind::Blanket(ty) => (false, Some(*ty)), 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::Positive | ty::ImplPolarity::Reservation => false,
ty::ImplPolarity::Negative => true, ty::ImplPolarity::Negative => true,
}; };
@ -721,8 +719,8 @@ impl FromWithTcx<clean::Impl> for Impl {
trait_: trait_.map(|path| path.into_tcx(tcx)), trait_: trait_.map(|path| path.into_tcx(tcx)),
for_: for_.into_tcx(tcx), for_: for_.into_tcx(tcx),
items: ids(items, tcx), items: ids(items, tcx),
negative: negative_polarity, is_negative,
synthetic, is_synthetic,
blanket_impl: blanket_impl.map(|x| x.into_tcx(tcx)), blanket_impl: blanket_impl.map(|x| x.into_tcx(tcx)),
} }
} }
@ -736,7 +734,7 @@ pub(crate) fn from_function(
) -> Function { ) -> Function {
let clean::Function { decl, generics } = *function; let clean::Function { decl, generics } = *function;
Function { Function {
decl: decl.into_tcx(tcx), sig: decl.into_tcx(tcx),
generics: generics.into_tcx(tcx), generics: generics.into_tcx(tcx),
header: from_fn_header(&header), header: from_fn_header(&header),
has_body, has_body,
@ -745,11 +743,11 @@ pub(crate) fn from_function(
impl FromWithTcx<clean::Enum> for Enum { impl FromWithTcx<clean::Enum> for Enum {
fn from_tcx(enum_: clean::Enum, tcx: TyCtxt<'_>) -> Self { 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_; let clean::Enum { variants, generics } = enum_;
Enum { Enum {
generics: generics.into_tcx(tcx), generics: generics.into_tcx(tcx),
variants_stripped, has_stripped_variants,
variants: ids(variants, tcx), variants: ids(variants, tcx),
impls: Vec::new(), // Added in JsonRenderer::item impls: Vec::new(), // Added in JsonRenderer::item
} }
@ -766,7 +764,7 @@ impl FromWithTcx<clean::Variant> for Variant {
CLike => VariantKind::Plain, CLike => VariantKind::Plain,
Tuple(fields) => VariantKind::Tuple(ids_keeping_stripped(fields, tcx)), Tuple(fields) => VariantKind::Tuple(ids_keeping_stripped(fields, tcx)),
Struct(s) => VariantKind::Struct { Struct(s) => VariantKind::Struct {
fields_stripped: s.has_stripped_entries(), has_stripped_fields: s.has_stripped_entries(),
fields: ids(s.fields, tcx), 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 { fn from_tcx(import: clean::Import, tcx: TyCtxt<'_>) -> Self {
use clean::ImportKind::*; use clean::ImportKind::*;
let (name, glob) = match import.kind { let (name, is_glob) = match import.kind {
Simple(s) => (s.to_string(), false), Simple(s) => (s.to_string(), false),
Glob => ( Glob => (
import.source.path.last_opt().unwrap_or_else(|| Symbol::intern("*")).to_string(), import.source.path.last_opt().unwrap_or_else(|| Symbol::intern("*")).to_string(),
true, true,
), ),
}; };
Import { Use {
source: import.source.path.whole_name(), source: import.source.path.whole_name(),
name, name,
id: import.source.did.map(ItemId::from).map(|i| id_from_item_default(i, tcx)), 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 { fn from_tcx(stat: clean::Static, tcx: TyCtxt<'_>) -> Self {
Static { Static {
type_: (*stat.type_).into_tcx(tcx), type_: (*stat.type_).into_tcx(tcx),
mutable: stat.mutability == ast::Mutability::Mut, is_mutable: stat.mutability == ast::Mutability::Mut,
expr: stat expr: stat
.expr .expr
.map(|e| rendered_const(tcx, tcx.hir().body(e), tcx.hir().body_owner_def_id(e))) .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 { match kind {
Module => ItemKind::Module, Module => ItemKind::Module,
ExternCrate => ItemKind::ExternCrate, ExternCrate => ItemKind::ExternCrate,
Import => ItemKind::Import, Import => ItemKind::Use,
Struct => ItemKind::Struct, Struct => ItemKind::Struct,
Union => ItemKind::Union, Union => ItemKind::Union,
Enum => ItemKind::Enum, Enum => ItemKind::Enum,
@ -872,7 +870,7 @@ impl FromWithTcx<ItemType> for ItemKind {
Primitive => ItemKind::Primitive, Primitive => ItemKind::Primitive,
AssocConst => ItemKind::AssocConst, AssocConst => ItemKind::AssocConst,
AssocType => ItemKind::AssocType, AssocType => ItemKind::AssocType,
ForeignType => ItemKind::ForeignType, ForeignType => ItemKind::ExternType,
Keyword => ItemKind::Keyword, Keyword => ItemKind::Keyword,
TraitAlias => ItemKind::TraitAlias, TraitAlias => ItemKind::TraitAlias,
ProcAttribute => ItemKind::ProcAttribute, ProcAttribute => ItemKind::ProcAttribute,

View File

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

View File

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

View File

@ -2,10 +2,11 @@ use std::collections::HashSet;
use std::hash::Hash; use std::hash::Hash;
use rustdoc_json_types::{ use rustdoc_json_types::{
Constant, Crate, DynTrait, Enum, FnDecl, Function, FunctionPointer, GenericArg, GenericArgs, AssocItemConstraint, AssocItemConstraintKind, Constant, Crate, DynTrait, Enum, Function,
GenericBound, GenericParamDef, Generics, Id, Impl, Import, ItemEnum, ItemSummary, Module, Path, FunctionPointer, FunctionSignature, GenericArg, GenericArgs, GenericBound, GenericParamDef,
Primitive, ProcMacro, Static, Struct, StructKind, Term, Trait, TraitAlias, Type, TypeAlias, Generics, Id, Impl, ItemEnum, ItemSummary, Module, Path, Primitive, ProcMacro, Static, Struct,
TypeBinding, TypeBindingKind, Union, Variant, VariantKind, WherePredicate, StructKind, Term, Trait, TraitAlias, Type, TypeAlias, Union, Use, Variant, VariantKind,
WherePredicate,
}; };
use serde_json::Value; use serde_json::Value;
@ -90,7 +91,7 @@ impl<'a> Validator<'a> {
item.links.values().for_each(|id| self.add_any_id(id)); item.links.values().for_each(|id| self.add_any_id(id));
match &item.inner { 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::Union(x) => self.check_union(x),
ItemEnum::Struct(x) => self.check_struct(x), ItemEnum::Struct(x) => self.check_struct(x),
ItemEnum::StructField(x) => self.check_struct_field(x), ItemEnum::StructField(x) => self.check_struct_field(x),
@ -106,18 +107,18 @@ impl<'a> Validator<'a> {
self.check_constant(const_); self.check_constant(const_);
} }
ItemEnum::Static(x) => self.check_static(x), ItemEnum::Static(x) => self.check_static(x),
ItemEnum::ForeignType => {} // nop ItemEnum::ExternType => {} // nop
ItemEnum::Macro(x) => self.check_macro(x), ItemEnum::Macro(x) => self.check_macro(x),
ItemEnum::ProcMacro(x) => self.check_proc_macro(x), ItemEnum::ProcMacro(x) => self.check_proc_macro(x),
ItemEnum::Primitive(x) => self.check_primitive_type(x), ItemEnum::Primitive(x) => self.check_primitive_type(x),
ItemEnum::Module(x) => self.check_module(x, id), ItemEnum::Module(x) => self.check_module(x, id),
// FIXME: Why don't these have their own structs? // FIXME: Why don't these have their own structs?
ItemEnum::ExternCrate { .. } => {} ItemEnum::ExternCrate { .. } => {}
ItemEnum::AssocConst { type_, default: _ } => self.check_type(type_), ItemEnum::AssocConst { type_, value: _ } => self.check_type(type_),
ItemEnum::AssocType { generics, bounds, default } => { ItemEnum::AssocType { generics, bounds, type_ } => {
self.check_generics(generics); self.check_generics(generics);
bounds.iter().for_each(|b| self.check_generic_bound(b)); bounds.iter().for_each(|b| self.check_generic_bound(b));
if let Some(ty) = default { if let Some(ty) = type_ {
self.check_type(ty); self.check_type(ty);
} }
} }
@ -133,8 +134,8 @@ impl<'a> Validator<'a> {
module.items.iter().for_each(|i| self.add_mod_item_id(i)); module.items.iter().for_each(|i| self.add_mod_item_id(i));
} }
fn check_import(&mut self, x: &'a Import) { fn check_use(&mut self, x: &'a Use) {
if x.glob { if x.is_glob {
self.add_glob_import_item_id(x.id.as_ref().unwrap()); self.add_glob_import_item_id(x.id.as_ref().unwrap());
} else if let Some(id) = &x.id { } else if let Some(id) = &x.id {
self.add_import_item_id(id); self.add_import_item_id(id);
@ -152,7 +153,7 @@ impl<'a> Validator<'a> {
match &x.kind { match &x.kind {
StructKind::Unit => {} StructKind::Unit => {}
StructKind::Tuple(fields) => fields.iter().flatten().for_each(|f| self.add_field_id(f)), 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)) fields.iter().for_each(|f| self.add_field_id(f))
} }
} }
@ -187,7 +188,7 @@ impl<'a> Validator<'a> {
match kind { match kind {
VariantKind::Plain => {} VariantKind::Plain => {}
VariantKind::Tuple(tys) => tys.iter().flatten().for_each(|t| self.add_field_id(t)), 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)) 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) { fn check_function(&mut self, x: &'a Function) {
self.check_generics(&x.generics); 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) { 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::Array { type_, len: _ } => self.check_type(&**type_),
Type::ImplTrait(bounds) => bounds.iter().for_each(|b| self.check_generic_bound(b)), Type::ImplTrait(bounds) => bounds.iter().for_each(|b| self.check_generic_bound(b)),
Type::Infer => {} Type::Infer => {}
Type::RawPointer { mutable: _, type_ } => self.check_type(&**type_), Type::RawPointer { is_mutable: _, type_ } => self.check_type(&**type_),
Type::BorrowedRef { lifetime: _, mutable: _, type_ } => self.check_type(&**type_), Type::BorrowedRef { lifetime: _, is_mutable: _, type_ } => self.check_type(&**type_),
Type::QualifiedPath { name: _, args, self_type, trait_ } => { Type::QualifiedPath { name: _, args, self_type, trait_ } => {
self.check_generic_args(&**args); self.check_generic_args(&**args);
self.check_type(&**self_type); 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)); x.inputs.iter().for_each(|(_name, ty)| self.check_type(ty));
if let Some(output) = &x.output { if let Some(output) = &x.output {
self.check_type(output); self.check_type(output);
@ -309,9 +310,9 @@ impl<'a> Validator<'a> {
fn check_generic_args(&mut self, x: &'a GenericArgs) { fn check_generic_args(&mut self, x: &'a GenericArgs) {
match x { match x {
GenericArgs::AngleBracketed { args, bindings } => { GenericArgs::AngleBracketed { args, constraints } => {
args.iter().for_each(|arg| self.check_generic_arg(arg)); 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 } => { GenericArgs::Parenthesized { inputs, output } => {
inputs.iter().for_each(|ty| self.check_type(ty)); 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) { fn check_generic_param_def(&mut self, gpd: &'a GenericParamDef) {
match &gpd.kind { match &gpd.kind {
rustdoc_json_types::GenericParamDefKind::Lifetime { outlives: _ } => {} 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)); bounds.iter().for_each(|b| self.check_generic_bound(b));
if let Some(ty) = default { if let Some(ty) = default {
self.check_type(ty); 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); self.check_generic_args(&bind.args);
match &bind.binding { match &bind.binding {
TypeBindingKind::Equality(term) => self.check_term(term), AssocItemConstraintKind::Equality(term) => self.check_term(term),
TypeBindingKind::Constraint(bounds) => { AssocItemConstraintKind::Constraint(bounds) => {
bounds.iter().for_each(|b| self.check_generic_bound(b)) 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) { 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)); 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] [target.'cfg(unix)'.dependencies]
libc = "0.2" libc = "0.2"
[target.'cfg(target_os = "linux")'.dependencies]
libffi = "3.2.0" libffi = "3.2.0"
libloading = "0.8" 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. file descriptors will be mixed up.
This is **work in progress**; currently, only integer arguments and return values are 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; 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. * `-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. 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 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 :: If any other steps are added, the "|| exit /b" must be appended to early
:: return from the script. If not, it will continue execution. :: 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 ^ 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 'stable' toolchain installed? & exit /b) || (echo Failed to build miri-script. Is the 'nightly' toolchain installed? & exit /b)
:: Forwards all arguments to this file to the executable. :: Forwards all arguments to this file to the executable.
:: We invoke the binary directly to avoid going through rustup, which would set some extra :: 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::cell::RefCell;
use std::cmp::max; use std::cmp::max;
use std::collections::hash_map::Entry;
use rand::Rng; use rand::Rng;
@ -151,6 +150,95 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
} }
} }
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 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
// adjusting `tcx` pointers (which never get freed). So assert that we are looking
// at a live allocation. This also ensures that we never re-assign an address to an
// allocation that previously had an address, but then was freed and the address
// information was removed.
assert!(!matches!(kind, AllocKind::Dead));
// This allocation does not have a base address yet, pick or reuse one.
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.
let base_ptr = match kind {
AllocKind::LiveData => {
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(|| {
panic!("Miri ran out of memory: cannot create allocation of {size:?} bytes")
});
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();
ptr
} else {
ecx.get_alloc_bytes_unchecked_raw(alloc_id)?
}
}
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());
let ptr = alloc_bytes.as_ptr();
// Leak the underlying memory to ensure it remains unique.
std::mem::forget(alloc_bytes);
ptr
}
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());
}
// 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);
}
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.
// We ensure that `(global_state.next_base_addr + slack) % 16` is uniformly distributed.
let slack = rng.gen_range(0..16);
// From next_base_addr + slack, round up to adjust for alignment.
let base_addr = global_state
.next_base_addr
.checked_add(slack)
.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!)
global_state.next_base_addr = base_addr
.checked_add(max(size.bytes(), 1))
.ok_or_else(|| err_exhaust!(AddressSpaceFull))?;
// Even if `Size` didn't overflow, we might still have filled up the address space.
if global_state.next_base_addr > ecx.target_usize_max() {
throw_exhaust!(AddressSpaceFull);
}
Ok(base_addr)
}
}
fn addr_from_alloc_id( fn addr_from_alloc_id(
&self, &self,
alloc_id: AllocId, alloc_id: AllocId,
@ -160,98 +248,16 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
let mut global_state = ecx.machine.alloc_addresses.borrow_mut(); let mut global_state = ecx.machine.alloc_addresses.borrow_mut();
let global_state = &mut *global_state; let global_state = &mut *global_state;
Ok(match global_state.base_addr.entry(alloc_id) { match global_state.base_addr.get(&alloc_id) {
Entry::Occupied(entry) => *entry.get(), Some(&addr) => Ok(addr),
Entry::Vacant(entry) => { None => {
let mut rng = ecx.machine.rng.borrow_mut(); // First time we're looking for the absolute address of this allocation.
let (size, align, kind) = ecx.get_alloc_info(alloc_id); let base_addr =
// This is either called immediately after allocation (and then cached), or when self.addr_from_alloc_id_uncached(global_state, alloc_id, memory_kind)?;
// adjusting `tcx` pointers (which never get freed). So assert that we are looking trace!("Assigning base address {:#x} to allocation {:?}", base_addr, alloc_id);
// at a live allocation. This also ensures that we never re-assign an address to an
// allocation that previously had an address, but then was freed and the address
// information was removed.
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() {
// 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 {
AllocKind::LiveData => {
let ptr = 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(|| {
panic!("Miri ran out of memory: cannot create allocation of {size:?} bytes")
});
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();
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();
// Leak the underlying memory to ensure it remains unique.
std::mem::forget(alloc_bytes);
addr
}
AllocKind::Dead => unreachable!()
}
} else 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
} else {
// We have to pick a fresh address.
// Leave some space to the previous allocation, to give it some chance to be less aligned.
// We ensure that `(global_state.next_base_addr + slack) % 16` is uniformly distributed.
let slack = rng.gen_range(0..16);
// From next_base_addr + slack, round up to adjust for alignment.
let base_addr = global_state
.next_base_addr
.checked_add(slack)
.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!)
global_state.next_base_addr = base_addr
.checked_add(max(size.bytes(), 1))
.ok_or_else(|| err_exhaust!(AddressSpaceFull))?;
// Even if `Size` didn't overflow, we might still have filled up the address space.
if global_state.next_base_addr > ecx.target_usize_max() {
throw_exhaust!(AddressSpaceFull);
}
base_addr
};
trace!(
"Assigning base address {:#x} to allocation {:?} (size: {}, align: {})",
base_addr,
alloc_id,
size.bytes(),
align.bytes(),
);
// Store address in cache. // 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. // 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. // 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)); 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 // 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. // 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(); 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`. // 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)?; 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 // 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 // `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`. // 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 let mut prepared_alloc_bytes = global_state
.prepared_alloc_bytes .prepared_alloc_bytes
.remove(&id) .remove(&id)
@ -378,10 +391,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
assert_eq!(prepared_alloc_bytes.len(), bytes.len()); assert_eq!(prepared_alloc_bytes.len(), bytes.len());
// Copy allocation contents into prepared memory. // Copy allocation contents into prepared memory.
prepared_alloc_bytes.copy_from_slice(bytes); prepared_alloc_bytes.copy_from_slice(bytes);
prepared_alloc_bytes Ok(prepared_alloc_bytes)
} else { } 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 /// When a pointer is used for a memory access, this computes where in which allocation the

View File

@ -35,8 +35,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
offset: u64, offset: u64,
) -> InterpResult<'tcx, InitOnceId> { ) -> InterpResult<'tcx, InitOnceId> {
let this = self.eval_context_mut(); 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(
.ok_or_else(|| err_ub_format!("init_once has invalid ID").into()) 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())
} }
#[inline] #[inline]

View File

@ -66,6 +66,27 @@ pub(super) use declare_id;
declare_id!(MutexId); 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. /// The mutex state.
#[derive(Default, Debug)] #[derive(Default, Debug)]
struct Mutex { struct Mutex {
@ -77,6 +98,9 @@ struct Mutex {
queue: VecDeque<ThreadId>, queue: VecDeque<ThreadId>,
/// Mutex clock. This tracks the moment of the last unlock. /// Mutex clock. This tracks the moment of the last unlock.
clock: VClock, clock: VClock,
/// Additional data that can be set by shim implementations.
data: Option<AdditionalMutexData>,
} }
declare_id!(RwLockId); 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. /// Returns `None` if memory stores a non-zero invalid ID.
/// ///
/// `get_objs` must return the `IndexVec` that stores all the objects of this type. /// `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] #[inline]
fn get_or_create_id<Id: SyncId + Idx, T: Default>( fn get_or_create_id<Id: SyncId + Idx, T>(
&mut self, &mut self,
lock_op: &OpTy<'tcx>, lock_op: &OpTy<'tcx>,
lock_layout: TyAndLayout<'tcx>, lock_layout: TyAndLayout<'tcx>,
offset: u64, offset: u64,
get_objs: impl for<'a> Fn(&'a mut MiriInterpCx<'tcx>) -> &'a mut IndexVec<Id, T>, 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>> { ) -> InterpResult<'tcx, Option<Id>> {
let this = self.eval_context_mut(); let this = self.eval_context_mut();
let value_place = 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") { 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 // We set the in-memory ID to `next_index`, now also create this object in the machine
// state. // 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); assert_eq!(next_index, new_index);
Some(new_index) Some(new_index)
} else { } 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( fn condvar_reacquire_mutex(
&mut self, &mut self,
mutex: MutexId, mutex: MutexId,
@ -236,15 +289,53 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// situations. // situations.
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'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( fn mutex_get_or_create_id(
&mut self, &mut self,
lock_op: &OpTy<'tcx>, lock_op: &OpTy<'tcx>,
lock_layout: TyAndLayout<'tcx>, lock_layout: TyAndLayout<'tcx>,
offset: u64, offset: u64,
initialize_data: impl for<'a> FnOnce(
&'a mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, Option<AdditionalMutexData>>,
) -> InterpResult<'tcx, MutexId> { ) -> InterpResult<'tcx, MutexId> {
let this = self.eval_context_mut(); 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(
.ok_or_else(|| err_ub_format!("mutex has invalid ID").into()) 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( fn rwlock_get_or_create_id(
@ -254,8 +345,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
offset: u64, offset: u64,
) -> InterpResult<'tcx, RwLockId> { ) -> InterpResult<'tcx, RwLockId> {
let this = self.eval_context_mut(); 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(
.ok_or_else(|| err_ub_format!("rwlock has invalid ID").into()) 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())
} }
fn condvar_get_or_create_id( fn condvar_get_or_create_id(
@ -265,8 +362,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
offset: u64, offset: u64,
) -> InterpResult<'tcx, CondvarId> { ) -> InterpResult<'tcx, CondvarId> {
let this = self.eval_context_mut(); 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(
.ok_or_else(|| err_ub_format!("condvar has invalid ID").into()) 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())
} }
#[inline] #[inline]

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))?; let alloc = this.ctfe_query(|tcx| tcx.eval_static_initializer(def_id))?;
// We make a full copy of this allocation. // We make a full copy of this allocation.
let mut alloc = let mut alloc = alloc.inner().adjust_from_tcx(
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.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. // This allocation will be deallocated when the thread dies, so it is not in read-only memory.
alloc.mutability = Mutability::Mut; alloc.mutability = Mutability::Mut;
// Create a fresh allocation with this content. // Create a fresh allocation with this content.

View File

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

View File

@ -535,9 +535,9 @@ pub struct MiriMachine<'tcx> {
pub(crate) basic_block_count: u64, pub(crate) basic_block_count: u64,
/// Handle of the optional shared object file for native functions. /// 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)>, pub native_lib: Option<(libloading::Library, std::path::PathBuf)>,
#[cfg(not(target_os = "linux"))] #[cfg(not(unix))]
pub native_lib: Option<!>, pub native_lib: Option<!>,
/// Run a garbage collector for BorTags every N basic blocks. /// Run a garbage collector for BorTags every N basic blocks.
@ -678,7 +678,7 @@ impl<'tcx> MiriMachine<'tcx> {
report_progress: config.report_progress, report_progress: config.report_progress,
basic_block_count: 0, basic_block_count: 0,
clock: Clock::new(config.isolated_op == IsolatedOp::Allow), 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| { native_lib: config.native_lib.as_ref().map(|lib_file_path| {
let target_triple = layout_cx.tcx.sess.opts.target_triple.triple(); let target_triple = layout_cx.tcx.sess.opts.target_triple.triple();
// Check if host target == the session target. // Check if host target == the session target.
@ -700,9 +700,9 @@ impl<'tcx> MiriMachine<'tcx> {
lib_file_path.clone(), lib_file_path.clone(),
) )
}), }),
#[cfg(not(target_os = "linux"))] #[cfg(not(unix))]
native_lib: config.native_lib.as_ref().map(|_| { 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, gc_interval: config.gc_interval,
since_gc: 0, 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>>> ) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra, Self::Bytes>>>
{ {
let kind = Self::GLOBAL_KIND.unwrap().into(); 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), |bytes, align| ecx.get_global_alloc_bytes(id, kind, bytes, align),
|ptr| ecx.global_root_pointer(ptr), |ptr| ecx.global_root_pointer(ptr),
)?; )?;
let extra = let extra = Self::init_alloc_extra(ecx, id, kind, alloc.size(), alloc.align)?;
Self::init_alloc_extra(ecx, id, kind, alloc.size(), alloc.align)?;
Ok(Cow::Owned(alloc.with_extra(extra))) 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(); let this = self.eval_context_mut();
// First deal with any external C functions in linked .so file. // 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() { if this.machine.native_lib.as_ref().is_some() {
use crate::shims::native_lib::EvalContextExt as _; use crate::shims::native_lib::EvalContextExt as _;
// An Ok(false) here means that the function being called was not exported // An Ok(false) here means that the function being called was not exported

View File

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

View File

@ -240,13 +240,16 @@ fn imm_to_carg<'tcx>(v: ImmTy<'tcx>, cx: &impl HasDataLayout) -> InterpResult<'t
ty::RawPtr(_, mutability) => { ty::RawPtr(_, mutability) => {
// Arbitrary mutable pointer accesses are not currently supported in Miri. // Arbitrary mutable pointer accesses are not currently supported in Miri.
if mutability.is_mut() { 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 { } else {
let s = v.to_scalar().to_pointer(cx)?.addr(); let s = v.to_scalar().to_pointer(cx)?.addr();
// This relies on the `expose_provenance` in `addr_from_alloc_id`. // This relies on the `expose_provenance` in `addr_from_alloc_id`.
CArg::RawPtr(std::ptr::with_exposed_provenance_mut(s.bytes_usize())) CArg::RawPtr(std::ptr::with_exposed_provenance_mut(s.bytes_usize()))
} }
}, }
_ => throw_unsup_format!("unsupported argument type for native call: {}", v.layout.ty), _ => 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 // 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 // must be assumed to be opaque. Therefore, we can just store our
// internal mutex ID in the structure without anyone noticing. // 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. // pthread_mutex_t is between 24 and 48 bytes, depending on the platform.
// We ignore the platform layout and store our own fields: // We ignore the platform layout and store our own fields:
// - id: u32 // - id: u32
// - kind: i32
fn mutex_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { fn mutex_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
let offset = match &*ecx.tcx.sess.target.os { 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): // Sanity-check this against PTHREAD_MUTEX_INITIALIZER (but only once):
// the id must start out as 0. // 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); static SANITY: AtomicBool = AtomicBool::new(false);
if !SANITY.swap(true, Ordering::Relaxed) { if !SANITY.swap(true, Ordering::Relaxed) {
let static_initializer = ecx.eval_path(&["libc", "PTHREAD_MUTEX_INITIALIZER"]); 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) Ok(offset)
} }
fn mutex_kind_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> u64 { /// Eagerly create and initialize a new mutex.
// These offsets are picked for compatibility with Linux's static initializer fn mutex_create<'tcx>(
// macros, e.g. PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP.) ecx: &mut MiriInterpCx<'tcx>,
let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 }; mutex_op: &OpTy<'tcx>,
kind: i32,
// Sanity-check this against PTHREAD_MUTEX_INITIALIZER (but only once): ) -> InterpResult<'tcx> {
// the kind must start out as PTHREAD_MUTEX_DEFAULT. // FIXME: might be worth changing mutex_create to take the mplace
static SANITY: AtomicBool = AtomicBool::new(false); // rather than the `OpTy`.
if !SANITY.swap(true, Ordering::Relaxed) { let address = ecx.read_pointer(mutex_op)?.addr().bytes();
let static_initializer = ecx.eval_path(&["libc", "PTHREAD_MUTEX_INITIALIZER"]); let kind = translate_kind(ecx, kind)?;
let kind_field = static_initializer let data = Some(AdditionalMutexData { address, kind });
.offset(Size::from_bytes(mutex_kind_offset(ecx)), ecx.machine.layouts.i32, ecx) ecx.mutex_create(mutex_op, ecx.libc_ty_layout("pthread_mutex_t"), mutex_id_offset(ecx)?, data)?;
.unwrap(); Ok(())
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
} }
/// 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>( fn mutex_get_id<'tcx>(
ecx: &mut MiriInterpCx<'tcx>, ecx: &mut MiriInterpCx<'tcx>,
mutex_op: &OpTy<'tcx>, mutex_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, MutexId> { ) -> 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, mutex_op,
ecx.libc_ty_layout("pthread_mutex_t"), ecx.libc_ty_layout("pthread_mutex_t"),
mutex_id_offset(ecx)?, 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>( /// Returns the kind of a static initializer.
ecx: &mut MiriInterpCx<'tcx>, fn kind_from_static_initializer<'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>(
ecx: &MiriInterpCx<'tcx>, ecx: &MiriInterpCx<'tcx>,
mutex_op: &OpTy<'tcx>, mutex_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, i32> { ) -> InterpResult<'tcx, MutexKind> {
ecx.deref_pointer_and_read( // Only linux has static initializers other than PTHREAD_MUTEX_DEFAULT.
mutex_op, let kind = match &*ecx.tcx.sess.target.os {
mutex_kind_offset(ecx), "linux" => {
ecx.libc_ty_layout("pthread_mutex_t"), let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 };
ecx.machine.layouts.i32,
)? ecx.deref_pointer_and_read(
.to_i32() mutex_op,
offset,
ecx.libc_ty_layout("pthread_mutex_t"),
ecx.machine.layouts.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>( fn translate_kind<'tcx>(ecx: &MiriInterpCx<'tcx>, kind: i32) -> InterpResult<'tcx, MutexKind> {
ecx: &mut MiriInterpCx<'tcx>, Ok(if is_mutex_kind_default(ecx, kind)? {
mutex_op: &OpTy<'tcx>, MutexKind::Default
kind: i32, } else if is_mutex_kind_normal(ecx, kind)? {
) -> InterpResult<'tcx, ()> { MutexKind::Normal
ecx.deref_pointer_and_write( } else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK") {
mutex_op, MutexKind::ErrorCheck
mutex_kind_offset(ecx), } else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE") {
Scalar::from_i32(kind), MutexKind::Recursive
ecx.libc_ty_layout("pthread_mutex_t"), } else {
ecx.machine.layouts.i32, throw_unsup_format!("unsupported type of mutex: {kind}");
) })
} }
// pthread_rwlock_t is between 32 and 56 bytes, depending on the platform. // 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)? mutexattr_get_kind(this, attr_op)?
}; };
// Write 0 to use the same code path as the static initializers. mutex_create(this, mutex_op, kind)?;
mutex_reset_id(this, mutex_op)?;
mutex_set_kind(this, mutex_op, kind)?;
Ok(()) Ok(())
} }
@ -467,8 +478,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
) -> InterpResult<'tcx> { ) -> InterpResult<'tcx> {
let this = self.eval_context_mut(); let this = self.eval_context_mut();
let kind = mutex_get_kind(this, mutex_op)?;
let id = mutex_get_id(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 ret = if this.mutex_is_locked(id) {
let owner_thread = this.mutex_get_owner(id); let owner_thread = this.mutex_get_owner(id);
@ -477,19 +489,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
return Ok(()); return Ok(());
} else { } else {
// Trying to acquire the same mutex again. // Trying to acquire the same mutex again.
if is_mutex_kind_default(this, kind)? { match kind {
throw_ub_format!("trying to acquire already locked default mutex"); MutexKind::Default =>
} else if is_mutex_kind_normal(this, kind)? { throw_ub_format!("trying to acquire already locked default mutex"),
throw_machine_stop!(TerminationInfo::Deadlock); MutexKind::Normal => throw_machine_stop!(TerminationInfo::Deadlock),
} else if kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK") { MutexKind::ErrorCheck => this.eval_libc_i32("EDEADLK"),
this.eval_libc_i32("EDEADLK") MutexKind::Recursive => {
} else if kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE") { this.mutex_lock(id);
this.mutex_lock(id); 0
0 }
} else { _ =>
throw_unsup_format!( throw_unsup_format!(
"called pthread_mutex_lock on an unsupported type of mutex" "called pthread_mutex_lock on an unsupported type of mutex"
); ),
} }
} }
} else { } 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> { fn pthread_mutex_trylock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut(); let this = self.eval_context_mut();
let kind = mutex_get_kind(this, mutex_op)?;
let id = mutex_get_id(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) { Ok(Scalar::from_i32(if this.mutex_is_locked(id) {
let owner_thread = this.mutex_get_owner(id); let owner_thread = this.mutex_get_owner(id);
if owner_thread != this.active_thread() { if owner_thread != this.active_thread() {
this.eval_libc_i32("EBUSY") this.eval_libc_i32("EBUSY")
} else { } else {
if is_mutex_kind_default(this, kind)? match kind {
|| is_mutex_kind_normal(this, kind)? MutexKind::Default | MutexKind::Normal | MutexKind::ErrorCheck =>
|| kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK") this.eval_libc_i32("EBUSY"),
{ MutexKind::Recursive => {
this.eval_libc_i32("EBUSY") this.mutex_lock(id);
} else if kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE") { 0
this.mutex_lock(id); }
0 _ =>
} else { throw_unsup_format!(
throw_unsup_format!( "called pthread_mutex_trylock on an unsupported type of mutex"
"called pthread_mutex_trylock on an unsupported type of mutex" ),
);
} }
} }
} else { } 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> { fn pthread_mutex_unlock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut(); let this = self.eval_context_mut();
let kind = mutex_get_kind(this, mutex_op)?;
let id = mutex_get_id(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)? { if let Some(_old_locked_count) = this.mutex_unlock(id)? {
// The mutex was locked by the current thread. // 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 mutex was locked by another thread or not locked at all. See
// the “Unlock When Not Owner” column in // the “Unlock When Not Owner” column in
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_unlock.html. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_unlock.html.
if is_mutex_kind_default(this, kind)? { match kind {
throw_ub_format!( MutexKind::Default =>
"unlocked a default mutex that was not locked by the current thread" throw_ub_format!(
); "unlocked a default mutex that was not locked by the current thread"
} else if is_mutex_kind_normal(this, kind)? { ),
throw_ub_format!( MutexKind::Normal =>
"unlocked a PTHREAD_MUTEX_NORMAL mutex that was not locked by the current thread" 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") MutexKind::ErrorCheck | MutexKind::Recursive =>
{ Ok(Scalar::from_i32(this.eval_libc_i32("EPERM"))),
Ok(Scalar::from_i32(this.eval_libc_i32("EPERM"))) _ =>
} else { throw_unsup_format!(
throw_unsup_format!("called pthread_mutex_unlock on an unsupported type of mutex"); "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. // 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)?; mutex_get_id(this, mutex_op)?;
// This might lead to false positives, see comment in pthread_mutexattr_destroy // 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 //@only-on-host
//@normalize-stderr-test: "OS `.*`" -> "$$OS" //@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 //@only-on-host
fn main() { fn main() {
@ -26,7 +28,7 @@ fn test_pointer() {
fn test_simple() { fn test_simple() {
#[repr(C)] #[repr(C)]
struct Simple { struct Simple {
field: i32 field: i32,
} }
extern "C" { extern "C" {
@ -41,7 +43,7 @@ fn test_simple() {
// Test function that dereferences nested struct pointers and accesses fields. // Test function that dereferences nested struct pointers and accesses fields.
fn test_nested() { fn test_nested() {
use std::ptr::NonNull; use std::ptr::NonNull;
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
#[repr(C)] #[repr(C)]
struct Nested { struct Nested {
@ -62,7 +64,6 @@ fn test_nested() {
// Test function that dereferences static struct pointers and accesses fields. // Test function that dereferences static struct pointers and accesses fields.
fn test_static() { fn test_static() {
#[repr(C)] #[repr(C)]
struct Static { struct Static {
value: i32, value: i32,
@ -72,11 +73,8 @@ fn test_static() {
extern "C" { extern "C" {
fn access_static(n_ptr: *const Static) -> i32; fn access_static(n_ptr: *const Static) -> i32;
} }
static STATIC: Static = Static { static STATIC: Static = Static { value: 9001, recurse: &STATIC };
value: 9001,
recurse: &STATIC,
};
assert_eq!(unsafe { access_static(&STATIC) }, 9001); 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 //@only-on-host
extern "C" { extern "C" {

View File

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

View File

@ -1,27 +1,35 @@
#include <stdio.h> #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; return 2 + x;
} }
void printer() { EXPORT void printer(void) {
printf("printing from C\n"); printf("printing from C\n");
} }
// function with many arguments, to test functionality when some args are stored // function with many arguments, to test functionality when some args are stored
// on the stack // 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; 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; return -10;
} }
short add_int16(short x) { EXPORT short add_int16(short x) {
return x + 3; 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; 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. // Create the directory if it does not already exist.
std::fs::create_dir_all(&so_target_dir) std::fs::create_dir_all(&so_target_dir)
.expect("Failed to create directory for shared object file"); .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) let cc_output = Command::new(cc)
.args([ .args([
"-shared", "-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", "-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. // FIXME: Automate gathering of all relevant C source files in the directory.
"tests/native-lib/scalar_arguments.c", "tests/native-lib/scalar_arguments.c",
"tests/native-lib/ptr_read_access.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. // Ensure we notice serious problems in the C code.
"-Wall", "-Wall",
"-Wextra", "-Wextra",
@ -64,7 +65,7 @@ fn build_native_lib() -> PathBuf {
String::from_utf8_lossy(&cc_output.stderr), 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 /// 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, WithDependencies,
tmpdir.path(), tmpdir.path(),
)?; )?;
if cfg!(target_os = "linux") { if cfg!(unix) {
ui(Mode::Pass, "tests/native-lib/pass", &target, WithoutDependencies, tmpdir.path())?; ui(Mode::Pass, "tests/native-lib/pass", &target, WithoutDependencies, tmpdir.path())?;
ui( ui(
Mode::Fail { require_patterns: true, rustfix: RustfixMode::Disabled }, Mode::Fail { require_patterns: true, rustfix: RustfixMode::Disabled },

View File

@ -110,6 +110,13 @@ pub struct LlvmDwarfdump {
cmd: Command, 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!(LlvmReadobj);
crate::macros::impl_common_helpers!(LlvmProfdata); crate::macros::impl_common_helpers!(LlvmProfdata);
crate::macros::impl_common_helpers!(LlvmFilecheck); 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!(LlvmNm);
crate::macros::impl_common_helpers!(LlvmBcanalyzer); crate::macros::impl_common_helpers!(LlvmBcanalyzer);
crate::macros::impl_common_helpers!(LlvmDwarfdump); crate::macros::impl_common_helpers!(LlvmDwarfdump);
crate::macros::impl_common_helpers!(LlvmPdbutil);
/// Generate the path to the bin directory of LLVM. /// Generate the path to the bin directory of LLVM.
#[must_use] #[must_use]
@ -360,3 +368,19 @@ impl LlvmDwarfdump {
self 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}; pub use run::{cmd, run, run_fail, run_with_args};
/// Helpers for checking target information. /// 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. /// Helpers for building names of output artifacts that are potentially target-specific.
pub use artifact_names::{ pub use artifact_names::{

View File

@ -28,6 +28,24 @@ pub fn is_darwin() -> bool {
target().contains("darwin") 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` /// Check if `component` is within `LLVM_COMPONENTS`
#[must_use] #[must_use]
pub fn llvm_components_contain(component: &str) -> bool { 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/issue-84395-lto-embed-bitcode/Makefile
run-make/jobserver-error/Makefile run-make/jobserver-error/Makefile
run-make/libs-through-symlinks/Makefile run-make/libs-through-symlinks/Makefile
run-make/macos-deployment-target/Makefile
run-make/split-debuginfo/Makefile run-make/split-debuginfo/Makefile
run-make/symbol-mangling-hashed/Makefile run-make/symbol-mangling-hashed/Makefile
run-make/translation/Makefile run-make/translation/Makefile

View File

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

View File

@ -552,6 +552,9 @@
//@ revisions: x86_64_unknown_haiku //@ revisions: x86_64_unknown_haiku
//@ [x86_64_unknown_haiku] compile-flags: --target x86_64-unknown-haiku //@ [x86_64_unknown_haiku] compile-flags: --target x86_64-unknown-haiku
//@ [x86_64_unknown_haiku] needs-llvm-components: x86 //@ [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 //@ revisions: x86_64_unknown_hermit
//@ [x86_64_unknown_hermit] compile-flags: --target x86_64-unknown-hermit //@ [x86_64_unknown_hermit] compile-flags: --target x86_64-unknown-hermit
//@ [x86_64_unknown_hermit] needs-llvm-components: x86 //@ [x86_64_unknown_hermit] needs-llvm-components: x86

View File

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

View File

@ -3,6 +3,8 @@
//@ compile-flags:-g //@ compile-flags:-g
//@ ignore-windows-gnu: #128973
// === GDB TESTS =================================================================================== // === GDB TESTS ===================================================================================
// gdb-command:run // 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 { pub trait EasyToImpl {
//@ has "$.index[*][?(@.docs=='ToDeclare trait')].inner.assoc_type" //@ 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" [] //@ is "$.index[*][?(@.docs=='ToDeclare trait')].inner.assoc_type.bounds" []
/// ToDeclare trait /// ToDeclare trait
type ToDeclare; type ToDeclare;
//@ has "$.index[*][?(@.docs=='AN_ATTRIBUTE trait')].inner.assoc_const" //@ 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"' //@ is "$.index[*][?(@.docs=='AN_ATTRIBUTE trait')].inner.assoc_const.type.primitive" '"usize"'
/// AN_ATTRIBUTE trait /// AN_ATTRIBUTE trait
const AN_ATTRIBUTE: usize; const AN_ATTRIBUTE: usize;
@ -22,13 +22,13 @@ pub trait EasyToImpl {
impl EasyToImpl for Simple { impl EasyToImpl for Simple {
//@ has "$.index[*][?(@.docs=='ToDeclare impl')].inner.assoc_type" //@ 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 /// ToDeclare impl
type ToDeclare = usize; type ToDeclare = usize;
//@ has "$.index[*][?(@.docs=='AN_ATTRIBUTE impl')].inner.assoc_const" //@ 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.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 /// AN_ATTRIBUTE impl
const AN_ATTRIBUTE: usize = 12; const AN_ATTRIBUTE: usize = 12;
} }

View File

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

View File

@ -5,7 +5,7 @@ pub enum Foo {
//@ is "$.index[*][?(@.name=='Unit')].inner.variant.kind" '"plain"' //@ is "$.index[*][?(@.name=='Unit')].inner.variant.kind" '"plain"'
Unit, Unit,
//@ set Named = "$.index[*][?(@.name=='Named')].id" //@ 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 {}, Named {},
//@ set Tuple = "$.index[*][?(@.name=='Tuple')].id" //@ set Tuple = "$.index[*][?(@.name=='Tuple')].id"
//@ is "$.index[*][?(@.name=='Tuple')].inner.variant.kind.tuple" [] //@ is "$.index[*][?(@.name=='Tuple')].inner.variant.kind.tuple" []
@ -13,7 +13,7 @@ pub enum Foo {
//@ set NamedField = "$.index[*][?(@.name=='NamedField')].id" //@ set NamedField = "$.index[*][?(@.name=='NamedField')].id"
//@ set x = "$.index[*][?(@.name=='x' && @.inner.struct_field)].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[*]" $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 }, NamedField { x: i32 },
//@ set TupleField = "$.index[*][?(@.name=='TupleField')].id" //@ set TupleField = "$.index[*][?(@.name=='TupleField')].id"
//@ set tup_field = "$.index[*][?(@.name=='0' && @.inner.struct_field)].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" //@ set y = "$.index[*][?(@.name=='y')].id"
y: i32, 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[0]" $b
//@ is "$.index[*][?(@.name=='Variant')].inner.variant.kind.struct.fields[1]" $y //@ is "$.index[*][?(@.name=='Variant')].inner.variant.kind.struct.fields[1]" $y
//@ count "$.index[*][?(@.name=='Variant')].inner.variant.kind.struct.fields[*]" 2 //@ count "$.index[*][?(@.name=='Variant')].inner.variant.kind.struct.fields[*]" 2

View File

@ -7,9 +7,9 @@ pub enum Color {
Blue, Blue,
} }
//@ set use_Color = "$.index[*][?(@.inner.import)].id" //@ set use_Color = "$.index[*][?(@.inner.use)].id"
//@ is "$.index[*][?(@.inner.import)].inner.import.id" $Color //@ is "$.index[*][?(@.inner.use)].inner.use.id" $Color
//@ is "$.index[*][?(@.inner.import)].inner.import.glob" true //@ is "$.index[*][?(@.inner.use)].inner.use.is_glob" true
pub use Color::*; pub use Color::*;
//@ ismany "$.index[*][?(@.name == 'use_glob')].inner.module.items[*]" $Color $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 //@ is "$.index[*][?(@.name == 'AlwaysNone')].inner.enum.variants[*]" $None
//@ set use_None = "$.index[*][?(@.inner.import)].id" //@ set use_None = "$.index[*][?(@.inner.use)].id"
//@ is "$.index[*][?(@.inner.import)].inner.import.id" $None //@ is "$.index[*][?(@.inner.use)].inner.use.id" $None
pub use AlwaysNone::None; pub use AlwaysNone::None;
//@ ismany "$.index[*][?(@.name == 'use_variant')].inner.module.items[*]" $AlwaysNone $use_None //@ ismany "$.index[*][?(@.name == 'use_variant')].inner.module.items[*]" $AlwaysNone $use_None

View File

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

View File

@ -1,9 +1,9 @@
// ignore-tidy-linelength // ignore-tidy-linelength
//@ count "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type_alias.type.function_pointer.decl.inputs[*]" 1 //@ count "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type_alias.type.function_pointer.sig.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.sig.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.sig.inputs[0][1].borrowed_ref.lifetime" \"\'c\"
//@ is "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type_alias.type.function_pointer.decl.output.primitive" \"i32\" //@ 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 //@ 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].name" \"\'c\"
//@ is "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type_alias.type.function_pointer.generic_params[0].kind" '{ "lifetime": { "outlives": [] } }' //@ 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 // 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.is_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.is_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_async" false
pub type FnPointer = fn(); 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.is_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.is_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_async" false
pub type UnsafePointer = unsafe fn(); pub type UnsafePointer = unsafe fn();

View File

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

View File

@ -1,6 +1,6 @@
extern "C" { 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); 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, ...); 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' //@ 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 //@ 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' //@ 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 //@ count "$.index[*][?(@.name=='generics')].inner.function.sig.inputs[*]" 1
//@ is "$.index[*][?(@.name=='generics')].inner.function.decl.inputs[0][0]" '"f"' //@ is "$.index[*][?(@.name=='generics')].inner.function.sig.inputs[0][0]" '"f"'
//@ is "$.index[*][?(@.name=='generics')].inner.function.decl.inputs[0][1].generic" '"F"' //@ is "$.index[*][?(@.name=='generics')].inner.function.sig.inputs[0][1].generic" '"F"'
pub fn generics<F: Foo>(f: F) {} pub fn generics<F: Foo>(f: F) {}
//@ is "$.index[*][?(@.name=='impl_trait')].inner.function.generics.where_predicates" "[]" //@ is "$.index[*][?(@.name=='impl_trait')].inner.function.generics.where_predicates" "[]"
//@ count "$.index[*][?(@.name=='impl_trait')].inner.function.generics.params[*]" 1 //@ 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].name" '"impl Foo"'
//@ is "$.index[*][?(@.name=='impl_trait')].inner.function.generics.params[0].kind.type.bounds[0].trait_bound.trait.id" $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 //@ count "$.index[*][?(@.name=='impl_trait')].inner.function.sig.inputs[*]" 1
//@ is "$.index[*][?(@.name=='impl_trait')].inner.function.decl.inputs[0][0]" '"f"' //@ is "$.index[*][?(@.name=='impl_trait')].inner.function.sig.inputs[0][0]" '"f"'
//@ count "$.index[*][?(@.name=='impl_trait')].inner.function.decl.inputs[0][1].impl_trait[*]" 1 //@ count "$.index[*][?(@.name=='impl_trait')].inner.function.sig.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 //@ 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) {} pub fn impl_trait(f: impl Foo) {}
//@ count "$.index[*][?(@.name=='where_clase')].inner.function.generics.params[*]" 3 //@ 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].name" '"F"'
//@ is "$.index[*][?(@.name=='where_clase')].inner.function.generics.params[0].kind" '{"type": {"bounds": [], "default": null, "synthetic": false}}' //@ 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.decl.inputs[*]" 3 //@ count "$.index[*][?(@.name=='where_clase')].inner.function.sig.inputs[*]" 3
//@ is "$.index[*][?(@.name=='where_clase')].inner.function.decl.inputs[0][0]" '"f"' //@ is "$.index[*][?(@.name=='where_clase')].inner.function.sig.inputs[0][0]" '"f"'
//@ is "$.index[*][?(@.name=='where_clase')].inner.function.decl.inputs[0][1].generic" '"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 //@ 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\" //@ 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" //@ set foo = "$.index[*][?(@.name=='Foo')].id"
pub trait Foo {} pub trait Foo {}
//@ is "$.index[*][?(@.name=='get_foo')].inner.function.decl.inputs" [] //@ is "$.index[*][?(@.name=='get_foo')].inner.function.sig.inputs" []
//@ count "$.index[*][?(@.name=='get_foo')].inner.function.decl.output.impl_trait[*]" 1 //@ count "$.index[*][?(@.name=='get_foo')].inner.function.sig.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.output.impl_trait[0].trait_bound.trait.id" $foo
pub fn get_foo() -> impl Foo { pub fn get_foo() -> impl Foo {
Fooer {} Fooer {}
} }

View File

@ -6,17 +6,17 @@ pub trait Wham {}
//@ is "$.index[*][?(@.name=='one_generic_param_fn')].inner.function.generics.where_predicates" [] //@ is "$.index[*][?(@.name=='one_generic_param_fn')].inner.function.generics.where_predicates" []
//@ count "$.index[*][?(@.name=='one_generic_param_fn')].inner.function.generics.params[*]" 1 //@ 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].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.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) {} pub fn one_generic_param_fn<T: Wham>(w: T) {}
//@ is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.generics.where_predicates" [] //@ 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 //@ 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].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 //@ 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 //@ count "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.sig.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.sig.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 //@ 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) {} 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 { pub fn fst<X, Y>((x, _): (X, Y)) -> X {
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) {} pub fn drop_int(_: i32) {}

View File

@ -1,33 +1,33 @@
//@ edition:2018 //@ edition:2018
//@ is "$.index[*][?(@.name=='nothing_fn')].inner.function.header.async" false //@ is "$.index[*][?(@.name=='nothing_fn')].inner.function.header.is_async" false
//@ is "$.index[*][?(@.name=='nothing_fn')].inner.function.header.const" false //@ is "$.index[*][?(@.name=='nothing_fn')].inner.function.header.is_const" false
//@ is "$.index[*][?(@.name=='nothing_fn')].inner.function.header.unsafe" false //@ is "$.index[*][?(@.name=='nothing_fn')].inner.function.header.is_unsafe" false
pub fn nothing_fn() {} pub fn nothing_fn() {}
//@ is "$.index[*][?(@.name=='unsafe_fn')].inner.function.header.async" false //@ is "$.index[*][?(@.name=='unsafe_fn')].inner.function.header.is_async" false
//@ is "$.index[*][?(@.name=='unsafe_fn')].inner.function.header.const" false //@ is "$.index[*][?(@.name=='unsafe_fn')].inner.function.header.is_const" false
//@ is "$.index[*][?(@.name=='unsafe_fn')].inner.function.header.unsafe" true //@ is "$.index[*][?(@.name=='unsafe_fn')].inner.function.header.is_unsafe" true
pub unsafe fn unsafe_fn() {} pub unsafe fn unsafe_fn() {}
//@ is "$.index[*][?(@.name=='const_fn')].inner.function.header.async" false //@ is "$.index[*][?(@.name=='const_fn')].inner.function.header.is_async" false
//@ is "$.index[*][?(@.name=='const_fn')].inner.function.header.const" true //@ is "$.index[*][?(@.name=='const_fn')].inner.function.header.is_const" true
//@ is "$.index[*][?(@.name=='const_fn')].inner.function.header.unsafe" false //@ is "$.index[*][?(@.name=='const_fn')].inner.function.header.is_unsafe" false
pub const fn const_fn() {} pub const fn const_fn() {}
//@ is "$.index[*][?(@.name=='async_fn')].inner.function.header.async" true //@ is "$.index[*][?(@.name=='async_fn')].inner.function.header.is_async" true
//@ is "$.index[*][?(@.name=='async_fn')].inner.function.header.const" false //@ is "$.index[*][?(@.name=='async_fn')].inner.function.header.is_const" false
//@ is "$.index[*][?(@.name=='async_fn')].inner.function.header.unsafe" false //@ is "$.index[*][?(@.name=='async_fn')].inner.function.header.is_unsafe" false
pub async fn async_fn() {} 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.is_async" true
//@ is "$.index[*][?(@.name=='async_unsafe_fn')].inner.function.header.const" false //@ is "$.index[*][?(@.name=='async_unsafe_fn')].inner.function.header.is_const" false
//@ is "$.index[*][?(@.name=='async_unsafe_fn')].inner.function.header.unsafe" true //@ is "$.index[*][?(@.name=='async_unsafe_fn')].inner.function.header.is_unsafe" true
pub async unsafe fn async_unsafe_fn() {} 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.is_async" false
//@ is "$.index[*][?(@.name=='const_unsafe_fn')].inner.function.header.const" true //@ is "$.index[*][?(@.name=='const_unsafe_fn')].inner.function.header.is_const" true
//@ is "$.index[*][?(@.name=='const_unsafe_fn')].inner.function.header.unsafe" true //@ is "$.index[*][?(@.name=='const_unsafe_fn')].inner.function.header.is_unsafe" true
pub const unsafe fn const_unsafe_fn() {} pub const unsafe fn const_unsafe_fn() {}
// It's impossible for a function to be both const and async, so no test for that // 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" ///@ set foo = "$.index[*][?(@.name=='Foo')].id"
pub type Foo = i32; 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 { pub fn demo() -> Foo {
42 42
} }

View File

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

View File

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

View File

@ -9,11 +9,11 @@ impl IntoIterator for AlwaysTrue {
/// type Item /// type Item
type Item = bool; type Item = bool;
//@ count '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.default.impl_trait[*]' 1 //@ count '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait[*]' 1
//@ is '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.default.impl_trait[0].trait_bound.trait.name' '"Iterator"' //@ 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.default.impl_trait[0].trait_bound.trait.args.angle_bracketed.bindings[*]' 1 //@ 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.default.impl_trait[0].trait_bound.trait.args.angle_bracketed.bindings[0].name' '"Item"' //@ 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.default.impl_trait[0].trait_bound.trait.args.angle_bracketed.bindings[0].binding.equality.type.primitive' '"bool"' //@ 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' //@ set IntoIter = '$.index[*][?(@.docs=="type IntoIter")].id'
/// type IntoIter /// 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.sig.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.sig.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[2]" \"N\"
pub fn hello<'a, T, const N: usize>() -> impl Sized + use<'a, T, 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.begin" "[13, 0]"
//@ is "$.index[*][?(@.docs=='has span')].span.end" "[15, 1]" //@ is "$.index[*][?(@.docs=='has span')].span.end" "[15, 1]"
// FIXME: this doesn't work due to https://github.com/freestrings/jsonpath/issues/91 // 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; pub struct Foo;

View File

@ -3,7 +3,7 @@ extern crate foreign_trait;
/// ForeignTrait id hack /// ForeignTrait id hack
pub use foreign_trait::ForeignTrait as _; 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; pub struct LocalStruct;
//@ set LocalStruct = "$.index[*][?(@.name=='LocalStruct')].id" //@ 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; pub use bar::Baz;
//@ is "$.index[*].inner.module.items[*]" $import //@ 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 //@ has "$.index[*][?(@.name == 'Baz')].inner.struct.impls[*]" $impl
//@ is "$.index[*][?(@.docs=='impl')].inner.impl.items[*]" $doit //@ is "$.index[*][?(@.docs=='impl')].inner.impl.items[*]" $doit

View File

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

View File

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

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