mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-28 11:07:42 +00:00
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:
commit
1f44f0a66f
@ -11,63 +11,12 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
|
||||
|
||||
[[package]]
|
||||
name = "boml"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85fdb93f04c73bff54305fa437ffea5449c41edcaadfe882f35836206b166ac5"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
|
||||
dependencies = [
|
||||
"errno-dragonfly",
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno-dragonfly"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764"
|
||||
|
||||
[[package]]
|
||||
name = "fm"
|
||||
version = "0.2.2"
|
||||
@ -132,12 +81,6 @@ version = "0.2.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
@ -154,24 +97,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.30.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.8.4"
|
||||
@ -196,22 +121,6 @@ dependencies = [
|
||||
"boml",
|
||||
"gccjit",
|
||||
"lang_tester",
|
||||
"object",
|
||||
"smallvec",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -223,25 +132,6 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc02fddf48964c42031a0b3fe0428320ecf3a73c401040fc0096f97794310651"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"redox_syscall",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.2.0"
|
||||
@ -315,69 +205,3 @@ name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
||||
|
@ -23,21 +23,11 @@ default = ["master"]
|
||||
|
||||
[dependencies]
|
||||
gccjit = "2.1"
|
||||
|
||||
# Local copy.
|
||||
#gccjit = { path = "../gccjit.rs" }
|
||||
|
||||
object = { version = "0.30.1", default-features = false, features = [
|
||||
"std",
|
||||
"read",
|
||||
] }
|
||||
smallvec = { version = "1.6.1", features = ["union", "may_dangle"] }
|
||||
# TODO(antoyo): make tempfile optional.
|
||||
tempfile = "3.7.1"
|
||||
|
||||
[dev-dependencies]
|
||||
lang_tester = "0.8.0"
|
||||
tempfile = "3.1.0"
|
||||
boml = "0.3.1"
|
||||
|
||||
[profile.dev]
|
||||
|
@ -24,6 +24,14 @@
|
||||
#![deny(clippy::pattern_type_mismatch)]
|
||||
#![allow(clippy::needless_lifetimes)]
|
||||
|
||||
// Some "regular" crates we want to share with rustc
|
||||
extern crate object;
|
||||
extern crate smallvec;
|
||||
extern crate tempfile;
|
||||
#[macro_use]
|
||||
extern crate tracing;
|
||||
|
||||
// The rustc crates we need
|
||||
extern crate rustc_apfloat;
|
||||
extern crate rustc_ast;
|
||||
extern crate rustc_attr;
|
||||
@ -42,8 +50,6 @@ extern crate rustc_middle;
|
||||
extern crate rustc_session;
|
||||
extern crate rustc_span;
|
||||
extern crate rustc_target;
|
||||
#[macro_use]
|
||||
extern crate tracing;
|
||||
|
||||
// This prevents duplicating functions and statics that are already part of the host rustc process.
|
||||
#[allow(unused_extern_crates)]
|
||||
|
@ -447,7 +447,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
// this time with enough precision to check that the value
|
||||
// whose address was taken can actually be made to live as long
|
||||
// as it needs to live.
|
||||
let region = self.next_region_var(infer::AddrOfRegion(expr.span));
|
||||
let region = self.next_region_var(infer::BorrowRegion(expr.span));
|
||||
Ty::new_ref(self.tcx, region, ty, mutbl)
|
||||
}
|
||||
}
|
||||
|
@ -458,8 +458,8 @@ pub enum RegionVariableOrigin {
|
||||
PatternRegion(Span),
|
||||
|
||||
/// Regions created by `&` operator.
|
||||
///
|
||||
AddrOfRegion(Span),
|
||||
BorrowRegion(Span),
|
||||
|
||||
/// Regions created as part of an autoref of a method receiver.
|
||||
Autoref(Span),
|
||||
|
||||
@ -1741,7 +1741,7 @@ impl RegionVariableOrigin {
|
||||
match *self {
|
||||
MiscVariable(a)
|
||||
| PatternRegion(a)
|
||||
| AddrOfRegion(a)
|
||||
| BorrowRegion(a)
|
||||
| Autoref(a)
|
||||
| Coercion(a)
|
||||
| RegionParameterDefinition(a, ..)
|
||||
|
@ -1645,6 +1645,7 @@ supported_targets! {
|
||||
("x86_64-unknown-haiku", x86_64_unknown_haiku),
|
||||
|
||||
("i686-unknown-hurd-gnu", i686_unknown_hurd_gnu),
|
||||
("x86_64-unknown-hurd-gnu", x86_64_unknown_hurd_gnu),
|
||||
|
||||
("aarch64-apple-darwin", aarch64_apple_darwin),
|
||||
("arm64e-apple-darwin", arm64e_apple_darwin),
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! The `wasm32-wasip2` target is the next evolution of the
|
||||
//! wasm32-wasi target. While the wasi specification is still under
|
||||
//! active development, the {review 2 iteration is considered an "island
|
||||
//! active development, the preview 2 iteration is considered an "island
|
||||
//! of stability" that should allow users to rely on it indefinitely.
|
||||
//!
|
||||
//! The `wasi` target is a proposal to define a standardized set of WebAssembly
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
@ -1018,7 +1018,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
||||
let var_description = match var_origin {
|
||||
infer::MiscVariable(_) => String::new(),
|
||||
infer::PatternRegion(_) => " for pattern".to_string(),
|
||||
infer::AddrOfRegion(_) => " for borrow expression".to_string(),
|
||||
infer::BorrowRegion(_) => " for borrow expression".to_string(),
|
||||
infer::Autoref(_) => " for autoref".to_string(),
|
||||
infer::Coercion(_) => " for automatic coercion".to_string(),
|
||||
infer::BoundRegion(_, br, infer::FnCall) => {
|
||||
|
@ -73,7 +73,7 @@ impl<'a, T> Iterator for Iter<'a, T> {
|
||||
fn advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
|
||||
let remaining = self.i1.advance_by(n);
|
||||
match remaining {
|
||||
Ok(()) => return Ok(()),
|
||||
Ok(()) => Ok(()),
|
||||
Err(n) => {
|
||||
mem::swap(&mut self.i1, &mut self.i2);
|
||||
self.i1.advance_by(n.get())
|
||||
@ -144,7 +144,7 @@ impl<'a, T> DoubleEndedIterator for Iter<'a, T> {
|
||||
|
||||
fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
|
||||
match self.i2.advance_back_by(n) {
|
||||
Ok(()) => return Ok(()),
|
||||
Ok(()) => Ok(()),
|
||||
Err(n) => {
|
||||
mem::swap(&mut self.i1, &mut self.i2);
|
||||
self.i2.advance_back_by(n.get())
|
||||
|
@ -64,7 +64,7 @@ impl<'a, T> Iterator for IterMut<'a, T> {
|
||||
|
||||
fn advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
|
||||
match self.i1.advance_by(n) {
|
||||
Ok(()) => return Ok(()),
|
||||
Ok(()) => Ok(()),
|
||||
Err(remaining) => {
|
||||
mem::swap(&mut self.i1, &mut self.i2);
|
||||
self.i1.advance_by(remaining.get())
|
||||
@ -135,7 +135,7 @@ impl<'a, T> DoubleEndedIterator for IterMut<'a, T> {
|
||||
|
||||
fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
|
||||
match self.i2.advance_back_by(n) {
|
||||
Ok(()) => return Ok(()),
|
||||
Ok(()) => Ok(()),
|
||||
Err(remaining) => {
|
||||
mem::swap(&mut self.i1, &mut self.i2);
|
||||
self.i2.advance_back_by(remaining.get())
|
||||
|
@ -208,7 +208,7 @@ const fn needs_realloc<SRC, DEST>(src_cap: usize, dst_cap: usize) -> bool {
|
||||
|
||||
// type layouts don't guarantee a fit, so do a runtime check to see if
|
||||
// the allocations happen to match
|
||||
return src_cap > 0 && src_cap * mem::size_of::<SRC>() != dst_cap * mem::size_of::<DEST>();
|
||||
src_cap > 0 && src_cap * mem::size_of::<SRC>() != dst_cap * mem::size_of::<DEST>()
|
||||
}
|
||||
|
||||
/// This provides a shorthand for the source type since local type aliases aren't a thing.
|
||||
|
@ -288,11 +288,11 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> {
|
||||
|
||||
// Safety: `len` is larger than the array size. Copy a fixed amount here to fully initialize
|
||||
// the array.
|
||||
return unsafe {
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(self.ptr.as_ptr(), raw_ary.as_mut_ptr() as *mut T, N);
|
||||
self.ptr = self.ptr.add(N);
|
||||
Ok(raw_ary.transpose().assume_init())
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn fold<B, F>(mut self, mut accum: B, mut f: F) -> B
|
||||
|
@ -1338,9 +1338,8 @@ impl<T> Option<T> {
|
||||
/// assert_eq!(x.iter().next(), None);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[rustc_const_unstable(feature = "const_option", issue = "67441")]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub const fn iter(&self) -> Iter<'_, T> {
|
||||
pub fn iter(&self) -> Iter<'_, T> {
|
||||
Iter { inner: Item { opt: self.as_ref() } }
|
||||
}
|
||||
|
||||
@ -1894,7 +1893,7 @@ impl<T> Option<&T> {
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
// FIXME: this implementation, which sidesteps using `Option::map` since it's not const
|
||||
// FIXME(const-hack): this implementation, which sidesteps using `Option::map` since it's not const
|
||||
// ready yet, should be reverted when possible to avoid code repetition
|
||||
match self {
|
||||
Some(&v) => Some(v),
|
||||
@ -1942,7 +1941,7 @@ impl<T> Option<&mut T> {
|
||||
/// ```
|
||||
#[must_use = "`self` will be dropped if the result is not used"]
|
||||
#[stable(feature = "copied", since = "1.35.0")]
|
||||
#[rustc_const_unstable(feature = "const_option_ext", issue = "91930")]
|
||||
#[rustc_const_unstable(feature = "const_option", issue = "67441")]
|
||||
pub const fn copied(self) -> Option<T>
|
||||
where
|
||||
T: Copy,
|
||||
|
@ -40,15 +40,17 @@ impl<T: ?Sized> *const T {
|
||||
|
||||
#[inline]
|
||||
const fn const_impl(ptr: *const u8) -> bool {
|
||||
// Compare via a cast to a thin pointer, so fat pointers are only
|
||||
// considering their "data" part for null-ness.
|
||||
match (ptr).guaranteed_eq(null_mut()) {
|
||||
None => false,
|
||||
Some(res) => res,
|
||||
// To remain maximally convervative, we stop execution when we don't
|
||||
// know whether the pointer is null or not.
|
||||
// We can *not* return `false` here, that would be unsound in `NonNull::new`!
|
||||
None => panic!("null-ness of this pointer cannot be determined in const context"),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_unsafe)]
|
||||
// Compare via a cast to a thin pointer, so fat pointers are only
|
||||
// considering their "data" part for null-ness.
|
||||
const_eval_select((self as *const u8,), const_impl, runtime_impl)
|
||||
}
|
||||
|
||||
|
@ -187,14 +187,14 @@ impl<Dyn: ?Sized> DynMetadata<Dyn> {
|
||||
// Consider a reference like `&(i32, dyn Send)`: the vtable will only store the size of the
|
||||
// `Send` part!
|
||||
// SAFETY: DynMetadata always contains a valid vtable pointer
|
||||
return unsafe { crate::intrinsics::vtable_size(self.vtable_ptr() as *const ()) };
|
||||
unsafe { crate::intrinsics::vtable_size(self.vtable_ptr() as *const ()) }
|
||||
}
|
||||
|
||||
/// Returns the alignment of the type associated with this vtable.
|
||||
#[inline]
|
||||
pub fn align_of(self) -> usize {
|
||||
// SAFETY: DynMetadata always contains a valid vtable pointer
|
||||
return unsafe { crate::intrinsics::vtable_align(self.vtable_ptr() as *const ()) };
|
||||
unsafe { crate::intrinsics::vtable_align(self.vtable_ptr() as *const ()) }
|
||||
}
|
||||
|
||||
/// Returns the size and alignment together as a `Layout`
|
||||
|
@ -33,22 +33,7 @@ impl<T: ?Sized> *mut T {
|
||||
#[rustc_diagnostic_item = "ptr_is_null"]
|
||||
#[inline]
|
||||
pub const fn is_null(self) -> bool {
|
||||
#[inline]
|
||||
fn runtime_impl(ptr: *mut u8) -> bool {
|
||||
ptr.addr() == 0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
const fn const_impl(ptr: *mut u8) -> bool {
|
||||
// Compare via a cast to a thin pointer, so fat pointers are only
|
||||
// considering their "data" part for null-ness.
|
||||
match (ptr).guaranteed_eq(null_mut()) {
|
||||
None => false,
|
||||
Some(res) => res,
|
||||
}
|
||||
}
|
||||
|
||||
const_eval_select((self as *mut u8,), const_impl, runtime_impl)
|
||||
self.cast_const().is_null()
|
||||
}
|
||||
|
||||
/// Casts to a pointer of another type.
|
||||
|
@ -1535,11 +1535,17 @@ impl<T, E> Result<&T, E> {
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "result_copied", since = "1.59.0")]
|
||||
pub fn copied(self) -> Result<T, E>
|
||||
#[rustc_const_unstable(feature = "const_result", issue = "82814")]
|
||||
pub const fn copied(self) -> Result<T, E>
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
self.map(|&t| t)
|
||||
// FIXME(const-hack): this implementation, which sidesteps using `Result::map` since it's not const
|
||||
// ready yet, should be reverted when possible to avoid code repetition
|
||||
match self {
|
||||
Ok(&v) => Ok(v),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps a `Result<&T, E>` to a `Result<T, E>` by cloning the contents of the
|
||||
@ -1579,11 +1585,17 @@ impl<T, E> Result<&mut T, E> {
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "result_copied", since = "1.59.0")]
|
||||
pub fn copied(self) -> Result<T, E>
|
||||
#[rustc_const_unstable(feature = "const_result", issue = "82814")]
|
||||
pub const fn copied(self) -> Result<T, E>
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
self.map(|&mut t| t)
|
||||
// FIXME(const-hack): this implementation, which sidesteps using `Result::map` since it's not const
|
||||
// ready yet, should be reverted when possible to avoid code repetition
|
||||
match self {
|
||||
Ok(&mut v) => Ok(v),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps a `Result<&mut T, E>` to a `Result<T, E>` by cloning the contents of the
|
||||
|
@ -1814,7 +1814,7 @@ fn simd_contains(needle: &str, haystack: &str) -> Option<bool> {
|
||||
}
|
||||
mask &= !(1 << trailing);
|
||||
}
|
||||
return false;
|
||||
false
|
||||
};
|
||||
|
||||
let test_chunk = |idx| -> u16 {
|
||||
@ -1830,7 +1830,7 @@ fn simd_contains(needle: &str, haystack: &str) -> Option<bool> {
|
||||
let both = eq_first.bitand(eq_last);
|
||||
let mask = both.to_bitmask() as u16;
|
||||
|
||||
return mask;
|
||||
mask
|
||||
};
|
||||
|
||||
let mut i = 0;
|
||||
|
@ -13,7 +13,7 @@ pub(crate) struct PidFd(FileDesc);
|
||||
|
||||
impl PidFd {
|
||||
pub fn kill(&self) -> io::Result<()> {
|
||||
return cvt(unsafe {
|
||||
cvt(unsafe {
|
||||
libc::syscall(
|
||||
libc::SYS_pidfd_send_signal,
|
||||
self.0.as_raw_fd(),
|
||||
@ -22,7 +22,7 @@ impl PidFd {
|
||||
0,
|
||||
)
|
||||
})
|
||||
.map(drop);
|
||||
.map(drop)
|
||||
}
|
||||
|
||||
pub fn wait(&self) -> io::Result<ExitStatus> {
|
||||
@ -30,7 +30,7 @@ impl PidFd {
|
||||
cvt(unsafe {
|
||||
libc::waitid(libc::P_PIDFD, self.0.as_raw_fd() as u32, &mut siginfo, libc::WEXITED)
|
||||
})?;
|
||||
return Ok(ExitStatus::from_waitid_siginfo(siginfo));
|
||||
Ok(ExitStatus::from_waitid_siginfo(siginfo))
|
||||
}
|
||||
|
||||
pub fn try_wait(&self) -> io::Result<Option<ExitStatus>> {
|
||||
@ -45,9 +45,10 @@ impl PidFd {
|
||||
)
|
||||
})?;
|
||||
if unsafe { siginfo.si_pid() } == 0 {
|
||||
return Ok(None);
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(ExitStatus::from_waitid_siginfo(siginfo)))
|
||||
}
|
||||
return Ok(Some(ExitStatus::from_waitid_siginfo(siginfo)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -265,7 +265,7 @@ impl OpenOptions {
|
||||
pub fn new() -> OpenOptions {
|
||||
let mut base = OpenOptions::default();
|
||||
base.dirflags = wasi::LOOKUPFLAGS_SYMLINK_FOLLOW;
|
||||
return base;
|
||||
base
|
||||
}
|
||||
|
||||
pub fn read(&mut self, read: bool) {
|
||||
@ -382,7 +382,7 @@ impl OpenOptions {
|
||||
base |= wasi::RIGHTS_PATH_UNLINK_FILE;
|
||||
base |= wasi::RIGHTS_POLL_FD_READWRITE;
|
||||
|
||||
return base;
|
||||
base
|
||||
}
|
||||
|
||||
fn rights_inheriting(&self) -> wasi::Rights {
|
||||
|
@ -115,7 +115,7 @@ pub fn hashmap_random_keys() -> (u64, u64) {
|
||||
let len = mem::size_of_val(&ret);
|
||||
wasi::random_get(base, len).expect("random_get failure");
|
||||
}
|
||||
return ret;
|
||||
ret
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -1912,7 +1912,7 @@ impl Step for Assemble {
|
||||
// delegates to the `rust-lld` binary for linking and then runs
|
||||
// logic to create the final binary. This is used by the
|
||||
// `wasm32-wasip2` target of Rust.
|
||||
if builder.build_wasm_component_ld() {
|
||||
if builder.tool_enabled("wasm-component-ld") {
|
||||
let wasm_component_ld_exe =
|
||||
builder.ensure(crate::core::build_steps::tool::WasmComponentLd {
|
||||
compiler: build_compiler,
|
||||
|
@ -473,7 +473,7 @@ impl Step for Rustc {
|
||||
);
|
||||
}
|
||||
}
|
||||
if builder.build_wasm_component_ld() {
|
||||
if builder.tool_enabled("wasm-component-ld") {
|
||||
let src_dir = builder.sysroot_libdir(compiler, host).parent().unwrap().join("bin");
|
||||
let ld = exe("wasm-component-ld", compiler.host);
|
||||
builder.copy_link(&src_dir.join(&ld), &dst_dir.join(&ld));
|
||||
|
@ -693,14 +693,7 @@ impl Step for Cargo {
|
||||
|
||||
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
|
||||
let builder = run.builder;
|
||||
run.path("src/tools/cargo").default_condition(
|
||||
builder.config.extended
|
||||
&& builder.config.tools.as_ref().map_or(
|
||||
true,
|
||||
// If `tools` is set, search list for this tool.
|
||||
|tools| tools.iter().any(|tool| tool == "cargo"),
|
||||
),
|
||||
)
|
||||
run.path("src/tools/cargo").default_condition(builder.tool_enabled("cargo"))
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig<'_>) {
|
||||
@ -772,14 +765,7 @@ impl Step for RustAnalyzer {
|
||||
|
||||
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
|
||||
let builder = run.builder;
|
||||
run.path("src/tools/rust-analyzer").default_condition(
|
||||
builder.config.extended
|
||||
&& builder
|
||||
.config
|
||||
.tools
|
||||
.as_ref()
|
||||
.map_or(true, |tools| tools.iter().any(|tool| tool == "rust-analyzer")),
|
||||
)
|
||||
run.path("src/tools/rust-analyzer").default_condition(builder.tool_enabled("rust-analyzer"))
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig<'_>) {
|
||||
@ -821,12 +807,8 @@ impl Step for RustAnalyzerProcMacroSrv {
|
||||
run.path("src/tools/rust-analyzer")
|
||||
.path("src/tools/rust-analyzer/crates/proc-macro-srv-cli")
|
||||
.default_condition(
|
||||
builder.config.extended
|
||||
&& builder.config.tools.as_ref().map_or(true, |tools| {
|
||||
tools.iter().any(|tool| {
|
||||
tool == "rust-analyzer" || tool == "rust-analyzer-proc-macro-srv"
|
||||
})
|
||||
}),
|
||||
builder.tool_enabled("rust-analyzer")
|
||||
|| builder.tool_enabled("rust-analyzer-proc-macro-srv"),
|
||||
)
|
||||
}
|
||||
|
||||
@ -874,16 +856,8 @@ impl Step for LlvmBitcodeLinker {
|
||||
|
||||
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
|
||||
let builder = run.builder;
|
||||
run.path("src/tools/llvm-bitcode-linker").default_condition(
|
||||
builder.config.extended
|
||||
&& builder
|
||||
.config
|
||||
.tools
|
||||
.as_ref()
|
||||
.map_or(builder.build.unstable_features(), |tools| {
|
||||
tools.iter().any(|tool| tool == "llvm-bitcode-linker")
|
||||
}),
|
||||
)
|
||||
run.path("src/tools/llvm-bitcode-linker")
|
||||
.default_condition(builder.tool_enabled("llvm-bitcode-linker"))
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig<'_>) {
|
||||
|
@ -1407,16 +1407,17 @@ Executed at: {executed_at}"#,
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns whether it's requested that `wasm-component-ld` is built as part
|
||||
/// of the sysroot. This is done either with the `extended` key in
|
||||
/// `config.toml` or with the `tools` set.
|
||||
fn build_wasm_component_ld(&self) -> bool {
|
||||
if self.config.extended {
|
||||
return true;
|
||||
/// Returns whether the specified tool is configured as part of this build.
|
||||
///
|
||||
/// This requires that both the `extended` key is set and the `tools` key is
|
||||
/// either unset or specifically contains the specified tool.
|
||||
fn tool_enabled(&self, tool: &str) -> bool {
|
||||
if !self.config.extended {
|
||||
return false;
|
||||
}
|
||||
match &self.config.tools {
|
||||
Some(set) => set.contains("wasm-component-ld"),
|
||||
None => false,
|
||||
Some(set) => set.contains(tool),
|
||||
None => true,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,8 +6,8 @@ IFS=$'\n\t'
|
||||
|
||||
source "$(cd "$(dirname "$0")" && pwd)/../shared.sh"
|
||||
|
||||
MINGW_ARCHIVE_32="i686-12.2.0-release-posix-dwarf-rt_v10-rev0.7z"
|
||||
MINGW_ARCHIVE_64="x86_64-12.2.0-release-posix-seh-rt_v10-rev0.7z"
|
||||
MINGW_ARCHIVE_32="i686-14.1.0-release-posix-dwarf-msvcrt-rt_v12-rev0.7z"
|
||||
MINGW_ARCHIVE_64="x86_64-14.1.0-release-posix-seh-msvcrt-rt_v12-rev0.7z"
|
||||
|
||||
if isWindows && isKnownToBeMingwBuild; then
|
||||
case "${CI_JOB_NAME}" in
|
||||
|
@ -382,6 +382,7 @@ target | std | host | notes
|
||||
[`x86_64-unikraft-linux-musl`](platform-support/unikraft-linux-musl.md) | ✓ | | 64-bit Unikraft with musl 1.2.3
|
||||
`x86_64-unknown-dragonfly` | ✓ | ✓ | 64-bit DragonFlyBSD
|
||||
`x86_64-unknown-haiku` | ✓ | ✓ | 64-bit Haiku
|
||||
[`x86_64-unknown-hurd-gnu`](platform-support/hurd.md) | ✓ | ✓ | 64-bit GNU/Hurd
|
||||
[`x86_64-unknown-hermit`](platform-support/hermit.md) | ✓ | | x86_64 Hermit
|
||||
`x86_64-unknown-l4re-uclibc` | ? | |
|
||||
[`x86_64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | 64-bit OpenBSD
|
||||
|
@ -1,4 +1,4 @@
|
||||
# `i686-unknown-hurd-gnu`
|
||||
# `i686-unknown-hurd-gnu` and `x86_64-unknown-hurd-gnu`
|
||||
|
||||
**Tier: 3**
|
||||
|
||||
@ -16,7 +16,8 @@ The GNU/Hurd target supports `std` and uses the standard ELF file format.
|
||||
|
||||
## Building the target
|
||||
|
||||
This target can be built by adding `i686-unknown-hurd-gnu` as target in the rustc list.
|
||||
This target can be built by adding `i686-unknown-hurd-gnu` and
|
||||
`x86_64-unknown-hurd-gnu` as targets in the rustc list.
|
||||
|
||||
## Building Rust programs
|
||||
|
||||
@ -32,4 +33,4 @@ Tests can be run in the same way as a regular binary.
|
||||
## Cross-compilation toolchains and C code
|
||||
|
||||
The target supports C code, the GNU toolchain calls the target
|
||||
`i686-unknown-gnu`.
|
||||
`i686-unknown-gnu` and `x86_64-unknown-gnu`.
|
||||
|
@ -156,7 +156,7 @@ impl FromWithTcx<clean::GenericArgs> for GenericArgs {
|
||||
match args {
|
||||
AngleBracketed { args, constraints } => GenericArgs::AngleBracketed {
|
||||
args: args.into_vec().into_tcx(tcx),
|
||||
bindings: constraints.into_tcx(tcx),
|
||||
constraints: constraints.into_tcx(tcx),
|
||||
},
|
||||
Parenthesized { inputs, output } => GenericArgs::Parenthesized {
|
||||
inputs: inputs.into_vec().into_tcx(tcx),
|
||||
@ -198,9 +198,9 @@ impl FromWithTcx<clean::ConstantKind> for Constant {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromWithTcx<clean::AssocItemConstraint> for TypeBinding {
|
||||
impl FromWithTcx<clean::AssocItemConstraint> for AssocItemConstraint {
|
||||
fn from_tcx(constraint: clean::AssocItemConstraint, tcx: TyCtxt<'_>) -> Self {
|
||||
TypeBinding {
|
||||
AssocItemConstraint {
|
||||
name: constraint.assoc.name.to_string(),
|
||||
args: constraint.assoc.args.into_tcx(tcx),
|
||||
binding: constraint.kind.into_tcx(tcx),
|
||||
@ -208,12 +208,12 @@ impl FromWithTcx<clean::AssocItemConstraint> for TypeBinding {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromWithTcx<clean::AssocItemConstraintKind> for TypeBindingKind {
|
||||
impl FromWithTcx<clean::AssocItemConstraintKind> for AssocItemConstraintKind {
|
||||
fn from_tcx(kind: clean::AssocItemConstraintKind, tcx: TyCtxt<'_>) -> Self {
|
||||
use clean::AssocItemConstraintKind::*;
|
||||
match kind {
|
||||
Equality { term } => TypeBindingKind::Equality(term.into_tcx(tcx)),
|
||||
Bound { bounds } => TypeBindingKind::Constraint(bounds.into_tcx(tcx)),
|
||||
Equality { term } => AssocItemConstraintKind::Equality(term.into_tcx(tcx)),
|
||||
Bound { bounds } => AssocItemConstraintKind::Constraint(bounds.into_tcx(tcx)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -314,7 +314,7 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
|
||||
ModuleItem(m) => {
|
||||
ItemEnum::Module(Module { is_crate, items: ids(m.items, tcx), is_stripped: false })
|
||||
}
|
||||
ImportItem(i) => ItemEnum::Import(i.into_tcx(tcx)),
|
||||
ImportItem(i) => ItemEnum::Use(i.into_tcx(tcx)),
|
||||
StructItem(s) => ItemEnum::Struct(s.into_tcx(tcx)),
|
||||
UnionItem(u) => ItemEnum::Union(u.into_tcx(tcx)),
|
||||
StructFieldItem(f) => ItemEnum::StructField(f.into_tcx(tcx)),
|
||||
@ -331,7 +331,7 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
|
||||
ImplItem(i) => ItemEnum::Impl((*i).into_tcx(tcx)),
|
||||
StaticItem(s) => ItemEnum::Static(s.into_tcx(tcx)),
|
||||
ForeignStaticItem(s, _) => ItemEnum::Static(s.into_tcx(tcx)),
|
||||
ForeignTypeItem => ItemEnum::ForeignType,
|
||||
ForeignTypeItem => ItemEnum::ExternType,
|
||||
TypeAliasItem(t) => ItemEnum::TypeAlias(t.into_tcx(tcx)),
|
||||
// FIXME(generic_const_items): Add support for generic free consts
|
||||
ConstantItem(ci) => {
|
||||
@ -347,21 +347,19 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
|
||||
}
|
||||
// FIXME(generic_const_items): Add support for generic associated consts.
|
||||
TyAssocConstItem(_generics, ty) => {
|
||||
ItemEnum::AssocConst { type_: (*ty).into_tcx(tcx), default: None }
|
||||
ItemEnum::AssocConst { type_: (*ty).into_tcx(tcx), value: None }
|
||||
}
|
||||
// FIXME(generic_const_items): Add support for generic associated consts.
|
||||
AssocConstItem(ci) => {
|
||||
ItemEnum::AssocConst { type_: ci.type_.into_tcx(tcx), default: Some(ci.kind.expr(tcx)) }
|
||||
ItemEnum::AssocConst { type_: ci.type_.into_tcx(tcx), value: Some(ci.kind.expr(tcx)) }
|
||||
}
|
||||
TyAssocTypeItem(g, b) => {
|
||||
ItemEnum::AssocType { generics: g.into_tcx(tcx), bounds: b.into_tcx(tcx), type_: None }
|
||||
}
|
||||
TyAssocTypeItem(g, b) => ItemEnum::AssocType {
|
||||
generics: g.into_tcx(tcx),
|
||||
bounds: b.into_tcx(tcx),
|
||||
default: None,
|
||||
},
|
||||
AssocTypeItem(t, b) => ItemEnum::AssocType {
|
||||
generics: t.generics.into_tcx(tcx),
|
||||
bounds: b.into_tcx(tcx),
|
||||
default: Some(t.item_type.unwrap_or(t.type_).into_tcx(tcx)),
|
||||
type_: Some(t.item_type.unwrap_or(t.type_).into_tcx(tcx)),
|
||||
},
|
||||
// `convert_item` early returns `None` for stripped items and keywords.
|
||||
KeywordItem => unreachable!(),
|
||||
@ -385,7 +383,7 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
|
||||
|
||||
impl FromWithTcx<clean::Struct> for Struct {
|
||||
fn from_tcx(struct_: clean::Struct, tcx: TyCtxt<'_>) -> Self {
|
||||
let fields_stripped = struct_.has_stripped_entries();
|
||||
let has_stripped_fields = struct_.has_stripped_entries();
|
||||
let clean::Struct { ctor_kind, generics, fields } = struct_;
|
||||
|
||||
let kind = match ctor_kind {
|
||||
@ -394,7 +392,7 @@ impl FromWithTcx<clean::Struct> for Struct {
|
||||
assert!(fields.is_empty());
|
||||
StructKind::Unit
|
||||
}
|
||||
None => StructKind::Plain { fields: ids(fields, tcx), fields_stripped },
|
||||
None => StructKind::Plain { fields: ids(fields, tcx), has_stripped_fields },
|
||||
};
|
||||
|
||||
Struct {
|
||||
@ -407,22 +405,22 @@ impl FromWithTcx<clean::Struct> for Struct {
|
||||
|
||||
impl FromWithTcx<clean::Union> for Union {
|
||||
fn from_tcx(union_: clean::Union, tcx: TyCtxt<'_>) -> Self {
|
||||
let fields_stripped = union_.has_stripped_entries();
|
||||
let has_stripped_fields = union_.has_stripped_entries();
|
||||
let clean::Union { generics, fields } = union_;
|
||||
Union {
|
||||
generics: generics.into_tcx(tcx),
|
||||
fields_stripped,
|
||||
has_stripped_fields,
|
||||
fields: ids(fields, tcx),
|
||||
impls: Vec::new(), // Added in JsonRenderer::item
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_fn_header(header: &rustc_hir::FnHeader) -> Header {
|
||||
Header {
|
||||
async_: header.is_async(),
|
||||
const_: header.is_const(),
|
||||
unsafe_: header.is_unsafe(),
|
||||
pub(crate) fn from_fn_header(header: &rustc_hir::FnHeader) -> FunctionHeader {
|
||||
FunctionHeader {
|
||||
is_async: header.is_async(),
|
||||
is_const: header.is_const(),
|
||||
is_unsafe: header.is_unsafe(),
|
||||
abi: convert_abi(header.abi),
|
||||
}
|
||||
}
|
||||
@ -474,7 +472,7 @@ impl FromWithTcx<clean::GenericParamDefKind> for GenericParamDefKind {
|
||||
Type { bounds, default, synthetic } => GenericParamDefKind::Type {
|
||||
bounds: bounds.into_tcx(tcx),
|
||||
default: default.map(|x| (*x).into_tcx(tcx)),
|
||||
synthetic,
|
||||
is_synthetic: synthetic,
|
||||
},
|
||||
Const { ty, default, synthetic: _ } => GenericParamDefKind::Const {
|
||||
type_: (*ty).into_tcx(tcx),
|
||||
@ -508,7 +506,7 @@ impl FromWithTcx<clean::WherePredicate> for WherePredicate {
|
||||
.map(|bound| bound.into_tcx(tcx))
|
||||
.collect(),
|
||||
default: default.map(|ty| (*ty).into_tcx(tcx)),
|
||||
synthetic,
|
||||
is_synthetic: synthetic,
|
||||
}
|
||||
}
|
||||
clean::GenericParamDefKind::Const { ty, default, synthetic: _ } => {
|
||||
@ -602,12 +600,12 @@ impl FromWithTcx<clean::Type> for Type {
|
||||
ImplTrait(g) => Type::ImplTrait(g.into_tcx(tcx)),
|
||||
Infer => Type::Infer,
|
||||
RawPointer(mutability, type_) => Type::RawPointer {
|
||||
mutable: mutability == ast::Mutability::Mut,
|
||||
is_mutable: mutability == ast::Mutability::Mut,
|
||||
type_: Box::new((*type_).into_tcx(tcx)),
|
||||
},
|
||||
BorrowedRef { lifetime, mutability, type_ } => Type::BorrowedRef {
|
||||
lifetime: lifetime.map(convert_lifetime),
|
||||
mutable: mutability == ast::Mutability::Mut,
|
||||
is_mutable: mutability == ast::Mutability::Mut,
|
||||
type_: Box::new((*type_).into_tcx(tcx)),
|
||||
},
|
||||
QPath(box clean::QPathData { assoc, self_type, trait_, .. }) => Type::QualifiedPath {
|
||||
@ -643,29 +641,29 @@ impl FromWithTcx<clean::BareFunctionDecl> for FunctionPointer {
|
||||
fn from_tcx(bare_decl: clean::BareFunctionDecl, tcx: TyCtxt<'_>) -> Self {
|
||||
let clean::BareFunctionDecl { safety, generic_params, decl, abi } = bare_decl;
|
||||
FunctionPointer {
|
||||
header: Header {
|
||||
unsafe_: matches!(safety, rustc_hir::Safety::Unsafe),
|
||||
const_: false,
|
||||
async_: false,
|
||||
header: FunctionHeader {
|
||||
is_unsafe: matches!(safety, rustc_hir::Safety::Unsafe),
|
||||
is_const: false,
|
||||
is_async: false,
|
||||
abi: convert_abi(abi),
|
||||
},
|
||||
generic_params: generic_params.into_tcx(tcx),
|
||||
decl: decl.into_tcx(tcx),
|
||||
sig: decl.into_tcx(tcx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromWithTcx<clean::FnDecl> for FnDecl {
|
||||
impl FromWithTcx<clean::FnDecl> for FunctionSignature {
|
||||
fn from_tcx(decl: clean::FnDecl, tcx: TyCtxt<'_>) -> Self {
|
||||
let clean::FnDecl { inputs, output, c_variadic } = decl;
|
||||
FnDecl {
|
||||
FunctionSignature {
|
||||
inputs: inputs
|
||||
.values
|
||||
.into_iter()
|
||||
.map(|arg| (arg.name.to_string(), arg.type_.into_tcx(tcx)))
|
||||
.collect(),
|
||||
output: if output.is_unit() { None } else { Some(output.into_tcx(tcx)) },
|
||||
c_variadic,
|
||||
is_c_variadic: c_variadic,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -702,12 +700,12 @@ impl FromWithTcx<clean::Impl> for Impl {
|
||||
let provided_trait_methods = impl_.provided_trait_methods(tcx);
|
||||
let clean::Impl { safety, generics, trait_, for_, items, polarity, kind } = impl_;
|
||||
// FIXME: use something like ImplKind in JSON?
|
||||
let (synthetic, blanket_impl) = match kind {
|
||||
let (is_synthetic, blanket_impl) = match kind {
|
||||
clean::ImplKind::Normal | clean::ImplKind::FakeVariadic => (false, None),
|
||||
clean::ImplKind::Auto => (true, None),
|
||||
clean::ImplKind::Blanket(ty) => (false, Some(*ty)),
|
||||
};
|
||||
let negative_polarity = match polarity {
|
||||
let is_negative = match polarity {
|
||||
ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => false,
|
||||
ty::ImplPolarity::Negative => true,
|
||||
};
|
||||
@ -721,8 +719,8 @@ impl FromWithTcx<clean::Impl> for Impl {
|
||||
trait_: trait_.map(|path| path.into_tcx(tcx)),
|
||||
for_: for_.into_tcx(tcx),
|
||||
items: ids(items, tcx),
|
||||
negative: negative_polarity,
|
||||
synthetic,
|
||||
is_negative,
|
||||
is_synthetic,
|
||||
blanket_impl: blanket_impl.map(|x| x.into_tcx(tcx)),
|
||||
}
|
||||
}
|
||||
@ -736,7 +734,7 @@ pub(crate) fn from_function(
|
||||
) -> Function {
|
||||
let clean::Function { decl, generics } = *function;
|
||||
Function {
|
||||
decl: decl.into_tcx(tcx),
|
||||
sig: decl.into_tcx(tcx),
|
||||
generics: generics.into_tcx(tcx),
|
||||
header: from_fn_header(&header),
|
||||
has_body,
|
||||
@ -745,11 +743,11 @@ pub(crate) fn from_function(
|
||||
|
||||
impl FromWithTcx<clean::Enum> for Enum {
|
||||
fn from_tcx(enum_: clean::Enum, tcx: TyCtxt<'_>) -> Self {
|
||||
let variants_stripped = enum_.has_stripped_entries();
|
||||
let has_stripped_variants = enum_.has_stripped_entries();
|
||||
let clean::Enum { variants, generics } = enum_;
|
||||
Enum {
|
||||
generics: generics.into_tcx(tcx),
|
||||
variants_stripped,
|
||||
has_stripped_variants,
|
||||
variants: ids(variants, tcx),
|
||||
impls: Vec::new(), // Added in JsonRenderer::item
|
||||
}
|
||||
@ -766,7 +764,7 @@ impl FromWithTcx<clean::Variant> for Variant {
|
||||
CLike => VariantKind::Plain,
|
||||
Tuple(fields) => VariantKind::Tuple(ids_keeping_stripped(fields, tcx)),
|
||||
Struct(s) => VariantKind::Struct {
|
||||
fields_stripped: s.has_stripped_entries(),
|
||||
has_stripped_fields: s.has_stripped_entries(),
|
||||
fields: ids(s.fields, tcx),
|
||||
},
|
||||
};
|
||||
@ -787,21 +785,21 @@ impl FromWithTcx<clean::Discriminant> for Discriminant {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromWithTcx<clean::Import> for Import {
|
||||
impl FromWithTcx<clean::Import> for Use {
|
||||
fn from_tcx(import: clean::Import, tcx: TyCtxt<'_>) -> Self {
|
||||
use clean::ImportKind::*;
|
||||
let (name, glob) = match import.kind {
|
||||
let (name, is_glob) = match import.kind {
|
||||
Simple(s) => (s.to_string(), false),
|
||||
Glob => (
|
||||
import.source.path.last_opt().unwrap_or_else(|| Symbol::intern("*")).to_string(),
|
||||
true,
|
||||
),
|
||||
};
|
||||
Import {
|
||||
Use {
|
||||
source: import.source.path.whole_name(),
|
||||
name,
|
||||
id: import.source.did.map(ItemId::from).map(|i| id_from_item_default(i, tcx)),
|
||||
glob,
|
||||
is_glob,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -835,7 +833,7 @@ impl FromWithTcx<clean::Static> for Static {
|
||||
fn from_tcx(stat: clean::Static, tcx: TyCtxt<'_>) -> Self {
|
||||
Static {
|
||||
type_: (*stat.type_).into_tcx(tcx),
|
||||
mutable: stat.mutability == ast::Mutability::Mut,
|
||||
is_mutable: stat.mutability == ast::Mutability::Mut,
|
||||
expr: stat
|
||||
.expr
|
||||
.map(|e| rendered_const(tcx, tcx.hir().body(e), tcx.hir().body_owner_def_id(e)))
|
||||
@ -856,7 +854,7 @@ impl FromWithTcx<ItemType> for ItemKind {
|
||||
match kind {
|
||||
Module => ItemKind::Module,
|
||||
ExternCrate => ItemKind::ExternCrate,
|
||||
Import => ItemKind::Import,
|
||||
Import => ItemKind::Use,
|
||||
Struct => ItemKind::Struct,
|
||||
Union => ItemKind::Union,
|
||||
Enum => ItemKind::Enum,
|
||||
@ -872,7 +870,7 @@ impl FromWithTcx<ItemType> for ItemKind {
|
||||
Primitive => ItemKind::Primitive,
|
||||
AssocConst => ItemKind::AssocConst,
|
||||
AssocType => ItemKind::AssocType,
|
||||
ForeignType => ItemKind::ForeignType,
|
||||
ForeignType => ItemKind::ExternType,
|
||||
Keyword => ItemKind::Keyword,
|
||||
TraitAlias => ItemKind::TraitAlias,
|
||||
ProcAttribute => ItemKind::ProcAttribute,
|
||||
|
@ -197,7 +197,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
|
||||
|
||||
types::ItemEnum::Function(_)
|
||||
| types::ItemEnum::Module(_)
|
||||
| types::ItemEnum::Import(_)
|
||||
| types::ItemEnum::Use(_)
|
||||
| types::ItemEnum::AssocConst { .. }
|
||||
| types::ItemEnum::AssocType { .. } => true,
|
||||
types::ItemEnum::ExternCrate { .. }
|
||||
@ -208,7 +208,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
|
||||
| types::ItemEnum::TypeAlias(_)
|
||||
| types::ItemEnum::Constant { .. }
|
||||
| types::ItemEnum::Static(_)
|
||||
| types::ItemEnum::ForeignType
|
||||
| types::ItemEnum::ExternType
|
||||
| types::ItemEnum::Macro(_)
|
||||
| types::ItemEnum::ProcMacro(_) => false,
|
||||
};
|
||||
|
@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize};
|
||||
/// This integer is incremented with every breaking change to the API,
|
||||
/// and is returned along with the JSON blob as [`Crate::format_version`].
|
||||
/// Consuming code should assert that this value matches the format version(s) that it supports.
|
||||
pub const FORMAT_VERSION: u32 = 33;
|
||||
pub const FORMAT_VERSION: u32 = 34;
|
||||
|
||||
/// The root of the emitted JSON blob.
|
||||
///
|
||||
@ -194,7 +194,7 @@ pub enum GenericArgs {
|
||||
/// ```
|
||||
args: Vec<GenericArg>,
|
||||
/// Associated type or constant bindings (e.g. `Item=i32` or `Item: Clone`) for this type.
|
||||
bindings: Vec<TypeBinding>,
|
||||
constraints: Vec<AssocItemConstraint>,
|
||||
},
|
||||
/// `Fn(A, B) -> C`
|
||||
Parenthesized {
|
||||
@ -258,19 +258,19 @@ pub struct Constant {
|
||||
/// ^^^^^^^^^^ ^^^^^^^^^^^^^^^
|
||||
/// ```
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct TypeBinding {
|
||||
pub struct AssocItemConstraint {
|
||||
/// The name of the associated type/constant.
|
||||
pub name: String,
|
||||
/// Arguments provided to the associated type/constant.
|
||||
pub args: GenericArgs,
|
||||
/// The kind of bound applied to the associated type/constant.
|
||||
pub binding: TypeBindingKind,
|
||||
pub binding: AssocItemConstraintKind,
|
||||
}
|
||||
|
||||
/// The way in which an associate type/constant is bound.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum TypeBindingKind {
|
||||
pub enum AssocItemConstraintKind {
|
||||
/// The required value/type is specified exactly. e.g.
|
||||
/// ```text
|
||||
/// Iterator<Item = u32, IntoIter: DoubleEndedIterator>
|
||||
@ -311,7 +311,7 @@ pub enum ItemKind {
|
||||
/// A crate imported via the `extern crate` syntax.
|
||||
ExternCrate,
|
||||
/// An import of 1 or more items into scope, using the `use` keyword.
|
||||
Import,
|
||||
Use,
|
||||
/// A `struct` declaration.
|
||||
Struct,
|
||||
/// A field of a struct.
|
||||
@ -341,7 +341,7 @@ pub enum ItemKind {
|
||||
/// `type`s from an `extern` block.
|
||||
///
|
||||
/// See [the tracking issue](https://github.com/rust-lang/rust/issues/43467)
|
||||
ForeignType,
|
||||
ExternType,
|
||||
/// A macro declaration.
|
||||
///
|
||||
/// Corresponds to either `ItemEnum::Macro(_)`
|
||||
@ -386,7 +386,7 @@ pub enum ItemEnum {
|
||||
rename: Option<String>,
|
||||
},
|
||||
/// An import of 1 or more items into scope, using the `use` keyword.
|
||||
Import(Import),
|
||||
Use(Use),
|
||||
|
||||
/// A `union` declaration.
|
||||
Union(Union),
|
||||
@ -429,7 +429,7 @@ pub enum ItemEnum {
|
||||
/// `type`s from an `extern` block.
|
||||
///
|
||||
/// See [the tracking issue](https://github.com/rust-lang/rust/issues/43467)
|
||||
ForeignType,
|
||||
ExternType,
|
||||
|
||||
/// A macro_rules! declarative macro. Contains a single string with the source
|
||||
/// representation of the macro with the patterns stripped.
|
||||
@ -447,12 +447,19 @@ pub enum ItemEnum {
|
||||
/// The type of the constant.
|
||||
#[serde(rename = "type")]
|
||||
type_: Type,
|
||||
/// The stringified expression for the default value, if provided, e.g.
|
||||
/// Inside a trait declaration, this is the default value for the associated constant,
|
||||
/// if provided.
|
||||
/// Inside an `impl` block, this is the value assigned to the associated constant,
|
||||
/// and will always be present.
|
||||
///
|
||||
/// The representation is implementation-defined and not guaranteed to be representative of
|
||||
/// either the resulting value or of the source code.
|
||||
///
|
||||
/// ```rust
|
||||
/// const X: usize = 640 * 1024;
|
||||
/// // ^^^^^^^^^^
|
||||
/// ```
|
||||
default: Option<String>,
|
||||
value: Option<String>,
|
||||
},
|
||||
/// An associated type of a trait or a type.
|
||||
AssocType {
|
||||
@ -467,12 +474,16 @@ pub enum ItemEnum {
|
||||
/// }
|
||||
/// ```
|
||||
bounds: Vec<GenericBound>,
|
||||
/// The default for this type, if provided, e.g.
|
||||
/// Inside a trait declaration, this is the default for the associated type, if provided.
|
||||
/// Inside an impl block, this is the type assigned to the associated type, and will always
|
||||
/// be present.
|
||||
///
|
||||
/// ```rust
|
||||
/// type X = usize;
|
||||
/// // ^^^^^
|
||||
/// ```
|
||||
default: Option<Type>,
|
||||
#[serde(rename = "type")]
|
||||
type_: Option<Type>,
|
||||
},
|
||||
}
|
||||
|
||||
@ -497,7 +508,7 @@ pub struct Union {
|
||||
/// The generic parameters and where clauses on this union.
|
||||
pub generics: Generics,
|
||||
/// Whether any fields have been removed from the result, due to being private or hidden.
|
||||
pub fields_stripped: bool,
|
||||
pub has_stripped_fields: bool,
|
||||
/// The list of fields in the union.
|
||||
///
|
||||
/// All of the corresponding [`Item`]s are of kind [`ItemEnum::StructField`].
|
||||
@ -554,7 +565,7 @@ pub enum StructKind {
|
||||
/// All of the corresponding [`Item`]s are of kind [`ItemEnum::StructField`].
|
||||
fields: Vec<Id>,
|
||||
/// Whether any fields have been removed from the result, due to being private or hidden.
|
||||
fields_stripped: bool,
|
||||
has_stripped_fields: bool,
|
||||
},
|
||||
}
|
||||
|
||||
@ -564,7 +575,7 @@ pub struct Enum {
|
||||
/// Information about the type parameters and `where` clauses of the enum.
|
||||
pub generics: Generics,
|
||||
/// Whether any variants have been removed from the result, due to being private or hidden.
|
||||
pub variants_stripped: bool,
|
||||
pub has_stripped_variants: bool,
|
||||
/// The list of variants in the enum.
|
||||
///
|
||||
/// All of the corresponding [`Item`]s are of kind [`ItemEnum::Variant`]
|
||||
@ -621,7 +632,7 @@ pub enum VariantKind {
|
||||
/// All of the corresponding [`Item`]s are of kind [`ItemEnum::Variant`].
|
||||
fields: Vec<Id>,
|
||||
/// Whether any variants have been removed from the result, due to being private or hidden.
|
||||
fields_stripped: bool,
|
||||
has_stripped_fields: bool,
|
||||
},
|
||||
}
|
||||
|
||||
@ -645,16 +656,13 @@ pub struct Discriminant {
|
||||
|
||||
/// A set of fundamental properties of a function.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct Header {
|
||||
pub struct FunctionHeader {
|
||||
/// Is this function marked as `const`?
|
||||
#[serde(rename = "const")]
|
||||
pub const_: bool,
|
||||
pub is_const: bool,
|
||||
/// Is this function unsafe?
|
||||
#[serde(rename = "unsafe")]
|
||||
pub unsafe_: bool,
|
||||
pub is_unsafe: bool,
|
||||
/// Is this function async?
|
||||
#[serde(rename = "async")]
|
||||
pub async_: bool,
|
||||
pub is_async: bool,
|
||||
/// The ABI used by the function.
|
||||
pub abi: Abi,
|
||||
}
|
||||
@ -697,11 +705,11 @@ pub enum Abi {
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct Function {
|
||||
/// Information about the function signature, or declaration.
|
||||
pub decl: FnDecl,
|
||||
pub sig: FunctionSignature,
|
||||
/// Information about the function’s type parameters and `where` clauses.
|
||||
pub generics: Generics,
|
||||
/// Information about core properties of the function, e.g. whether it's `const`, its ABI, etc.
|
||||
pub header: Header,
|
||||
pub header: FunctionHeader,
|
||||
/// Whether the function has a body, i.e. an implementation.
|
||||
pub has_body: bool,
|
||||
}
|
||||
@ -784,7 +792,7 @@ pub enum GenericParamDefKind {
|
||||
/// In this example, the generic parameter named `impl Trait` (and which
|
||||
/// is bound by `Trait`) is synthetic, because it was not originally in
|
||||
/// the Rust source text.
|
||||
synthetic: bool,
|
||||
is_synthetic: bool,
|
||||
},
|
||||
|
||||
/// Denotes a constant parameter.
|
||||
@ -894,7 +902,7 @@ pub enum TraitBoundModifier {
|
||||
}
|
||||
|
||||
/// Either a type or a constant, usually stored as the right-hand side of an equation in places like
|
||||
/// [`TypeBinding`]
|
||||
/// [`AssocItemConstraint`]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum Term {
|
||||
@ -963,7 +971,7 @@ pub enum Type {
|
||||
/// A raw pointer type, e.g. `*mut u32`, `*const u8`, etc.
|
||||
RawPointer {
|
||||
/// This is `true` for `*mut _` and `false` for `*const _`.
|
||||
mutable: bool,
|
||||
is_mutable: bool,
|
||||
/// The type of the pointee.
|
||||
#[serde(rename = "type")]
|
||||
type_: Box<Type>,
|
||||
@ -973,7 +981,7 @@ pub enum Type {
|
||||
/// The name of the lifetime of the reference, if provided.
|
||||
lifetime: Option<String>,
|
||||
/// This is `true` for `&mut i32` and `false` for `&i32`
|
||||
mutable: bool,
|
||||
is_mutable: bool,
|
||||
/// The type of the pointee, e.g. the `i32` in `&'a mut i32`
|
||||
#[serde(rename = "type")]
|
||||
type_: Box<Type>,
|
||||
@ -1036,7 +1044,7 @@ pub struct Path {
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct FunctionPointer {
|
||||
/// The signature of the function.
|
||||
pub decl: FnDecl,
|
||||
pub sig: FunctionSignature,
|
||||
/// Used for Higher-Rank Trait Bounds (HRTBs)
|
||||
///
|
||||
/// ```ignore (incomplete expression)
|
||||
@ -1045,12 +1053,12 @@ pub struct FunctionPointer {
|
||||
/// ```
|
||||
pub generic_params: Vec<GenericParamDef>,
|
||||
/// The core properties of the function, such as the ABI it conforms to, whether it's unsafe, etc.
|
||||
pub header: Header,
|
||||
pub header: FunctionHeader,
|
||||
}
|
||||
|
||||
/// The signature of a function.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct FnDecl {
|
||||
pub struct FunctionSignature {
|
||||
/// List of argument names and their type.
|
||||
///
|
||||
/// Note that not all names will be valid identifiers, as some of
|
||||
@ -1063,7 +1071,7 @@ pub struct FnDecl {
|
||||
/// ```ignore (incomplete code)
|
||||
/// fn printf(fmt: &str, ...);
|
||||
/// ```
|
||||
pub c_variadic: bool,
|
||||
pub is_c_variadic: bool,
|
||||
}
|
||||
|
||||
/// A `trait` declaration.
|
||||
@ -1127,10 +1135,10 @@ pub struct Impl {
|
||||
/// The list of associated items contained in this impl block.
|
||||
pub items: Vec<Id>,
|
||||
/// Whether this is a negative impl (e.g. `!Sized` or `!Send`).
|
||||
pub negative: bool,
|
||||
pub is_negative: bool,
|
||||
/// Whether this is an impl that’s implied by the compiler
|
||||
/// (for autotraits, e.g. `Send` or `Sync`).
|
||||
pub synthetic: bool,
|
||||
pub is_synthetic: bool,
|
||||
// FIXME: document this
|
||||
pub blanket_impl: Option<Type>,
|
||||
}
|
||||
@ -1138,7 +1146,7 @@ pub struct Impl {
|
||||
/// A `use` statement.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct Import {
|
||||
pub struct Use {
|
||||
/// The full path being imported.
|
||||
pub source: String,
|
||||
/// May be different from the last segment of `source` when renaming imports:
|
||||
@ -1150,7 +1158,7 @@ pub struct Import {
|
||||
/// ```
|
||||
pub id: Option<Id>,
|
||||
/// Whether this statement is a wildcard `use`, e.g. `use source::*;`
|
||||
pub glob: bool,
|
||||
pub is_glob: bool,
|
||||
}
|
||||
|
||||
/// A procedural macro.
|
||||
@ -1205,7 +1213,7 @@ pub struct Static {
|
||||
#[serde(rename = "type")]
|
||||
pub type_: Type,
|
||||
/// This is `true` for mutable statics, declared as `static mut X: T = f();`
|
||||
pub mutable: bool,
|
||||
pub is_mutable: bool,
|
||||
/// The stringified expression for the initial value.
|
||||
///
|
||||
/// It's not guaranteed that it'll match the actual source code for the initial value.
|
||||
|
@ -4,7 +4,7 @@ use super::*;
|
||||
fn test_struct_info_roundtrip() {
|
||||
let s = ItemEnum::Struct(Struct {
|
||||
generics: Generics { params: vec![], where_predicates: vec![] },
|
||||
kind: StructKind::Plain { fields: vec![], fields_stripped: false },
|
||||
kind: StructKind::Plain { fields: vec![], has_stripped_fields: false },
|
||||
impls: vec![],
|
||||
});
|
||||
|
||||
@ -23,7 +23,7 @@ fn test_struct_info_roundtrip() {
|
||||
fn test_union_info_roundtrip() {
|
||||
let u = ItemEnum::Union(Union {
|
||||
generics: Generics { params: vec![], where_predicates: vec![] },
|
||||
fields_stripped: false,
|
||||
has_stripped_fields: false,
|
||||
fields: vec![],
|
||||
impls: vec![],
|
||||
});
|
||||
|
@ -5,7 +5,7 @@ use rustdoc_json_types::{Item, ItemEnum, ItemKind, ItemSummary};
|
||||
pub(crate) enum Kind {
|
||||
Module,
|
||||
ExternCrate,
|
||||
Import,
|
||||
Use,
|
||||
Struct,
|
||||
StructField,
|
||||
Union,
|
||||
@ -18,7 +18,7 @@ pub(crate) enum Kind {
|
||||
TraitAlias,
|
||||
Impl,
|
||||
Static,
|
||||
ForeignType,
|
||||
ExternType,
|
||||
Macro,
|
||||
ProcAttribute,
|
||||
ProcDerive,
|
||||
@ -36,7 +36,7 @@ impl Kind {
|
||||
match self {
|
||||
Module => true,
|
||||
ExternCrate => true,
|
||||
Import => true,
|
||||
Use => true,
|
||||
Union => true,
|
||||
Struct => true,
|
||||
Enum => true,
|
||||
@ -50,7 +50,7 @@ impl Kind {
|
||||
Macro => true,
|
||||
ProcMacro => true,
|
||||
Primitive => true,
|
||||
ForeignType => true,
|
||||
ExternType => true,
|
||||
|
||||
// FIXME(adotinthevoid): I'm not sure if these are correct
|
||||
Keyword => false,
|
||||
@ -69,7 +69,7 @@ impl Kind {
|
||||
pub fn can_appear_in_import(self) -> bool {
|
||||
match self {
|
||||
Kind::Variant => true,
|
||||
Kind::Import => false,
|
||||
Kind::Use => false,
|
||||
other => other.can_appear_in_mod(),
|
||||
}
|
||||
}
|
||||
@ -90,7 +90,7 @@ impl Kind {
|
||||
|
||||
Kind::Module => false,
|
||||
Kind::ExternCrate => false,
|
||||
Kind::Import => false,
|
||||
Kind::Use => false,
|
||||
Kind::Struct => false,
|
||||
Kind::StructField => false,
|
||||
Kind::Union => false,
|
||||
@ -102,7 +102,7 @@ impl Kind {
|
||||
Kind::TraitAlias => false,
|
||||
Kind::Impl => false,
|
||||
Kind::Static => false,
|
||||
Kind::ForeignType => false,
|
||||
Kind::ExternType => false,
|
||||
Kind::Macro => false,
|
||||
Kind::ProcAttribute => false,
|
||||
Kind::ProcDerive => false,
|
||||
@ -135,7 +135,7 @@ impl Kind {
|
||||
use Kind::*;
|
||||
match i.inner {
|
||||
ItemEnum::Module(_) => Module,
|
||||
ItemEnum::Import(_) => Import,
|
||||
ItemEnum::Use(_) => Use,
|
||||
ItemEnum::Union(_) => Union,
|
||||
ItemEnum::Struct(_) => Struct,
|
||||
ItemEnum::StructField(_) => StructField,
|
||||
@ -151,7 +151,7 @@ impl Kind {
|
||||
ItemEnum::Macro(_) => Macro,
|
||||
ItemEnum::ProcMacro(_) => ProcMacro,
|
||||
ItemEnum::Primitive(_) => Primitive,
|
||||
ItemEnum::ForeignType => ForeignType,
|
||||
ItemEnum::ExternType => ExternType,
|
||||
ItemEnum::ExternCrate { .. } => ExternCrate,
|
||||
ItemEnum::AssocConst { .. } => AssocConst,
|
||||
ItemEnum::AssocType { .. } => AssocType,
|
||||
@ -166,10 +166,10 @@ impl Kind {
|
||||
ItemKind::Constant => Constant,
|
||||
ItemKind::Enum => Enum,
|
||||
ItemKind::ExternCrate => ExternCrate,
|
||||
ItemKind::ForeignType => ForeignType,
|
||||
ItemKind::ExternType => ExternType,
|
||||
ItemKind::Function => Function,
|
||||
ItemKind::Impl => Impl,
|
||||
ItemKind::Import => Import,
|
||||
ItemKind::Use => Use,
|
||||
ItemKind::Keyword => Keyword,
|
||||
ItemKind::Macro => Macro,
|
||||
ItemKind::Module => Module,
|
||||
|
@ -2,10 +2,11 @@ use std::collections::HashSet;
|
||||
use std::hash::Hash;
|
||||
|
||||
use rustdoc_json_types::{
|
||||
Constant, Crate, DynTrait, Enum, FnDecl, Function, FunctionPointer, GenericArg, GenericArgs,
|
||||
GenericBound, GenericParamDef, Generics, Id, Impl, Import, ItemEnum, ItemSummary, Module, Path,
|
||||
Primitive, ProcMacro, Static, Struct, StructKind, Term, Trait, TraitAlias, Type, TypeAlias,
|
||||
TypeBinding, TypeBindingKind, Union, Variant, VariantKind, WherePredicate,
|
||||
AssocItemConstraint, AssocItemConstraintKind, Constant, Crate, DynTrait, Enum, Function,
|
||||
FunctionPointer, FunctionSignature, GenericArg, GenericArgs, GenericBound, GenericParamDef,
|
||||
Generics, Id, Impl, ItemEnum, ItemSummary, Module, Path, Primitive, ProcMacro, Static, Struct,
|
||||
StructKind, Term, Trait, TraitAlias, Type, TypeAlias, Union, Use, Variant, VariantKind,
|
||||
WherePredicate,
|
||||
};
|
||||
use serde_json::Value;
|
||||
|
||||
@ -90,7 +91,7 @@ impl<'a> Validator<'a> {
|
||||
item.links.values().for_each(|id| self.add_any_id(id));
|
||||
|
||||
match &item.inner {
|
||||
ItemEnum::Import(x) => self.check_import(x),
|
||||
ItemEnum::Use(x) => self.check_use(x),
|
||||
ItemEnum::Union(x) => self.check_union(x),
|
||||
ItemEnum::Struct(x) => self.check_struct(x),
|
||||
ItemEnum::StructField(x) => self.check_struct_field(x),
|
||||
@ -106,18 +107,18 @@ impl<'a> Validator<'a> {
|
||||
self.check_constant(const_);
|
||||
}
|
||||
ItemEnum::Static(x) => self.check_static(x),
|
||||
ItemEnum::ForeignType => {} // nop
|
||||
ItemEnum::ExternType => {} // nop
|
||||
ItemEnum::Macro(x) => self.check_macro(x),
|
||||
ItemEnum::ProcMacro(x) => self.check_proc_macro(x),
|
||||
ItemEnum::Primitive(x) => self.check_primitive_type(x),
|
||||
ItemEnum::Module(x) => self.check_module(x, id),
|
||||
// FIXME: Why don't these have their own structs?
|
||||
ItemEnum::ExternCrate { .. } => {}
|
||||
ItemEnum::AssocConst { type_, default: _ } => self.check_type(type_),
|
||||
ItemEnum::AssocType { generics, bounds, default } => {
|
||||
ItemEnum::AssocConst { type_, value: _ } => self.check_type(type_),
|
||||
ItemEnum::AssocType { generics, bounds, type_ } => {
|
||||
self.check_generics(generics);
|
||||
bounds.iter().for_each(|b| self.check_generic_bound(b));
|
||||
if let Some(ty) = default {
|
||||
if let Some(ty) = type_ {
|
||||
self.check_type(ty);
|
||||
}
|
||||
}
|
||||
@ -133,8 +134,8 @@ impl<'a> Validator<'a> {
|
||||
module.items.iter().for_each(|i| self.add_mod_item_id(i));
|
||||
}
|
||||
|
||||
fn check_import(&mut self, x: &'a Import) {
|
||||
if x.glob {
|
||||
fn check_use(&mut self, x: &'a Use) {
|
||||
if x.is_glob {
|
||||
self.add_glob_import_item_id(x.id.as_ref().unwrap());
|
||||
} else if let Some(id) = &x.id {
|
||||
self.add_import_item_id(id);
|
||||
@ -152,7 +153,7 @@ impl<'a> Validator<'a> {
|
||||
match &x.kind {
|
||||
StructKind::Unit => {}
|
||||
StructKind::Tuple(fields) => fields.iter().flatten().for_each(|f| self.add_field_id(f)),
|
||||
StructKind::Plain { fields, fields_stripped: _ } => {
|
||||
StructKind::Plain { fields, has_stripped_fields: _ } => {
|
||||
fields.iter().for_each(|f| self.add_field_id(f))
|
||||
}
|
||||
}
|
||||
@ -187,7 +188,7 @@ impl<'a> Validator<'a> {
|
||||
match kind {
|
||||
VariantKind::Plain => {}
|
||||
VariantKind::Tuple(tys) => tys.iter().flatten().for_each(|t| self.add_field_id(t)),
|
||||
VariantKind::Struct { fields, fields_stripped: _ } => {
|
||||
VariantKind::Struct { fields, has_stripped_fields: _ } => {
|
||||
fields.iter().for_each(|f| self.add_field_id(f))
|
||||
}
|
||||
}
|
||||
@ -195,7 +196,7 @@ impl<'a> Validator<'a> {
|
||||
|
||||
fn check_function(&mut self, x: &'a Function) {
|
||||
self.check_generics(&x.generics);
|
||||
self.check_fn_decl(&x.decl);
|
||||
self.check_function_signature(&x.sig);
|
||||
}
|
||||
|
||||
fn check_trait(&mut self, x: &'a Trait, id: &Id) {
|
||||
@ -267,8 +268,8 @@ impl<'a> Validator<'a> {
|
||||
Type::Array { type_, len: _ } => self.check_type(&**type_),
|
||||
Type::ImplTrait(bounds) => bounds.iter().for_each(|b| self.check_generic_bound(b)),
|
||||
Type::Infer => {}
|
||||
Type::RawPointer { mutable: _, type_ } => self.check_type(&**type_),
|
||||
Type::BorrowedRef { lifetime: _, mutable: _, type_ } => self.check_type(&**type_),
|
||||
Type::RawPointer { is_mutable: _, type_ } => self.check_type(&**type_),
|
||||
Type::BorrowedRef { lifetime: _, is_mutable: _, type_ } => self.check_type(&**type_),
|
||||
Type::QualifiedPath { name: _, args, self_type, trait_ } => {
|
||||
self.check_generic_args(&**args);
|
||||
self.check_type(&**self_type);
|
||||
@ -279,7 +280,7 @@ impl<'a> Validator<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_fn_decl(&mut self, x: &'a FnDecl) {
|
||||
fn check_function_signature(&mut self, x: &'a FunctionSignature) {
|
||||
x.inputs.iter().for_each(|(_name, ty)| self.check_type(ty));
|
||||
if let Some(output) = &x.output {
|
||||
self.check_type(output);
|
||||
@ -309,9 +310,9 @@ impl<'a> Validator<'a> {
|
||||
|
||||
fn check_generic_args(&mut self, x: &'a GenericArgs) {
|
||||
match x {
|
||||
GenericArgs::AngleBracketed { args, bindings } => {
|
||||
GenericArgs::AngleBracketed { args, constraints } => {
|
||||
args.iter().for_each(|arg| self.check_generic_arg(arg));
|
||||
bindings.iter().for_each(|bind| self.check_type_binding(bind));
|
||||
constraints.iter().for_each(|bind| self.check_assoc_item_constraint(bind));
|
||||
}
|
||||
GenericArgs::Parenthesized { inputs, output } => {
|
||||
inputs.iter().for_each(|ty| self.check_type(ty));
|
||||
@ -325,7 +326,7 @@ impl<'a> Validator<'a> {
|
||||
fn check_generic_param_def(&mut self, gpd: &'a GenericParamDef) {
|
||||
match &gpd.kind {
|
||||
rustdoc_json_types::GenericParamDefKind::Lifetime { outlives: _ } => {}
|
||||
rustdoc_json_types::GenericParamDefKind::Type { bounds, default, synthetic: _ } => {
|
||||
rustdoc_json_types::GenericParamDefKind::Type { bounds, default, is_synthetic: _ } => {
|
||||
bounds.iter().for_each(|b| self.check_generic_bound(b));
|
||||
if let Some(ty) = default {
|
||||
self.check_type(ty);
|
||||
@ -346,11 +347,11 @@ impl<'a> Validator<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_type_binding(&mut self, bind: &'a TypeBinding) {
|
||||
fn check_assoc_item_constraint(&mut self, bind: &'a AssocItemConstraint) {
|
||||
self.check_generic_args(&bind.args);
|
||||
match &bind.binding {
|
||||
TypeBindingKind::Equality(term) => self.check_term(term),
|
||||
TypeBindingKind::Constraint(bounds) => {
|
||||
AssocItemConstraintKind::Equality(term) => self.check_term(term),
|
||||
AssocItemConstraintKind::Constraint(bounds) => {
|
||||
bounds.iter().for_each(|b| self.check_generic_bound(b))
|
||||
}
|
||||
}
|
||||
@ -388,7 +389,7 @@ impl<'a> Validator<'a> {
|
||||
}
|
||||
|
||||
fn check_function_pointer(&mut self, fp: &'a FunctionPointer) {
|
||||
self.check_fn_decl(&fp.decl);
|
||||
self.check_function_signature(&fp.sig);
|
||||
fp.generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd));
|
||||
}
|
||||
|
||||
|
@ -37,8 +37,6 @@ features = ['unprefixed_malloc_on_supported_platforms']
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc = "0.2"
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
libffi = "3.2.0"
|
||||
libloading = "0.8"
|
||||
|
||||
|
@ -383,7 +383,7 @@ to Miri failing to detect cases of undefined behavior in a program.
|
||||
file descriptors will be mixed up.
|
||||
This is **work in progress**; currently, only integer arguments and return values are
|
||||
supported (and no, pointer/integer casts to work around this limitation will not work;
|
||||
they will fail horribly). It also only works on Linux hosts for now.
|
||||
they will fail horribly). It also only works on Unix hosts for now.
|
||||
* `-Zmiri-measureme=<name>` enables `measureme` profiling for the interpreted program.
|
||||
This can be used to find which parts of your program are executing slowly under Miri.
|
||||
The profile is written out to a file inside a directory called `<name>`, and can be processed
|
||||
|
@ -5,8 +5,8 @@ set MIRI_SCRIPT_TARGET_DIR=%0\..\miri-script\target
|
||||
|
||||
:: If any other steps are added, the "|| exit /b" must be appended to early
|
||||
:: return from the script. If not, it will continue execution.
|
||||
cargo +stable build %CARGO_EXTRA_FLAGS% -q --target-dir %MIRI_SCRIPT_TARGET_DIR% --manifest-path %0\..\miri-script\Cargo.toml ^
|
||||
|| (echo Failed to build miri-script. Is the 'stable' toolchain installed? & exit /b)
|
||||
cargo +nightly build %CARGO_EXTRA_FLAGS% -q --target-dir %MIRI_SCRIPT_TARGET_DIR% --manifest-path %0\..\miri-script\Cargo.toml ^
|
||||
|| (echo Failed to build miri-script. Is the 'nightly' toolchain installed? & exit /b)
|
||||
|
||||
:: Forwards all arguments to this file to the executable.
|
||||
:: We invoke the binary directly to avoid going through rustup, which would set some extra
|
||||
|
@ -1 +1 @@
|
||||
0d634185dfddefe09047881175f35c65d68dcff1
|
||||
54fdef7799d9ff9470bb5cabd29fde9471a99eaa
|
||||
|
@ -5,7 +5,6 @@ mod reuse_pool;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::cmp::max;
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
use rand::Rng;
|
||||
|
||||
@ -151,18 +150,13 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn addr_from_alloc_id(
|
||||
fn addr_from_alloc_id_uncached(
|
||||
&self,
|
||||
global_state: &mut GlobalStateInner,
|
||||
alloc_id: AllocId,
|
||||
memory_kind: MemoryKind,
|
||||
) -> InterpResult<'tcx, u64> {
|
||||
let ecx = self.eval_context_ref();
|
||||
let mut global_state = ecx.machine.alloc_addresses.borrow_mut();
|
||||
let global_state = &mut *global_state;
|
||||
|
||||
Ok(match global_state.base_addr.entry(alloc_id) {
|
||||
Entry::Occupied(entry) => *entry.get(),
|
||||
Entry::Vacant(entry) => {
|
||||
let mut rng = ecx.machine.rng.borrow_mut();
|
||||
let (size, align, kind) = ecx.get_alloc_info(alloc_id);
|
||||
// This is either called immediately after allocation (and then cached), or when
|
||||
@ -173,12 +167,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
assert!(!matches!(kind, AllocKind::Dead));
|
||||
|
||||
// This allocation does not have a base address yet, pick or reuse one.
|
||||
let base_addr = if ecx.machine.native_lib.is_some() {
|
||||
if ecx.machine.native_lib.is_some() {
|
||||
// In native lib mode, we use the "real" address of the bytes for this allocation.
|
||||
// This ensures the interpreted program and native code have the same view of memory.
|
||||
match kind {
|
||||
let base_ptr = match kind {
|
||||
AllocKind::LiveData => {
|
||||
let ptr = if ecx.tcx.try_get_global_alloc(alloc_id).is_some() {
|
||||
if ecx.tcx.try_get_global_alloc(alloc_id).is_some() {
|
||||
// For new global allocations, we always pre-allocate the memory to be able use the machine address directly.
|
||||
let prepared_bytes = MiriAllocBytes::zeroed(size, align)
|
||||
.unwrap_or_else(|| {
|
||||
@ -186,36 +180,37 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
});
|
||||
let ptr = prepared_bytes.as_ptr();
|
||||
// Store prepared allocation space to be picked up for use later.
|
||||
global_state.prepared_alloc_bytes.try_insert(alloc_id, prepared_bytes).unwrap();
|
||||
global_state
|
||||
.prepared_alloc_bytes
|
||||
.try_insert(alloc_id, prepared_bytes)
|
||||
.unwrap();
|
||||
ptr
|
||||
} else {
|
||||
ecx.get_alloc_bytes_unchecked_raw(alloc_id)?
|
||||
};
|
||||
// Ensure this pointer's provenance is exposed, so that it can be used by FFI code.
|
||||
ptr.expose_provenance().try_into().unwrap()
|
||||
}
|
||||
}
|
||||
AllocKind::Function | AllocKind::VTable => {
|
||||
// Allocate some dummy memory to get a unique address for this function/vtable.
|
||||
let alloc_bytes = MiriAllocBytes::from_bytes(&[0u8; 1], Align::from_bytes(1).unwrap());
|
||||
// We don't need to expose these bytes as nobody is allowed to access them.
|
||||
let addr = alloc_bytes.as_ptr().addr().try_into().unwrap();
|
||||
let alloc_bytes =
|
||||
MiriAllocBytes::from_bytes(&[0u8; 1], Align::from_bytes(1).unwrap());
|
||||
let ptr = alloc_bytes.as_ptr();
|
||||
// Leak the underlying memory to ensure it remains unique.
|
||||
std::mem::forget(alloc_bytes);
|
||||
addr
|
||||
ptr
|
||||
}
|
||||
AllocKind::Dead => unreachable!()
|
||||
AllocKind::Dead => unreachable!(),
|
||||
};
|
||||
// Ensure this pointer's provenance is exposed, so that it can be used by FFI code.
|
||||
return Ok(base_ptr.expose_provenance().try_into().unwrap());
|
||||
}
|
||||
} else if let Some((reuse_addr, clock)) = global_state.reuse.take_addr(
|
||||
&mut *rng,
|
||||
size,
|
||||
align,
|
||||
memory_kind,
|
||||
ecx.active_thread(),
|
||||
) {
|
||||
// We are not in native lib mode, so we control the addresses ourselves.
|
||||
if let Some((reuse_addr, clock)) =
|
||||
global_state.reuse.take_addr(&mut *rng, size, align, memory_kind, ecx.active_thread())
|
||||
{
|
||||
if let Some(clock) = clock {
|
||||
ecx.acquire_clock(&clock);
|
||||
}
|
||||
reuse_addr
|
||||
Ok(reuse_addr)
|
||||
} else {
|
||||
// We have to pick a fresh address.
|
||||
// Leave some space to the previous allocation, to give it some chance to be less aligned.
|
||||
@ -228,10 +223,10 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
.ok_or_else(|| err_exhaust!(AddressSpaceFull))?;
|
||||
let base_addr = align_addr(base_addr, align.bytes());
|
||||
|
||||
// Remember next base address. If this allocation is zero-sized, leave a gap
|
||||
// of at least 1 to avoid two allocations having the same base address.
|
||||
// (The logic in `alloc_id_from_addr` assumes unique addresses, and different
|
||||
// function/vtable pointers need to be distinguishable!)
|
||||
// Remember next base address. If this allocation is zero-sized, leave a gap of at
|
||||
// least 1 to avoid two allocations having the same base address. (The logic in
|
||||
// `alloc_id_from_addr` assumes unique addresses, and different function/vtable pointers
|
||||
// need to be distinguishable!)
|
||||
global_state.next_base_addr = base_addr
|
||||
.checked_add(max(size.bytes(), 1))
|
||||
.ok_or_else(|| err_exhaust!(AddressSpaceFull))?;
|
||||
@ -240,18 +235,29 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
throw_exhaust!(AddressSpaceFull);
|
||||
}
|
||||
|
||||
base_addr
|
||||
};
|
||||
trace!(
|
||||
"Assigning base address {:#x} to allocation {:?} (size: {}, align: {})",
|
||||
base_addr,
|
||||
alloc_id,
|
||||
size.bytes(),
|
||||
align.bytes(),
|
||||
);
|
||||
Ok(base_addr)
|
||||
}
|
||||
}
|
||||
|
||||
fn addr_from_alloc_id(
|
||||
&self,
|
||||
alloc_id: AllocId,
|
||||
memory_kind: MemoryKind,
|
||||
) -> InterpResult<'tcx, u64> {
|
||||
let ecx = self.eval_context_ref();
|
||||
let mut global_state = ecx.machine.alloc_addresses.borrow_mut();
|
||||
let global_state = &mut *global_state;
|
||||
|
||||
match global_state.base_addr.get(&alloc_id) {
|
||||
Some(&addr) => Ok(addr),
|
||||
None => {
|
||||
// First time we're looking for the absolute address of this allocation.
|
||||
let base_addr =
|
||||
self.addr_from_alloc_id_uncached(global_state, alloc_id, memory_kind)?;
|
||||
trace!("Assigning base address {:#x} to allocation {:?}", base_addr, alloc_id);
|
||||
|
||||
// Store address in cache.
|
||||
entry.insert(base_addr);
|
||||
global_state.base_addr.try_insert(alloc_id, base_addr).unwrap();
|
||||
|
||||
// Also maintain the opposite mapping in `int_to_ptr_map`, ensuring we keep it sorted.
|
||||
// We have a fast-path for the common case that this address is bigger than all previous ones.
|
||||
@ -269,9 +275,9 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
};
|
||||
global_state.int_to_ptr_map.insert(pos, (base_addr, alloc_id));
|
||||
|
||||
base_addr
|
||||
Ok(base_addr)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -359,16 +365,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
|
||||
// This returns some prepared `MiriAllocBytes`, either because `addr_from_alloc_id` reserved
|
||||
// memory space in the past, or by doing the pre-allocation right upon being called.
|
||||
fn get_global_alloc_bytes(&self, id: AllocId, kind: MemoryKind, bytes: &[u8], align: Align) -> InterpResult<'tcx, MiriAllocBytes> {
|
||||
fn get_global_alloc_bytes(
|
||||
&self,
|
||||
id: AllocId,
|
||||
kind: MemoryKind,
|
||||
bytes: &[u8],
|
||||
align: Align,
|
||||
) -> InterpResult<'tcx, MiriAllocBytes> {
|
||||
let ecx = self.eval_context_ref();
|
||||
Ok(if ecx.machine.native_lib.is_some() {
|
||||
if ecx.machine.native_lib.is_some() {
|
||||
// In native lib mode, MiriAllocBytes for global allocations are handled via `prepared_alloc_bytes`.
|
||||
// This additional call ensures that some `MiriAllocBytes` are always prepared.
|
||||
// This additional call ensures that some `MiriAllocBytes` are always prepared, just in case
|
||||
// this function gets called before the first time `addr_from_alloc_id` gets called.
|
||||
ecx.addr_from_alloc_id(id, kind)?;
|
||||
let mut global_state = ecx.machine.alloc_addresses.borrow_mut();
|
||||
// The memory we need here will have already been allocated during an earlier call to
|
||||
// `addr_from_alloc_id` for this allocation. So don't create a new `MiriAllocBytes` here, instead
|
||||
// fetch the previously prepared bytes from `prepared_alloc_bytes`.
|
||||
let mut global_state = ecx.machine.alloc_addresses.borrow_mut();
|
||||
let mut prepared_alloc_bytes = global_state
|
||||
.prepared_alloc_bytes
|
||||
.remove(&id)
|
||||
@ -378,10 +391,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
assert_eq!(prepared_alloc_bytes.len(), bytes.len());
|
||||
// Copy allocation contents into prepared memory.
|
||||
prepared_alloc_bytes.copy_from_slice(bytes);
|
||||
prepared_alloc_bytes
|
||||
Ok(prepared_alloc_bytes)
|
||||
} else {
|
||||
MiriAllocBytes::from_bytes(std::borrow::Cow::Borrowed(&*bytes), align)
|
||||
})
|
||||
Ok(MiriAllocBytes::from_bytes(std::borrow::Cow::Borrowed(bytes), align))
|
||||
}
|
||||
}
|
||||
|
||||
/// When a pointer is used for a memory access, this computes where in which allocation the
|
||||
|
@ -35,7 +35,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
offset: u64,
|
||||
) -> InterpResult<'tcx, InitOnceId> {
|
||||
let this = self.eval_context_mut();
|
||||
this.get_or_create_id(lock_op, lock_layout, offset, |ecx| &mut ecx.machine.sync.init_onces)?
|
||||
this.get_or_create_id(
|
||||
lock_op,
|
||||
lock_layout,
|
||||
offset,
|
||||
|ecx| &mut ecx.machine.sync.init_onces,
|
||||
|_| Ok(Default::default()),
|
||||
)?
|
||||
.ok_or_else(|| err_ub_format!("init_once has invalid ID").into())
|
||||
}
|
||||
|
||||
|
@ -66,6 +66,27 @@ pub(super) use declare_id;
|
||||
|
||||
declare_id!(MutexId);
|
||||
|
||||
/// The mutex kind.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[non_exhaustive]
|
||||
pub enum MutexKind {
|
||||
Invalid,
|
||||
Normal,
|
||||
Default,
|
||||
Recursive,
|
||||
ErrorCheck,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Additional data that may be used by shim implementations.
|
||||
pub struct AdditionalMutexData {
|
||||
/// The mutex kind, used by some mutex implementations like pthreads mutexes.
|
||||
pub kind: MutexKind,
|
||||
|
||||
/// The address of the mutex.
|
||||
pub address: u64,
|
||||
}
|
||||
|
||||
/// The mutex state.
|
||||
#[derive(Default, Debug)]
|
||||
struct Mutex {
|
||||
@ -77,6 +98,9 @@ struct Mutex {
|
||||
queue: VecDeque<ThreadId>,
|
||||
/// Mutex clock. This tracks the moment of the last unlock.
|
||||
clock: VClock,
|
||||
|
||||
/// Additional data that can be set by shim implementations.
|
||||
data: Option<AdditionalMutexData>,
|
||||
}
|
||||
|
||||
declare_id!(RwLockId);
|
||||
@ -168,13 +192,15 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
/// Returns `None` if memory stores a non-zero invalid ID.
|
||||
///
|
||||
/// `get_objs` must return the `IndexVec` that stores all the objects of this type.
|
||||
/// `create_obj` must create the new object if initialization is needed.
|
||||
#[inline]
|
||||
fn get_or_create_id<Id: SyncId + Idx, T: Default>(
|
||||
fn get_or_create_id<Id: SyncId + Idx, T>(
|
||||
&mut self,
|
||||
lock_op: &OpTy<'tcx>,
|
||||
lock_layout: TyAndLayout<'tcx>,
|
||||
offset: u64,
|
||||
get_objs: impl for<'a> Fn(&'a mut MiriInterpCx<'tcx>) -> &'a mut IndexVec<Id, T>,
|
||||
create_obj: impl for<'a> FnOnce(&'a mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, T>,
|
||||
) -> InterpResult<'tcx, Option<Id>> {
|
||||
let this = self.eval_context_mut();
|
||||
let value_place =
|
||||
@ -196,7 +222,8 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
Ok(if success.to_bool().expect("compare_exchange's second return value is a bool") {
|
||||
// We set the in-memory ID to `next_index`, now also create this object in the machine
|
||||
// state.
|
||||
let new_index = get_objs(this).push(T::default());
|
||||
let obj = create_obj(this)?;
|
||||
let new_index = get_objs(this).push(obj);
|
||||
assert_eq!(next_index, new_index);
|
||||
Some(new_index)
|
||||
} else {
|
||||
@ -210,6 +237,32 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Eagerly creates a Miri sync structure.
|
||||
///
|
||||
/// `create_id` will store the index of the sync_structure in the memory pointed to by
|
||||
/// `lock_op`, so that future calls to `get_or_create_id` will see it as initialized.
|
||||
/// - `lock_op` must hold a pointer to the sync structure.
|
||||
/// - `lock_layout` must be the memory layout of the sync structure.
|
||||
/// - `offset` must be the offset inside the sync structure where its miri id will be stored.
|
||||
/// - `get_objs` is described in `get_or_create_id`.
|
||||
/// - `obj` must be the new sync object.
|
||||
fn create_id<Id: SyncId + Idx, T>(
|
||||
&mut self,
|
||||
lock_op: &OpTy<'tcx>,
|
||||
lock_layout: TyAndLayout<'tcx>,
|
||||
offset: u64,
|
||||
get_objs: impl for<'a> Fn(&'a mut MiriInterpCx<'tcx>) -> &'a mut IndexVec<Id, T>,
|
||||
obj: T,
|
||||
) -> InterpResult<'tcx, Id> {
|
||||
let this = self.eval_context_mut();
|
||||
let value_place =
|
||||
this.deref_pointer_and_offset(lock_op, offset, lock_layout, this.machine.layouts.u32)?;
|
||||
|
||||
let new_index = get_objs(this).push(obj);
|
||||
this.write_scalar(Scalar::from_u32(new_index.to_u32()), &value_place)?;
|
||||
Ok(new_index)
|
||||
}
|
||||
|
||||
fn condvar_reacquire_mutex(
|
||||
&mut self,
|
||||
mutex: MutexId,
|
||||
@ -236,17 +289,55 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// situations.
|
||||
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
|
||||
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
/// Eagerly create and initialize a new mutex.
|
||||
fn mutex_create(
|
||||
&mut self,
|
||||
lock_op: &OpTy<'tcx>,
|
||||
lock_layout: TyAndLayout<'tcx>,
|
||||
offset: u64,
|
||||
data: Option<AdditionalMutexData>,
|
||||
) -> InterpResult<'tcx, MutexId> {
|
||||
let this = self.eval_context_mut();
|
||||
this.create_id(
|
||||
lock_op,
|
||||
lock_layout,
|
||||
offset,
|
||||
|ecx| &mut ecx.machine.sync.mutexes,
|
||||
Mutex { data, ..Default::default() },
|
||||
)
|
||||
}
|
||||
|
||||
/// Lazily create a new mutex.
|
||||
/// `initialize_data` must return any additional data that a user wants to associate with the mutex.
|
||||
fn mutex_get_or_create_id(
|
||||
&mut self,
|
||||
lock_op: &OpTy<'tcx>,
|
||||
lock_layout: TyAndLayout<'tcx>,
|
||||
offset: u64,
|
||||
initialize_data: impl for<'a> FnOnce(
|
||||
&'a mut MiriInterpCx<'tcx>,
|
||||
) -> InterpResult<'tcx, Option<AdditionalMutexData>>,
|
||||
) -> InterpResult<'tcx, MutexId> {
|
||||
let this = self.eval_context_mut();
|
||||
this.get_or_create_id(lock_op, lock_layout, offset, |ecx| &mut ecx.machine.sync.mutexes)?
|
||||
this.get_or_create_id(
|
||||
lock_op,
|
||||
lock_layout,
|
||||
offset,
|
||||
|ecx| &mut ecx.machine.sync.mutexes,
|
||||
|ecx| initialize_data(ecx).map(|data| Mutex { data, ..Default::default() }),
|
||||
)?
|
||||
.ok_or_else(|| err_ub_format!("mutex has invalid ID").into())
|
||||
}
|
||||
|
||||
/// Retrieve the additional data stored for a mutex.
|
||||
fn mutex_get_data<'a>(&'a mut self, id: MutexId) -> Option<&'a AdditionalMutexData>
|
||||
where
|
||||
'tcx: 'a,
|
||||
{
|
||||
let this = self.eval_context_ref();
|
||||
this.machine.sync.mutexes[id].data.as_ref()
|
||||
}
|
||||
|
||||
fn rwlock_get_or_create_id(
|
||||
&mut self,
|
||||
lock_op: &OpTy<'tcx>,
|
||||
@ -254,7 +345,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
offset: u64,
|
||||
) -> InterpResult<'tcx, RwLockId> {
|
||||
let this = self.eval_context_mut();
|
||||
this.get_or_create_id(lock_op, lock_layout, offset, |ecx| &mut ecx.machine.sync.rwlocks)?
|
||||
this.get_or_create_id(
|
||||
lock_op,
|
||||
lock_layout,
|
||||
offset,
|
||||
|ecx| &mut ecx.machine.sync.rwlocks,
|
||||
|_| Ok(Default::default()),
|
||||
)?
|
||||
.ok_or_else(|| err_ub_format!("rwlock has invalid ID").into())
|
||||
}
|
||||
|
||||
@ -265,7 +362,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
offset: u64,
|
||||
) -> InterpResult<'tcx, CondvarId> {
|
||||
let this = self.eval_context_mut();
|
||||
this.get_or_create_id(lock_op, lock_layout, offset, |ecx| &mut ecx.machine.sync.condvars)?
|
||||
this.get_or_create_id(
|
||||
lock_op,
|
||||
lock_layout,
|
||||
offset,
|
||||
|ecx| &mut ecx.machine.sync.condvars,
|
||||
|_| Ok(Default::default()),
|
||||
)?
|
||||
.ok_or_else(|| err_ub_format!("condvar has invalid ID").into())
|
||||
}
|
||||
|
||||
|
@ -888,8 +888,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
}
|
||||
let alloc = this.ctfe_query(|tcx| tcx.eval_static_initializer(def_id))?;
|
||||
// We make a full copy of this allocation.
|
||||
let mut alloc =
|
||||
alloc.inner().adjust_from_tcx(&this.tcx, |bytes, align| Ok(MiriAllocBytes::from_bytes(std::borrow::Cow::Borrowed(bytes), align)), |ptr| this.global_root_pointer(ptr))?;
|
||||
let mut alloc = alloc.inner().adjust_from_tcx(
|
||||
&this.tcx,
|
||||
|bytes, align| {
|
||||
Ok(MiriAllocBytes::from_bytes(std::borrow::Cow::Borrowed(bytes), align))
|
||||
},
|
||||
|ptr| this.global_root_pointer(ptr),
|
||||
)?;
|
||||
// This allocation will be deallocated when the thread dies, so it is not in read-only memory.
|
||||
alloc.mutability = Mutability::Mut;
|
||||
// Create a fresh allocation with this content.
|
||||
|
@ -133,7 +133,10 @@ pub use crate::concurrency::{
|
||||
cpu_affinity::MAX_CPUS,
|
||||
data_race::{AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, EvalContextExt as _},
|
||||
init_once::{EvalContextExt as _, InitOnceId},
|
||||
sync::{CondvarId, EvalContextExt as _, MutexId, RwLockId, SynchronizationObjects},
|
||||
sync::{
|
||||
AdditionalMutexData, CondvarId, EvalContextExt as _, MutexId, MutexKind, RwLockId,
|
||||
SynchronizationObjects,
|
||||
},
|
||||
thread::{
|
||||
BlockReason, EvalContextExt as _, StackEmptyCallback, ThreadId, ThreadManager,
|
||||
TimeoutAnchor, TimeoutClock, UnblockCallback,
|
||||
|
@ -535,9 +535,9 @@ pub struct MiriMachine<'tcx> {
|
||||
pub(crate) basic_block_count: u64,
|
||||
|
||||
/// Handle of the optional shared object file for native functions.
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(unix)]
|
||||
pub native_lib: Option<(libloading::Library, std::path::PathBuf)>,
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
#[cfg(not(unix))]
|
||||
pub native_lib: Option<!>,
|
||||
|
||||
/// Run a garbage collector for BorTags every N basic blocks.
|
||||
@ -678,7 +678,7 @@ impl<'tcx> MiriMachine<'tcx> {
|
||||
report_progress: config.report_progress,
|
||||
basic_block_count: 0,
|
||||
clock: Clock::new(config.isolated_op == IsolatedOp::Allow),
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(unix)]
|
||||
native_lib: config.native_lib.as_ref().map(|lib_file_path| {
|
||||
let target_triple = layout_cx.tcx.sess.opts.target_triple.triple();
|
||||
// Check if host target == the session target.
|
||||
@ -700,9 +700,9 @@ impl<'tcx> MiriMachine<'tcx> {
|
||||
lib_file_path.clone(),
|
||||
)
|
||||
}),
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
#[cfg(not(unix))]
|
||||
native_lib: config.native_lib.as_ref().map(|_| {
|
||||
panic!("loading external .so files is only supported on Linux")
|
||||
panic!("calling functions from native libraries via FFI is only supported on Unix")
|
||||
}),
|
||||
gc_interval: config.gc_interval,
|
||||
since_gc: 0,
|
||||
@ -1277,12 +1277,12 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
|
||||
) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra, Self::Bytes>>>
|
||||
{
|
||||
let kind = Self::GLOBAL_KIND.unwrap().into();
|
||||
let alloc = alloc.adjust_from_tcx(&ecx.tcx,
|
||||
let alloc = alloc.adjust_from_tcx(
|
||||
&ecx.tcx,
|
||||
|bytes, align| ecx.get_global_alloc_bytes(id, kind, bytes, align),
|
||||
|ptr| ecx.global_root_pointer(ptr),
|
||||
)?;
|
||||
let extra =
|
||||
Self::init_alloc_extra(ecx, id, kind, alloc.size(), alloc.align)?;
|
||||
let extra = Self::init_alloc_extra(ecx, id, kind, alloc.size(), alloc.align)?;
|
||||
Ok(Cow::Owned(alloc.with_extra(extra)))
|
||||
}
|
||||
|
||||
|
@ -226,7 +226,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
// First deal with any external C functions in linked .so file.
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(unix)]
|
||||
if this.machine.native_lib.as_ref().is_some() {
|
||||
use crate::shims::native_lib::EvalContextExt as _;
|
||||
// An Ok(false) here means that the function being called was not exported
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
mod alloc;
|
||||
mod backtrace;
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(unix)]
|
||||
mod native_lib;
|
||||
mod unix;
|
||||
mod wasi;
|
||||
|
@ -240,13 +240,16 @@ fn imm_to_carg<'tcx>(v: ImmTy<'tcx>, cx: &impl HasDataLayout) -> InterpResult<'t
|
||||
ty::RawPtr(_, mutability) => {
|
||||
// Arbitrary mutable pointer accesses are not currently supported in Miri.
|
||||
if mutability.is_mut() {
|
||||
throw_unsup_format!("unsupported mutable pointer type for native call: {}", v.layout.ty);
|
||||
throw_unsup_format!(
|
||||
"unsupported mutable pointer type for native call: {}",
|
||||
v.layout.ty
|
||||
);
|
||||
} else {
|
||||
let s = v.to_scalar().to_pointer(cx)?.addr();
|
||||
// This relies on the `expose_provenance` in `addr_from_alloc_id`.
|
||||
CArg::RawPtr(std::ptr::with_exposed_provenance_mut(s.bytes_usize()))
|
||||
}
|
||||
},
|
||||
}
|
||||
_ => throw_unsup_format!("unsupported argument type for native call: {}", v.layout.ty),
|
||||
})
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// os_unfair_lock holds a 32-bit value, is initialized with zero and
|
||||
// must be assumed to be opaque. Therefore, we can just store our
|
||||
// internal mutex ID in the structure without anyone noticing.
|
||||
this.mutex_get_or_create_id(lock_op, this.libc_ty_layout("os_unfair_lock"), 0)
|
||||
this.mutex_get_or_create_id(lock_op, this.libc_ty_layout("os_unfair_lock"), 0, |_| Ok(None))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,6 @@ fn is_mutex_kind_normal<'tcx>(ecx: &MiriInterpCx<'tcx>, kind: i32) -> InterpResu
|
||||
// pthread_mutex_t is between 24 and 48 bytes, depending on the platform.
|
||||
// We ignore the platform layout and store our own fields:
|
||||
// - id: u32
|
||||
// - kind: i32
|
||||
|
||||
fn mutex_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
|
||||
let offset = match &*ecx.tcx.sess.target.os {
|
||||
@ -74,6 +73,8 @@ fn mutex_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
|
||||
|
||||
// Sanity-check this against PTHREAD_MUTEX_INITIALIZER (but only once):
|
||||
// the id must start out as 0.
|
||||
// FIXME on some platforms (e.g linux) there are more static initializers for
|
||||
// recursive or error checking mutexes. We should also add thme in this sanity check.
|
||||
static SANITY: AtomicBool = AtomicBool::new(false);
|
||||
if !SANITY.swap(true, Ordering::Relaxed) {
|
||||
let static_initializer = ecx.eval_path(&["libc", "PTHREAD_MUTEX_INITIALIZER"]);
|
||||
@ -90,79 +91,92 @@ fn mutex_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
|
||||
Ok(offset)
|
||||
}
|
||||
|
||||
fn mutex_kind_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> u64 {
|
||||
// These offsets are picked for compatibility with Linux's static initializer
|
||||
// macros, e.g. PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP.)
|
||||
let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 };
|
||||
|
||||
// Sanity-check this against PTHREAD_MUTEX_INITIALIZER (but only once):
|
||||
// the kind must start out as PTHREAD_MUTEX_DEFAULT.
|
||||
static SANITY: AtomicBool = AtomicBool::new(false);
|
||||
if !SANITY.swap(true, Ordering::Relaxed) {
|
||||
let static_initializer = ecx.eval_path(&["libc", "PTHREAD_MUTEX_INITIALIZER"]);
|
||||
let kind_field = static_initializer
|
||||
.offset(Size::from_bytes(mutex_kind_offset(ecx)), ecx.machine.layouts.i32, ecx)
|
||||
.unwrap();
|
||||
let kind = ecx.read_scalar(&kind_field).unwrap().to_i32().unwrap();
|
||||
assert_eq!(
|
||||
kind,
|
||||
ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT"),
|
||||
"PTHREAD_MUTEX_INITIALIZER is incompatible with our pthread_mutex layout: kind is not PTHREAD_MUTEX_DEFAULT"
|
||||
);
|
||||
}
|
||||
|
||||
offset
|
||||
/// Eagerly create and initialize a new mutex.
|
||||
fn mutex_create<'tcx>(
|
||||
ecx: &mut MiriInterpCx<'tcx>,
|
||||
mutex_op: &OpTy<'tcx>,
|
||||
kind: i32,
|
||||
) -> InterpResult<'tcx> {
|
||||
// FIXME: might be worth changing mutex_create to take the mplace
|
||||
// rather than the `OpTy`.
|
||||
let address = ecx.read_pointer(mutex_op)?.addr().bytes();
|
||||
let kind = translate_kind(ecx, kind)?;
|
||||
let data = Some(AdditionalMutexData { address, kind });
|
||||
ecx.mutex_create(mutex_op, ecx.libc_ty_layout("pthread_mutex_t"), mutex_id_offset(ecx)?, data)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the `MutexId` of the mutex stored at `mutex_op`.
|
||||
///
|
||||
/// `mutex_get_id` will also check if the mutex has been moved since its first use and
|
||||
/// return an error if it has.
|
||||
fn mutex_get_id<'tcx>(
|
||||
ecx: &mut MiriInterpCx<'tcx>,
|
||||
mutex_op: &OpTy<'tcx>,
|
||||
) -> InterpResult<'tcx, MutexId> {
|
||||
ecx.mutex_get_or_create_id(
|
||||
let address = ecx.read_pointer(mutex_op)?.addr().bytes();
|
||||
|
||||
// FIXME: might be worth changing mutex_get_or_create_id to take the mplace
|
||||
// rather than the `OpTy`.
|
||||
let id = ecx.mutex_get_or_create_id(
|
||||
mutex_op,
|
||||
ecx.libc_ty_layout("pthread_mutex_t"),
|
||||
mutex_id_offset(ecx)?,
|
||||
)
|
||||
|ecx| {
|
||||
// This is called if a static initializer was used and the lock has not been assigned
|
||||
// an ID yet. We have to determine the mutex kind from the static initializer.
|
||||
let kind = kind_from_static_initializer(ecx, mutex_op)?;
|
||||
|
||||
Ok(Some(AdditionalMutexData { kind, address }))
|
||||
},
|
||||
)?;
|
||||
|
||||
// Check that the mutex has not been moved since last use.
|
||||
let data = ecx.mutex_get_data(id).expect("data should be always exist for pthreads");
|
||||
if data.address != address {
|
||||
throw_ub_format!("pthread_mutex_t can't be moved after first use")
|
||||
}
|
||||
|
||||
fn mutex_reset_id<'tcx>(
|
||||
ecx: &mut MiriInterpCx<'tcx>,
|
||||
mutex_op: &OpTy<'tcx>,
|
||||
) -> InterpResult<'tcx, ()> {
|
||||
ecx.deref_pointer_and_write(
|
||||
mutex_op,
|
||||
mutex_id_offset(ecx)?,
|
||||
Scalar::from_u32(0),
|
||||
ecx.libc_ty_layout("pthread_mutex_t"),
|
||||
ecx.machine.layouts.u32,
|
||||
)
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
fn mutex_get_kind<'tcx>(
|
||||
/// Returns the kind of a static initializer.
|
||||
fn kind_from_static_initializer<'tcx>(
|
||||
ecx: &MiriInterpCx<'tcx>,
|
||||
mutex_op: &OpTy<'tcx>,
|
||||
) -> InterpResult<'tcx, i32> {
|
||||
) -> InterpResult<'tcx, MutexKind> {
|
||||
// Only linux has static initializers other than PTHREAD_MUTEX_DEFAULT.
|
||||
let kind = match &*ecx.tcx.sess.target.os {
|
||||
"linux" => {
|
||||
let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 };
|
||||
|
||||
ecx.deref_pointer_and_read(
|
||||
mutex_op,
|
||||
mutex_kind_offset(ecx),
|
||||
offset,
|
||||
ecx.libc_ty_layout("pthread_mutex_t"),
|
||||
ecx.machine.layouts.i32,
|
||||
)?
|
||||
.to_i32()
|
||||
.to_i32()?
|
||||
}
|
||||
| "illumos" | "solaris" | "macos" => ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT"),
|
||||
os => throw_unsup_format!("`pthread_mutex` is not supported on {os}"),
|
||||
};
|
||||
|
||||
translate_kind(ecx, kind)
|
||||
}
|
||||
|
||||
fn mutex_set_kind<'tcx>(
|
||||
ecx: &mut MiriInterpCx<'tcx>,
|
||||
mutex_op: &OpTy<'tcx>,
|
||||
kind: i32,
|
||||
) -> InterpResult<'tcx, ()> {
|
||||
ecx.deref_pointer_and_write(
|
||||
mutex_op,
|
||||
mutex_kind_offset(ecx),
|
||||
Scalar::from_i32(kind),
|
||||
ecx.libc_ty_layout("pthread_mutex_t"),
|
||||
ecx.machine.layouts.i32,
|
||||
)
|
||||
fn translate_kind<'tcx>(ecx: &MiriInterpCx<'tcx>, kind: i32) -> InterpResult<'tcx, MutexKind> {
|
||||
Ok(if is_mutex_kind_default(ecx, kind)? {
|
||||
MutexKind::Default
|
||||
} else if is_mutex_kind_normal(ecx, kind)? {
|
||||
MutexKind::Normal
|
||||
} else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK") {
|
||||
MutexKind::ErrorCheck
|
||||
} else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE") {
|
||||
MutexKind::Recursive
|
||||
} else {
|
||||
throw_unsup_format!("unsupported type of mutex: {kind}");
|
||||
})
|
||||
}
|
||||
|
||||
// pthread_rwlock_t is between 32 and 56 bytes, depending on the platform.
|
||||
@ -452,10 +466,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
mutexattr_get_kind(this, attr_op)?
|
||||
};
|
||||
|
||||
// Write 0 to use the same code path as the static initializers.
|
||||
mutex_reset_id(this, mutex_op)?;
|
||||
|
||||
mutex_set_kind(this, mutex_op, kind)?;
|
||||
mutex_create(this, mutex_op, kind)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -467,8 +478,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
let kind = mutex_get_kind(this, mutex_op)?;
|
||||
let id = mutex_get_id(this, mutex_op)?;
|
||||
let kind =
|
||||
this.mutex_get_data(id).expect("data should always exist for pthread mutexes").kind;
|
||||
|
||||
let ret = if this.mutex_is_locked(id) {
|
||||
let owner_thread = this.mutex_get_owner(id);
|
||||
@ -477,19 +489,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
return Ok(());
|
||||
} else {
|
||||
// Trying to acquire the same mutex again.
|
||||
if is_mutex_kind_default(this, kind)? {
|
||||
throw_ub_format!("trying to acquire already locked default mutex");
|
||||
} else if is_mutex_kind_normal(this, kind)? {
|
||||
throw_machine_stop!(TerminationInfo::Deadlock);
|
||||
} else if kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK") {
|
||||
this.eval_libc_i32("EDEADLK")
|
||||
} else if kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE") {
|
||||
match kind {
|
||||
MutexKind::Default =>
|
||||
throw_ub_format!("trying to acquire already locked default mutex"),
|
||||
MutexKind::Normal => throw_machine_stop!(TerminationInfo::Deadlock),
|
||||
MutexKind::ErrorCheck => this.eval_libc_i32("EDEADLK"),
|
||||
MutexKind::Recursive => {
|
||||
this.mutex_lock(id);
|
||||
0
|
||||
} else {
|
||||
}
|
||||
_ =>
|
||||
throw_unsup_format!(
|
||||
"called pthread_mutex_lock on an unsupported type of mutex"
|
||||
);
|
||||
),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -504,26 +516,26 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
fn pthread_mutex_trylock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
let kind = mutex_get_kind(this, mutex_op)?;
|
||||
let id = mutex_get_id(this, mutex_op)?;
|
||||
let kind =
|
||||
this.mutex_get_data(id).expect("data should always exist for pthread mutexes").kind;
|
||||
|
||||
Ok(Scalar::from_i32(if this.mutex_is_locked(id) {
|
||||
let owner_thread = this.mutex_get_owner(id);
|
||||
if owner_thread != this.active_thread() {
|
||||
this.eval_libc_i32("EBUSY")
|
||||
} else {
|
||||
if is_mutex_kind_default(this, kind)?
|
||||
|| is_mutex_kind_normal(this, kind)?
|
||||
|| kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK")
|
||||
{
|
||||
this.eval_libc_i32("EBUSY")
|
||||
} else if kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE") {
|
||||
match kind {
|
||||
MutexKind::Default | MutexKind::Normal | MutexKind::ErrorCheck =>
|
||||
this.eval_libc_i32("EBUSY"),
|
||||
MutexKind::Recursive => {
|
||||
this.mutex_lock(id);
|
||||
0
|
||||
} else {
|
||||
}
|
||||
_ =>
|
||||
throw_unsup_format!(
|
||||
"called pthread_mutex_trylock on an unsupported type of mutex"
|
||||
);
|
||||
),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -536,8 +548,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
fn pthread_mutex_unlock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
let kind = mutex_get_kind(this, mutex_op)?;
|
||||
let id = mutex_get_id(this, mutex_op)?;
|
||||
let kind =
|
||||
this.mutex_get_data(id).expect("data should always exist for pthread mutexes").kind;
|
||||
|
||||
if let Some(_old_locked_count) = this.mutex_unlock(id)? {
|
||||
// The mutex was locked by the current thread.
|
||||
@ -546,20 +559,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// The mutex was locked by another thread or not locked at all. See
|
||||
// the “Unlock When Not Owner” column in
|
||||
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_unlock.html.
|
||||
if is_mutex_kind_default(this, kind)? {
|
||||
match kind {
|
||||
MutexKind::Default =>
|
||||
throw_ub_format!(
|
||||
"unlocked a default mutex that was not locked by the current thread"
|
||||
);
|
||||
} else if is_mutex_kind_normal(this, kind)? {
|
||||
),
|
||||
MutexKind::Normal =>
|
||||
throw_ub_format!(
|
||||
"unlocked a PTHREAD_MUTEX_NORMAL mutex that was not locked by the current thread"
|
||||
);
|
||||
} else if kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK")
|
||||
|| kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE")
|
||||
{
|
||||
Ok(Scalar::from_i32(this.eval_libc_i32("EPERM")))
|
||||
} else {
|
||||
throw_unsup_format!("called pthread_mutex_unlock on an unsupported type of mutex");
|
||||
),
|
||||
MutexKind::ErrorCheck | MutexKind::Recursive =>
|
||||
Ok(Scalar::from_i32(this.eval_libc_i32("EPERM"))),
|
||||
_ =>
|
||||
throw_unsup_format!(
|
||||
"called pthread_mutex_unlock on an unsupported type of mutex"
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -574,7 +588,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
}
|
||||
|
||||
// Destroying an uninit pthread_mutex is UB, so check to make sure it's not uninit.
|
||||
mutex_get_kind(this, mutex_op)?;
|
||||
mutex_get_id(this, mutex_op)?;
|
||||
|
||||
// This might lead to false positives, see comment in pthread_mutexattr_destroy
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -1,4 +1,6 @@
|
||||
//@only-target-linux
|
||||
// Only works on Unix targets
|
||||
//@ignore-target-windows
|
||||
//@ignore-target-wasm
|
||||
//@only-on-host
|
||||
//@normalize-stderr-test: "OS `.*`" -> "$$OS"
|
||||
|
||||
|
15
src/tools/miri/tests/native-lib/fail/private_function.rs
Normal file
15
src/tools/miri/tests/native-lib/fail/private_function.rs
Normal 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`
|
||||
}
|
||||
}
|
15
src/tools/miri/tests/native-lib/fail/private_function.stderr
Normal file
15
src/tools/miri/tests/native-lib/fail/private_function.stderr
Normal 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
|
||||
|
@ -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: *;
|
||||
};
|
@ -1,4 +1,6 @@
|
||||
//@only-target-linux
|
||||
// Only works on Unix targets
|
||||
//@ignore-target-windows
|
||||
//@ignore-target-wasm
|
||||
//@only-on-host
|
||||
|
||||
fn main() {
|
||||
@ -26,7 +28,7 @@ fn test_pointer() {
|
||||
fn test_simple() {
|
||||
#[repr(C)]
|
||||
struct Simple {
|
||||
field: i32
|
||||
field: i32,
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
@ -62,7 +64,6 @@ fn test_nested() {
|
||||
|
||||
// Test function that dereferences static struct pointers and accesses fields.
|
||||
fn test_static() {
|
||||
|
||||
#[repr(C)]
|
||||
struct Static {
|
||||
value: i32,
|
||||
@ -73,10 +74,7 @@ fn test_static() {
|
||||
fn access_static(n_ptr: *const Static) -> i32;
|
||||
}
|
||||
|
||||
static STATIC: Static = Static {
|
||||
value: 9001,
|
||||
recurse: &STATIC,
|
||||
};
|
||||
static STATIC: Static = Static { value: 9001, recurse: &STATIC };
|
||||
|
||||
assert_eq!(unsafe { access_static(&STATIC) }, 9001);
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
//@only-target-linux
|
||||
// Only works on Unix targets
|
||||
//@ignore-target-windows
|
||||
//@ignore-target-wasm
|
||||
//@only-on-host
|
||||
|
||||
extern "C" {
|
||||
|
@ -1,8 +1,11 @@
|
||||
#include <stdio.h>
|
||||
|
||||
// See comments in build_native_lib()
|
||||
#define EXPORT __attribute__((visibility("default")))
|
||||
|
||||
/* Test: test_pointer */
|
||||
|
||||
void print_pointer(const int *ptr) {
|
||||
EXPORT void print_pointer(const int *ptr) {
|
||||
printf("printing pointer dereference from C: %d\n", *ptr);
|
||||
}
|
||||
|
||||
@ -12,7 +15,7 @@ typedef struct Simple {
|
||||
int field;
|
||||
} Simple;
|
||||
|
||||
int access_simple(const Simple *s_ptr) {
|
||||
EXPORT int access_simple(const Simple *s_ptr) {
|
||||
return s_ptr->field;
|
||||
}
|
||||
|
||||
@ -24,7 +27,7 @@ typedef struct Nested {
|
||||
} Nested;
|
||||
|
||||
// Returns the innermost/last value of a Nested pointer chain.
|
||||
int access_nested(const Nested *n_ptr) {
|
||||
EXPORT int access_nested(const Nested *n_ptr) {
|
||||
// Edge case: `n_ptr == NULL` (i.e. first Nested is None).
|
||||
if (!n_ptr) { return 0; }
|
||||
|
||||
@ -42,6 +45,6 @@ typedef struct Static {
|
||||
struct Static *recurse;
|
||||
} Static;
|
||||
|
||||
int access_static(const Static *s_ptr) {
|
||||
EXPORT int access_static(const Static *s_ptr) {
|
||||
return s_ptr->recurse->recurse->value;
|
||||
}
|
||||
|
@ -1,27 +1,35 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int add_one_int(int x) {
|
||||
// See comments in build_native_lib()
|
||||
#define EXPORT __attribute__((visibility("default")))
|
||||
|
||||
EXPORT int add_one_int(int x) {
|
||||
return 2 + x;
|
||||
}
|
||||
|
||||
void printer() {
|
||||
EXPORT void printer(void) {
|
||||
printf("printing from C\n");
|
||||
}
|
||||
|
||||
// function with many arguments, to test functionality when some args are stored
|
||||
// on the stack
|
||||
int test_stack_spill(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l) {
|
||||
EXPORT int test_stack_spill(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l) {
|
||||
return a+b+c+d+e+f+g+h+i+j+k+l;
|
||||
}
|
||||
|
||||
unsigned int get_unsigned_int() {
|
||||
EXPORT unsigned int get_unsigned_int(void) {
|
||||
return -10;
|
||||
}
|
||||
|
||||
short add_int16(short x) {
|
||||
EXPORT short add_int16(short x) {
|
||||
return x + 3;
|
||||
}
|
||||
|
||||
long add_short_to_long(short x, long y) {
|
||||
EXPORT long add_short_to_long(short x, long y) {
|
||||
return x + y;
|
||||
}
|
||||
|
||||
// To test that functions not marked with EXPORT cannot be called by Miri.
|
||||
int not_exported(void) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -36,20 +36,21 @@ fn build_native_lib() -> PathBuf {
|
||||
// Create the directory if it does not already exist.
|
||||
std::fs::create_dir_all(&so_target_dir)
|
||||
.expect("Failed to create directory for shared object file");
|
||||
let so_file_path = so_target_dir.join("native-lib.so");
|
||||
// We use a platform-neutral file extension to avoid having to hard-code alternatives.
|
||||
let native_lib_path = so_target_dir.join("native-lib.module");
|
||||
let cc_output = Command::new(cc)
|
||||
.args([
|
||||
"-shared",
|
||||
"-fPIC",
|
||||
// We hide all symbols by default and export just the ones we need
|
||||
// This is to future-proof against a more complex shared object which eg defines its own malloc
|
||||
// (but we wouldn't want miri to call that, as it would if it was exported).
|
||||
"-fvisibility=hidden",
|
||||
"-o",
|
||||
so_file_path.to_str().unwrap(),
|
||||
native_lib_path.to_str().unwrap(),
|
||||
// FIXME: Automate gathering of all relevant C source files in the directory.
|
||||
"tests/native-lib/scalar_arguments.c",
|
||||
"tests/native-lib/ptr_read_access.c",
|
||||
// Only add the functions specified in libcode.version to the shared object file.
|
||||
// This is to avoid automatically adding `malloc`, etc.
|
||||
// Source: https://anadoxin.org/blog/control-over-symbol-exports-in-gcc.html/
|
||||
"-fPIC",
|
||||
"-Wl,--version-script=tests/native-lib/native-lib.map",
|
||||
// Ensure we notice serious problems in the C code.
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
@ -64,7 +65,7 @@ fn build_native_lib() -> PathBuf {
|
||||
String::from_utf8_lossy(&cc_output.stderr),
|
||||
);
|
||||
}
|
||||
so_file_path
|
||||
native_lib_path
|
||||
}
|
||||
|
||||
/// Does *not* set any args or env vars, since it is shared between the test runner and
|
||||
@ -300,7 +301,7 @@ fn main() -> Result<()> {
|
||||
WithDependencies,
|
||||
tmpdir.path(),
|
||||
)?;
|
||||
if cfg!(target_os = "linux") {
|
||||
if cfg!(unix) {
|
||||
ui(Mode::Pass, "tests/native-lib/pass", &target, WithoutDependencies, tmpdir.path())?;
|
||||
ui(
|
||||
Mode::Fail { require_patterns: true, rustfix: RustfixMode::Disabled },
|
||||
|
@ -110,6 +110,13 @@ pub struct LlvmDwarfdump {
|
||||
cmd: Command,
|
||||
}
|
||||
|
||||
/// A `llvm-pdbutil` invocation builder.
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
pub struct LlvmPdbutil {
|
||||
cmd: Command,
|
||||
}
|
||||
|
||||
crate::macros::impl_common_helpers!(LlvmReadobj);
|
||||
crate::macros::impl_common_helpers!(LlvmProfdata);
|
||||
crate::macros::impl_common_helpers!(LlvmFilecheck);
|
||||
@ -118,6 +125,7 @@ crate::macros::impl_common_helpers!(LlvmAr);
|
||||
crate::macros::impl_common_helpers!(LlvmNm);
|
||||
crate::macros::impl_common_helpers!(LlvmBcanalyzer);
|
||||
crate::macros::impl_common_helpers!(LlvmDwarfdump);
|
||||
crate::macros::impl_common_helpers!(LlvmPdbutil);
|
||||
|
||||
/// Generate the path to the bin directory of LLVM.
|
||||
#[must_use]
|
||||
@ -360,3 +368,19 @@ impl LlvmDwarfdump {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl LlvmPdbutil {
|
||||
/// Construct a new `llvm-pdbutil` invocation. This assumes that `llvm-pdbutil` is available
|
||||
/// at `$LLVM_BIN_DIR/llvm-pdbutil`.
|
||||
pub fn new() -> Self {
|
||||
let llvm_pdbutil = llvm_bin_dir().join("llvm-pdbutil");
|
||||
let cmd = Command::new(llvm_pdbutil);
|
||||
Self { cmd }
|
||||
}
|
||||
|
||||
/// Provide an input file.
|
||||
pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
|
||||
self.cmd.arg(path.as_ref());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ pub use env::{env_var, env_var_os, set_current_dir};
|
||||
pub use run::{cmd, run, run_fail, run_with_args};
|
||||
|
||||
/// Helpers for checking target information.
|
||||
pub use targets::{is_darwin, is_msvc, is_windows, llvm_components_contain, target, uname};
|
||||
pub use targets::{is_darwin, is_msvc, is_windows, llvm_components_contain, target, uname, apple_os};
|
||||
|
||||
/// Helpers for building names of output artifacts that are potentially target-specific.
|
||||
pub use artifact_names::{
|
||||
|
@ -28,6 +28,24 @@ pub fn is_darwin() -> bool {
|
||||
target().contains("darwin")
|
||||
}
|
||||
|
||||
/// Get the target OS on Apple operating systems.
|
||||
#[must_use]
|
||||
pub fn apple_os() -> &'static str {
|
||||
if target().contains("darwin") {
|
||||
"macos"
|
||||
} else if target().contains("ios") {
|
||||
"ios"
|
||||
} else if target().contains("tvos") {
|
||||
"tvos"
|
||||
} else if target().contains("watchos") {
|
||||
"watchos"
|
||||
} else if target().contains("visionos") {
|
||||
"visionos"
|
||||
} else {
|
||||
panic!("not an Apple OS")
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if `component` is within `LLVM_COMPONENTS`
|
||||
#[must_use]
|
||||
pub fn llvm_components_contain(component: &str) -> bool {
|
||||
|
@ -6,7 +6,6 @@ run-make/incr-add-rust-src-component/Makefile
|
||||
run-make/issue-84395-lto-embed-bitcode/Makefile
|
||||
run-make/jobserver-error/Makefile
|
||||
run-make/libs-through-symlinks/Makefile
|
||||
run-make/macos-deployment-target/Makefile
|
||||
run-make/split-debuginfo/Makefile
|
||||
run-make/symbol-mangling-hashed/Makefile
|
||||
run-make/translation/Makefile
|
||||
|
@ -1,7 +1,7 @@
|
||||
# `wasm-component-ld`
|
||||
|
||||
This wrapper is a wrapper around the [`wasm-component-ld`] crates.io crate. That
|
||||
crate. That crate is itself a thin wrapper around two pieces:
|
||||
This wrapper is a wrapper around the [`wasm-component-ld`] crates.io crate.
|
||||
That crate is itself a thin wrapper around two pieces:
|
||||
|
||||
* `wasm-ld` - the LLVM-based linker distributed as part of LLD and packaged in
|
||||
Rust as `rust-lld`.
|
||||
|
@ -552,6 +552,9 @@
|
||||
//@ revisions: x86_64_unknown_haiku
|
||||
//@ [x86_64_unknown_haiku] compile-flags: --target x86_64-unknown-haiku
|
||||
//@ [x86_64_unknown_haiku] needs-llvm-components: x86
|
||||
//@ revisions: x86_64_unknown_hurd_gnu
|
||||
//@ [x86_64_unknown_hurd_gnu] compile-flags: --target x86_64-unknown-hurd-gnu
|
||||
//@ [x86_64_unknown_hurd_gnu] needs-llvm-components: x86
|
||||
//@ revisions: x86_64_unknown_hermit
|
||||
//@ [x86_64_unknown_hermit] compile-flags: --target x86_64-unknown-hermit
|
||||
//@ [x86_64_unknown_hermit] needs-llvm-components: x86
|
||||
|
@ -1,6 +1,7 @@
|
||||
//@ min-lldb-version: 1800
|
||||
//@ min-gdb-version: 13.0
|
||||
//@ compile-flags:-g
|
||||
//@ ignore-windows-gnu: #128973
|
||||
|
||||
// === GDB TESTS ===================================================================================
|
||||
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
//@ compile-flags:-g
|
||||
|
||||
//@ ignore-windows-gnu: #128973
|
||||
|
||||
// === GDB TESTS ===================================================================================
|
||||
|
||||
// gdb-command:run
|
||||
|
1
tests/run-make/apple-deployment-target/foo.rs
Normal file
1
tests/run-make/apple-deployment-target/foo.rs
Normal file
@ -0,0 +1 @@
|
||||
fn main() {}
|
157
tests/run-make/apple-deployment-target/rmake.rs
Normal file
157
tests/run-make/apple-deployment-target/rmake.rs
Normal 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);
|
||||
}
|
||||
});
|
||||
}
|
@ -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)
|
@ -1,4 +0,0 @@
|
||||
#![crate_type = "cdylib"]
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn something_and_nothing() {}
|
@ -9,12 +9,12 @@ impl Simple {
|
||||
|
||||
pub trait EasyToImpl {
|
||||
//@ has "$.index[*][?(@.docs=='ToDeclare trait')].inner.assoc_type"
|
||||
//@ is "$.index[*][?(@.docs=='ToDeclare trait')].inner.assoc_type.default" null
|
||||
//@ is "$.index[*][?(@.docs=='ToDeclare trait')].inner.assoc_type.type" null
|
||||
//@ is "$.index[*][?(@.docs=='ToDeclare trait')].inner.assoc_type.bounds" []
|
||||
/// ToDeclare trait
|
||||
type ToDeclare;
|
||||
//@ has "$.index[*][?(@.docs=='AN_ATTRIBUTE trait')].inner.assoc_const"
|
||||
//@ is "$.index[*][?(@.docs=='AN_ATTRIBUTE trait')].inner.assoc_const.default" null
|
||||
//@ is "$.index[*][?(@.docs=='AN_ATTRIBUTE trait')].inner.assoc_const.value" null
|
||||
//@ is "$.index[*][?(@.docs=='AN_ATTRIBUTE trait')].inner.assoc_const.type.primitive" '"usize"'
|
||||
/// AN_ATTRIBUTE trait
|
||||
const AN_ATTRIBUTE: usize;
|
||||
@ -22,13 +22,13 @@ pub trait EasyToImpl {
|
||||
|
||||
impl EasyToImpl for Simple {
|
||||
//@ has "$.index[*][?(@.docs=='ToDeclare impl')].inner.assoc_type"
|
||||
//@ is "$.index[*][?(@.docs=='ToDeclare impl')].inner.assoc_type.default.primitive" \"usize\"
|
||||
//@ is "$.index[*][?(@.docs=='ToDeclare impl')].inner.assoc_type.type.primitive" \"usize\"
|
||||
/// ToDeclare impl
|
||||
type ToDeclare = usize;
|
||||
|
||||
//@ has "$.index[*][?(@.docs=='AN_ATTRIBUTE impl')].inner.assoc_const"
|
||||
//@ is "$.index[*][?(@.docs=='AN_ATTRIBUTE impl')].inner.assoc_const.type.primitive" \"usize\"
|
||||
//@ is "$.index[*][?(@.docs=='AN_ATTRIBUTE impl')].inner.assoc_const.default" \"12\"
|
||||
//@ is "$.index[*][?(@.docs=='AN_ATTRIBUTE impl')].inner.assoc_const.value" \"12\"
|
||||
/// AN_ATTRIBUTE impl
|
||||
const AN_ATTRIBUTE: usize = 12;
|
||||
}
|
||||
|
@ -3,6 +3,6 @@
|
||||
#![no_std]
|
||||
|
||||
//@ has "$.index[*][?(@.name=='Error')].inner.assoc_type"
|
||||
//@ has "$.index[*][?(@.name=='Error')].inner.assoc_type.default.resolved_path"
|
||||
//@ has "$.index[*][?(@.name=='Error')].inner.assoc_type.default.resolved_path.name" \"Infallible\"
|
||||
//@ has "$.index[*][?(@.name=='Error')].inner.assoc_type.type.resolved_path"
|
||||
//@ has "$.index[*][?(@.name=='Error')].inner.assoc_type.type.resolved_path.name" \"Infallible\"
|
||||
pub struct ForBlanketTryFromImpl;
|
||||
|
@ -5,7 +5,7 @@ pub enum Foo {
|
||||
//@ is "$.index[*][?(@.name=='Unit')].inner.variant.kind" '"plain"'
|
||||
Unit,
|
||||
//@ set Named = "$.index[*][?(@.name=='Named')].id"
|
||||
//@ is "$.index[*][?(@.name=='Named')].inner.variant.kind.struct" '{"fields": [], "fields_stripped": false}'
|
||||
//@ is "$.index[*][?(@.name=='Named')].inner.variant.kind.struct" '{"fields": [], "has_stripped_fields": false}'
|
||||
Named {},
|
||||
//@ set Tuple = "$.index[*][?(@.name=='Tuple')].id"
|
||||
//@ is "$.index[*][?(@.name=='Tuple')].inner.variant.kind.tuple" []
|
||||
@ -13,7 +13,7 @@ pub enum Foo {
|
||||
//@ set NamedField = "$.index[*][?(@.name=='NamedField')].id"
|
||||
//@ set x = "$.index[*][?(@.name=='x' && @.inner.struct_field)].id"
|
||||
//@ is "$.index[*][?(@.name=='NamedField')].inner.variant.kind.struct.fields[*]" $x
|
||||
//@ is "$.index[*][?(@.name=='NamedField')].inner.variant.kind.struct.fields_stripped" false
|
||||
//@ is "$.index[*][?(@.name=='NamedField')].inner.variant.kind.struct.has_stripped_fields" false
|
||||
NamedField { x: i32 },
|
||||
//@ set TupleField = "$.index[*][?(@.name=='TupleField')].id"
|
||||
//@ set tup_field = "$.index[*][?(@.name=='0' && @.inner.struct_field)].id"
|
||||
|
@ -9,7 +9,7 @@ pub enum Foo {
|
||||
//@ set y = "$.index[*][?(@.name=='y')].id"
|
||||
y: i32,
|
||||
},
|
||||
//@ is "$.index[*][?(@.name=='Variant')].inner.variant.kind.struct.fields_stripped" true
|
||||
//@ is "$.index[*][?(@.name=='Variant')].inner.variant.kind.struct.has_stripped_fields" true
|
||||
//@ is "$.index[*][?(@.name=='Variant')].inner.variant.kind.struct.fields[0]" $b
|
||||
//@ is "$.index[*][?(@.name=='Variant')].inner.variant.kind.struct.fields[1]" $y
|
||||
//@ count "$.index[*][?(@.name=='Variant')].inner.variant.kind.struct.fields[*]" 2
|
||||
|
@ -7,9 +7,9 @@ pub enum Color {
|
||||
Blue,
|
||||
}
|
||||
|
||||
//@ set use_Color = "$.index[*][?(@.inner.import)].id"
|
||||
//@ is "$.index[*][?(@.inner.import)].inner.import.id" $Color
|
||||
//@ is "$.index[*][?(@.inner.import)].inner.import.glob" true
|
||||
//@ set use_Color = "$.index[*][?(@.inner.use)].id"
|
||||
//@ is "$.index[*][?(@.inner.use)].inner.use.id" $Color
|
||||
//@ is "$.index[*][?(@.inner.use)].inner.use.is_glob" true
|
||||
pub use Color::*;
|
||||
|
||||
//@ ismany "$.index[*][?(@.name == 'use_glob')].inner.module.items[*]" $Color $use_Color
|
||||
|
@ -5,8 +5,8 @@ pub enum AlwaysNone {
|
||||
}
|
||||
//@ is "$.index[*][?(@.name == 'AlwaysNone')].inner.enum.variants[*]" $None
|
||||
|
||||
//@ set use_None = "$.index[*][?(@.inner.import)].id"
|
||||
//@ is "$.index[*][?(@.inner.import)].inner.import.id" $None
|
||||
//@ set use_None = "$.index[*][?(@.inner.use)].id"
|
||||
//@ is "$.index[*][?(@.inner.use)].inner.use.id" $None
|
||||
pub use AlwaysNone::None;
|
||||
|
||||
//@ ismany "$.index[*][?(@.name == 'use_variant')].inner.module.items[*]" $AlwaysNone $use_None
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
extern crate color;
|
||||
|
||||
//@ has "$.index[*].inner.import[?(@.name == 'Red')]"
|
||||
//@ has "$.index[*].inner.use[?(@.name == 'Red')]"
|
||||
pub use color::Color::Red;
|
||||
|
||||
//@ !has "$.index[*][?(@.name == 'Red')]"
|
||||
|
@ -1,9 +1,9 @@
|
||||
// ignore-tidy-linelength
|
||||
|
||||
//@ count "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type_alias.type.function_pointer.decl.inputs[*]" 1
|
||||
//@ is "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type_alias.type.function_pointer.decl.inputs[0][0]" '"val"'
|
||||
//@ is "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type_alias.type.function_pointer.decl.inputs[0][1].borrowed_ref.lifetime" \"\'c\"
|
||||
//@ is "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type_alias.type.function_pointer.decl.output.primitive" \"i32\"
|
||||
//@ count "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type_alias.type.function_pointer.sig.inputs[*]" 1
|
||||
//@ is "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type_alias.type.function_pointer.sig.inputs[0][0]" '"val"'
|
||||
//@ is "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type_alias.type.function_pointer.sig.inputs[0][1].borrowed_ref.lifetime" \"\'c\"
|
||||
//@ is "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type_alias.type.function_pointer.sig.output.primitive" \"i32\"
|
||||
//@ count "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type_alias.type.function_pointer.generic_params[*]" 1
|
||||
//@ is "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type_alias.type.function_pointer.generic_params[0].name" \"\'c\"
|
||||
//@ is "$.index[*][?(@.name=='WithHigherRankTraitBounds')].inner.type_alias.type.function_pointer.generic_params[0].kind" '{ "lifetime": { "outlives": [] } }'
|
||||
|
@ -1,11 +1,11 @@
|
||||
// ignore-tidy-linelength
|
||||
|
||||
//@ is "$.index[*][?(@.name=='FnPointer')].inner.type_alias.type.function_pointer.header.unsafe" false
|
||||
//@ is "$.index[*][?(@.name=='FnPointer')].inner.type_alias.type.function_pointer.header.const" false
|
||||
//@ is "$.index[*][?(@.name=='FnPointer')].inner.type_alias.type.function_pointer.header.async" false
|
||||
//@ is "$.index[*][?(@.name=='FnPointer')].inner.type_alias.type.function_pointer.header.is_unsafe" false
|
||||
//@ is "$.index[*][?(@.name=='FnPointer')].inner.type_alias.type.function_pointer.header.is_const" false
|
||||
//@ is "$.index[*][?(@.name=='FnPointer')].inner.type_alias.type.function_pointer.header.is_async" false
|
||||
pub type FnPointer = fn();
|
||||
|
||||
//@ is "$.index[*][?(@.name=='UnsafePointer')].inner.type_alias.type.function_pointer.header.unsafe" true
|
||||
//@ is "$.index[*][?(@.name=='UnsafePointer')].inner.type_alias.type.function_pointer.header.const" false
|
||||
//@ is "$.index[*][?(@.name=='UnsafePointer')].inner.type_alias.type.function_pointer.header.async" false
|
||||
//@ is "$.index[*][?(@.name=='UnsafePointer')].inner.type_alias.type.function_pointer.header.is_unsafe" true
|
||||
//@ is "$.index[*][?(@.name=='UnsafePointer')].inner.type_alias.type.function_pointer.header.is_const" false
|
||||
//@ is "$.index[*][?(@.name=='UnsafePointer')].inner.type_alias.type.function_pointer.header.is_async" false
|
||||
pub type UnsafePointer = unsafe fn();
|
||||
|
@ -5,30 +5,30 @@
|
||||
|
||||
use std::future::Future;
|
||||
|
||||
//@ is "$.index[*][?(@.name=='get_int')].inner.function.decl.output.primitive" \"i32\"
|
||||
//@ is "$.index[*][?(@.name=='get_int')].inner.function.header.async" false
|
||||
//@ is "$.index[*][?(@.name=='get_int')].inner.function.sig.output.primitive" \"i32\"
|
||||
//@ is "$.index[*][?(@.name=='get_int')].inner.function.header.is_async" false
|
||||
pub fn get_int() -> i32 {
|
||||
42
|
||||
}
|
||||
|
||||
//@ is "$.index[*][?(@.name=='get_int_async')].inner.function.decl.output.primitive" \"i32\"
|
||||
//@ is "$.index[*][?(@.name=='get_int_async')].inner.function.header.async" true
|
||||
//@ is "$.index[*][?(@.name=='get_int_async')].inner.function.sig.output.primitive" \"i32\"
|
||||
//@ is "$.index[*][?(@.name=='get_int_async')].inner.function.header.is_async" true
|
||||
pub async fn get_int_async() -> i32 {
|
||||
42
|
||||
}
|
||||
|
||||
//@ is "$.index[*][?(@.name=='get_int_future')].inner.function.decl.output.impl_trait[0].trait_bound.trait.name" '"Future"'
|
||||
//@ is "$.index[*][?(@.name=='get_int_future')].inner.function.decl.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.bindings[0].name" '"Output"'
|
||||
//@ is "$.index[*][?(@.name=='get_int_future')].inner.function.decl.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.bindings[0].binding.equality.type.primitive" \"i32\"
|
||||
//@ is "$.index[*][?(@.name=='get_int_future')].inner.function.header.async" false
|
||||
//@ is "$.index[*][?(@.name=='get_int_future')].inner.function.sig.output.impl_trait[0].trait_bound.trait.name" '"Future"'
|
||||
//@ is "$.index[*][?(@.name=='get_int_future')].inner.function.sig.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[0].name" '"Output"'
|
||||
//@ is "$.index[*][?(@.name=='get_int_future')].inner.function.sig.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[0].binding.equality.type.primitive" \"i32\"
|
||||
//@ is "$.index[*][?(@.name=='get_int_future')].inner.function.header.is_async" false
|
||||
pub fn get_int_future() -> impl Future<Output = i32> {
|
||||
async { 42 }
|
||||
}
|
||||
|
||||
//@ is "$.index[*][?(@.name=='get_int_future_async')].inner.function.decl.output.impl_trait[0].trait_bound.trait.name" '"Future"'
|
||||
//@ is "$.index[*][?(@.name=='get_int_future_async')].inner.function.decl.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.bindings[0].name" '"Output"'
|
||||
//@ is "$.index[*][?(@.name=='get_int_future_async')].inner.function.decl.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.bindings[0].binding.equality.type.primitive" \"i32\"
|
||||
//@ is "$.index[*][?(@.name=='get_int_future_async')].inner.function.header.async" true
|
||||
//@ is "$.index[*][?(@.name=='get_int_future_async')].inner.function.sig.output.impl_trait[0].trait_bound.trait.name" '"Future"'
|
||||
//@ is "$.index[*][?(@.name=='get_int_future_async')].inner.function.sig.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[0].name" '"Output"'
|
||||
//@ is "$.index[*][?(@.name=='get_int_future_async')].inner.function.sig.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[0].binding.equality.type.primitive" \"i32\"
|
||||
//@ is "$.index[*][?(@.name=='get_int_future_async')].inner.function.header.is_async" true
|
||||
pub async fn get_int_future_async() -> impl Future<Output = i32> {
|
||||
async { 42 }
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
extern "C" {
|
||||
//@ is "$.index[*][?(@.name == 'not_variadic')].inner.function.decl.c_variadic" false
|
||||
//@ is "$.index[*][?(@.name == 'not_variadic')].inner.function.sig.is_c_variadic" false
|
||||
pub fn not_variadic(_: i32);
|
||||
//@ is "$.index[*][?(@.name == 'variadic')].inner.function.decl.c_variadic" true
|
||||
//@ is "$.index[*][?(@.name == 'variadic')].inner.function.sig.is_c_variadic" true
|
||||
pub fn variadic(_: i32, ...);
|
||||
}
|
||||
|
@ -12,27 +12,27 @@ pub trait GenericFoo<'a> {}
|
||||
//@ is "$.index[*][?(@.name=='generics')].inner.function.generics.params[0].kind.type.default" 'null'
|
||||
//@ count "$.index[*][?(@.name=='generics')].inner.function.generics.params[0].kind.type.bounds[*]" 1
|
||||
//@ is "$.index[*][?(@.name=='generics')].inner.function.generics.params[0].kind.type.bounds[0].trait_bound.trait.id" '$foo'
|
||||
//@ count "$.index[*][?(@.name=='generics')].inner.function.decl.inputs[*]" 1
|
||||
//@ is "$.index[*][?(@.name=='generics')].inner.function.decl.inputs[0][0]" '"f"'
|
||||
//@ is "$.index[*][?(@.name=='generics')].inner.function.decl.inputs[0][1].generic" '"F"'
|
||||
//@ count "$.index[*][?(@.name=='generics')].inner.function.sig.inputs[*]" 1
|
||||
//@ is "$.index[*][?(@.name=='generics')].inner.function.sig.inputs[0][0]" '"f"'
|
||||
//@ is "$.index[*][?(@.name=='generics')].inner.function.sig.inputs[0][1].generic" '"F"'
|
||||
pub fn generics<F: Foo>(f: F) {}
|
||||
|
||||
//@ is "$.index[*][?(@.name=='impl_trait')].inner.function.generics.where_predicates" "[]"
|
||||
//@ count "$.index[*][?(@.name=='impl_trait')].inner.function.generics.params[*]" 1
|
||||
//@ is "$.index[*][?(@.name=='impl_trait')].inner.function.generics.params[0].name" '"impl Foo"'
|
||||
//@ is "$.index[*][?(@.name=='impl_trait')].inner.function.generics.params[0].kind.type.bounds[0].trait_bound.trait.id" $foo
|
||||
//@ count "$.index[*][?(@.name=='impl_trait')].inner.function.decl.inputs[*]" 1
|
||||
//@ is "$.index[*][?(@.name=='impl_trait')].inner.function.decl.inputs[0][0]" '"f"'
|
||||
//@ count "$.index[*][?(@.name=='impl_trait')].inner.function.decl.inputs[0][1].impl_trait[*]" 1
|
||||
//@ is "$.index[*][?(@.name=='impl_trait')].inner.function.decl.inputs[0][1].impl_trait[0].trait_bound.trait.id" $foo
|
||||
//@ count "$.index[*][?(@.name=='impl_trait')].inner.function.sig.inputs[*]" 1
|
||||
//@ is "$.index[*][?(@.name=='impl_trait')].inner.function.sig.inputs[0][0]" '"f"'
|
||||
//@ count "$.index[*][?(@.name=='impl_trait')].inner.function.sig.inputs[0][1].impl_trait[*]" 1
|
||||
//@ is "$.index[*][?(@.name=='impl_trait')].inner.function.sig.inputs[0][1].impl_trait[0].trait_bound.trait.id" $foo
|
||||
pub fn impl_trait(f: impl Foo) {}
|
||||
|
||||
//@ count "$.index[*][?(@.name=='where_clase')].inner.function.generics.params[*]" 3
|
||||
//@ is "$.index[*][?(@.name=='where_clase')].inner.function.generics.params[0].name" '"F"'
|
||||
//@ is "$.index[*][?(@.name=='where_clase')].inner.function.generics.params[0].kind" '{"type": {"bounds": [], "default": null, "synthetic": false}}'
|
||||
//@ count "$.index[*][?(@.name=='where_clase')].inner.function.decl.inputs[*]" 3
|
||||
//@ is "$.index[*][?(@.name=='where_clase')].inner.function.decl.inputs[0][0]" '"f"'
|
||||
//@ is "$.index[*][?(@.name=='where_clase')].inner.function.decl.inputs[0][1].generic" '"F"'
|
||||
//@ is "$.index[*][?(@.name=='where_clase')].inner.function.generics.params[0].kind" '{"type": {"bounds": [], "default": null, "is_synthetic": false}}'
|
||||
//@ count "$.index[*][?(@.name=='where_clase')].inner.function.sig.inputs[*]" 3
|
||||
//@ is "$.index[*][?(@.name=='where_clase')].inner.function.sig.inputs[0][0]" '"f"'
|
||||
//@ is "$.index[*][?(@.name=='where_clase')].inner.function.sig.inputs[0][1].generic" '"F"'
|
||||
//@ count "$.index[*][?(@.name=='where_clase')].inner.function.generics.where_predicates[*]" 3
|
||||
|
||||
//@ is "$.index[*][?(@.name=='where_clase')].inner.function.generics.where_predicates[0].bound_predicate.type.generic" \"F\"
|
||||
|
@ -5,9 +5,9 @@
|
||||
//@ set foo = "$.index[*][?(@.name=='Foo')].id"
|
||||
pub trait Foo {}
|
||||
|
||||
//@ is "$.index[*][?(@.name=='get_foo')].inner.function.decl.inputs" []
|
||||
//@ count "$.index[*][?(@.name=='get_foo')].inner.function.decl.output.impl_trait[*]" 1
|
||||
//@ is "$.index[*][?(@.name=='get_foo')].inner.function.decl.output.impl_trait[0].trait_bound.trait.id" $foo
|
||||
//@ is "$.index[*][?(@.name=='get_foo')].inner.function.sig.inputs" []
|
||||
//@ count "$.index[*][?(@.name=='get_foo')].inner.function.sig.output.impl_trait[*]" 1
|
||||
//@ is "$.index[*][?(@.name=='get_foo')].inner.function.sig.output.impl_trait[0].trait_bound.trait.id" $foo
|
||||
pub fn get_foo() -> impl Foo {
|
||||
Fooer {}
|
||||
}
|
||||
|
@ -6,17 +6,17 @@ pub trait Wham {}
|
||||
//@ is "$.index[*][?(@.name=='one_generic_param_fn')].inner.function.generics.where_predicates" []
|
||||
//@ count "$.index[*][?(@.name=='one_generic_param_fn')].inner.function.generics.params[*]" 1
|
||||
//@ is "$.index[*][?(@.name=='one_generic_param_fn')].inner.function.generics.params[0].name" '"T"'
|
||||
//@ is "$.index[*][?(@.name=='one_generic_param_fn')].inner.function.generics.params[0].kind.type.synthetic" false
|
||||
//@ is "$.index[*][?(@.name=='one_generic_param_fn')].inner.function.generics.params[0].kind.type.is_synthetic" false
|
||||
//@ is "$.index[*][?(@.name=='one_generic_param_fn')].inner.function.generics.params[0].kind.type.bounds[0].trait_bound.trait.id" $wham_id
|
||||
//@ is "$.index[*][?(@.name=='one_generic_param_fn')].inner.function.decl.inputs" '[["w", {"generic": "T"}]]'
|
||||
//@ is "$.index[*][?(@.name=='one_generic_param_fn')].inner.function.sig.inputs" '[["w", {"generic": "T"}]]'
|
||||
pub fn one_generic_param_fn<T: Wham>(w: T) {}
|
||||
|
||||
//@ is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.generics.where_predicates" []
|
||||
//@ count "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.generics.params[*]" 1
|
||||
//@ is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.generics.params[0].name" '"impl Wham"'
|
||||
//@ is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.generics.params[0].kind.type.synthetic" true
|
||||
//@ is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.generics.params[0].kind.type.is_synthetic" true
|
||||
//@ is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.generics.params[0].kind.type.bounds[0].trait_bound.trait.id" $wham_id
|
||||
//@ count "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.decl.inputs[*]" 1
|
||||
//@ is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.decl.inputs[0][0]" '"w"'
|
||||
//@ is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.decl.inputs[0][1].impl_trait[0].trait_bound.trait.id" $wham_id
|
||||
//@ count "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.sig.inputs[*]" 1
|
||||
//@ is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.sig.inputs[0][0]" '"w"'
|
||||
//@ is "$.index[*][?(@.name=='one_synthetic_generic_param_fn')].inner.function.sig.inputs[0][1].impl_trait[0].trait_bound.trait.id" $wham_id
|
||||
pub fn one_synthetic_generic_param_fn(w: impl Wham) {}
|
||||
|
@ -1,7 +1,7 @@
|
||||
//@ is "$.index[*][?(@.name=='fst')].inner.function.decl.inputs[0][0]" '"(x, _)"'
|
||||
//@ is "$.index[*][?(@.name=='fst')].inner.function.sig.inputs[0][0]" '"(x, _)"'
|
||||
pub fn fst<X, Y>((x, _): (X, Y)) -> X {
|
||||
x
|
||||
}
|
||||
|
||||
//@ is "$.index[*][?(@.name=='drop_int')].inner.function.decl.inputs[0][0]" '"_"'
|
||||
//@ is "$.index[*][?(@.name=='drop_int')].inner.function.sig.inputs[0][0]" '"_"'
|
||||
pub fn drop_int(_: i32) {}
|
||||
|
@ -1,33 +1,33 @@
|
||||
//@ edition:2018
|
||||
|
||||
//@ is "$.index[*][?(@.name=='nothing_fn')].inner.function.header.async" false
|
||||
//@ is "$.index[*][?(@.name=='nothing_fn')].inner.function.header.const" false
|
||||
//@ is "$.index[*][?(@.name=='nothing_fn')].inner.function.header.unsafe" false
|
||||
//@ is "$.index[*][?(@.name=='nothing_fn')].inner.function.header.is_async" false
|
||||
//@ is "$.index[*][?(@.name=='nothing_fn')].inner.function.header.is_const" false
|
||||
//@ is "$.index[*][?(@.name=='nothing_fn')].inner.function.header.is_unsafe" false
|
||||
pub fn nothing_fn() {}
|
||||
|
||||
//@ is "$.index[*][?(@.name=='unsafe_fn')].inner.function.header.async" false
|
||||
//@ is "$.index[*][?(@.name=='unsafe_fn')].inner.function.header.const" false
|
||||
//@ is "$.index[*][?(@.name=='unsafe_fn')].inner.function.header.unsafe" true
|
||||
//@ is "$.index[*][?(@.name=='unsafe_fn')].inner.function.header.is_async" false
|
||||
//@ is "$.index[*][?(@.name=='unsafe_fn')].inner.function.header.is_const" false
|
||||
//@ is "$.index[*][?(@.name=='unsafe_fn')].inner.function.header.is_unsafe" true
|
||||
pub unsafe fn unsafe_fn() {}
|
||||
|
||||
//@ is "$.index[*][?(@.name=='const_fn')].inner.function.header.async" false
|
||||
//@ is "$.index[*][?(@.name=='const_fn')].inner.function.header.const" true
|
||||
//@ is "$.index[*][?(@.name=='const_fn')].inner.function.header.unsafe" false
|
||||
//@ is "$.index[*][?(@.name=='const_fn')].inner.function.header.is_async" false
|
||||
//@ is "$.index[*][?(@.name=='const_fn')].inner.function.header.is_const" true
|
||||
//@ is "$.index[*][?(@.name=='const_fn')].inner.function.header.is_unsafe" false
|
||||
pub const fn const_fn() {}
|
||||
|
||||
//@ is "$.index[*][?(@.name=='async_fn')].inner.function.header.async" true
|
||||
//@ is "$.index[*][?(@.name=='async_fn')].inner.function.header.const" false
|
||||
//@ is "$.index[*][?(@.name=='async_fn')].inner.function.header.unsafe" false
|
||||
//@ is "$.index[*][?(@.name=='async_fn')].inner.function.header.is_async" true
|
||||
//@ is "$.index[*][?(@.name=='async_fn')].inner.function.header.is_const" false
|
||||
//@ is "$.index[*][?(@.name=='async_fn')].inner.function.header.is_unsafe" false
|
||||
pub async fn async_fn() {}
|
||||
|
||||
//@ is "$.index[*][?(@.name=='async_unsafe_fn')].inner.function.header.async" true
|
||||
//@ is "$.index[*][?(@.name=='async_unsafe_fn')].inner.function.header.const" false
|
||||
//@ is "$.index[*][?(@.name=='async_unsafe_fn')].inner.function.header.unsafe" true
|
||||
//@ is "$.index[*][?(@.name=='async_unsafe_fn')].inner.function.header.is_async" true
|
||||
//@ is "$.index[*][?(@.name=='async_unsafe_fn')].inner.function.header.is_const" false
|
||||
//@ is "$.index[*][?(@.name=='async_unsafe_fn')].inner.function.header.is_unsafe" true
|
||||
pub async unsafe fn async_unsafe_fn() {}
|
||||
|
||||
//@ is "$.index[*][?(@.name=='const_unsafe_fn')].inner.function.header.async" false
|
||||
//@ is "$.index[*][?(@.name=='const_unsafe_fn')].inner.function.header.const" true
|
||||
//@ is "$.index[*][?(@.name=='const_unsafe_fn')].inner.function.header.unsafe" true
|
||||
//@ is "$.index[*][?(@.name=='const_unsafe_fn')].inner.function.header.is_async" false
|
||||
//@ is "$.index[*][?(@.name=='const_unsafe_fn')].inner.function.header.is_const" true
|
||||
//@ is "$.index[*][?(@.name=='const_unsafe_fn')].inner.function.header.is_unsafe" true
|
||||
pub const unsafe fn const_unsafe_fn() {}
|
||||
|
||||
// It's impossible for a function to be both const and async, so no test for that
|
||||
|
@ -3,7 +3,7 @@
|
||||
///@ set foo = "$.index[*][?(@.name=='Foo')].id"
|
||||
pub type Foo = i32;
|
||||
|
||||
//@ is "$.index[*][?(@.name=='demo')].inner.function.decl.output.resolved_path.id" $foo
|
||||
//@ is "$.index[*][?(@.name=='demo')].inner.function.sig.output.resolved_path.id" $foo
|
||||
pub fn demo() -> Foo {
|
||||
42
|
||||
}
|
||||
|
@ -13,10 +13,10 @@ pub trait LendingIterator {
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
//@ count "$.index[*][?(@.name=='lending_next')].inner.function.decl.output.qualified_path.args.angle_bracketed.args[*]" 1
|
||||
//@ count "$.index[*][?(@.name=='lending_next')].inner.function.decl.output.qualified_path.args.angle_bracketed.bindings[*]" 0
|
||||
//@ is "$.index[*][?(@.name=='lending_next')].inner.function.decl.output.qualified_path.self_type.generic" \"Self\"
|
||||
//@ is "$.index[*][?(@.name=='lending_next')].inner.function.decl.output.qualified_path.name" \"LendingItem\"
|
||||
//@ count "$.index[*][?(@.name=='lending_next')].inner.function.sig.output.qualified_path.args.angle_bracketed.args[*]" 1
|
||||
//@ count "$.index[*][?(@.name=='lending_next')].inner.function.sig.output.qualified_path.args.angle_bracketed.bindings[*]" 0
|
||||
//@ is "$.index[*][?(@.name=='lending_next')].inner.function.sig.output.qualified_path.self_type.generic" \"Self\"
|
||||
//@ is "$.index[*][?(@.name=='lending_next')].inner.function.sig.output.qualified_path.name" \"LendingItem\"
|
||||
fn lending_next<'a>(&'a self) -> Self::LendingItem<'a>;
|
||||
}
|
||||
|
||||
@ -26,9 +26,9 @@ pub trait Iterator {
|
||||
//@ count "$.index[*][?(@.name=='Item')].inner.assoc_type.bounds[*]" 1
|
||||
type Item: Display;
|
||||
|
||||
//@ count "$.index[*][?(@.name=='next')].inner.function.decl.output.qualified_path.args.angle_bracketed.args[*]" 0
|
||||
//@ count "$.index[*][?(@.name=='next')].inner.function.decl.output.qualified_path.args.angle_bracketed.bindings[*]" 0
|
||||
//@ is "$.index[*][?(@.name=='next')].inner.function.decl.output.qualified_path.self_type.generic" \"Self\"
|
||||
//@ is "$.index[*][?(@.name=='next')].inner.function.decl.output.qualified_path.name" \"Item\"
|
||||
//@ count "$.index[*][?(@.name=='next')].inner.function.sig.output.qualified_path.args.angle_bracketed.args[*]" 0
|
||||
//@ count "$.index[*][?(@.name=='next')].inner.function.sig.output.qualified_path.args.angle_bracketed.bindings[*]" 0
|
||||
//@ is "$.index[*][?(@.name=='next')].inner.function.sig.output.qualified_path.self_type.generic" \"Self\"
|
||||
//@ is "$.index[*][?(@.name=='next')].inner.function.sig.output.qualified_path.name" \"Item\"
|
||||
fn next<'a>(&'a self) -> Self::Item;
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
#![no_std]
|
||||
|
||||
//@ has "$.index[*][?(@.name=='glob')]"
|
||||
//@ has "$.index[*][?(@.inner.import)].inner.import.name" \"*\"
|
||||
//@ has "$.index[*][?(@.inner.use)].inner.use.name" \"*\"
|
||||
|
||||
mod m1 {
|
||||
pub fn f() {}
|
||||
|
@ -9,11 +9,11 @@ impl IntoIterator for AlwaysTrue {
|
||||
/// type Item
|
||||
type Item = bool;
|
||||
|
||||
//@ count '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.default.impl_trait[*]' 1
|
||||
//@ is '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.default.impl_trait[0].trait_bound.trait.name' '"Iterator"'
|
||||
//@ count '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.default.impl_trait[0].trait_bound.trait.args.angle_bracketed.bindings[*]' 1
|
||||
//@ is '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.default.impl_trait[0].trait_bound.trait.args.angle_bracketed.bindings[0].name' '"Item"'
|
||||
//@ is '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.default.impl_trait[0].trait_bound.trait.args.angle_bracketed.bindings[0].binding.equality.type.primitive' '"bool"'
|
||||
//@ count '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait[*]' 1
|
||||
//@ is '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait[0].trait_bound.trait.name' '"Iterator"'
|
||||
//@ count '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[*]' 1
|
||||
//@ is '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[0].name' '"Item"'
|
||||
//@ is '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[0].binding.equality.type.primitive' '"bool"'
|
||||
|
||||
//@ set IntoIter = '$.index[*][?(@.docs=="type IntoIter")].id'
|
||||
/// type IntoIter
|
||||
|
@ -1,4 +1,4 @@
|
||||
//@ is "$.index[*][?(@.name=='hello')].inner.function.decl.output.impl_trait[1].use[0]" \"\'a\"
|
||||
//@ is "$.index[*][?(@.name=='hello')].inner.function.decl.output.impl_trait[1].use[1]" \"T\"
|
||||
//@ is "$.index[*][?(@.name=='hello')].inner.function.decl.output.impl_trait[1].use[2]" \"N\"
|
||||
//@ is "$.index[*][?(@.name=='hello')].inner.function.sig.output.impl_trait[1].use[0]" \"\'a\"
|
||||
//@ is "$.index[*][?(@.name=='hello')].inner.function.sig.output.impl_trait[1].use[1]" \"T\"
|
||||
//@ is "$.index[*][?(@.name=='hello')].inner.function.sig.output.impl_trait[1].use[2]" \"N\"
|
||||
pub fn hello<'a, T, const N: usize>() -> impl Sized + use<'a, T, N> {}
|
||||
|
@ -18,5 +18,5 @@ impl Foo {
|
||||
//@ is "$.index[*][?(@.docs=='has span')].span.begin" "[13, 0]"
|
||||
//@ is "$.index[*][?(@.docs=='has span')].span.end" "[15, 1]"
|
||||
// FIXME: this doesn't work due to https://github.com/freestrings/jsonpath/issues/91
|
||||
// is "$.index[*][?(@.inner.impl.synthetic==true)].span" null
|
||||
// is "$.index[*][?(@.inner.impl.is_synthetic==true)].span" null
|
||||
pub struct Foo;
|
||||
|
@ -3,7 +3,7 @@ extern crate foreign_trait;
|
||||
|
||||
/// ForeignTrait id hack
|
||||
pub use foreign_trait::ForeignTrait as _;
|
||||
//@ set ForeignTrait = "$.index[*][?(@.docs=='ForeignTrait id hack')].inner.import.id"
|
||||
//@ set ForeignTrait = "$.index[*][?(@.docs=='ForeignTrait id hack')].inner.use.id"
|
||||
|
||||
pub struct LocalStruct;
|
||||
//@ set LocalStruct = "$.index[*][?(@.name=='LocalStruct')].id"
|
||||
|
@ -11,10 +11,10 @@ mod bar {
|
||||
}
|
||||
}
|
||||
|
||||
//@ set import = "$.index[*][?(@.inner.import)].id"
|
||||
//@ set import = "$.index[*][?(@.inner.use)].id"
|
||||
pub use bar::Baz;
|
||||
|
||||
//@ is "$.index[*].inner.module.items[*]" $import
|
||||
//@ is "$.index[*].inner.import.id" $baz
|
||||
//@ is "$.index[*].inner.use.id" $baz
|
||||
//@ has "$.index[*][?(@.name == 'Baz')].inner.struct.impls[*]" $impl
|
||||
//@ is "$.index[*][?(@.docs=='impl')].inner.impl.items[*]" $doit
|
||||
|
@ -3,7 +3,7 @@ extern crate foreign_struct;
|
||||
|
||||
/// ForeignStruct id hack
|
||||
pub use foreign_struct::ForeignStruct as _;
|
||||
//@ set ForeignStruct = "$.index[*][?(@.docs=='ForeignStruct id hack')].inner.import.id"
|
||||
//@ set ForeignStruct = "$.index[*][?(@.docs=='ForeignStruct id hack')].inner.use.id"
|
||||
|
||||
pub trait LocalTrait {}
|
||||
//@ set LocalTrait = "$.index[*][?(@.name=='LocalTrait')].id"
|
||||
|
@ -6,21 +6,21 @@
|
||||
//@ count "$.index[*][?(@.name=='longest')].inner.function.generics.params[*]" 1
|
||||
//@ is "$.index[*][?(@.name=='longest')].inner.function.generics.where_predicates" []
|
||||
|
||||
//@ count "$.index[*][?(@.name=='longest')].inner.function.decl.inputs[*]" 2
|
||||
//@ is "$.index[*][?(@.name=='longest')].inner.function.decl.inputs[0][0]" '"l"'
|
||||
//@ is "$.index[*][?(@.name=='longest')].inner.function.decl.inputs[1][0]" '"r"'
|
||||
//@ count "$.index[*][?(@.name=='longest')].inner.function.sig.inputs[*]" 2
|
||||
//@ is "$.index[*][?(@.name=='longest')].inner.function.sig.inputs[0][0]" '"l"'
|
||||
//@ is "$.index[*][?(@.name=='longest')].inner.function.sig.inputs[1][0]" '"r"'
|
||||
|
||||
//@ is "$.index[*][?(@.name=='longest')].inner.function.decl.inputs[0][1].borrowed_ref.lifetime" \"\'a\"
|
||||
//@ is "$.index[*][?(@.name=='longest')].inner.function.decl.inputs[0][1].borrowed_ref.mutable" false
|
||||
//@ is "$.index[*][?(@.name=='longest')].inner.function.decl.inputs[0][1].borrowed_ref.type.primitive" \"str\"
|
||||
//@ is "$.index[*][?(@.name=='longest')].inner.function.sig.inputs[0][1].borrowed_ref.lifetime" \"\'a\"
|
||||
//@ is "$.index[*][?(@.name=='longest')].inner.function.sig.inputs[0][1].borrowed_ref.is_mutable" false
|
||||
//@ is "$.index[*][?(@.name=='longest')].inner.function.sig.inputs[0][1].borrowed_ref.type.primitive" \"str\"
|
||||
|
||||
//@ is "$.index[*][?(@.name=='longest')].inner.function.decl.inputs[1][1].borrowed_ref.lifetime" \"\'a\"
|
||||
//@ is "$.index[*][?(@.name=='longest')].inner.function.decl.inputs[1][1].borrowed_ref.mutable" false
|
||||
//@ is "$.index[*][?(@.name=='longest')].inner.function.decl.inputs[1][1].borrowed_ref.type.primitive" \"str\"
|
||||
//@ is "$.index[*][?(@.name=='longest')].inner.function.sig.inputs[1][1].borrowed_ref.lifetime" \"\'a\"
|
||||
//@ is "$.index[*][?(@.name=='longest')].inner.function.sig.inputs[1][1].borrowed_ref.is_mutable" false
|
||||
//@ is "$.index[*][?(@.name=='longest')].inner.function.sig.inputs[1][1].borrowed_ref.type.primitive" \"str\"
|
||||
|
||||
//@ is "$.index[*][?(@.name=='longest')].inner.function.decl.output.borrowed_ref.lifetime" \"\'a\"
|
||||
//@ is "$.index[*][?(@.name=='longest')].inner.function.decl.output.borrowed_ref.mutable" false
|
||||
//@ is "$.index[*][?(@.name=='longest')].inner.function.decl.output.borrowed_ref.type.primitive" \"str\"
|
||||
//@ is "$.index[*][?(@.name=='longest')].inner.function.sig.output.borrowed_ref.lifetime" \"\'a\"
|
||||
//@ is "$.index[*][?(@.name=='longest')].inner.function.sig.output.borrowed_ref.is_mutable" false
|
||||
//@ is "$.index[*][?(@.name=='longest')].inner.function.sig.output.borrowed_ref.type.primitive" \"str\"
|
||||
|
||||
pub fn longest<'a>(l: &'a str, r: &'a str) -> &'a str {
|
||||
if l.len() > r.len() { l } else { r }
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user