Merge from rustc

This commit is contained in:
Ralf Jung 2023-03-21 09:24:57 +01:00
commit 4bfde41afa
268 changed files with 4884 additions and 1765 deletions

View file

@ -48,19 +48,19 @@ jobs:
include:
- name: mingw-check
tidy: false
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: mingw-check-tidy
tidy: true
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: x86_64-gnu-llvm-14
tidy: false
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: x86_64-gnu-tools
tidy: false
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
timeout-minutes: 600
runs-on: "${{ matrix.os }}"
@ -181,136 +181,136 @@ jobs:
- ARM64
- linux
- name: arm-android
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: armhf-gnu
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: dist-aarch64-linux
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: dist-android
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: dist-arm-linux
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: dist-armhf-linux
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: dist-armv7-linux
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: dist-i586-gnu-i586-i686-musl
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: dist-i686-linux
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: dist-mips-linux
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: dist-mips64-linux
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: dist-mips64el-linux
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: dist-mipsel-linux
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: dist-powerpc-linux
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: dist-powerpc64-linux
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: dist-powerpc64le-linux
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: dist-riscv64-linux
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: dist-s390x-linux
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: dist-various-1
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: dist-various-2
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: dist-x86_64-freebsd
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: dist-x86_64-illumos
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: dist-x86_64-linux
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: dist-x86_64-linux-alt
env:
IMAGE: dist-x86_64-linux
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
- name: dist-x86_64-musl
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: dist-x86_64-netbsd
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: i686-gnu
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: i686-gnu-nopt
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: mingw-check
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: test-various
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: wasm32
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: x86_64-gnu
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: x86_64-gnu-stable
env:
IMAGE: x86_64-gnu
RUST_CI_OVERRIDE_RELEASE_CHANNEL: stable
CI_ONLY_WHEN_CHANNEL: nightly
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
- name: x86_64-gnu-aux
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: x86_64-gnu-debug
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: x86_64-gnu-distcheck
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: x86_64-gnu-llvm-15
env:
RUST_BACKTRACE: 1
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
- name: x86_64-gnu-llvm-14
env:
RUST_BACKTRACE: 1
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
- name: x86_64-gnu-llvm-14-stage1
env:
RUST_BACKTRACE: 1
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
- name: x86_64-gnu-nopt
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
- name: x86_64-gnu-tools
env:
DEPLOY_TOOLSTATES_JSON: toolstates-linux.json
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
- name: dist-x86_64-apple
env:
SCRIPT: "./x.py dist bootstrap --include-default-paths --host=x86_64-apple-darwin --target=x86_64-apple-darwin"
@ -386,80 +386,80 @@ jobs:
env:
RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc --enable-profiler"
SCRIPT: make ci-subset-1
os: windows-latest-xl
os: windows-2019-8core-32gb
- name: x86_64-msvc-2
env:
RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc --enable-profiler"
SCRIPT: make ci-subset-2
os: windows-latest-xl
os: windows-2019-8core-32gb
- name: i686-msvc-1
env:
RUST_CONFIGURE_ARGS: "--build=i686-pc-windows-msvc"
SCRIPT: make ci-subset-1
os: windows-latest-xl
os: windows-2019-8core-32gb
- name: i686-msvc-2
env:
RUST_CONFIGURE_ARGS: "--build=i686-pc-windows-msvc"
SCRIPT: make ci-subset-2
os: windows-latest-xl
os: windows-2019-8core-32gb
- name: x86_64-msvc-cargo
env:
SCRIPT: python x.py --stage 2 test src/tools/cargotest src/tools/cargo
RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc --enable-lld"
os: windows-latest-xl
os: windows-2019-8core-32gb
- name: x86_64-msvc-tools
env:
SCRIPT: src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh x.py /tmp/toolstate/toolstates.json windows
RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc --save-toolstates=/tmp/toolstate/toolstates.json"
DEPLOY_TOOLSTATES_JSON: toolstates-windows.json
os: windows-latest-xl
os: windows-2019-8core-32gb
- name: i686-mingw-1
env:
RUST_CONFIGURE_ARGS: "--build=i686-pc-windows-gnu"
SCRIPT: make ci-mingw-subset-1
NO_DOWNLOAD_CI_LLVM: 1
CUSTOM_MINGW: 1
os: windows-latest-xl
os: windows-2019-8core-32gb
- name: i686-mingw-2
env:
RUST_CONFIGURE_ARGS: "--build=i686-pc-windows-gnu"
SCRIPT: make ci-mingw-subset-2
NO_DOWNLOAD_CI_LLVM: 1
CUSTOM_MINGW: 1
os: windows-latest-xl
os: windows-2019-8core-32gb
- name: x86_64-mingw-1
env:
SCRIPT: make ci-mingw-subset-1
RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-gnu --enable-profiler"
NO_DOWNLOAD_CI_LLVM: 1
CUSTOM_MINGW: 1
os: windows-latest-xl
os: windows-2019-8core-32gb
- name: x86_64-mingw-2
env:
SCRIPT: make ci-mingw-subset-2
RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-gnu --enable-profiler"
NO_DOWNLOAD_CI_LLVM: 1
CUSTOM_MINGW: 1
os: windows-latest-xl
os: windows-2019-8core-32gb
- name: dist-x86_64-msvc
env:
RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc --host=x86_64-pc-windows-msvc --target=x86_64-pc-windows-msvc --enable-full-tools --enable-profiler"
SCRIPT: PGO_HOST=x86_64-pc-windows-msvc python src/ci/stage-build.py python x.py dist bootstrap --include-default-paths
DIST_REQUIRE_ALL_TOOLS: 1
os: windows-latest-xl
os: windows-2019-8core-32gb
- name: dist-i686-msvc
env:
RUST_CONFIGURE_ARGS: "--build=i686-pc-windows-msvc --host=i686-pc-windows-msvc --target=i686-pc-windows-msvc,i586-pc-windows-msvc --enable-full-tools --enable-profiler"
SCRIPT: python x.py dist bootstrap --include-default-paths
DIST_REQUIRE_ALL_TOOLS: 1
os: windows-latest-xl
os: windows-2019-8core-32gb
- name: dist-aarch64-msvc
env:
RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc --host=aarch64-pc-windows-msvc --enable-full-tools --enable-profiler"
SCRIPT: python x.py dist bootstrap --include-default-paths
DIST_REQUIRE_ALL_TOOLS: 1
WINDOWS_SDK_20348_HACK: 1
os: windows-latest-xl
os: windows-2019-8core-32gb
- name: dist-i686-mingw
env:
RUST_CONFIGURE_ARGS: "--build=i686-pc-windows-gnu --enable-full-tools --enable-profiler"
@ -467,7 +467,7 @@ jobs:
SCRIPT: python x.py dist bootstrap --include-default-paths
CUSTOM_MINGW: 1
DIST_REQUIRE_ALL_TOOLS: 1
os: windows-latest-xl
os: windows-2019-8core-32gb
- name: dist-x86_64-mingw
env:
SCRIPT: python x.py dist bootstrap --include-default-paths
@ -475,12 +475,12 @@ jobs:
NO_DOWNLOAD_CI_LLVM: 1
CUSTOM_MINGW: 1
DIST_REQUIRE_ALL_TOOLS: 1
os: windows-latest-xl
os: windows-2019-8core-32gb
- name: dist-x86_64-msvc-alt
env:
RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc --enable-extended --enable-profiler"
SCRIPT: python x.py dist bootstrap --include-default-paths
os: windows-latest-xl
os: windows-2019-8core-32gb
timeout-minutes: 600
runs-on: "${{ matrix.os }}"
steps:
@ -595,7 +595,7 @@ jobs:
matrix:
include:
- name: dist-x86_64-linux
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
env: {}
timeout-minutes: 600
runs-on: "${{ matrix.os }}"

1
.gitignore vendored
View file

@ -21,6 +21,7 @@ Session.vim
.project
.favorites.json
.settings/
.vs/
## Tool
.valgrindrc

View file

@ -898,7 +898,7 @@ dependencies = [
"tracing-subscriber",
"unified-diff",
"walkdir",
"winapi",
"windows 0.46.0",
]
[[package]]
@ -2273,7 +2273,7 @@ dependencies = [
"dirs",
"gix-path",
"libc",
"windows",
"windows 0.43.0",
]
[[package]]
@ -3103,9 +3103,9 @@ dependencies = [
[[package]]
name = "mdbook"
version = "0.4.25"
version = "0.4.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1ed28d5903dde77bd5182645078a37ee57014cac6ccb2d54e1d6496386648e4"
checksum = "764dcbfc2e5f868bc1b566eb179dff1a06458fd0cff846aae2579392dd3f01a0"
dependencies = [
"ammonia",
"anyhow",
@ -4529,7 +4529,7 @@ dependencies = [
"tempfile",
"thin-vec",
"tracing",
"winapi",
"windows 0.46.0",
]
[[package]]
@ -4588,7 +4588,7 @@ dependencies = [
"rustc_ty_utils",
"serde_json",
"tracing",
"winapi",
"windows 0.46.0",
]
[[package]]
@ -4636,7 +4636,7 @@ dependencies = [
"termize",
"tracing",
"unicode-width",
"winapi",
"windows 0.46.0",
]
[[package]]
@ -5277,7 +5277,7 @@ dependencies = [
"smallvec",
"termize",
"tracing",
"winapi",
"windows 0.46.0",
]
[[package]]
@ -6908,6 +6908,15 @@ dependencies = [
"windows_x86_64_msvc",
]
[[package]]
name = "windows"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.42.0"
@ -6934,9 +6943,9 @@ dependencies = [
[[package]]
name = "windows-targets"
version = "0.42.1"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
@ -6949,45 +6958,45 @@ dependencies = [
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.1"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.1"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_i686_gnu"
version = "0.42.1"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_msvc"
version = "0.42.1"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.1"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.1"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.1"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "writeable"

View file

@ -8,7 +8,7 @@ use rustc_errors::Applicability;
use rustc_expand::base::*;
use rustc_session::Session;
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::Span;
use rustc_span::{FileNameDisplayPreference, Span};
use std::iter;
use thin_vec::{thin_vec, ThinVec};
@ -33,7 +33,23 @@ pub fn expand_test_case(
}
let sp = ecx.with_def_site_ctxt(attr_sp);
let mut item = anno_item.expect_item();
let (mut item, is_stmt) = match anno_item {
Annotatable::Item(item) => (item, false),
Annotatable::Stmt(stmt) if let ast::StmtKind::Item(_) = stmt.kind => if let ast::StmtKind::Item(i) = stmt.into_inner().kind {
(i, true)
} else {
unreachable!()
},
_ => {
ecx.struct_span_err(
anno_item.span(),
"`#[test_case]` attribute is only allowed on items",
)
.emit();
return vec![];
}
};
item = item.map(|mut item| {
let test_path_symbol = Symbol::intern(&item_path(
// skip the name of the root module
@ -50,7 +66,13 @@ pub fn expand_test_case(
item
});
return vec![Annotatable::Item(item)];
let ret = if is_stmt {
Annotatable::Stmt(P(ecx.stmt_item(item.span, item)))
} else {
Annotatable::Item(item)
};
vec![ret]
}
pub fn expand_test(
@ -231,6 +253,8 @@ pub fn expand_test_or_bench(
&item.ident,
));
let location_info = get_location_info(cx, &item);
let mut test_const = cx.item(
sp,
Ident::new(item.ident.name, sp),
@ -280,6 +304,16 @@ pub fn expand_test_or_bench(
cx.expr_none(sp)
},
),
// source_file: <relative_path_of_source_file>
field("source_file", cx.expr_str(sp, location_info.0)),
// start_line: start line of the test fn identifier.
field("start_line", cx.expr_usize(sp, location_info.1)),
// start_col: start column of the test fn identifier.
field("start_col", cx.expr_usize(sp, location_info.2)),
// end_line: end line of the test fn identifier.
field("end_line", cx.expr_usize(sp, location_info.3)),
// end_col: end column of the test fn identifier.
field("end_col", cx.expr_usize(sp, location_info.4)),
// compile_fail: true | false
field("compile_fail", cx.expr_bool(sp, false)),
// no_run: true | false
@ -364,6 +398,19 @@ pub fn expand_test_or_bench(
}
}
fn get_location_info(cx: &ExtCtxt<'_>, item: &ast::Item) -> (Symbol, usize, usize, usize, usize) {
let span = item.ident.span;
let (source_file, lo_line, lo_col, hi_line, hi_col) =
cx.sess.source_map().span_to_location_info(span);
let file_name = match source_file {
Some(sf) => sf.name.display(FileNameDisplayPreference::Remapped).to_string(),
None => "no-location".to_string(),
};
(Symbol::intern(&file_name), lo_line, lo_col, hi_line, hi_col)
}
fn item_path(mod_path: &[Ident], item_ident: &Ident) -> String {
mod_path
.iter()

View file

@ -358,9 +358,9 @@ fn link_rlib<'a>(
let (data, _) = create_wrapper_file(sess, b".bundled_lib".to_vec(), &src);
let wrapper_file = emit_wrapper_file(sess, &data, tmpdir, filename.as_str());
packed_bundled_libs.push(wrapper_file);
} else if let Some(name) = lib.name {
} else {
let path =
find_native_static_library(name.as_str(), lib.verbatim, &lib_search_paths, sess);
find_native_static_library(lib.name.as_str(), lib.verbatim, &lib_search_paths, sess);
ab.add_archive(&path, Box::new(|_| false)).unwrap_or_else(|error| {
sess.emit_fatal(errors::AddNativeLibrary { library_path: path, error })});
}
@ -436,7 +436,7 @@ fn collate_raw_dylibs<'a, 'b>(
for lib in used_libraries {
if lib.kind == NativeLibKind::RawDylib {
let ext = if lib.verbatim { "" } else { ".dll" };
let name = format!("{}{}", lib.name.expect("unnamed raw-dylib library"), ext);
let name = format!("{}{}", lib.name, ext);
let imports = dylib_table.entry(name.clone()).or_default();
for import in &lib.dll_imports {
if let Some(old_import) = imports.insert(import.name, import) {
@ -1296,7 +1296,7 @@ fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLib]) {
.iter()
.filter(|l| relevant_lib(sess, l))
.filter_map(|lib| {
let name = lib.name?;
let name = lib.name;
match lib.kind {
NativeLibKind::Static { bundle: Some(false), .. }
| NativeLibKind::Dylib { .. }
@ -1317,6 +1317,7 @@ fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLib]) {
// These are included, no need to print them
NativeLibKind::Static { bundle: None | Some(true), .. }
| NativeLibKind::LinkArg
| NativeLibKind::WasmImportModule
| NativeLibKind::RawDylib => None,
}
})
@ -2275,21 +2276,18 @@ fn add_native_libs_from_crate(
let mut last = (None, NativeLibKind::Unspecified, false);
for lib in native_libs {
let Some(name) = lib.name else {
continue;
};
if !relevant_lib(sess, lib) {
continue;
}
// Skip if this library is the same as the last.
last = if (lib.name, lib.kind, lib.verbatim) == last {
last = if (Some(lib.name), lib.kind, lib.verbatim) == last {
continue;
} else {
(lib.name, lib.kind, lib.verbatim)
(Some(lib.name), lib.kind, lib.verbatim)
};
let name = name.as_str();
let name = lib.name.as_str();
let verbatim = lib.verbatim;
match lib.kind {
NativeLibKind::Static { bundle, whole_archive } => {
@ -2346,6 +2344,7 @@ fn add_native_libs_from_crate(
NativeLibKind::RawDylib => {
// Handled separately in `linker_with_args`.
}
NativeLibKind::WasmImportModule => {}
NativeLibKind::LinkArg => {
if link_static {
cmd.arg(name);

View file

@ -595,7 +595,7 @@ fn wasm_import_module_map(tcx: TyCtxt<'_>, cnum: CrateNum) -> FxHashMap<DefId, S
let mut ret = FxHashMap::default();
for (def_id, lib) in tcx.foreign_modules(cnum).iter() {
let module = def_id_to_native_lib.get(&def_id).and_then(|s| s.wasm_import_module);
let module = def_id_to_native_lib.get(&def_id).and_then(|s| s.wasm_import_module());
let Some(module) = module else { continue };
ret.extend(lib.foreign_items.iter().map(|id| {
assert_eq!(id.krate, cnum);

View file

@ -118,7 +118,7 @@ bitflags::bitflags! {
#[derive(Clone, Debug, Encodable, Decodable, HashStable)]
pub struct NativeLib {
pub kind: NativeLibKind,
pub name: Option<Symbol>,
pub name: Symbol,
pub filename: Option<Symbol>,
pub cfg: Option<ast::MetaItem>,
pub verbatim: bool,

View file

@ -1475,7 +1475,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
) -> OperandRef<'tcx, Bx::Value> {
let tcx = bx.tcx();
let mut span_to_caller_location = |span: Span| {
let mut span_to_caller_location = |mut span: Span| {
// Remove `Inlined` marks as they pollute `expansion_cause`.
while span.is_inlined() {
span.remove_mark();
}
let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
let caller = tcx.sess.source_map().lookup_char_pos(topmost.lo());
let const_loc = tcx.const_caller_location((

View file

@ -111,7 +111,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
location
}
pub(crate) fn location_triple_for_span(&self, span: Span) -> (Symbol, u32, u32) {
pub(crate) fn location_triple_for_span(&self, mut span: Span) -> (Symbol, u32, u32) {
// Remove `Inlined` marks as they pollute `expansion_cause`.
while span.is_inlined() {
span.remove_mark();
}
let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo());
(

View file

@ -36,8 +36,15 @@ elsa = "1.8"
[dependencies.parking_lot]
version = "0.11"
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["fileapi", "psapi", "winerror"] }
[target.'cfg(windows)'.dependencies.windows]
version = "0.46.0"
features = [
"Win32_Foundation",
"Win32_Storage_FileSystem",
"Win32_System_IO",
"Win32_System_ProcessStatus",
"Win32_System_Threading",
]
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
memmap2 = "0.2.1"

View file

@ -4,9 +4,6 @@
//! green/native threading. This is just a bare-bones enough solution for
//! librustdoc, it is not production quality at all.
#![allow(non_camel_case_types)]
#![allow(nonstandard_style)]
cfg_if! {
if #[cfg(target_os = "linux")] {
mod linux;
@ -16,7 +13,7 @@ cfg_if! {
use unix as imp;
} else if #[cfg(windows)] {
mod windows;
use windows as imp;
use self::windows as imp;
} else {
mod unsupported;
use unsupported as imp;

View file

@ -1,13 +1,16 @@
use std::fs::{File, OpenOptions};
use std::io;
use std::mem;
use std::os::windows::prelude::*;
use std::path::Path;
use winapi::shared::winerror::ERROR_INVALID_FUNCTION;
use winapi::um::fileapi::LockFileEx;
use winapi::um::minwinbase::{LOCKFILE_EXCLUSIVE_LOCK, LOCKFILE_FAIL_IMMEDIATELY, OVERLAPPED};
use winapi::um::winnt::{FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE};
use windows::{
Win32::Foundation::{ERROR_INVALID_FUNCTION, HANDLE},
Win32::Storage::FileSystem::{
LockFileEx, FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE, LOCKFILE_EXCLUSIVE_LOCK,
LOCKFILE_FAIL_IMMEDIATELY, LOCK_FILE_FLAGS,
},
Win32::System::IO::OVERLAPPED,
};
#[derive(Debug)]
pub struct Lock {
@ -25,7 +28,7 @@ impl Lock {
let share_mode = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE;
let mut open_options = OpenOptions::new();
open_options.read(true).share_mode(share_mode);
open_options.read(true).share_mode(share_mode.0);
if create {
open_options.create(true).write(true);
@ -43,33 +46,42 @@ impl Lock {
}
};
let ret = unsafe {
let mut overlapped: OVERLAPPED = mem::zeroed();
let mut dwFlags = 0;
if !wait {
dwFlags |= LOCKFILE_FAIL_IMMEDIATELY;
}
if exclusive {
dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
}
debug!("attempting to acquire lock on lock file `{}`", p.display());
LockFileEx(file.as_raw_handle(), dwFlags, 0, 0xFFFF_FFFF, 0xFFFF_FFFF, &mut overlapped)
};
if ret == 0 {
let err = io::Error::last_os_error();
debug!("failed acquiring file lock: {}", err);
Err(err)
} else {
debug!("successfully acquired lock");
Ok(Lock { _file: file })
let mut flags = LOCK_FILE_FLAGS::default();
if !wait {
flags |= LOCKFILE_FAIL_IMMEDIATELY;
}
if exclusive {
flags |= LOCKFILE_EXCLUSIVE_LOCK;
}
let mut overlapped = OVERLAPPED::default();
debug!("attempting to acquire lock on lock file `{}`", p.display());
unsafe {
LockFileEx(
HANDLE(file.as_raw_handle() as isize),
flags,
0,
u32::MAX,
u32::MAX,
&mut overlapped,
)
}
.ok()
.map_err(|e| {
let err = io::Error::from_raw_os_error(e.code().0);
debug!("failed acquiring file lock: {}", err);
err
})?;
debug!("successfully acquired lock");
Ok(Lock { _file: file })
}
pub fn error_unsupported(err: &io::Error) -> bool {
err.raw_os_error() == Some(ERROR_INVALID_FUNCTION as i32)
err.raw_os_error() == Some(ERROR_INVALID_FUNCTION.0 as i32)
}
}

View file

@ -796,21 +796,26 @@ fn get_thread_id() -> u32 {
cfg_if! {
if #[cfg(windows)] {
pub fn get_resident_set_size() -> Option<usize> {
use std::mem::{self, MaybeUninit};
use winapi::shared::minwindef::DWORD;
use winapi::um::processthreadsapi::GetCurrentProcess;
use winapi::um::psapi::{GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS};
use std::mem;
let mut pmc = MaybeUninit::<PROCESS_MEMORY_COUNTERS>::uninit();
match unsafe {
GetProcessMemoryInfo(GetCurrentProcess(), pmc.as_mut_ptr(), mem::size_of_val(&pmc) as DWORD)
} {
0 => None,
_ => {
let pmc = unsafe { pmc.assume_init() };
Some(pmc.WorkingSetSize as usize)
}
use windows::{
Win32::System::ProcessStatus::{K32GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS},
Win32::System::Threading::GetCurrentProcess,
};
let mut pmc = PROCESS_MEMORY_COUNTERS::default();
let pmc_size = mem::size_of_val(&pmc);
unsafe {
K32GetProcessMemoryInfo(
GetCurrentProcess(),
&mut pmc,
pmc_size as u32,
)
}
.ok()
.ok()?;
Some(pmc.WorkingSetSize)
}
} else if #[cfg(target_os = "macos")] {
pub fn get_resident_set_size() -> Option<usize> {

View file

@ -54,8 +54,11 @@ rustc_hir_analysis = { path = "../rustc_hir_analysis" }
[target.'cfg(unix)'.dependencies]
libc = "0.2"
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["consoleapi", "debugapi", "processenv"] }
[target.'cfg(windows)'.dependencies.windows]
version = "0.46.0"
features = [
"Win32_System_Diagnostics_Debug",
]
[features]
llvm = ['rustc_interface/llvm']

View file

@ -1246,11 +1246,9 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
interface::try_print_query_stack(&handler, num_frames);
#[cfg(windows)]
unsafe {
if env::var("RUSTC_BREAK_ON_ICE").is_ok() {
// Trigger a debugger if we crashed during bootstrap
winapi::um::debugapi::DebugBreak();
}
if env::var("RUSTC_BREAK_ON_ICE").is_ok() {
// Trigger a debugger if we crashed during bootstrap
unsafe { windows::Win32::System::Diagnostics::Debug::DebugBreak() };
}
}

View file

@ -25,8 +25,14 @@ termize = "0.1.1"
serde = { version = "1.0.125", features = [ "derive" ] }
serde_json = "1.0.59"
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = [ "handleapi", "synchapi", "winbase" ] }
[target.'cfg(windows)'.dependencies.windows]
version = "0.46.0"
features = [
"Win32_Foundation",
"Win32_Security",
"Win32_System_Threading",
"Win32_System_WindowsProgramming",
]
[features]
rustc_use_parallel_compiler = ['rustc_error_messages/rustc_use_parallel_compiler']

View file

@ -16,10 +16,12 @@ pub fn acquire_global_lock(name: &str) -> Box<dyn Any> {
use std::ffi::CString;
use std::io;
use winapi::shared::ntdef::HANDLE;
use winapi::um::handleapi::CloseHandle;
use winapi::um::synchapi::{CreateMutexA, ReleaseMutex, WaitForSingleObject};
use winapi::um::winbase::{INFINITE, WAIT_ABANDONED, WAIT_OBJECT_0};
use windows::{
core::PCSTR,
Win32::Foundation::{CloseHandle, HANDLE, WAIT_ABANDONED, WAIT_OBJECT_0},
Win32::System::Threading::{CreateMutexA, ReleaseMutex, WaitForSingleObject},
Win32::System::WindowsProgramming::INFINITE,
};
struct Handle(HANDLE);
@ -42,49 +44,38 @@ pub fn acquire_global_lock(name: &str) -> Box<dyn Any> {
}
let cname = CString::new(name).unwrap();
unsafe {
// Create a named mutex, with no security attributes and also not
// acquired when we create it.
//
// This will silently create one if it doesn't already exist, or it'll
// open up a handle to one if it already exists.
let mutex = CreateMutexA(std::ptr::null_mut(), 0, cname.as_ptr());
if mutex.is_null() {
panic!(
"failed to create global mutex named `{}`: {}",
name,
io::Error::last_os_error()
);
}
let mutex = Handle(mutex);
// Create a named mutex, with no security attributes and also not
// acquired when we create it.
//
// This will silently create one if it doesn't already exist, or it'll
// open up a handle to one if it already exists.
let mutex = unsafe { CreateMutexA(None, false, PCSTR::from_raw(cname.as_ptr().cast())) }
.unwrap_or_else(|_| panic!("failed to create global mutex named `{}`", name));
let mutex = Handle(mutex);
// Acquire the lock through `WaitForSingleObject`.
//
// A return value of `WAIT_OBJECT_0` means we successfully acquired it.
//
// A return value of `WAIT_ABANDONED` means that the previous holder of
// the thread exited without calling `ReleaseMutex`. This can happen,
// for example, when the compiler crashes or is interrupted via ctrl-c
// or the like. In this case, however, we are still transferred
// ownership of the lock so we continue.
//
// If an error happens.. well... that's surprising!
match WaitForSingleObject(mutex.0, INFINITE) {
WAIT_OBJECT_0 | WAIT_ABANDONED => {}
code => {
panic!(
"WaitForSingleObject failed on global mutex named \
`{}`: {} (ret={:x})",
name,
io::Error::last_os_error(),
code
);
}
}
// Return a guard which will call `ReleaseMutex` when dropped.
Box::new(Guard(mutex))
// Acquire the lock through `WaitForSingleObject`.
//
// A return value of `WAIT_OBJECT_0` means we successfully acquired it.
//
// A return value of `WAIT_ABANDONED` means that the previous holder of
// the thread exited without calling `ReleaseMutex`. This can happen,
// for example, when the compiler crashes or is interrupted via ctrl-c
// or the like. In this case, however, we are still transferred
// ownership of the lock so we continue.
//
// If an error happens.. well... that's surprising!
match unsafe { WaitForSingleObject(mutex.0, INFINITE) } {
WAIT_OBJECT_0 | WAIT_ABANDONED => (),
err => panic!(
"WaitForSingleObject failed on global mutex named `{}`: {} (ret={:x})",
name,
io::Error::last_os_error(),
err.0
),
}
// Return a guard which will call `ReleaseMutex` when dropped.
Box::new(Guard(mutex))
}
#[cfg(not(windows))]

View file

@ -3068,7 +3068,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
// generate the def_id of an associated type for the trait and return as
// type a projection.
let def_id = if in_trait && tcx.lower_impl_trait_in_trait_to_assoc_ty() {
tcx.associated_item_for_impl_trait_in_trait(local_def_id).to_def_id()
tcx.associated_type_for_impl_trait_in_trait(local_def_id).to_def_id()
} else {
local_def_id.to_def_id()
};
@ -3152,8 +3152,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
debug!("impl_trait_ty_to_ty: generics={:?}", generics);
let substs = InternalSubsts::for_item(tcx, def_id, |param, _| {
if let Some(i) = (param.index as usize).checked_sub(generics.parent_count) {
// Our own parameters are the resolved lifetimes.
// We use `generics.count() - lifetimes.len()` here instead of `generics.parent_count`
// since return-position impl trait in trait squashes all of the generics from its source fn
// into its own generics, so the opaque's "own" params isn't always just lifetimes.
if let Some(i) = (param.index as usize).checked_sub(generics.count() - lifetimes.len())
{
// Resolve our own lifetime parameters.
let GenericParamDefKind::Lifetime { .. } = param.kind else { bug!() };
let hir::GenericArg::Lifetime(lifetime) = &lifetimes[i] else { bug!() };
self.ast_region_to_region(lifetime, None).into()

View file

@ -278,8 +278,11 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::EarlyBinder<Ty<'_>>
}
TraitItemKind::Const(ty, body_id) => body_id
.and_then(|body_id| {
is_suggestable_infer_ty(ty)
.then(|| infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident, "constant",))
is_suggestable_infer_ty(ty).then(|| {
infer_placeholder_type(
tcx, def_id, body_id, ty.span, item.ident, "constant",
)
})
})
.unwrap_or_else(|| icx.to_ty(ty)),
TraitItemKind::Type(_, Some(ty)) => icx.to_ty(ty),
@ -335,14 +338,15 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::EarlyBinder<Ty<'_>>
}
}
ItemKind::TyAlias(self_ty, _) => icx.to_ty(self_ty),
ItemKind::Impl(hir::Impl { self_ty, .. }) => {
match self_ty.find_self_aliases() {
spans if spans.len() > 0 => {
let guar = tcx.sess.emit_err(crate::errors::SelfInImplSelf { span: spans.into(), note: () });
tcx.ty_error(guar)
},
_ => icx.to_ty(*self_ty),
ItemKind::Impl(hir::Impl { self_ty, .. }) => match self_ty.find_self_aliases() {
spans if spans.len() > 0 => {
let guar = tcx.sess.emit_err(crate::errors::SelfInImplSelf {
span: spans.into(),
note: (),
});
tcx.ty_error(guar)
}
_ => icx.to_ty(*self_ty),
},
ItemKind::Fn(..) => {
let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id());
@ -364,7 +368,10 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::EarlyBinder<Ty<'_>>
..
}) => {
if in_trait && !tcx.impl_defaultness(owner).has_value() {
span_bug!(tcx.def_span(def_id), "tried to get type of this RPITIT with no definition");
span_bug!(
tcx.def_span(def_id),
"tried to get type of this RPITIT with no definition"
);
}
find_opaque_ty_constraints_for_rpit(tcx, def_id, owner)
}
@ -453,15 +460,12 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::EarlyBinder<Ty<'_>>
tcx.adt_def(tcx.hir().get_parent_item(hir_id)).repr().discr_type().to_ty(tcx)
}
Node::TypeBinding(
TypeBinding {
hir_id: binding_id,
kind: TypeBindingKind::Equality { term: Term::Const(e) },
ident,
..
},
) if let Node::TraitRef(trait_ref) =
tcx.hir().get_parent(*binding_id)
Node::TypeBinding(TypeBinding {
hir_id: binding_id,
kind: TypeBindingKind::Equality { term: Term::Const(e) },
ident,
..
}) if let Node::TraitRef(trait_ref) = tcx.hir().get_parent(*binding_id)
&& e.hir_id == hir_id =>
{
let Some(trait_def_id) = trait_ref.trait_def_id() else {
@ -475,7 +479,9 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::EarlyBinder<Ty<'_>>
def_id.to_def_id(),
);
if let Some(assoc_item) = assoc_item {
tcx.type_of(assoc_item.def_id).subst_identity()
tcx.type_of(assoc_item.def_id)
.no_bound_vars()
.expect("const parameter types cannot be generic")
} else {
// FIXME(associated_const_equality): add a useful error message here.
tcx.ty_error_with_message(
@ -485,10 +491,13 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::EarlyBinder<Ty<'_>>
}
}
Node::TypeBinding(
TypeBinding { hir_id: binding_id, gen_args, kind, ident, .. },
) if let Node::TraitRef(trait_ref) =
tcx.hir().get_parent(*binding_id)
Node::TypeBinding(TypeBinding {
hir_id: binding_id,
gen_args,
kind,
ident,
..
}) if let Node::TraitRef(trait_ref) = tcx.hir().get_parent(*binding_id)
&& let Some((idx, _)) =
gen_args.args.iter().enumerate().find(|(_, arg)| {
if let GenericArg::Const(ct) = arg {
@ -517,15 +526,18 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::EarlyBinder<Ty<'_>>
},
def_id.to_def_id(),
);
if let Some(param)
= assoc_item.map(|item| &tcx.generics_of(item.def_id).params[idx]).filter(|param| param.kind.is_ty_or_const())
if let Some(assoc_item) = assoc_item
&& let param = &tcx.generics_of(assoc_item.def_id).params[idx]
&& matches!(param.kind, ty::GenericParamDefKind::Const { .. })
{
tcx.type_of(param.def_id).subst_identity()
tcx.type_of(param.def_id)
.no_bound_vars()
.expect("const parameter types cannot be generic")
} else {
// FIXME(associated_const_equality): add a useful error message here.
tcx.ty_error_with_message(
DUMMY_SP,
"Could not find associated const on trait",
"Could not find const param on associated item",
)
}
}

View file

@ -1028,6 +1028,15 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
true
}
})
// ensure that we don't suggest unstable methods
.filter(|candidate| {
// note that `DUMMY_SP` is ok here because it is only used for
// suggestions and macro stuff which isn't applicable here.
!matches!(
self.tcx.eval_stability(candidate.item.def_id, None, DUMMY_SP, None),
stability::EvalResult::Deny { .. }
)
})
.map(|candidate| candidate.item.ident(self.tcx))
.filter(|&name| set.insert(name))
.collect();

View file

@ -615,9 +615,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}
let report_path_match = |err: &mut Diagnostic, did1: DefId, did2: DefId| {
// Only external crates, if either is from a local
// module we could have false positives
if !(did1.is_local() || did2.is_local()) && did1.krate != did2.krate {
// Only report definitions from different crates. If both definitions
// are from a local module we could have false positives, e.g.
// let _ = [{struct Foo; Foo}, {struct Foo; Foo}];
if did1.krate != did2.krate {
let abs_path =
|def_id| AbsolutePathPrinter { tcx: self.tcx }.print_def_path(def_id, &[]);
@ -629,10 +630,16 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
};
if same_path().unwrap_or(false) {
let crate_name = self.tcx.crate_name(did1.krate);
err.note(&format!(
"perhaps two different versions of crate `{}` are being used?",
crate_name
));
let msg = if did1.is_local() || did2.is_local() {
format!(
"the crate `{crate_name}` is compiled multiple times, possibly with different configurations"
)
} else {
format!(
"perhaps two different versions of crate `{crate_name}` are being used?"
)
};
err.note(msg);
}
}
};

View file

@ -508,3 +508,6 @@ lint_opaque_hidden_inferred_bound = opaque type `{$ty}` does not satisfy its ass
.specifically = this associated type bound is unsatisfied for `{$proj_ty}`
lint_opaque_hidden_inferred_bound_sugg = add this bound
lint_useless_anonymous_reexport = useless anonymous re-export
.note = only anonymous re-exports of traits are useful, this is {$article} `{$desc}`

View file

@ -2781,8 +2781,7 @@ impl ClashingExternDeclarations {
// Given a transparent newtype, reach through and grab the inner
// type unless the newtype makes the type non-null.
let non_transparent_ty = |ty: Ty<'tcx>| -> Ty<'tcx> {
let mut ty = ty;
let non_transparent_ty = |mut ty: Ty<'tcx>| -> Ty<'tcx> {
loop {
if let ty::Adt(def, substs) = *ty.kind() {
let is_transparent = def.repr().transparent();
@ -2792,14 +2791,14 @@ impl ClashingExternDeclarations {
ty, is_transparent, is_non_null
);
if is_transparent && !is_non_null {
debug_assert!(def.variants().len() == 1);
debug_assert_eq!(def.variants().len(), 1);
let v = &def.variant(VariantIdx::new(0));
ty = transparent_newtype_field(tcx, v)
.expect(
"single-variant transparent structure with zero-sized field",
)
.ty(tcx, substs);
continue;
// continue with `ty`'s non-ZST field,
// otherwise `ty` is a ZST and we can return
if let Some(field) = transparent_newtype_field(tcx, v) {
ty = field.ty(tcx, substs);
continue;
}
}
}
debug!("non_transparent_ty -> {:?}", ty);
@ -2813,10 +2812,8 @@ impl ClashingExternDeclarations {
if !seen_types.insert((a, b)) {
// We've encountered a cycle. There's no point going any further -- the types are
// structurally the same.
return true;
}
let tcx = cx.tcx;
if a == b {
true
} else if a == b {
// All nominally-same types are structurally same, too.
true
} else {

View file

@ -74,6 +74,7 @@ mod opaque_hidden_inferred_bound;
mod pass_by_value;
mod passes;
mod redundant_semicolon;
mod reexports;
mod traits;
mod types;
mod unused;
@ -111,6 +112,7 @@ use noop_method_call::*;
use opaque_hidden_inferred_bound::*;
use pass_by_value::*;
use redundant_semicolon::*;
use reexports::*;
use traits::*;
use types::*;
use unused::*;
@ -242,6 +244,7 @@ late_lint_methods!(
OpaqueHiddenInferredBound: OpaqueHiddenInferredBound,
MultipleSupertraitUpcastable: MultipleSupertraitUpcastable,
MapUnitFn: MapUnitFn,
UselessAnonymousReexport: UselessAnonymousReexport,
]
]
);

View file

@ -1528,3 +1528,11 @@ pub struct UnusedAllocationDiag;
#[derive(LintDiagnostic)]
#[diag(lint_unused_allocation_mut)]
pub struct UnusedAllocationMutDiag;
#[derive(LintDiagnostic)]
#[diag(lint_useless_anonymous_reexport)]
#[note]
pub struct UselessAnonymousReexportDiag {
pub article: &'static str,
pub desc: &'static str,
}

View file

@ -0,0 +1,82 @@
use crate::lints::UselessAnonymousReexportDiag;
use crate::{LateContext, LateLintPass, LintContext};
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
use rustc_hir::{Item, ItemKind, UseKind};
use rustc_middle::ty::Visibility;
use rustc_span::symbol::kw;
use rustc_span::Span;
declare_lint! {
/// The `useless_anonymous_reexport` lint checks if anonymous re-exports
/// are re-exports of traits.
///
/// ### Example
///
/// ```rust,compile_fail
/// #![deny(useless_anonymous_reexport)]
///
/// mod sub {
/// pub struct Bar;
/// }
///
/// pub use self::sub::Bar as _;
/// # fn main() {}
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Anonymous re-exports are only useful if it's a re-export of a trait
/// in case you want to give access to it. If you re-export any other kind,
/// you won't be able to use it since its name won't be accessible.
pub USELESS_ANONYMOUS_REEXPORT,
Warn,
"useless anonymous re-export"
}
declare_lint_pass!(UselessAnonymousReexport => [USELESS_ANONYMOUS_REEXPORT]);
fn emit_err(cx: &LateContext<'_>, span: Span, def_id: DefId) {
let article = cx.tcx.def_descr_article(def_id);
let desc = cx.tcx.def_descr(def_id);
cx.emit_spanned_lint(
USELESS_ANONYMOUS_REEXPORT,
span,
UselessAnonymousReexportDiag { article, desc },
);
}
impl<'tcx> LateLintPass<'tcx> for UselessAnonymousReexport {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
if let ItemKind::Use(path, kind) = item.kind &&
!matches!(kind, UseKind::Glob) &&
item.ident.name == kw::Underscore &&
// We only want re-exports. If it's just a `use X;`, then we ignore it.
match cx.tcx.local_visibility(item.owner_id.def_id) {
Visibility::Public => true,
Visibility::Restricted(level) => {
level != cx.tcx.parent_module_from_def_id(item.owner_id.def_id)
}
}
{
for def_id in path.res.iter().filter_map(|r| r.opt_def_id()) {
match cx.tcx.def_kind(def_id) {
DefKind::Trait | DefKind::TraitAlias => {}
DefKind::TyAlias => {
let ty = cx.tcx.type_of(def_id);
if !ty.0.is_trait() {
emit_err(cx, item.span, def_id);
break;
}
}
_ => {
emit_err(cx, item.span, def_id);
break;
}
}
}
}
}
}

View file

@ -46,7 +46,7 @@ pub fn find_native_static_library(
}
fn find_bundled_library(
name: Option<Symbol>,
name: Symbol,
verbatim: Option<bool>,
kind: NativeLibKind,
has_cfg: bool,
@ -58,7 +58,7 @@ fn find_bundled_library(
{
let verbatim = verbatim.unwrap_or(false);
let search_paths = &sess.target_filesearch(PathKind::Native).search_path_dirs();
return find_native_static_library(name.unwrap().as_str(), verbatim, search_paths, sess)
return find_native_static_library(name.as_str(), verbatim, search_paths, sess)
.file_name()
.and_then(|s| s.to_str())
.map(Symbol::intern);
@ -336,10 +336,16 @@ impl<'tcx> Collector<'tcx> {
if name.is_some() || kind.is_some() || modifiers.is_some() || cfg.is_some() {
sess.emit_err(errors::IncompatibleWasmLink { span });
}
} else if name.is_none() {
sess.emit_err(errors::LinkRequiresName { span: m.span });
}
if wasm_import_module.is_some() {
(name, kind) = (wasm_import_module, Some(NativeLibKind::WasmImportModule));
}
let Some((name, name_span)) = name else {
sess.emit_err(errors::LinkRequiresName { span: m.span });
continue;
};
// Do this outside of the loop so that `import_name_type` can be specified before `kind`.
if let Some((_, span)) = import_name_type {
if kind != Some(NativeLibKind::RawDylib) {
@ -349,8 +355,8 @@ impl<'tcx> Collector<'tcx> {
let dll_imports = match kind {
Some(NativeLibKind::RawDylib) => {
if let Some((name, span)) = name && name.as_str().contains('\0') {
sess.emit_err(errors::RawDylibNoNul { span });
if name.as_str().contains('\0') {
sess.emit_err(errors::RawDylibNoNul { span: name_span });
}
foreign_mod_items
.iter()
@ -389,7 +395,6 @@ impl<'tcx> Collector<'tcx> {
}
};
let name = name.map(|(name, _)| name);
let kind = kind.unwrap_or(NativeLibKind::Unspecified);
let filename = find_bundled_library(name, verbatim, kind, cfg.is_some(), sess);
self.libs.push(NativeLib {
@ -398,7 +403,6 @@ impl<'tcx> Collector<'tcx> {
kind,
cfg,
foreign_module: Some(it.owner_id.to_def_id()),
wasm_import_module: wasm_import_module.map(|(name, _)| name),
verbatim,
dll_imports,
});
@ -415,11 +419,7 @@ impl<'tcx> Collector<'tcx> {
self.tcx.sess.emit_err(errors::LibFrameworkApple);
}
if let Some(ref new_name) = lib.new_name {
let any_duplicate = self
.libs
.iter()
.filter_map(|lib| lib.name.as_ref())
.any(|n| n.as_str() == lib.name);
let any_duplicate = self.libs.iter().any(|n| n.name.as_str() == lib.name);
if new_name.is_empty() {
self.tcx.sess.emit_err(errors::EmptyRenamingTarget { lib_name: &lib.name });
} else if !any_duplicate {
@ -444,33 +444,28 @@ impl<'tcx> Collector<'tcx> {
let mut existing = self
.libs
.drain_filter(|lib| {
if let Some(lib_name) = lib.name {
if lib_name.as_str() == passed_lib.name {
// FIXME: This whole logic is questionable, whether modifiers are
// involved or not, library reordering and kind overriding without
// explicit `:rename` in particular.
if lib.has_modifiers() || passed_lib.has_modifiers() {
match lib.foreign_module {
Some(def_id) => {
self.tcx.sess.emit_err(errors::NoLinkModOverride {
span: Some(self.tcx.def_span(def_id)),
})
}
None => self
.tcx
.sess
.emit_err(errors::NoLinkModOverride { span: None }),
};
}
if passed_lib.kind != NativeLibKind::Unspecified {
lib.kind = passed_lib.kind;
}
if let Some(new_name) = &passed_lib.new_name {
lib.name = Some(Symbol::intern(new_name));
}
lib.verbatim = passed_lib.verbatim;
return true;
if lib.name.as_str() == passed_lib.name {
// FIXME: This whole logic is questionable, whether modifiers are
// involved or not, library reordering and kind overriding without
// explicit `:rename` in particular.
if lib.has_modifiers() || passed_lib.has_modifiers() {
match lib.foreign_module {
Some(def_id) => self.tcx.sess.emit_err(errors::NoLinkModOverride {
span: Some(self.tcx.def_span(def_id)),
}),
None => {
self.tcx.sess.emit_err(errors::NoLinkModOverride { span: None })
}
};
}
if passed_lib.kind != NativeLibKind::Unspecified {
lib.kind = passed_lib.kind;
}
if let Some(new_name) = &passed_lib.new_name {
lib.name = Symbol::intern(new_name);
}
lib.verbatim = passed_lib.verbatim;
return true;
}
false
})
@ -478,7 +473,7 @@ impl<'tcx> Collector<'tcx> {
if existing.is_empty() {
// Add if not found
let new_name: Option<&str> = passed_lib.new_name.as_deref();
let name = Some(Symbol::intern(new_name.unwrap_or(&passed_lib.name)));
let name = Symbol::intern(new_name.unwrap_or(&passed_lib.name));
let sess = self.tcx.sess;
let filename =
find_bundled_library(name, passed_lib.verbatim, passed_lib.kind, false, sess);
@ -488,7 +483,6 @@ impl<'tcx> Collector<'tcx> {
kind: passed_lib.kind,
cfg: None,
foreign_module: None,
wasm_import_module: None,
verbatim: passed_lib.verbatim,
dll_imports: Vec::new(),
});

View file

@ -226,15 +226,7 @@ provide! { tcx, def_id, other, cdata,
lookup_default_body_stability => { table }
lookup_deprecation_entry => { table }
params_in_repr => { table }
// FIXME: Could be defaulted, but `LazyValue<UnusedGenericParams>` is not `FixedSizeEncoding`..
unused_generic_params => {
cdata
.root
.tables
.unused_generic_params
.get(cdata, def_id.index)
.map_or_else(|| ty::UnusedGenericParams::new_all_used(), |lazy| lazy.decode((cdata, tcx)))
}
unused_generic_params => { cdata.root.tables.unused_generic_params.get(cdata, def_id.index) }
opt_def_kind => { table_direct }
impl_parent => { table }
impl_polarity => { table_direct }
@ -262,7 +254,7 @@ provide! { tcx, def_id, other, cdata,
.process_decoded(tcx, || panic!("{def_id:?} does not have trait_impl_trait_tys")))
}
associated_items_for_impl_trait_in_trait => { table_defaulted_array }
associated_types_for_impl_traits_in_associated_fn => { table_defaulted_array }
visibility => { cdata.get_visibility(def_id.index) }
adt_def => { cdata.get_adt_def(def_id.index, tcx) }

View file

@ -609,10 +609,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
_ = stat!("mir", || self.encode_mir());
_ = stat!("items", || {
self.encode_def_ids();
self.encode_info_for_items();
});
_ = stat!("def-ids", || self.encode_def_ids());
_ = stat!("items", || self.encode_info_for_items());
let interpret_alloc_index = stat!("interpret-alloc-index", || {
let mut interpret_alloc_index = Vec::new();
@ -1198,8 +1197,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
record!(self.tables.trait_impl_trait_tys[def_id] <- table);
}
if should_encode_fn_impl_trait_in_trait(tcx, def_id) {
let table = tcx.associated_items_for_impl_trait_in_trait(def_id);
record_defaulted_array!(self.tables.associated_items_for_impl_trait_in_trait[def_id] <- table);
let table = tcx.associated_types_for_impl_traits_in_associated_fn(def_id);
record_defaulted_array!(self.tables.associated_types_for_impl_traits_in_associated_fn[def_id] <- table);
}
}
@ -1440,9 +1439,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
let instance =
ty::InstanceDef::Item(ty::WithOptConstParam::unknown(def_id.to_def_id()));
let unused = tcx.unused_generic_params(instance);
if !unused.all_used() {
record!(self.tables.unused_generic_params[def_id.to_def_id()] <- unused);
}
self.tables.unused_generic_params.set(def_id.local_def_index, unused);
}
// Encode all the deduced parameter attributes for everything that has MIR, even for items

View file

@ -354,8 +354,9 @@ define_tables! {
explicit_item_bounds: Table<DefIndex, LazyArray<(ty::Predicate<'static>, Span)>>,
inferred_outlives_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>,
inherent_impls: Table<DefIndex, LazyArray<DefIndex>>,
associated_items_for_impl_trait_in_trait: Table<DefIndex, LazyArray<DefId>>,
associated_types_for_impl_traits_in_associated_fn: Table<DefIndex, LazyArray<DefId>>,
opt_rpitit_info: Table<DefIndex, Option<LazyValue<ty::ImplTraitInTraitData>>>,
unused_generic_params: Table<DefIndex, UnusedGenericParams>,
- optional:
attributes: Table<DefIndex, LazyArray<ast::Attribute>>,
@ -398,7 +399,6 @@ define_tables! {
trait_def: Table<DefIndex, LazyValue<ty::TraitDef>>,
trait_item_def_id: Table<DefIndex, RawDefId>,
expn_that_defined: Table<DefIndex, LazyValue<ExpnId>>,
unused_generic_params: Table<DefIndex, LazyValue<UnusedGenericParams>>,
params_in_repr: Table<DefIndex, LazyValue<BitSet<u32>>>,
repr_options: Table<DefIndex, LazyValue<ReprOptions>>,
// `def_keys` and `def_path_hashes` represent a lazy version of a

View file

@ -3,7 +3,7 @@ use crate::rmeta::*;
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_hir::def::{CtorKind, CtorOf};
use rustc_index::vec::Idx;
use rustc_middle::ty::ParameterizedOverTcx;
use rustc_middle::ty::{ParameterizedOverTcx, UnusedGenericParams};
use rustc_serialize::opaque::FileEncoder;
use rustc_serialize::Encoder as _;
use rustc_span::hygiene::MacroKind;
@ -50,6 +50,16 @@ impl IsDefault for DefPathHash {
}
}
impl IsDefault for UnusedGenericParams {
fn is_default(&self) -> bool {
// UnusedGenericParams encodes the *un*usedness as a bitset.
// This means that 0 corresponds to all bits used, which is indeed the default.
let is_default = self.bits() == 0;
debug_assert_eq!(is_default, self.all_used());
is_default
}
}
/// Helper trait, for encoding to, and decoding from, a fixed number of bytes.
/// Used mainly for Lazy positions and lengths.
/// Unchecked invariant: `Self::default()` should encode as `[0; BYTE_LEN]`,
@ -271,6 +281,21 @@ impl FixedSizeEncoding for bool {
}
}
impl FixedSizeEncoding for UnusedGenericParams {
type ByteArray = [u8; 4];
#[inline]
fn from_bytes(b: &[u8; 4]) -> Self {
let x: u32 = u32::from_bytes(b);
UnusedGenericParams::from_bits(x)
}
#[inline]
fn write_to_bytes(self, b: &mut [u8; 4]) {
self.bits().write_to_bytes(b);
}
}
// NOTE(eddyb) there could be an impl for `usize`, which would enable a more
// generic `LazyValue<T>` impl, but in the general case we might not need / want
// to fit every `usize` in `u32`.

View file

@ -785,7 +785,7 @@ rustc_queries! {
/// if `fn_def_id` is the def id of a function defined inside an impl that implements a trait, then it
/// creates and returns the associated items that correspond to each impl trait in return position
/// of the implemented trait.
query associated_items_for_impl_trait_in_trait(fn_def_id: DefId) -> &'tcx [DefId] {
query associated_types_for_impl_traits_in_associated_fn(fn_def_id: DefId) -> &'tcx [DefId] {
desc { |tcx| "creating associated items for impl trait in trait returned by `{}`", tcx.def_path_str(fn_def_id) }
cache_on_disk_if { fn_def_id.is_local() }
separate_provide_extern
@ -793,7 +793,7 @@ rustc_queries! {
/// Given an impl trait in trait `opaque_ty_def_id`, create and return the corresponding
/// associated item.
query associated_item_for_impl_trait_in_trait(opaque_ty_def_id: LocalDefId) -> LocalDefId {
query associated_type_for_impl_trait_in_trait(opaque_ty_def_id: LocalDefId) -> LocalDefId {
desc { |tcx| "creates the associated item corresponding to the opaque type `{}`", tcx.def_path_str(opaque_ty_def_id.to_def_id()) }
cache_on_disk_if { true }
separate_provide_extern

View file

@ -781,6 +781,12 @@ fn needs_fn_once_adapter_shim(
#[derive(Debug, Copy, Clone, Eq, PartialEq, Decodable, Encodable, HashStable)]
pub struct UnusedGenericParams(FiniteBitSet<u32>);
impl Default for UnusedGenericParams {
fn default() -> Self {
UnusedGenericParams::new_all_used()
}
}
impl UnusedGenericParams {
pub fn new_all_unused(amount: u32) -> Self {
let mut bitset = FiniteBitSet::new_empty();
@ -807,4 +813,12 @@ impl UnusedGenericParams {
pub fn all_used(&self) -> bool {
self.0.is_empty()
}
pub fn bits(&self) -> u32 {
self.0.0
}
pub fn from_bits(bits: u32) -> UnusedGenericParams {
UnusedGenericParams(FiniteBitSet(bits))
}
}

View file

@ -2579,7 +2579,9 @@ impl<'tcx> TyCtxt<'tcx> {
let Some(trait_item_def_id) = item.trait_item_def_id else { return false; };
if self.lower_impl_trait_in_trait_to_assoc_ty() {
return !self.associated_items_for_impl_trait_in_trait(trait_item_def_id).is_empty();
return !self
.associated_types_for_impl_traits_in_associated_fn(trait_item_def_id)
.is_empty();
}
// FIXME(RPITIT): This does a somewhat manual walk through the signature

View file

@ -6,7 +6,6 @@ use rustc_data_structures::sharded::{self, Sharded};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::steal::Steal;
use rustc_data_structures::sync::{AtomicU32, AtomicU64, Lock, Lrc, Ordering};
use rustc_data_structures::OnDrop;
use rustc_index::vec::IndexVec;
use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
use smallvec::{smallvec, SmallVec};
@ -54,6 +53,11 @@ impl From<DepNodeIndex> for QueryInvocationId {
}
}
pub struct MarkFrame<'a> {
index: SerializedDepNodeIndex,
parent: Option<&'a MarkFrame<'a>>,
}
#[derive(PartialEq)]
pub enum DepNodeColor {
Red,
@ -710,32 +714,26 @@ impl<K: DepKind> DepGraphData<K> {
let prev_index = self.previous.node_to_index_opt(dep_node)?;
match self.colors.get(prev_index) {
Some(DepNodeColor::Green(dep_node_index)) => return Some((prev_index, dep_node_index)),
Some(DepNodeColor::Red) => return None,
None => {}
Some(DepNodeColor::Green(dep_node_index)) => Some((prev_index, dep_node_index)),
Some(DepNodeColor::Red) => None,
None => {
// This DepNode and the corresponding query invocation existed
// in the previous compilation session too, so we can try to
// mark it as green by recursively marking all of its
// dependencies green.
self.try_mark_previous_green(qcx, prev_index, &dep_node, None)
.map(|dep_node_index| (prev_index, dep_node_index))
}
}
let backtrace = backtrace_printer(qcx.dep_context().sess(), self, prev_index);
// This DepNode and the corresponding query invocation existed
// in the previous compilation session too, so we can try to
// mark it as green by recursively marking all of its
// dependencies green.
let ret = self
.try_mark_previous_green(qcx, prev_index, &dep_node)
.map(|dep_node_index| (prev_index, dep_node_index));
// We succeeded, no backtrace.
backtrace.disable();
return ret;
}
#[instrument(skip(self, qcx, parent_dep_node_index), level = "debug")]
#[instrument(skip(self, qcx, parent_dep_node_index, frame), level = "debug")]
fn try_mark_parent_green<Qcx: QueryContext<DepKind = K>>(
&self,
qcx: Qcx,
parent_dep_node_index: SerializedDepNodeIndex,
dep_node: &DepNode<K>,
frame: Option<&MarkFrame<'_>>,
) -> Option<()> {
let dep_dep_node_color = self.colors.get(parent_dep_node_index);
let dep_dep_node = &self.previous.index_to_node(parent_dep_node_index);
@ -767,7 +765,8 @@ impl<K: DepKind> DepGraphData<K> {
dep_dep_node, dep_dep_node.hash,
);
let node_index = self.try_mark_previous_green(qcx, parent_dep_node_index, dep_dep_node);
let node_index =
self.try_mark_previous_green(qcx, parent_dep_node_index, dep_dep_node, frame);
if node_index.is_some() {
debug!("managed to MARK dependency {dep_dep_node:?} as green",);
@ -777,7 +776,7 @@ impl<K: DepKind> DepGraphData<K> {
// We failed to mark it green, so we try to force the query.
debug!("trying to force dependency {dep_dep_node:?}");
if !qcx.dep_context().try_force_from_dep_node(*dep_dep_node) {
if !qcx.dep_context().try_force_from_dep_node(*dep_dep_node, frame) {
// The DepNode could not be forced.
debug!("dependency {dep_dep_node:?} could not be forced");
return None;
@ -816,13 +815,16 @@ impl<K: DepKind> DepGraphData<K> {
}
/// Try to mark a dep-node which existed in the previous compilation session as green.
#[instrument(skip(self, qcx, prev_dep_node_index), level = "debug")]
#[instrument(skip(self, qcx, prev_dep_node_index, frame), level = "debug")]
fn try_mark_previous_green<Qcx: QueryContext<DepKind = K>>(
&self,
qcx: Qcx,
prev_dep_node_index: SerializedDepNodeIndex,
dep_node: &DepNode<K>,
frame: Option<&MarkFrame<'_>>,
) -> Option<DepNodeIndex> {
let frame = MarkFrame { index: prev_dep_node_index, parent: frame };
#[cfg(not(parallel_compiler))]
{
debug_assert!(!self.dep_node_exists(dep_node));
@ -837,10 +839,7 @@ impl<K: DepKind> DepGraphData<K> {
let prev_deps = self.previous.edge_targets_from(prev_dep_node_index);
for &dep_dep_node_index in prev_deps {
let backtrace = backtrace_printer(qcx.dep_context().sess(), self, dep_dep_node_index);
let success = self.try_mark_parent_green(qcx, dep_dep_node_index, dep_node);
backtrace.disable();
success?;
self.try_mark_parent_green(qcx, dep_dep_node_index, dep_node, Some(&frame))?;
}
// If we got here without hitting a `return` that means that all
@ -970,6 +969,7 @@ impl<K: DepKind> DepGraph<K> {
}
pub(crate) fn next_virtual_depnode_index(&self) -> DepNodeIndex {
debug_assert!(self.data.is_none());
let index = self.virtual_dep_node_index.fetch_add(1, Relaxed);
DepNodeIndex::from_u32(index)
}
@ -1414,25 +1414,25 @@ impl DepNodeColorMap {
}
}
fn backtrace_printer<'a, K: DepKind>(
sess: &'a rustc_session::Session,
graph: &'a DepGraphData<K>,
node: SerializedDepNodeIndex,
) -> OnDrop<impl Fn() + 'a> {
OnDrop(
#[inline(never)]
#[cold]
move || {
let node = graph.previous.index_to_node(node);
// Do not try to rely on DepNode's Debug implementation, since it may panic.
let diag = rustc_errors::Diagnostic::new(
rustc_errors::Level::FailureNote,
&format!(
"encountered while trying to mark dependency green: {:?}({})",
node.kind, node.hash
),
);
sess.diagnostic().force_print_diagnostic(diag);
},
)
#[inline(never)]
#[cold]
pub(crate) fn print_markframe_trace<K: DepKind>(
graph: &DepGraph<K>,
frame: Option<&MarkFrame<'_>>,
) {
let data = graph.data.as_ref().unwrap();
eprintln!("there was a panic while trying to force a dep node");
eprintln!("try_mark_green dep node stack:");
let mut i = 0;
let mut current = frame;
while let Some(frame) = current {
let node = data.previous.index_to_node(frame.index);
eprintln!("#{i} {:?}", node);
current = frame.parent;
i += 1;
}
eprintln!("end of try_mark_green dep node stack");
}

View file

@ -17,8 +17,10 @@ use rustc_data_structures::profiling::SelfProfilerRef;
use rustc_serialize::{opaque::FileEncoder, Encodable};
use rustc_session::Session;
use std::fmt;
use std::hash::Hash;
use std::{fmt, panic};
use self::graph::{print_markframe_trace, MarkFrame};
pub trait DepContext: Copy {
type DepKind: self::DepKind;
@ -53,11 +55,23 @@ pub trait DepContext: Copy {
}
/// Try to force a dep node to execute and see if it's green.
#[instrument(skip(self), level = "debug")]
fn try_force_from_dep_node(self, dep_node: DepNode<Self::DepKind>) -> bool {
#[inline]
#[instrument(skip(self, frame), level = "debug")]
fn try_force_from_dep_node(
self,
dep_node: DepNode<Self::DepKind>,
frame: Option<&MarkFrame<'_>>,
) -> bool {
let cb = self.dep_kind_info(dep_node.kind);
if let Some(f) = cb.force_from_dep_node {
f(self, dep_node);
if let Err(value) = panic::catch_unwind(panic::AssertUnwindSafe(|| {
f(self, dep_node);
})) {
if !value.is::<rustc_errors::FatalErrorMarker>() {
print_markframe_trace(self.dep_graph(), frame);
}
panic::resume_unwind(value)
}
true
} else {
false

View file

@ -379,7 +379,11 @@ where
match JobOwner::<'_, Q::Key, Qcx::DepKind>::try_start(&qcx, state, state_lock, span, key) {
TryGetJob::NotYetStarted(job) => {
let (result, dep_node_index) = execute_job(query, qcx, key.clone(), dep_node, job.id);
let (result, dep_node_index) = match qcx.dep_context().dep_graph().data() {
None => execute_job_non_incr(query, qcx, key, job.id),
Some(data) => execute_job_incr(query, qcx, data, key, dep_node, job.id),
};
let cache = query.query_cache(qcx);
if query.feedable() {
// We should not compute queries that also got a value via feeding.
@ -413,11 +417,48 @@ where
}
}
// Fast path for when incr. comp. is off.
#[inline(always)]
fn execute_job<Q, Qcx>(
fn execute_job_non_incr<Q, Qcx>(
query: Q,
qcx: Qcx,
key: Q::Key,
job_id: QueryJobId,
) -> (Q::Value, DepNodeIndex)
where
Q: QueryConfig<Qcx>,
Qcx: QueryContext,
{
debug_assert!(!qcx.dep_context().dep_graph().is_fully_enabled());
// Fingerprint the key, just to assert that it doesn't
// have anything we don't consider hashable
if cfg!(debug_assertions) {
let _ = key.to_fingerprint(*qcx.dep_context());
}
let prof_timer = qcx.dep_context().profiler().query_provider();
let result = qcx.start_query(job_id, query.depth_limit(), None, || query.compute(qcx, key));
let dep_node_index = qcx.dep_context().dep_graph().next_virtual_depnode_index();
prof_timer.finish_with_query_invocation_id(dep_node_index.into());
// Similarly, fingerprint the result to assert that
// it doesn't have anything not considered hashable.
if cfg!(debug_assertions) && let Some(hash_result) = query.hash_result() {
qcx.dep_context().with_stable_hashing_context(|mut hcx| {
hash_result(&mut hcx, &result);
});
}
(result, dep_node_index)
}
#[inline(always)]
fn execute_job_incr<Q, Qcx>(
query: Q,
qcx: Qcx,
dep_graph_data: &DepGraphData<Qcx::DepKind>,
key: Q::Key,
mut dep_node_opt: Option<DepNode<Qcx::DepKind>>,
job_id: QueryJobId,
) -> (Q::Value, DepNodeIndex)
@ -425,36 +466,6 @@ where
Q: QueryConfig<Qcx>,
Qcx: QueryContext,
{
let dep_graph = qcx.dep_context().dep_graph();
let dep_graph_data = match dep_graph.data() {
// Fast path for when incr. comp. is off.
None => {
// Fingerprint the key, just to assert that it doesn't
// have anything we don't consider hashable
if cfg!(debug_assertions) {
let _ = key.to_fingerprint(*qcx.dep_context());
}
let prof_timer = qcx.dep_context().profiler().query_provider();
let result =
qcx.start_query(job_id, query.depth_limit(), None, || query.compute(qcx, key));
let dep_node_index = dep_graph.next_virtual_depnode_index();
prof_timer.finish_with_query_invocation_id(dep_node_index.into());
// Similarly, fingerprint the result to assert that
// it doesn't have anything not considered hashable.
if cfg!(debug_assertions) && let Some(hash_result) = query.hash_result()
{
qcx.dep_context().with_stable_hashing_context(|mut hcx| {
hash_result(&mut hcx, &result);
});
}
return (result, dep_node_index);
}
Some(data) => data,
};
if !query.anon() && !query.eval_always() {
// `to_dep_node` is expensive for some `DepKind`s.
let dep_node =

View file

@ -590,7 +590,6 @@ struct LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
parent_scope: ParentScope<'a>,
/// The current set of local scopes for types and values.
/// FIXME #4948: Reuse ribs to avoid allocation.
ribs: PerNS<Vec<Rib<'a>>>,
/// Previous poped `rib`, only used for diagnostic.

View file

@ -24,5 +24,9 @@ termize = "0.1.1"
[target.'cfg(unix)'.dependencies]
libc = "0.2"
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["libloaderapi"] }
[target.'cfg(windows)'.dependencies.windows]
version = "0.46.0"
features = [
"Win32_Foundation",
"Win32_System_LibraryLoader",
]

View file

@ -67,12 +67,11 @@ pub enum LinkagePreference {
#[derive(Debug, Encodable, Decodable, HashStable_Generic)]
pub struct NativeLib {
pub kind: NativeLibKind,
pub name: Option<Symbol>,
pub name: Symbol,
/// If packed_bundled_libs enabled, actual filename of library is stored.
pub filename: Option<Symbol>,
pub cfg: Option<ast::MetaItem>,
pub foreign_module: Option<DefId>,
pub wasm_import_module: Option<Symbol>,
pub verbatim: Option<bool>,
pub dll_imports: Vec<DllImport>,
}
@ -81,6 +80,10 @@ impl NativeLib {
pub fn has_modifiers(&self) -> bool {
self.verbatim.is_some() || self.kind.has_modifiers()
}
pub fn wasm_import_module(&self) -> Option<Symbol> {
if self.kind == NativeLibKind::WasmImportModule { Some(self.name) } else { None }
}
}
/// Different ways that the PE Format can decorate a symbol name.

View file

@ -87,35 +87,38 @@ fn current_dll_path() -> Result<PathBuf, String> {
use std::ffi::OsString;
use std::io;
use std::os::windows::prelude::*;
use std::ptr;
use winapi::um::libloaderapi::{
GetModuleFileNameW, GetModuleHandleExW, GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
use windows::{
core::PCWSTR,
Win32::Foundation::HINSTANCE,
Win32::System::LibraryLoader::{
GetModuleFileNameW, GetModuleHandleExW, GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
},
};
let mut module = HINSTANCE::default();
unsafe {
let mut module = ptr::null_mut();
let r = GetModuleHandleExW(
GetModuleHandleExW(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
current_dll_path as usize as *mut _,
PCWSTR(current_dll_path as *mut u16),
&mut module,
);
if r == 0 {
return Err(format!("GetModuleHandleExW failed: {}", io::Error::last_os_error()));
}
let mut space = Vec::with_capacity(1024);
let r = GetModuleFileNameW(module, space.as_mut_ptr(), space.capacity() as u32);
if r == 0 {
return Err(format!("GetModuleFileNameW failed: {}", io::Error::last_os_error()));
}
let r = r as usize;
if r >= space.capacity() {
return Err(format!("our buffer was too small? {}", io::Error::last_os_error()));
}
space.set_len(r);
let os = OsString::from_wide(&space);
Ok(PathBuf::from(os))
)
}
.ok()
.map_err(|e| e.to_string())?;
let mut filename = vec![0; 1024];
let n = unsafe { GetModuleFileNameW(module, &mut filename) } as usize;
if n == 0 {
return Err(format!("GetModuleFileNameW failed: {}", io::Error::last_os_error()));
}
if n >= filename.capacity() {
return Err(format!("our buffer was too small? {}", io::Error::last_os_error()));
}
filename.truncate(n);
Ok(OsString::from_wide(&filename).into())
}
pub fn sysroot_candidates() -> SmallVec<[PathBuf; 2]> {

View file

@ -37,6 +37,10 @@ pub enum NativeLibKind {
/// Argument which is passed to linker, relative order with libraries and other arguments
/// is preserved
LinkArg,
/// Module imported from WebAssembly
WasmImportModule,
/// The library kind wasn't specified, `Dylib` is currently used as a default.
Unspecified,
}
@ -50,7 +54,10 @@ impl NativeLibKind {
NativeLibKind::Dylib { as_needed } | NativeLibKind::Framework { as_needed } => {
as_needed.is_some()
}
NativeLibKind::RawDylib | NativeLibKind::Unspecified | NativeLibKind::LinkArg => false,
NativeLibKind::RawDylib
| NativeLibKind::Unspecified
| NativeLibKind::LinkArg
| NativeLibKind::WasmImportModule => false,
}
}

View file

@ -880,7 +880,7 @@ impl Span {
pub fn fresh_expansion(self, expn_id: LocalExpnId) -> Span {
HygieneData::with(|data| {
self.with_ctxt(data.apply_mark(
SyntaxContext::root(),
self.ctxt(),
expn_id.to_expn_id(),
Transparency::Transparent,
))

View file

@ -448,23 +448,34 @@ impl SourceMap {
sp: Span,
filename_display_pref: FileNameDisplayPreference,
) -> String {
let (source_file, lo_line, lo_col, hi_line, hi_col) = self.span_to_location_info(sp);
let file_name = match source_file {
Some(sf) => sf.name.display(filename_display_pref).to_string(),
None => return "no-location".to_string(),
};
format!(
"{file_name}:{lo_line}:{lo_col}{}",
if let FileNameDisplayPreference::Short = filename_display_pref {
String::new()
} else {
format!(": {hi_line}:{hi_col}")
}
)
}
pub fn span_to_location_info(
&self,
sp: Span,
) -> (Option<Lrc<SourceFile>>, usize, usize, usize, usize) {
if self.files.borrow().source_files.is_empty() || sp.is_dummy() {
return "no-location".to_string();
return (None, 0, 0, 0, 0);
}
let lo = self.lookup_char_pos(sp.lo());
let hi = self.lookup_char_pos(sp.hi());
format!(
"{}:{}:{}{}",
lo.file.name.display(filename_display_pref),
lo.line,
lo.col.to_usize() + 1,
if let FileNameDisplayPreference::Short = filename_display_pref {
String::new()
} else {
format!(": {}:{}", hi.line, hi.col.to_usize() + 1)
}
)
(Some(lo.file), lo.line, lo.col.to_usize() + 1, hi.line, hi.col.to_usize() + 1)
}
/// Format the span location suitable for embedding in build artifacts

View file

@ -11,8 +11,8 @@ pub fn provide(providers: &mut ty::query::Providers) {
associated_item,
associated_item_def_ids,
associated_items,
associated_items_for_impl_trait_in_trait,
associated_item_for_impl_trait_in_trait,
associated_types_for_impl_traits_in_associated_fn,
associated_type_for_impl_trait_in_trait,
impl_item_implementor_ids,
..*providers
};
@ -24,7 +24,7 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] {
hir::ItemKind::Trait(.., ref trait_item_refs) => {
if tcx.lower_impl_trait_in_trait_to_assoc_ty() {
// We collect RPITITs for each trait method's return type and create a
// corresponding associated item using associated_items_for_impl_trait_in_trait
// corresponding associated item using associated_types_for_impl_traits_in_associated_fn
// query.
tcx.arena.alloc_from_iter(
trait_item_refs
@ -39,7 +39,9 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] {
.flat_map(|trait_item_ref| {
let trait_fn_def_id =
trait_item_ref.id.owner_id.def_id.to_def_id();
tcx.associated_items_for_impl_trait_in_trait(trait_fn_def_id)
tcx.associated_types_for_impl_traits_in_associated_fn(
trait_fn_def_id,
)
})
.map(|def_id| *def_id),
),
@ -56,7 +58,7 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] {
if tcx.lower_impl_trait_in_trait_to_assoc_ty() {
// We collect RPITITs for each trait method's return type, on the impl side too and
// create a corresponding associated item using
// associated_items_for_impl_trait_in_trait query.
// associated_types_for_impl_traits_in_associated_fn query.
tcx.arena.alloc_from_iter(
impl_
.items
@ -72,7 +74,9 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] {
.flat_map(|impl_item_ref| {
let impl_fn_def_id =
impl_item_ref.id.owner_id.def_id.to_def_id();
tcx.associated_items_for_impl_trait_in_trait(impl_fn_def_id)
tcx.associated_types_for_impl_traits_in_associated_fn(
impl_fn_def_id,
)
})
.map(|def_id| *def_id)
})),
@ -176,13 +180,19 @@ fn associated_item_from_impl_item_ref(impl_item_ref: &hir::ImplItemRef) -> ty::A
}
}
/// Given an `fn_def_id` of a trait or of an impl that implements a given trait:
/// if `fn_def_id` is the def id of a function defined inside a trait, then it creates and returns
/// the associated items that correspond to each impl trait in return position for that trait.
/// if `fn_def_id` is the def id of a function defined inside an impl that implements a trait, then it
/// creates and returns the associated items that correspond to each impl trait in return position
/// of the implemented trait.
fn associated_items_for_impl_trait_in_trait(tcx: TyCtxt<'_>, fn_def_id: DefId) -> &'_ [DefId] {
/// Given an `fn_def_id` of a trait or a trait implementation:
///
/// if `fn_def_id` is a function defined inside a trait, then it synthesizes
/// a new def id corresponding to a new associated type for each return-
/// position `impl Trait` in the signature.
///
/// if `fn_def_id` is a function inside of an impl, then for each synthetic
/// associated type generated for the corresponding trait function described
/// above, synthesize a corresponding associated type in the impl.
fn associated_types_for_impl_traits_in_associated_fn(
tcx: TyCtxt<'_>,
fn_def_id: DefId,
) -> &'_ [DefId] {
let parent_def_id = tcx.parent(fn_def_id);
match tcx.def_kind(parent_def_id) {
@ -206,7 +216,7 @@ fn associated_items_for_impl_trait_in_trait(tcx: TyCtxt<'_>, fn_def_id: DefId) -
visitor.visit_fn_ret_ty(output);
tcx.arena.alloc_from_iter(visitor.rpits.iter().map(|opaque_ty_def_id| {
tcx.associated_item_for_impl_trait_in_trait(opaque_ty_def_id).to_def_id()
tcx.associated_type_for_impl_trait_in_trait(opaque_ty_def_id).to_def_id()
}))
} else {
&[]
@ -217,9 +227,9 @@ fn associated_items_for_impl_trait_in_trait(tcx: TyCtxt<'_>, fn_def_id: DefId) -
let Some(trait_fn_def_id) = tcx.associated_item(fn_def_id).trait_item_def_id else { return &[] };
tcx.arena.alloc_from_iter(
tcx.associated_items_for_impl_trait_in_trait(trait_fn_def_id).iter().map(
tcx.associated_types_for_impl_traits_in_associated_fn(trait_fn_def_id).iter().map(
move |trait_assoc_def_id| {
impl_associated_item_for_impl_trait_in_trait(
associated_type_for_impl_trait_in_impl(
tcx,
trait_assoc_def_id.expect_local(),
fn_def_id.expect_local(),
@ -231,16 +241,17 @@ fn associated_items_for_impl_trait_in_trait(tcx: TyCtxt<'_>, fn_def_id: DefId) -
}
def_kind => bug!(
"associated_items_for_impl_trait_in_trait: {:?} should be Trait or Impl but is {:?}",
"associated_types_for_impl_traits_in_associated_fn: {:?} should be Trait or Impl but is {:?}",
parent_def_id,
def_kind
),
}
}
/// Given an `opaque_ty_def_id` corresponding to an impl trait in trait, create and return the
/// corresponding associated item.
fn associated_item_for_impl_trait_in_trait(
/// Given an `opaque_ty_def_id` corresponding to an `impl Trait` in an associated
/// function from a trait, synthesize an associated type for that `impl Trait`
/// that inherits properties that we infer from the method and the opaque type.
fn associated_type_for_impl_trait_in_trait(
tcx: TyCtxt<'_>,
opaque_ty_def_id: LocalDefId,
) -> LocalDefId {
@ -335,10 +346,12 @@ fn associated_item_for_impl_trait_in_trait(
local_def_id
}
/// Given an `trait_assoc_def_id` that corresponds to a previously synthesized impl trait in trait
/// into an associated type and an `impl_def_id` corresponding to an impl block, create and return
/// the corresponding associated item inside the impl block.
fn impl_associated_item_for_impl_trait_in_trait(
/// Given an `trait_assoc_def_id` corresponding to an associated item synthesized
/// from an `impl Trait` in an associated function from a trait, and an
/// `impl_fn_def_id` that represents an implementation of the associated function
/// that the `impl Trait` comes from, synthesize an associated type for that `impl Trait`
/// that inherits properties that we infer from the method and the associated type.
fn associated_type_for_impl_trait_in_impl(
tcx: TyCtxt<'_>,
trait_assoc_def_id: LocalDefId,
impl_fn_def_id: LocalDefId,
@ -383,6 +396,8 @@ fn impl_associated_item_for_impl_trait_in_trait(
impl_assoc_ty.impl_defaultness(tcx.impl_defaultness(impl_fn_def_id));
// Copy generics_of the trait's associated item but the impl as the parent.
// FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty) resolves to the trait instead of the impl
// generics.
impl_assoc_ty.generics_of({
let trait_assoc_generics = tcx.generics_of(trait_assoc_def_id);
let trait_assoc_parent_count = trait_assoc_generics.parent_count;
@ -391,16 +406,10 @@ fn impl_associated_item_for_impl_trait_in_trait(
let parent_generics = tcx.generics_of(impl_def_id);
let parent_count = parent_generics.parent_count + parent_generics.params.len();
let mut impl_fn_params = tcx.generics_of(impl_fn_def_id).params.clone();
for param in &mut params {
param.index = param.index + parent_count as u32 + impl_fn_params.len() as u32
- trait_assoc_parent_count as u32;
param.index = param.index + parent_count as u32 - trait_assoc_parent_count as u32;
}
impl_fn_params.extend(params);
params = impl_fn_params;
let param_def_id_to_index =
params.iter().map(|param| (param.def_id, param.index)).collect();

View file

@ -8,7 +8,7 @@
//!
//! The documentation for this module describes how to use this feature. If you are interested in
//! hacking on the implementation, most of that documentation lives at
//! `rustc_mir_building/src/build/custom/mod.rs`.
//! `rustc_mir_build/src/build/custom/mod.rs`.
//!
//! Typical usage will look like this:
//!

View file

@ -1,4 +1,5 @@
use crate::convert::TryFrom;
use crate::marker::Destruct;
use crate::mem;
use crate::ops::{self, Try};
@ -20,7 +21,8 @@ unsafe_impl_trusted_step![char i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usi
/// The *successor* operation moves towards values that compare greater.
/// The *predecessor* operation moves towards values that compare lesser.
#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
pub trait Step: Clone + PartialOrd + Sized {
#[const_trait]
pub trait Step: ~const Clone + ~const PartialOrd + Sized {
/// Returns the number of *successor* steps required to get from `start` to `end`.
///
/// Returns `None` if the number of steps would overflow `usize`
@ -234,7 +236,8 @@ macro_rules! step_integer_impls {
$(
#[allow(unreachable_patterns)]
#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
impl Step for $u_narrower {
#[rustc_const_unstable(feature = "const_iter", issue = "92476")]
impl const Step for $u_narrower {
step_identical_methods!();
#[inline]
@ -266,7 +269,8 @@ macro_rules! step_integer_impls {
#[allow(unreachable_patterns)]
#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
impl Step for $i_narrower {
#[rustc_const_unstable(feature = "const_iter", issue = "92476")]
impl const Step for $i_narrower {
step_identical_methods!();
#[inline]
@ -330,7 +334,8 @@ macro_rules! step_integer_impls {
$(
#[allow(unreachable_patterns)]
#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
impl Step for $u_wider {
#[rustc_const_unstable(feature = "const_iter", issue = "92476")]
impl const Step for $u_wider {
step_identical_methods!();
#[inline]
@ -355,7 +360,8 @@ macro_rules! step_integer_impls {
#[allow(unreachable_patterns)]
#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
impl Step for $i_wider {
#[rustc_const_unstable(feature = "const_iter", issue = "92476")]
impl const Step for $i_wider {
step_identical_methods!();
#[inline]
@ -405,7 +411,8 @@ step_integer_impls! {
}
#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
impl Step for char {
#[rustc_const_unstable(feature = "const_iter", issue = "92476")]
impl const Step for char {
#[inline]
fn steps_between(&start: &char, &end: &char) -> Option<usize> {
let start = start as u32;
@ -423,6 +430,7 @@ impl Step for char {
}
#[inline]
#[rustc_allow_const_fn_unstable(const_try)]
fn forward_checked(start: char, count: usize) -> Option<char> {
let start = start as u32;
let mut res = Step::forward_checked(start, count)?;
@ -439,6 +447,7 @@ impl Step for char {
}
#[inline]
#[rustc_allow_const_fn_unstable(const_try)]
fn backward_checked(start: char, count: usize) -> Option<char> {
let start = start as u32;
let mut res = Step::backward_checked(start, count)?;
@ -514,6 +523,7 @@ macro_rules! range_incl_exact_iter_impl {
}
/// Specialization implementations for `Range`.
#[const_trait]
trait RangeIteratorImpl {
type Item;
@ -528,7 +538,7 @@ trait RangeIteratorImpl {
fn spec_advance_back_by(&mut self, n: usize) -> Result<(), usize>;
}
impl<A: Step> RangeIteratorImpl for ops::Range<A> {
impl<A: ~const Step + ~const Destruct> const RangeIteratorImpl for ops::Range<A> {
type Item = A;
#[inline]
@ -614,7 +624,7 @@ impl<A: Step> RangeIteratorImpl for ops::Range<A> {
}
}
impl<T: TrustedStep> RangeIteratorImpl for ops::Range<T> {
impl<T: ~const TrustedStep + ~const Destruct> const RangeIteratorImpl for ops::Range<T> {
#[inline]
fn spec_next(&mut self) -> Option<T> {
if self.start < self.end {
@ -702,7 +712,8 @@ impl<T: TrustedStep> RangeIteratorImpl for ops::Range<T> {
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<A: Step> Iterator for ops::Range<A> {
#[rustc_const_unstable(feature = "const_iter", issue = "92476")]
impl<A: ~const Step + ~const Destruct> const Iterator for ops::Range<A> {
type Item = A;
#[inline]
@ -812,7 +823,8 @@ range_incl_exact_iter_impl! {
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<A: Step> DoubleEndedIterator for ops::Range<A> {
#[rustc_const_unstable(feature = "const_iter", issue = "92476")]
impl<A: ~const Step + ~const Destruct> const DoubleEndedIterator for ops::Range<A> {
#[inline]
fn next_back(&mut self) -> Option<A> {
self.spec_next_back()

View file

@ -1,3 +1,4 @@
use crate::marker::Destruct;
use crate::ops::{ControlFlow, Try};
/// An iterator able to yield elements from both ends.
@ -37,6 +38,7 @@ use crate::ops::{ControlFlow, Try};
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "DoubleEndedIterator")]
#[const_trait]
pub trait DoubleEndedIterator: Iterator {
/// Removes and returns an element from the end of the iterator.
///
@ -131,7 +133,10 @@ pub trait DoubleEndedIterator: Iterator {
/// [`Err(k)`]: Err
#[inline]
#[unstable(feature = "iter_advance_by", reason = "recently added", issue = "77404")]
fn advance_back_by(&mut self, n: usize) -> Result<(), usize> {
fn advance_back_by(&mut self, n: usize) -> Result<(), usize>
where
Self::Item: ~const Destruct,
{
for i in 0..n {
self.next_back().ok_or(i)?;
}
@ -181,6 +186,7 @@ pub trait DoubleEndedIterator: Iterator {
/// ```
#[inline]
#[stable(feature = "iter_nth_back", since = "1.37.0")]
#[rustc_do_not_const_check]
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
self.advance_back_by(n).ok()?;
self.next_back()
@ -218,6 +224,7 @@ pub trait DoubleEndedIterator: Iterator {
/// ```
#[inline]
#[stable(feature = "iterator_try_fold", since = "1.27.0")]
#[rustc_do_not_const_check]
fn try_rfold<B, F, R>(&mut self, init: B, mut f: F) -> R
where
Self: Sized,
@ -289,6 +296,7 @@ pub trait DoubleEndedIterator: Iterator {
#[doc(alias = "foldr")]
#[inline]
#[stable(feature = "iter_rfold", since = "1.27.0")]
#[rustc_do_not_const_check]
fn rfold<B, F>(mut self, init: B, mut f: F) -> B
where
Self: Sized,
@ -344,6 +352,7 @@ pub trait DoubleEndedIterator: Iterator {
/// ```
#[inline]
#[stable(feature = "iter_rfind", since = "1.27.0")]
#[rustc_do_not_const_check]
fn rfind<P>(&mut self, predicate: P) -> Option<Self::Item>
where
Self: Sized,

View file

@ -1,5 +1,6 @@
use crate::array;
use crate::cmp::{self, Ordering};
use crate::marker::Destruct;
use crate::ops::{ChangeOutputType, ControlFlow, FromResidual, Residual, Try};
use super::super::try_process;
@ -336,8 +337,10 @@ pub trait Iterator {
/// ```
#[inline]
#[unstable(feature = "iter_advance_by", reason = "recently added", issue = "77404")]
#[rustc_do_not_const_check]
fn advance_by(&mut self, n: usize) -> Result<(), usize> {
fn advance_by(&mut self, n: usize) -> Result<(), usize>
where
Self::Item: ~const Destruct,
{
for i in 0..n {
self.next().ok_or(i)?;
}
@ -385,8 +388,10 @@ pub trait Iterator {
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_do_not_const_check]
fn nth(&mut self, n: usize) -> Option<Self::Item> {
fn nth(&mut self, n: usize) -> Option<Self::Item>
where
Self::Item: ~const Destruct,
{
self.advance_by(n).ok()?;
self.next()
}
@ -1998,7 +2003,7 @@ pub trait Iterator {
/// a.iter().map(|&x| x * 2).collect_into(&mut vec);
/// a.iter().map(|&x| x * 10).collect_into(&mut vec);
///
/// assert_eq!(vec![0, 1, 2, 4, 6, 10, 20, 30], vec);
/// assert_eq!(vec, vec![0, 1, 2, 4, 6, 10, 20, 30]);
/// ```
///
/// `Vec` can have a manual set capacity to avoid reallocating it:
@ -2013,7 +2018,7 @@ pub trait Iterator {
/// a.iter().map(|&x| x * 10).collect_into(&mut vec);
///
/// assert_eq!(6, vec.capacity());
/// println!("{:?}", vec);
/// assert_eq!(vec, vec![2, 4, 6, 10, 20, 30]);
/// ```
///
/// The returned mutable reference can be used to continue the call chain:
@ -2027,12 +2032,12 @@ pub trait Iterator {
/// let count = a.iter().collect_into(&mut vec).iter().count();
///
/// assert_eq!(count, vec.len());
/// println!("Vec len is {}", count);
/// assert_eq!(vec, vec![1, 2, 3]);
///
/// let count = a.iter().collect_into(&mut vec).iter().count();
///
/// assert_eq!(count, vec.len());
/// println!("Vec len now is {}", count);
/// assert_eq!(vec, vec![1, 2, 3, 1, 2, 3]);
/// ```
#[inline]
#[unstable(feature = "iter_collect_into", reason = "new API", issue = "94780")]

View file

@ -86,4 +86,5 @@ pub unsafe trait InPlaceIterable: Iterator {}
/// for details. Consumers are free to rely on the invariants in unsafe code.
#[unstable(feature = "trusted_step", issue = "85731")]
#[rustc_specialization_trait]
pub unsafe trait TrustedStep: Step {}
#[const_trait]
pub unsafe trait TrustedStep: ~const Step {}

View file

@ -123,9 +123,11 @@
#![feature(const_index_range_slice_index)]
#![feature(const_inherent_unchecked_arith)]
#![feature(const_int_unchecked_arith)]
#![feature(const_intoiterator_identity)]
#![feature(const_intrinsic_forget)]
#![feature(const_ipv4)]
#![feature(const_ipv6)]
#![feature(const_iter)]
#![feature(const_likely)]
#![feature(const_maybe_uninit_uninit_array)]
#![feature(const_maybe_uninit_as_mut_ptr)]

View file

@ -340,9 +340,9 @@ pub macro debug_assert_matches($($arg:tt)*) {
#[stable(feature = "matches_macro", since = "1.42.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "matches_macro")]
macro_rules! matches {
($expression:expr, $(|)? $( $pattern:pat_param )|+ $( if $guard: expr )? $(,)?) => {
($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => {
match $expression {
$( $pattern )|+ $( if $guard )? => true,
$pattern $(if $guard)? => true,
_ => false
}
};

View file

@ -192,6 +192,7 @@ pub struct BiasedFp {
}
impl BiasedFp {
#[inline]
pub const fn zero_pow2(e: i32) -> Self {
Self { f: 0, e }
}

View file

@ -118,11 +118,13 @@ impl RawFloat for f32 {
const SMALLEST_POWER_OF_TEN: i32 = -65;
const LARGEST_POWER_OF_TEN: i32 = 38;
#[inline]
fn from_u64(v: u64) -> Self {
debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH);
v as _
}
#[inline]
fn from_u64_bits(v: u64) -> Self {
f32::from_bits((v & 0xFFFFFFFF) as u32)
}
@ -169,11 +171,13 @@ impl RawFloat for f64 {
const SMALLEST_POWER_OF_TEN: i32 = -342;
const LARGEST_POWER_OF_TEN: i32 = 308;
#[inline]
fn from_u64(v: u64) -> Self {
debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH);
v as _
}
#[inline]
fn from_u64_bits(v: u64) -> Self {
f64::from_bits(v)
}

View file

@ -118,10 +118,12 @@ pub fn compute_float<F: RawFloat>(q: i64, mut w: u64) -> BiasedFp {
/// This uses a pre-computed integer approximation for
/// log2(10), where 217706 / 2^16 is accurate for the
/// entire range of non-finite decimal exponents.
#[inline]
fn power(q: i32) -> i32 {
(q.wrapping_mul(152_170 + 65536) >> 16) + 63
}
#[inline]
fn full_multiplication(a: u64, b: u64) -> (u64, u64) {
let r = (a as u128) * (b as u128);
(r as u64, (r >> 64) as u64)

View file

@ -147,7 +147,13 @@ macro_rules! from_str_float_impl {
/// representable floating-point number to the number represented
/// by `src` (following the same rules for rounding as for the
/// results of primitive operations).
#[inline]
// We add the `#[inline(never)]` attribute, since its content will
// be filled with that of `dec2flt`, which has #[inline(always)].
// Since `dec2flt` is generic, a normal inline attribute on this function
// with `dec2flt` having no attributes results in heavily repeated
// generation of `dec2flt`, despite the fact only a maximum of 2
// possible instances can ever exist. Adding #[inline(never)] avoids this.
#[inline(never)]
fn from_str(src: &str) -> Result<Self, ParseFloatError> {
dec2flt(src)
}
@ -202,12 +208,14 @@ impl fmt::Display for ParseFloatError {
}
}
#[inline]
pub(super) fn pfe_empty() -> ParseFloatError {
ParseFloatError { kind: FloatErrorKind::Empty }
}
// Used in unit tests, keep public.
// This is much better than making FloatErrorKind and ParseFloatError::kind public.
#[inline]
pub fn pfe_invalid() -> ParseFloatError {
ParseFloatError { kind: FloatErrorKind::Invalid }
}
@ -220,6 +228,7 @@ fn biased_fp_to_float<T: RawFloat>(x: BiasedFp) -> T {
}
/// Converts a decimal string into a floating point number.
#[inline(always)] // Will be inlined into a function with `#[inline(never)]`, see above
pub fn dec2flt<F: RawFloat>(s: &str) -> Result<F, ParseFloatError> {
let mut s = s.as_bytes();
let c = if let Some(&c) = s.first() {

View file

@ -33,6 +33,7 @@ pub struct Number {
impl Number {
/// Detect if the float can be accurately reconstructed from native floats.
#[inline]
fn is_fast_path<F: RawFloat>(&self) -> bool {
F::MIN_EXPONENT_FAST_PATH <= self.exponent
&& self.exponent <= F::MAX_EXPONENT_DISGUISED_FAST_PATH

View file

@ -1003,22 +1003,25 @@ impl<P, U> CoerceUnsized<Pin<U>> for Pin<P> where P: CoerceUnsized<U> {}
#[stable(feature = "pin", since = "1.33.0")]
impl<P, U> DispatchFromDyn<Pin<U>> for Pin<P> where P: DispatchFromDyn<U> {}
/// Constructs a <code>[Pin]<[&mut] T></code>, by pinning[^1] a `value: T` _locally_[^2].
/// Constructs a <code>[Pin]<[&mut] T></code>, by pinning a `value: T` locally.
///
/// Unlike [`Box::pin`], this does not involve a heap allocation.
/// Unlike [`Box::pin`], this does not create a new heap allocation. As explained
/// below, the element might still end up on the heap however.
///
/// [^1]: If the (type `T` of the) given value does not implement [`Unpin`], then this
/// effectively pins the `value` in memory, where it will be unable to be moved.
/// Otherwise, <code>[Pin]<[&mut] T></code> behaves like <code>[&mut] T</code>, and operations such
/// as [`mem::replace()`][crate::mem::replace] will allow extracting that value, and therefore,
/// moving it.
/// See [the `Unpin` section of the `pin` module][self#unpin] for more info.
/// The local pinning performed by this macro is usually dubbed "stack"-pinning.
/// Outside of `async` contexts locals do indeed get stored on the stack. In
/// `async` functions or blocks however, any locals crossing an `.await` point
/// are part of the state captured by the `Future`, and will use the storage of
/// those. That storage can either be on the heap or on the stack. Therefore,
/// local pinning is a more accurate term.
///
/// [^2]: This is usually dubbed "stack"-pinning. And whilst local values are almost always located
/// in the stack (_e.g._, when within the body of a non-`async` function), the truth is that inside
/// the body of an `async fn` or block —more generally, the body of a generator— any locals crossing
/// an `.await` point —a `yield` point— end up being part of the state captured by the `Future` —by
/// the `Generator`—, and thus will be stored wherever that one is.
/// If the type of the given value does not implement [`Unpin`], then this macro
/// pins the value in memory in a way that prevents moves. On the other hand,
/// if the type does implement [`Unpin`], <code>[Pin]<[&mut] T></code> behaves
/// like <code>[&mut] T</code>, and operations such as
/// [`mem::replace()`][crate::mem::replace] or [`mem::take()`](crate::mem::take)
/// will allow moves of the value.
/// See [the `Unpin` section of the `pin` module][self#unpin] for details.
///
/// ## Examples
///
@ -1158,9 +1161,9 @@ impl<P, U> DispatchFromDyn<Pin<U>> for Pin<P> where P: DispatchFromDyn<U> {}
///
/// If you really need to return a pinned value, consider using [`Box::pin`] instead.
///
/// On the other hand, pinning to the stack[<sup>2</sup>](#fn2) using [`pin!`] is likely to be
/// cheaper than pinning into a fresh heap allocation using [`Box::pin`]. Moreover, by virtue of not
/// even needing an allocator, [`pin!`] is the main non-`unsafe` `#![no_std]`-compatible [`Pin`]
/// On the other hand, local pinning using [`pin!`] is likely to be cheaper than
/// pinning into a fresh heap allocation using [`Box::pin`]. Moreover, by virtue of not
/// requiring an allocator, [`pin!`] is the main non-`unsafe` `#![no_std]`-compatible [`Pin`]
/// constructor.
///
/// [`Box::pin`]: ../../std/boxed/struct.Box.html#method.pin

View file

@ -132,9 +132,7 @@ iterator! {struct Iter -> *const T, &'a T, const, {/* no mut */}, {
Self: Sized,
F: FnMut(&Self::Item, &Self::Item) -> Option<Ordering>,
{
self.as_slice().windows(2).all(|w| {
compare(&&w[0], &&w[1]).map(|o| o != Ordering::Greater).unwrap_or(false)
})
self.as_slice().is_sorted_by(|a, b| compare(&a, &b))
}
}}

View file

@ -3822,7 +3822,7 @@ impl<T> [T] {
where
F: FnMut(&'a T, &'a T) -> Option<Ordering>,
{
self.iter().is_sorted_by(|a, b| compare(*a, *b))
self.array_windows().all(|[a, b]| compare(a, b).map_or(false, Ordering::is_le))
}
/// Checks if the elements of this slice are sorted using the given key extraction function.

View file

@ -0,0 +1,36 @@
#[test]
fn const_manual_iter() {
struct S(bool);
impl const Iterator for S {
type Item = ();
fn next(&mut self) -> Option<Self::Item> {
if self.0 == false {
self.0 = true;
Some(())
} else {
None
}
}
}
const {
let mut val = S(false);
assert!(val.next().is_some());
assert!(val.next().is_none());
assert!(val.next().is_none());
}
}
#[test]
fn const_range() {
const {
let mut arr = [0; 3];
for i in 0..arr.len() {
arr[i] = i;
}
assert!(arr[0] == 0);
assert!(arr[1] == 1);
assert!(arr[2] == 2);
}
}

View file

@ -20,6 +20,8 @@ mod range;
mod sources;
mod traits;
mod consts;
use core::cell::Cell;
use core::convert::TryFrom;
use core::iter::*;

View file

@ -12,8 +12,11 @@
#![feature(const_caller_location)]
#![feature(const_cell_into_inner)]
#![feature(const_convert)]
#![feature(const_for)]
#![feature(const_hash)]
#![feature(const_heap)]
#![feature(const_intoiterator_identity)]
#![feature(const_iter)]
#![feature(const_maybe_uninit_as_mut_ptr)]
#![feature(const_maybe_uninit_assume_init_read)]
#![feature(const_nonnull_new)]

View file

@ -823,8 +823,22 @@ pub trait Read {
/// Read the exact number of bytes required to fill `cursor`.
///
/// This is equivalent to the [`read_exact`](Read::read_exact) method, except that it is passed a [`BorrowedCursor`] rather than `[u8]` to
/// allow use with uninitialized buffers.
/// This is similar to the [`read_exact`](Read::read_exact) method, except
/// that it is passed a [`BorrowedCursor`] rather than `[u8]` to allow use
/// with uninitialized buffers.
///
/// # Errors
///
/// If this function encounters an error of the kind [`ErrorKind::Interrupted`]
/// then the error is ignored and the operation will continue.
///
/// If this function encounters an "end of file" before completely filling
/// the buffer, it returns an error of the kind [`ErrorKind::UnexpectedEof`].
///
/// If any other read error is encountered then this function immediately
/// returns.
///
/// If this function returns an error, all bytes read will be appended to `cursor`.
#[unstable(feature = "read_buf", issue = "78485")]
fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> Result<()> {
while cursor.capacity() > 0 {

View file

@ -308,8 +308,7 @@ pub fn get_backtrace_style() -> Option<BacktraceStyle> {
BacktraceStyle::Short
}
})
.unwrap_or(if cfg!(target_os = "fuchsia") {
// Fuchsia components default to full backtrace.
.unwrap_or(if crate::sys::FULL_BACKTRACE_DEFAULT {
BacktraceStyle::Full
} else {
BacktraceStyle::Off

View file

@ -76,3 +76,12 @@ cfg_if::cfg_if! {
pub mod c;
}
}
cfg_if::cfg_if! {
// Fuchsia components default to full backtrace.
if #[cfg(target_os = "fuchsia")] {
pub const FULL_BACKTRACE_DEFAULT: bool = true;
} else {
pub const FULL_BACKTRACE_DEFAULT: bool = false;
}
}

View file

@ -296,7 +296,6 @@ pub const STATUS_INVALID_PARAMETER: NTSTATUS = 0xc000000d_u32 as _;
pub const STATUS_PENDING: NTSTATUS = 0x103 as _;
pub const STATUS_END_OF_FILE: NTSTATUS = 0xC0000011_u32 as _;
pub const STATUS_NOT_IMPLEMENTED: NTSTATUS = 0xC0000002_u32 as _;
// Equivalent to the `NT_SUCCESS` C preprocessor macro.
// See: https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/using-ntstatus-values
@ -1282,6 +1281,46 @@ extern "system" {
) -> NTSTATUS;
}
#[link(name = "ntdll")]
extern "system" {
pub fn NtCreateFile(
FileHandle: *mut HANDLE,
DesiredAccess: ACCESS_MASK,
ObjectAttributes: *const OBJECT_ATTRIBUTES,
IoStatusBlock: *mut IO_STATUS_BLOCK,
AllocationSize: *mut i64,
FileAttributes: ULONG,
ShareAccess: ULONG,
CreateDisposition: ULONG,
CreateOptions: ULONG,
EaBuffer: *mut c_void,
EaLength: ULONG,
) -> NTSTATUS;
pub fn NtReadFile(
FileHandle: BorrowedHandle<'_>,
Event: HANDLE,
ApcRoutine: Option<IO_APC_ROUTINE>,
ApcContext: *mut c_void,
IoStatusBlock: &mut IO_STATUS_BLOCK,
Buffer: *mut crate::mem::MaybeUninit<u8>,
Length: ULONG,
ByteOffset: Option<&LARGE_INTEGER>,
Key: Option<&ULONG>,
) -> NTSTATUS;
pub fn NtWriteFile(
FileHandle: BorrowedHandle<'_>,
Event: HANDLE,
ApcRoutine: Option<IO_APC_ROUTINE>,
ApcContext: *mut c_void,
IoStatusBlock: &mut IO_STATUS_BLOCK,
Buffer: *const u8,
Length: ULONG,
ByteOffset: Option<&LARGE_INTEGER>,
Key: Option<&ULONG>,
) -> NTSTATUS;
pub fn RtlNtStatusToDosError(Status: NTSTATUS) -> ULONG;
}
// Functions that aren't available on every version of Windows that we support,
// but we still use them and just provide some form of a fallback implementation.
compat_fn_with_fallback! {
@ -1322,52 +1361,6 @@ compat_fn_optional! {
compat_fn_with_fallback! {
pub static NTDLL: &CStr = ansi_str!("ntdll");
pub fn NtCreateFile(
FileHandle: *mut HANDLE,
DesiredAccess: ACCESS_MASK,
ObjectAttributes: *const OBJECT_ATTRIBUTES,
IoStatusBlock: *mut IO_STATUS_BLOCK,
AllocationSize: *mut i64,
FileAttributes: ULONG,
ShareAccess: ULONG,
CreateDisposition: ULONG,
CreateOptions: ULONG,
EaBuffer: *mut c_void,
EaLength: ULONG
) -> NTSTATUS {
STATUS_NOT_IMPLEMENTED
}
pub fn NtReadFile(
FileHandle: BorrowedHandle<'_>,
Event: HANDLE,
ApcRoutine: Option<IO_APC_ROUTINE>,
ApcContext: *mut c_void,
IoStatusBlock: &mut IO_STATUS_BLOCK,
Buffer: *mut crate::mem::MaybeUninit<u8>,
Length: ULONG,
ByteOffset: Option<&LARGE_INTEGER>,
Key: Option<&ULONG>
) -> NTSTATUS {
STATUS_NOT_IMPLEMENTED
}
pub fn NtWriteFile(
FileHandle: BorrowedHandle<'_>,
Event: HANDLE,
ApcRoutine: Option<IO_APC_ROUTINE>,
ApcContext: *mut c_void,
IoStatusBlock: &mut IO_STATUS_BLOCK,
Buffer: *const u8,
Length: ULONG,
ByteOffset: Option<&LARGE_INTEGER>,
Key: Option<&ULONG>
) -> NTSTATUS {
STATUS_NOT_IMPLEMENTED
}
pub fn RtlNtStatusToDosError(
Status: NTSTATUS
) -> ULONG {
Status as ULONG
}
pub fn NtCreateKeyedEvent(
KeyedEventHandle: LPHANDLE,
DesiredAccess: ACCESS_MASK,

View file

@ -41,6 +41,46 @@ impl<T: Write> Write for OutputLocation<T> {
}
}
pub struct ConsoleTestDiscoveryState {
pub log_out: Option<File>,
pub tests: usize,
pub benchmarks: usize,
pub ignored: usize,
pub options: Options,
}
impl ConsoleTestDiscoveryState {
pub fn new(opts: &TestOpts) -> io::Result<ConsoleTestDiscoveryState> {
let log_out = match opts.logfile {
Some(ref path) => Some(File::create(path)?),
None => None,
};
Ok(ConsoleTestDiscoveryState {
log_out,
tests: 0,
benchmarks: 0,
ignored: 0,
options: opts.options,
})
}
pub fn write_log<F, S>(&mut self, msg: F) -> io::Result<()>
where
S: AsRef<str>,
F: FnOnce() -> S,
{
match self.log_out {
None => Ok(()),
Some(ref mut o) => {
let msg = msg();
let msg = msg.as_ref();
o.write_all(msg.as_bytes())
}
}
}
}
pub struct ConsoleTestState {
pub log_out: Option<File>,
pub total: usize,
@ -138,53 +178,44 @@ impl ConsoleTestState {
// List the tests to console, and optionally to logfile. Filters are honored.
pub fn list_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Result<()> {
let mut output = match term::stdout() {
let output = match term::stdout() {
None => OutputLocation::Raw(io::stdout().lock()),
Some(t) => OutputLocation::Pretty(t),
};
let quiet = opts.format == OutputFormat::Terse;
let mut st = ConsoleTestState::new(opts)?;
let mut ntest = 0;
let mut nbench = 0;
let mut out: Box<dyn OutputFormatter> = match opts.format {
OutputFormat::Pretty | OutputFormat::Junit => {
Box::new(PrettyFormatter::new(output, false, 0, false, None))
}
OutputFormat::Terse => Box::new(TerseFormatter::new(output, false, 0, false)),
OutputFormat::Json => Box::new(JsonFormatter::new(output)),
};
let mut st = ConsoleTestDiscoveryState::new(opts)?;
out.write_discovery_start()?;
for test in filter_tests(opts, tests).into_iter() {
use crate::TestFn::*;
let TestDescAndFn { desc: TestDesc { name, .. }, testfn } = test;
let TestDescAndFn { desc, testfn } = test;
let fntype = match testfn {
StaticTestFn(..) | DynTestFn(..) => {
ntest += 1;
st.tests += 1;
"test"
}
StaticBenchFn(..) | DynBenchFn(..) => {
nbench += 1;
st.benchmarks += 1;
"benchmark"
}
};
writeln!(output, "{name}: {fntype}")?;
st.write_log(|| format!("{fntype} {name}\n"))?;
st.ignored += if desc.ignore { 1 } else { 0 };
out.write_test_discovered(&desc, fntype)?;
st.write_log(|| format!("{fntype} {}\n", desc.name))?;
}
fn plural(count: u32, s: &str) -> String {
match count {
1 => format!("1 {s}"),
n => format!("{n} {s}s"),
}
}
if !quiet {
if ntest != 0 || nbench != 0 {
writeln!(output)?;
}
writeln!(output, "{}, {}", plural(ntest, "test"), plural(nbench, "benchmark"))?;
}
Ok(())
out.write_discovery_finish(&st)
}
// Updates `ConsoleTestState` depending on result of the test execution.

View file

@ -2,7 +2,7 @@ use std::{borrow::Cow, io, io::prelude::Write};
use super::OutputFormatter;
use crate::{
console::{ConsoleTestState, OutputLocation},
console::{ConsoleTestDiscoveryState, ConsoleTestState, OutputLocation},
test_result::TestResult,
time,
types::TestDesc,
@ -60,6 +60,56 @@ impl<T: Write> JsonFormatter<T> {
}
impl<T: Write> OutputFormatter for JsonFormatter<T> {
fn write_discovery_start(&mut self) -> io::Result<()> {
self.writeln_message(&format!(r#"{{ "type": "suite", "event": "discovery" }}"#))
}
fn write_test_discovered(&mut self, desc: &TestDesc, test_type: &str) -> io::Result<()> {
let TestDesc {
name,
ignore,
ignore_message,
#[cfg(not(bootstrap))]
source_file,
#[cfg(not(bootstrap))]
start_line,
#[cfg(not(bootstrap))]
start_col,
#[cfg(not(bootstrap))]
end_line,
#[cfg(not(bootstrap))]
end_col,
..
} = desc;
#[cfg(bootstrap)]
let source_file = "";
#[cfg(bootstrap)]
let start_line = 0;
#[cfg(bootstrap)]
let start_col = 0;
#[cfg(bootstrap)]
let end_line = 0;
#[cfg(bootstrap)]
let end_col = 0;
self.writeln_message(&format!(
r#"{{ "type": "{test_type}", "event": "discovered", "name": "{}", "ignore": {ignore}, "ignore_message": "{}", "source_path": "{}", "start_line": {start_line}, "start_col": {start_col}, "end_line": {end_line}, "end_col": {end_col} }}"#,
EscapedString(name.as_slice()),
ignore_message.unwrap_or(""),
EscapedString(source_file),
))
}
fn write_discovery_finish(&mut self, state: &ConsoleTestDiscoveryState) -> io::Result<()> {
let ConsoleTestDiscoveryState { tests, benchmarks, ignored, .. } = state;
let total = tests + benchmarks;
self.writeln_message(&format!(
r#"{{ "type": "suite", "event": "completed", "tests": {tests}, "benchmarks": {benchmarks}, "total": {total}, "ignored": {ignored} }}"#
))
}
fn write_run_start(&mut self, test_count: usize, shuffle_seed: Option<u64>) -> io::Result<()> {
let shuffle_seed_json = if let Some(shuffle_seed) = shuffle_seed {
format!(r#", "shuffle_seed": {shuffle_seed}"#)

View file

@ -3,7 +3,7 @@ use std::time::Duration;
use super::OutputFormatter;
use crate::{
console::{ConsoleTestState, OutputLocation},
console::{ConsoleTestDiscoveryState, ConsoleTestState, OutputLocation},
test_result::TestResult,
time,
types::{TestDesc, TestType},
@ -27,6 +27,18 @@ impl<T: Write> JunitFormatter<T> {
}
impl<T: Write> OutputFormatter for JunitFormatter<T> {
fn write_discovery_start(&mut self) -> io::Result<()> {
Err(io::Error::new(io::ErrorKind::NotFound, "Not yet implemented!"))
}
fn write_test_discovered(&mut self, _desc: &TestDesc, _test_type: &str) -> io::Result<()> {
Err(io::Error::new(io::ErrorKind::NotFound, "Not yet implemented!"))
}
fn write_discovery_finish(&mut self, _state: &ConsoleTestDiscoveryState) -> io::Result<()> {
Err(io::Error::new(io::ErrorKind::NotFound, "Not yet implemented!"))
}
fn write_run_start(
&mut self,
_test_count: usize,

View file

@ -1,7 +1,7 @@
use std::{io, io::prelude::Write};
use crate::{
console::ConsoleTestState,
console::{ConsoleTestDiscoveryState, ConsoleTestState},
test_result::TestResult,
time,
types::{TestDesc, TestName},
@ -18,6 +18,10 @@ pub(crate) use self::pretty::PrettyFormatter;
pub(crate) use self::terse::TerseFormatter;
pub(crate) trait OutputFormatter {
fn write_discovery_start(&mut self) -> io::Result<()>;
fn write_test_discovered(&mut self, desc: &TestDesc, test_type: &str) -> io::Result<()>;
fn write_discovery_finish(&mut self, state: &ConsoleTestDiscoveryState) -> io::Result<()>;
fn write_run_start(&mut self, test_count: usize, shuffle_seed: Option<u64>) -> io::Result<()>;
fn write_test_start(&mut self, desc: &TestDesc) -> io::Result<()>;
fn write_timeout(&mut self, desc: &TestDesc) -> io::Result<()>;

View file

@ -3,7 +3,7 @@ use std::{io, io::prelude::Write};
use super::OutputFormatter;
use crate::{
bench::fmt_bench_samples,
console::{ConsoleTestState, OutputLocation},
console::{ConsoleTestDiscoveryState, ConsoleTestState, OutputLocation},
term,
test_result::TestResult,
time,
@ -181,6 +181,33 @@ impl<T: Write> PrettyFormatter<T> {
}
impl<T: Write> OutputFormatter for PrettyFormatter<T> {
fn write_discovery_start(&mut self) -> io::Result<()> {
Ok(())
}
fn write_test_discovered(&mut self, desc: &TestDesc, test_type: &str) -> io::Result<()> {
self.write_plain(format!("{}: {test_type}\n", desc.name))
}
fn write_discovery_finish(&mut self, state: &ConsoleTestDiscoveryState) -> io::Result<()> {
fn plural(count: usize, s: &str) -> String {
match count {
1 => format!("1 {s}"),
n => format!("{n} {s}s"),
}
}
if state.tests != 0 || state.benchmarks != 0 {
self.write_plain("\n")?;
}
self.write_plain(format!(
"{}, {}\n",
plural(state.tests, "test"),
plural(state.benchmarks, "benchmark")
))
}
fn write_run_start(&mut self, test_count: usize, shuffle_seed: Option<u64>) -> io::Result<()> {
let noun = if test_count != 1 { "tests" } else { "test" };
let shuffle_seed_msg = if let Some(shuffle_seed) = shuffle_seed {

View file

@ -3,7 +3,7 @@ use std::{io, io::prelude::Write};
use super::OutputFormatter;
use crate::{
bench::fmt_bench_samples,
console::{ConsoleTestState, OutputLocation},
console::{ConsoleTestDiscoveryState, ConsoleTestState, OutputLocation},
term,
test_result::TestResult,
time,
@ -167,6 +167,18 @@ impl<T: Write> TerseFormatter<T> {
}
impl<T: Write> OutputFormatter for TerseFormatter<T> {
fn write_discovery_start(&mut self) -> io::Result<()> {
Ok(())
}
fn write_test_discovered(&mut self, desc: &TestDesc, test_type: &str) -> io::Result<()> {
self.write_plain(format!("{}: {test_type}\n", desc.name))
}
fn write_discovery_finish(&mut self, _state: &ConsoleTestDiscoveryState) -> io::Result<()> {
Ok(())
}
fn write_run_start(&mut self, test_count: usize, shuffle_seed: Option<u64>) -> io::Result<()> {
self.total_test_count = test_count;
let noun = if test_count != 1 { "tests" } else { "test" };

View file

@ -63,6 +63,16 @@ fn one_ignored_one_unignored_test() -> Vec<TestDescAndFn> {
name: StaticTestName("1"),
ignore: true,
ignore_message: None,
#[cfg(not(bootstrap))]
source_file: "",
#[cfg(not(bootstrap))]
start_line: 0,
#[cfg(not(bootstrap))]
start_col: 0,
#[cfg(not(bootstrap))]
end_line: 0,
#[cfg(not(bootstrap))]
end_col: 0,
should_panic: ShouldPanic::No,
compile_fail: false,
no_run: false,
@ -75,6 +85,16 @@ fn one_ignored_one_unignored_test() -> Vec<TestDescAndFn> {
name: StaticTestName("2"),
ignore: false,
ignore_message: None,
#[cfg(not(bootstrap))]
source_file: "",
#[cfg(not(bootstrap))]
start_line: 0,
#[cfg(not(bootstrap))]
start_col: 0,
#[cfg(not(bootstrap))]
end_line: 0,
#[cfg(not(bootstrap))]
end_col: 0,
should_panic: ShouldPanic::No,
compile_fail: false,
no_run: false,
@ -95,6 +115,16 @@ pub fn do_not_run_ignored_tests() {
name: StaticTestName("whatever"),
ignore: true,
ignore_message: None,
#[cfg(not(bootstrap))]
source_file: "",
#[cfg(not(bootstrap))]
start_line: 0,
#[cfg(not(bootstrap))]
start_col: 0,
#[cfg(not(bootstrap))]
end_line: 0,
#[cfg(not(bootstrap))]
end_col: 0,
should_panic: ShouldPanic::No,
compile_fail: false,
no_run: false,
@ -118,6 +148,16 @@ pub fn ignored_tests_result_in_ignored() {
name: StaticTestName("whatever"),
ignore: true,
ignore_message: None,
#[cfg(not(bootstrap))]
source_file: "",
#[cfg(not(bootstrap))]
start_line: 0,
#[cfg(not(bootstrap))]
start_col: 0,
#[cfg(not(bootstrap))]
end_line: 0,
#[cfg(not(bootstrap))]
end_col: 0,
should_panic: ShouldPanic::No,
compile_fail: false,
no_run: false,
@ -143,6 +183,16 @@ fn test_should_panic() {
name: StaticTestName("whatever"),
ignore: false,
ignore_message: None,
#[cfg(not(bootstrap))]
source_file: "",
#[cfg(not(bootstrap))]
start_line: 0,
#[cfg(not(bootstrap))]
start_col: 0,
#[cfg(not(bootstrap))]
end_line: 0,
#[cfg(not(bootstrap))]
end_col: 0,
should_panic: ShouldPanic::Yes,
compile_fail: false,
no_run: false,
@ -168,6 +218,16 @@ fn test_should_panic_good_message() {
name: StaticTestName("whatever"),
ignore: false,
ignore_message: None,
#[cfg(not(bootstrap))]
source_file: "",
#[cfg(not(bootstrap))]
start_line: 0,
#[cfg(not(bootstrap))]
start_col: 0,
#[cfg(not(bootstrap))]
end_line: 0,
#[cfg(not(bootstrap))]
end_col: 0,
should_panic: ShouldPanic::YesWithMessage("error message"),
compile_fail: false,
no_run: false,
@ -198,6 +258,16 @@ fn test_should_panic_bad_message() {
name: StaticTestName("whatever"),
ignore: false,
ignore_message: None,
#[cfg(not(bootstrap))]
source_file: "",
#[cfg(not(bootstrap))]
start_line: 0,
#[cfg(not(bootstrap))]
start_col: 0,
#[cfg(not(bootstrap))]
end_line: 0,
#[cfg(not(bootstrap))]
end_col: 0,
should_panic: ShouldPanic::YesWithMessage(expected),
compile_fail: false,
no_run: false,
@ -232,6 +302,16 @@ fn test_should_panic_non_string_message_type() {
name: StaticTestName("whatever"),
ignore: false,
ignore_message: None,
#[cfg(not(bootstrap))]
source_file: "",
#[cfg(not(bootstrap))]
start_line: 0,
#[cfg(not(bootstrap))]
start_col: 0,
#[cfg(not(bootstrap))]
end_line: 0,
#[cfg(not(bootstrap))]
end_col: 0,
should_panic: ShouldPanic::YesWithMessage(expected),
compile_fail: false,
no_run: false,
@ -260,6 +340,16 @@ fn test_should_panic_but_succeeds() {
name: StaticTestName("whatever"),
ignore: false,
ignore_message: None,
#[cfg(not(bootstrap))]
source_file: "",
#[cfg(not(bootstrap))]
start_line: 0,
#[cfg(not(bootstrap))]
start_col: 0,
#[cfg(not(bootstrap))]
end_line: 0,
#[cfg(not(bootstrap))]
end_col: 0,
should_panic,
compile_fail: false,
no_run: false,
@ -288,6 +378,16 @@ fn report_time_test_template(report_time: bool) -> Option<TestExecTime> {
name: StaticTestName("whatever"),
ignore: false,
ignore_message: None,
#[cfg(not(bootstrap))]
source_file: "",
#[cfg(not(bootstrap))]
start_line: 0,
#[cfg(not(bootstrap))]
start_col: 0,
#[cfg(not(bootstrap))]
end_line: 0,
#[cfg(not(bootstrap))]
end_col: 0,
should_panic: ShouldPanic::No,
compile_fail: false,
no_run: false,
@ -325,6 +425,16 @@ fn time_test_failure_template(test_type: TestType) -> TestResult {
name: StaticTestName("whatever"),
ignore: false,
ignore_message: None,
#[cfg(not(bootstrap))]
source_file: "",
#[cfg(not(bootstrap))]
start_line: 0,
#[cfg(not(bootstrap))]
start_col: 0,
#[cfg(not(bootstrap))]
end_line: 0,
#[cfg(not(bootstrap))]
end_col: 0,
should_panic: ShouldPanic::No,
compile_fail: false,
no_run: false,
@ -364,6 +474,16 @@ fn typed_test_desc(test_type: TestType) -> TestDesc {
name: StaticTestName("whatever"),
ignore: false,
ignore_message: None,
#[cfg(not(bootstrap))]
source_file: "",
#[cfg(not(bootstrap))]
start_line: 0,
#[cfg(not(bootstrap))]
start_col: 0,
#[cfg(not(bootstrap))]
end_line: 0,
#[cfg(not(bootstrap))]
end_col: 0,
should_panic: ShouldPanic::No,
compile_fail: false,
no_run: false,
@ -476,6 +596,16 @@ pub fn exclude_should_panic_option() {
name: StaticTestName("3"),
ignore: false,
ignore_message: None,
#[cfg(not(bootstrap))]
source_file: "",
#[cfg(not(bootstrap))]
start_line: 0,
#[cfg(not(bootstrap))]
start_col: 0,
#[cfg(not(bootstrap))]
end_line: 0,
#[cfg(not(bootstrap))]
end_col: 0,
should_panic: ShouldPanic::Yes,
compile_fail: false,
no_run: false,
@ -500,6 +630,16 @@ pub fn exact_filter_match() {
name: StaticTestName(name),
ignore: false,
ignore_message: None,
#[cfg(not(bootstrap))]
source_file: "",
#[cfg(not(bootstrap))]
start_line: 0,
#[cfg(not(bootstrap))]
start_col: 0,
#[cfg(not(bootstrap))]
end_line: 0,
#[cfg(not(bootstrap))]
end_col: 0,
should_panic: ShouldPanic::No,
compile_fail: false,
no_run: false,
@ -591,6 +731,16 @@ fn sample_tests() -> Vec<TestDescAndFn> {
name: DynTestName((*name).clone()),
ignore: false,
ignore_message: None,
#[cfg(not(bootstrap))]
source_file: "",
#[cfg(not(bootstrap))]
start_line: 0,
#[cfg(not(bootstrap))]
start_col: 0,
#[cfg(not(bootstrap))]
end_line: 0,
#[cfg(not(bootstrap))]
end_col: 0,
should_panic: ShouldPanic::No,
compile_fail: false,
no_run: false,
@ -720,6 +870,16 @@ pub fn test_bench_no_iter() {
name: StaticTestName("f"),
ignore: false,
ignore_message: None,
#[cfg(not(bootstrap))]
source_file: "",
#[cfg(not(bootstrap))]
start_line: 0,
#[cfg(not(bootstrap))]
start_col: 0,
#[cfg(not(bootstrap))]
end_line: 0,
#[cfg(not(bootstrap))]
end_col: 0,
should_panic: ShouldPanic::No,
compile_fail: false,
no_run: false,
@ -743,6 +903,16 @@ pub fn test_bench_iter() {
name: StaticTestName("f"),
ignore: false,
ignore_message: None,
#[cfg(not(bootstrap))]
source_file: "",
#[cfg(not(bootstrap))]
start_line: 0,
#[cfg(not(bootstrap))]
start_col: 0,
#[cfg(not(bootstrap))]
end_line: 0,
#[cfg(not(bootstrap))]
end_col: 0,
should_panic: ShouldPanic::No,
compile_fail: false,
no_run: false,
@ -759,6 +929,16 @@ fn should_sort_failures_before_printing_them() {
name: StaticTestName("a"),
ignore: false,
ignore_message: None,
#[cfg(not(bootstrap))]
source_file: "",
#[cfg(not(bootstrap))]
start_line: 0,
#[cfg(not(bootstrap))]
start_col: 0,
#[cfg(not(bootstrap))]
end_line: 0,
#[cfg(not(bootstrap))]
end_col: 0,
should_panic: ShouldPanic::No,
compile_fail: false,
no_run: false,
@ -769,6 +949,16 @@ fn should_sort_failures_before_printing_them() {
name: StaticTestName("b"),
ignore: false,
ignore_message: None,
#[cfg(not(bootstrap))]
source_file: "",
#[cfg(not(bootstrap))]
start_line: 0,
#[cfg(not(bootstrap))]
start_col: 0,
#[cfg(not(bootstrap))]
end_line: 0,
#[cfg(not(bootstrap))]
end_col: 0,
should_panic: ShouldPanic::No,
compile_fail: false,
no_run: false,
@ -816,6 +1006,16 @@ fn test_dyn_bench_returning_err_fails_when_run_as_test() {
name: StaticTestName("whatever"),
ignore: false,
ignore_message: None,
#[cfg(not(bootstrap))]
source_file: "",
#[cfg(not(bootstrap))]
start_line: 0,
#[cfg(not(bootstrap))]
start_col: 0,
#[cfg(not(bootstrap))]
end_line: 0,
#[cfg(not(bootstrap))]
end_col: 0,
should_panic: ShouldPanic::No,
compile_fail: false,
no_run: false,

View file

@ -119,6 +119,16 @@ pub struct TestDesc {
pub name: TestName,
pub ignore: bool,
pub ignore_message: Option<&'static str>,
#[cfg(not(bootstrap))]
pub source_file: &'static str,
#[cfg(not(bootstrap))]
pub start_line: usize,
#[cfg(not(bootstrap))]
pub start_col: usize,
#[cfg(not(bootstrap))]
pub end_line: usize,
#[cfg(not(bootstrap))]
pub end_col: usize,
pub should_panic: options::ShouldPanic,
pub compile_fail: bool,
pub no_run: bool,

View file

@ -57,7 +57,7 @@ dependencies = [
"tar",
"toml",
"walkdir",
"winapi",
"windows",
"xz2",
]
@ -720,6 +720,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.42.0"
@ -736,46 +745,61 @@ dependencies = [
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.0"
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
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.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.0"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_i686_gnu"
version = "0.42.0"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_msvc"
version = "0.42.0"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.0"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.0"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.0"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "xattr"

View file

@ -59,18 +59,20 @@ sysinfo = { version = "0.26.0", optional = true }
[target.'cfg(not(target_os = "solaris"))'.dependencies]
fd-lock = "3.0.8"
[target.'cfg(windows)'.dependencies.winapi]
version = "0.3"
[target.'cfg(windows)'.dependencies.windows]
version = "0.46.0"
features = [
"fileapi",
"ioapiset",
"jobapi2",
"handleapi",
"winioctl",
"psapi",
"impl-default",
"timezoneapi",
"winbase",
"Win32_Foundation",
"Win32_Security",
"Win32_Storage_FileSystem",
"Win32_System_Diagnostics_Debug",
"Win32_System_IO",
"Win32_System_Ioctl",
"Win32_System_JobObjects",
"Win32_System_ProcessStatus",
"Win32_System_SystemServices",
"Win32_System_Threading",
"Win32_System_Time",
]
[dev-dependencies]

View file

@ -281,41 +281,49 @@ fn format_rusage_data(_child: Child) -> Option<String> {
#[cfg(windows)]
fn format_rusage_data(child: Child) -> Option<String> {
use std::os::windows::io::AsRawHandle;
use winapi::um::{processthreadsapi, psapi, timezoneapi};
let handle = child.as_raw_handle();
macro_rules! try_bool {
($e:expr) => {
if $e != 1 {
return None;
}
};
}
use windows::{
Win32::Foundation::HANDLE,
Win32::System::ProcessStatus::{
K32GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS, PROCESS_MEMORY_COUNTERS_EX,
},
Win32::System::Threading::GetProcessTimes,
Win32::System::Time::FileTimeToSystemTime,
};
let handle = HANDLE(child.as_raw_handle() as isize);
let mut user_filetime = Default::default();
let mut user_time = Default::default();
let mut kernel_filetime = Default::default();
let mut kernel_time = Default::default();
let mut memory_counters = psapi::PROCESS_MEMORY_COUNTERS::default();
let mut memory_counters = PROCESS_MEMORY_COUNTERS::default();
unsafe {
try_bool!(processthreadsapi::GetProcessTimes(
GetProcessTimes(
handle,
&mut Default::default(),
&mut Default::default(),
&mut kernel_filetime,
&mut user_filetime,
));
try_bool!(timezoneapi::FileTimeToSystemTime(&user_filetime, &mut user_time));
try_bool!(timezoneapi::FileTimeToSystemTime(&kernel_filetime, &mut kernel_time));
// Unlike on Linux with RUSAGE_CHILDREN, this will only return memory information for the process
// with the given handle and none of that process's children.
try_bool!(psapi::GetProcessMemoryInfo(
handle as _,
&mut memory_counters as *mut _ as _,
std::mem::size_of::<psapi::PROCESS_MEMORY_COUNTERS_EX>() as u32,
));
)
}
.ok()
.ok()?;
unsafe { FileTimeToSystemTime(&user_filetime, &mut user_time) }.ok().ok()?;
unsafe { FileTimeToSystemTime(&kernel_filetime, &mut kernel_time) }.ok().ok()?;
// Unlike on Linux with RUSAGE_CHILDREN, this will only return memory information for the process
// with the given handle and none of that process's children.
unsafe {
K32GetProcessMemoryInfo(
handle,
&mut memory_counters,
std::mem::size_of::<PROCESS_MEMORY_COUNTERS_EX>() as u32,
)
}
.ok()
.ok()?;
// Guide on interpreting these numbers:
// https://docs.microsoft.com/en-us/windows/win32/psapi/process-memory-usage-information

View file

@ -11,6 +11,7 @@ import sys
from shutil import rmtree
import bootstrap
import configure
class VerifyTestCase(unittest.TestCase):
@ -74,12 +75,50 @@ class ProgramOutOfDate(unittest.TestCase):
self.assertFalse(self.build.program_out_of_date(self.rustc_stamp_path, self.key))
class GenerateAndParseConfig(unittest.TestCase):
"""Test that we can serialize and deserialize a config.toml file"""
def serialize_and_parse(self, args):
from io import StringIO
section_order, sections, targets = configure.parse_args(args)
buffer = StringIO()
configure.write_config_toml(buffer, section_order, targets, sections)
build = bootstrap.RustBuild()
build.config_toml = buffer.getvalue()
try:
import tomllib
# Verify this is actually valid TOML.
tomllib.loads(build.config_toml)
except ImportError:
print("warning: skipping TOML validation, need at least python 3.11", file=sys.stderr)
return build
def test_no_args(self):
build = self.serialize_and_parse([])
self.assertEqual(build.get_toml("changelog-seen"), '2')
self.assertIsNone(build.get_toml("llvm.download-ci-llvm"))
def test_set_section(self):
build = self.serialize_and_parse(["--set", "llvm.download-ci-llvm"])
self.assertEqual(build.get_toml("download-ci-llvm", section="llvm"), 'true')
def test_set_target(self):
build = self.serialize_and_parse(["--set", "target.x86_64-unknown-linux-gnu.cc=gcc"])
self.assertEqual(build.get_toml("cc", section="target.x86_64-unknown-linux-gnu"), 'gcc')
# Uncomment when #108928 is fixed.
# def test_set_top_level(self):
# build = self.serialize_and_parse(["--set", "profile=compiler"])
# self.assertEqual(build.get_toml("profile"), 'compiler')
if __name__ == '__main__':
SUITE = unittest.TestSuite()
TEST_LOADER = unittest.TestLoader()
SUITE.addTest(doctest.DocTestSuite(bootstrap))
SUITE.addTests([
TEST_LOADER.loadTestsFromTestCase(VerifyTestCase),
TEST_LOADER.loadTestsFromTestCase(GenerateAndParseConfig),
TEST_LOADER.loadTestsFromTestCase(ProgramOutOfDate)])
RUNNER = unittest.TextTestRunner(stream=sys.stdout, verbosity=2)

View file

@ -1303,6 +1303,14 @@ impl<'a> Builder<'a> {
}
};
// By default, windows-rs depends on a native library that doesn't get copied into the
// sysroot. Passing this cfg enables raw-dylib support instead, which makes the native
// library unnecessary. This can be removed when windows-rs enables raw-dylib
// unconditionally.
if let Mode::Rustc | Mode::ToolRustc = mode {
rustflags.arg("--cfg=windows_raw_dylib");
}
if use_new_symbol_mangling {
rustflags.arg("-Csymbol-mangling-version=v0");
} else {

View file

@ -205,77 +205,78 @@ if '--help' in sys.argv or '-h' in sys.argv:
# Parse all command line arguments into one of these three lists, handling
# boolean and value-based options separately
unknown_args = []
need_value_args = []
known_args = {}
def parse_args(args):
unknown_args = []
need_value_args = []
known_args = {}
p("processing command line")
i = 1
while i < len(sys.argv):
arg = sys.argv[i]
i += 1
if not arg.startswith('--'):
unknown_args.append(arg)
continue
i = 0
while i < len(args):
arg = args[i]
i += 1
if not arg.startswith('--'):
unknown_args.append(arg)
continue
found = False
for option in options:
value = None
if option.value:
keyval = arg[2:].split('=', 1)
key = keyval[0]
if option.name != key:
continue
found = False
for option in options:
value = None
if option.value:
keyval = arg[2:].split('=', 1)
key = keyval[0]
if option.name != key:
continue
if len(keyval) > 1:
value = keyval[1]
elif i < len(sys.argv):
value = sys.argv[i]
i += 1
if len(keyval) > 1:
value = keyval[1]
elif i < len(args):
value = args[i]
i += 1
else:
need_value_args.append(arg)
continue
else:
need_value_args.append(arg)
continue
else:
if arg[2:] == 'enable-' + option.name:
value = True
elif arg[2:] == 'disable-' + option.name:
value = False
else:
continue
if arg[2:] == 'enable-' + option.name:
value = True
elif arg[2:] == 'disable-' + option.name:
value = False
else:
continue
found = True
if option.name not in known_args:
known_args[option.name] = []
known_args[option.name].append((option, value))
break
found = True
if option.name not in known_args:
known_args[option.name] = []
known_args[option.name].append((option, value))
break
if not found:
unknown_args.append(arg)
p("")
if not found:
unknown_args.append(arg)
# Note: here and a few other places, we use [-1] to apply the *last* value
# passed. But if option-checking is enabled, then the known_args loop will
# also assert that options are only passed once.
option_checking = ('option-checking' not in known_args
or known_args['option-checking'][-1][1])
if option_checking:
if len(unknown_args) > 0:
err("Option '" + unknown_args[0] + "' is not recognized")
if len(need_value_args) > 0:
err("Option '{0}' needs a value ({0}=val)".format(need_value_args[0]))
# Note: here and a few other places, we use [-1] to apply the *last* value
# passed. But if option-checking is enabled, then the known_args loop will
# also assert that options are only passed once.
option_checking = ('option-checking' not in known_args
or known_args['option-checking'][-1][1])
if option_checking:
if len(unknown_args) > 0:
err("Option '" + unknown_args[0] + "' is not recognized")
if len(need_value_args) > 0:
err("Option '{0}' needs a value ({0}=val)".format(need_value_args[0]))
# Parse all known arguments into a configuration structure that reflects the
# TOML we're going to write out
config = {}
config = {}
set('build.configure-args', sys.argv[1:], config)
apply_args(known_args, option_checking, config)
return parse_example_config(known_args, config)
def build():
def build(known_args):
if 'build' in known_args:
return known_args['build'][-1][1]
return bootstrap.default_build_triple(verbose=False)
def set(key, value):
def set(key, value, config):
if isinstance(value, list):
# Remove empty values, which value.split(',') tends to generate.
value = [v for v in value if v]
@ -297,75 +298,76 @@ def set(key, value):
arr = arr[part]
for key in known_args:
# The `set` option is special and can be passed a bunch of times
if key == 'set':
for option, value in known_args[key]:
keyval = value.split('=', 1)
if len(keyval) == 1 or keyval[1] == "true":
value = True
elif keyval[1] == "false":
value = False
else:
value = keyval[1]
set(keyval[0], value)
continue
def apply_args(known_args, option_checking, config):
for key in known_args:
# The `set` option is special and can be passed a bunch of times
if key == 'set':
for option, value in known_args[key]:
keyval = value.split('=', 1)
if len(keyval) == 1 or keyval[1] == "true":
value = True
elif keyval[1] == "false":
value = False
else:
value = keyval[1]
set(keyval[0], value, config)
continue
# Ensure each option is only passed once
arr = known_args[key]
if option_checking and len(arr) > 1:
err("Option '{}' provided more than once".format(key))
option, value = arr[-1]
# Ensure each option is only passed once
arr = known_args[key]
if option_checking and len(arr) > 1:
err("Option '{}' provided more than once".format(key))
option, value = arr[-1]
# If we have a clear avenue to set our value in rustbuild, do so
if option.rustbuild is not None:
set(option.rustbuild, value)
continue
# If we have a clear avenue to set our value in rustbuild, do so
if option.rustbuild is not None:
set(option.rustbuild, value, config)
continue
# Otherwise we're a "special" option and need some extra handling, so do
# that here.
if option.name == 'sccache':
set('llvm.ccache', 'sccache')
elif option.name == 'local-rust':
for path in os.environ['PATH'].split(os.pathsep):
if os.path.exists(path + '/rustc'):
set('build.rustc', path + '/rustc')
break
for path in os.environ['PATH'].split(os.pathsep):
if os.path.exists(path + '/cargo'):
set('build.cargo', path + '/cargo')
break
elif option.name == 'local-rust-root':
set('build.rustc', value + '/bin/rustc')
set('build.cargo', value + '/bin/cargo')
elif option.name == 'llvm-root':
set('target.{}.llvm-config'.format(build()), value + '/bin/llvm-config')
elif option.name == 'llvm-config':
set('target.{}.llvm-config'.format(build()), value)
elif option.name == 'llvm-filecheck':
set('target.{}.llvm-filecheck'.format(build()), value)
elif option.name == 'tools':
set('build.tools', value.split(','))
elif option.name == 'codegen-backends':
set('rust.codegen-backends', value.split(','))
elif option.name == 'host':
set('build.host', value.split(','))
elif option.name == 'target':
set('build.target', value.split(','))
elif option.name == 'full-tools':
set('rust.codegen-backends', ['llvm'])
set('rust.lld', True)
set('rust.llvm-tools', True)
set('build.extended', True)
elif option.name == 'option-checking':
# this was handled above
pass
elif option.name == 'dist-compression-formats':
set('dist.compression-formats', value.split(','))
else:
raise RuntimeError("unhandled option {}".format(option.name))
# Otherwise we're a "special" option and need some extra handling, so do
# that here.
build_triple = build(known_args)
set('build.configure-args', sys.argv[1:])
if option.name == 'sccache':
set('llvm.ccache', 'sccache', config)
elif option.name == 'local-rust':
for path in os.environ['PATH'].split(os.pathsep):
if os.path.exists(path + '/rustc'):
set('build.rustc', path + '/rustc', config)
break
for path in os.environ['PATH'].split(os.pathsep):
if os.path.exists(path + '/cargo'):
set('build.cargo', path + '/cargo', config)
break
elif option.name == 'local-rust-root':
set('build.rustc', value + '/bin/rustc', config)
set('build.cargo', value + '/bin/cargo', config)
elif option.name == 'llvm-root':
set('target.{}.llvm-config'.format(build_triple), value + '/bin/llvm-config', config)
elif option.name == 'llvm-config':
set('target.{}.llvm-config'.format(build_triple), value, config)
elif option.name == 'llvm-filecheck':
set('target.{}.llvm-filecheck'.format(build_triple), value, config)
elif option.name == 'tools':
set('build.tools', value.split(','), config)
elif option.name == 'codegen-backends':
set('rust.codegen-backends', value.split(','), config)
elif option.name == 'host':
set('build.host', value.split(','), config)
elif option.name == 'target':
set('build.target', value.split(','), config)
elif option.name == 'full-tools':
set('rust.codegen-backends', ['llvm'], config)
set('rust.lld', True, config)
set('rust.llvm-tools', True, config)
set('build.extended', True, config)
elif option.name == 'option-checking':
# this was handled above
pass
elif option.name == 'dist-compression-formats':
set('dist.compression-formats', value.split(','), config)
else:
raise RuntimeError("unhandled option {}".format(option.name))
# "Parse" the `config.example.toml` file into the various sections, and we'll
# use this as a template of a `config.toml` to write out which preserves
@ -373,46 +375,50 @@ set('build.configure-args', sys.argv[1:])
#
# Note that the `target` section is handled separately as we'll duplicate it
# per configured target, so there's a bit of special handling for that here.
sections = {}
cur_section = None
sections[None] = []
section_order = [None]
targets = {}
top_level_keys = []
def parse_example_config(known_args, config):
sections = {}
cur_section = None
sections[None] = []
section_order = [None]
targets = {}
top_level_keys = []
for line in open(rust_dir + '/config.example.toml').read().split("\n"):
if cur_section == None:
if line.count('=') == 1:
top_level_key = line.split('=')[0]
top_level_key = top_level_key.strip(' #')
top_level_keys.append(top_level_key)
if line.startswith('['):
cur_section = line[1:-1]
if cur_section.startswith('target'):
cur_section = 'target'
elif '.' in cur_section:
raise RuntimeError("don't know how to deal with section: {}".format(cur_section))
sections[cur_section] = [line]
section_order.append(cur_section)
else:
sections[cur_section].append(line)
for line in open(rust_dir + '/config.example.toml').read().split("\n"):
if cur_section == None:
if line.count('=') == 1:
top_level_key = line.split('=')[0]
top_level_key = top_level_key.strip(' #')
top_level_keys.append(top_level_key)
if line.startswith('['):
cur_section = line[1:-1]
if cur_section.startswith('target'):
cur_section = 'target'
elif '.' in cur_section:
raise RuntimeError("don't know how to deal with section: {}".format(cur_section))
sections[cur_section] = [line]
section_order.append(cur_section)
else:
sections[cur_section].append(line)
# Fill out the `targets` array by giving all configured targets a copy of the
# `target` section we just loaded from the example config
configured_targets = [build()]
if 'build' in config:
if 'host' in config['build']:
configured_targets += config['build']['host']
if 'target' in config['build']:
configured_targets += config['build']['target']
if 'target' in config:
for target in config['target']:
configured_targets.append(target)
for target in configured_targets:
targets[target] = sections['target'][:]
# For `.` to be valid TOML, it needs to be quoted. But `bootstrap.py` doesn't use a proper TOML parser and fails to parse the target.
# Avoid using quotes unless it's necessary.
targets[target][0] = targets[target][0].replace("x86_64-unknown-linux-gnu", "'{}'".format(target) if "." in target else target)
# Fill out the `targets` array by giving all configured targets a copy of the
# `target` section we just loaded from the example config
configured_targets = [build(known_args)]
if 'build' in config:
if 'host' in config['build']:
configured_targets += config['build']['host']
if 'target' in config['build']:
configured_targets += config['build']['target']
if 'target' in config:
for target in config['target']:
configured_targets.append(target)
for target in configured_targets:
targets[target] = sections['target'][:]
# For `.` to be valid TOML, it needs to be quoted. But `bootstrap.py` doesn't use a proper TOML parser and fails to parse the target.
# Avoid using quotes unless it's necessary.
targets[target][0] = targets[target][0].replace("x86_64-unknown-linux-gnu", "'{}'".format(target) if "." in target else target)
configure_file(sections, top_level_keys, targets, config)
return section_order, sections, targets
def is_number(value):
@ -475,17 +481,20 @@ def configure_top_level_key(lines, top_level_key, value):
raise RuntimeError("failed to find config line for {}".format(top_level_key))
for section_key, section_config in config.items():
if section_key not in sections and section_key not in top_level_keys:
raise RuntimeError("config key {} not in sections or top_level_keys".format(section_key))
if section_key in top_level_keys:
configure_top_level_key(sections[None], section_key, section_config)
# Modify `sections` to reflect the parsed arguments and example configs.
def configure_file(sections, top_level_keys, targets, config):
for section_key, section_config in config.items():
if section_key not in sections and section_key not in top_level_keys:
raise RuntimeError("config key {} not in sections or top_level_keys".format(section_key))
if section_key in top_level_keys:
configure_top_level_key(sections[None], section_key, section_config)
elif section_key == 'target':
for target in section_config:
configure_section(targets[target], section_config[target])
else:
configure_section(sections[section_key], section_config)
elif section_key == 'target':
for target in section_config:
configure_section(targets[target], section_config[target])
else:
configure_section(sections[section_key], section_config)
def write_uncommented(target, f):
block = []
@ -503,24 +512,36 @@ def write_uncommented(target, f):
is_comment = is_comment and line.startswith('#')
return f
# Now that we've built up our `config.toml`, write it all out in the same
# order that we read it in.
p("")
p("writing `config.toml` in current directory")
with bootstrap.output('config.toml') as f:
def write_config_toml(writer, section_order, targets, sections):
for section in section_order:
if section == 'target':
for target in targets:
f = write_uncommented(targets[target], f)
writer = write_uncommented(targets[target], writer)
else:
f = write_uncommented(sections[section], f)
writer = write_uncommented(sections[section], writer)
with bootstrap.output('Makefile') as f:
contents = os.path.join(rust_dir, 'src', 'bootstrap', 'mk', 'Makefile.in')
contents = open(contents).read()
contents = contents.replace("$(CFG_SRC_DIR)", rust_dir + '/')
contents = contents.replace("$(CFG_PYTHON)", sys.executable)
f.write(contents)
p("")
p("run `python {}/x.py --help`".format(rust_dir))
if __name__ == "__main__":
p("processing command line")
# Parse all known arguments into a configuration structure that reflects the
# TOML we're going to write out
p("")
section_order, sections, targets = parse_args(sys.argv[1:])
# Now that we've built up our `config.toml`, write it all out in the same
# order that we read it in.
p("")
p("writing `config.toml` in current directory")
with bootstrap.output('config.toml') as f:
write_config_toml(f, section_order, targets, sections)
with bootstrap.output('Makefile') as f:
contents = os.path.join(rust_dir, 'src', 'bootstrap', 'mk', 'Makefile.in')
contents = open(contents).read()
contents = contents.replace("$(CFG_SRC_DIR)", rust_dir + '/')
contents = contents.replace("$(CFG_PYTHON)", sys.executable)
f.write(contents)
p("")
p("run `python {}/x.py --help`".format(rust_dir))

View file

@ -210,6 +210,8 @@ fn make_win_dist(
rustc_dlls.push("libgcc_s_seh-1.dll");
}
// Libraries necessary to link the windows-gnu toolchains.
// System libraries will be preferred if they are available (see #67429).
let target_libs = [
//MinGW libs
"libgcc.a",
@ -223,6 +225,7 @@ fn make_win_dist(
"libmoldname.a",
"libpthread.a",
//Windows import libs
//This should contain only the set of libraries necessary to link the standard library.
"libadvapi32.a",
"libbcrypt.a",
"libcomctl32.a",
@ -236,6 +239,7 @@ fn make_win_dist(
"libkernel32.a",
"libmsimg32.a",
"libmsvcrt.a",
"libntdll.a",
"libodbc32.a",
"libole32.a",
"liboleaut32.a",

View file

@ -27,52 +27,54 @@
//! Note that this module has a #[cfg(windows)] above it as none of this logic
//! is required on Unix.
#![allow(nonstandard_style, dead_code)]
use crate::Build;
use std::env;
use std::ffi::c_void;
use std::io;
use std::mem;
use std::ptr;
use winapi::shared::minwindef::{DWORD, FALSE, LPVOID};
use winapi::um::errhandlingapi::SetErrorMode;
use winapi::um::handleapi::{CloseHandle, DuplicateHandle};
use winapi::um::jobapi2::{AssignProcessToJobObject, CreateJobObjectW, SetInformationJobObject};
use winapi::um::processthreadsapi::{GetCurrentProcess, OpenProcess};
use winapi::um::winbase::{BELOW_NORMAL_PRIORITY_CLASS, SEM_NOGPFAULTERRORBOX};
use winapi::um::winnt::{
JobObjectExtendedLimitInformation, DUPLICATE_SAME_ACCESS, JOBOBJECT_EXTENDED_LIMIT_INFORMATION,
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, JOB_OBJECT_LIMIT_PRIORITY_CLASS, PROCESS_DUP_HANDLE,
use windows::{
core::PCWSTR,
Win32::Foundation::{CloseHandle, DuplicateHandle, DUPLICATE_SAME_ACCESS, HANDLE},
Win32::System::Diagnostics::Debug::{SetErrorMode, SEM_NOGPFAULTERRORBOX, THREAD_ERROR_MODE},
Win32::System::JobObjects::{
AssignProcessToJobObject, CreateJobObjectW, JobObjectExtendedLimitInformation,
SetInformationJobObject, JOBOBJECT_EXTENDED_LIMIT_INFORMATION,
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, JOB_OBJECT_LIMIT_PRIORITY_CLASS,
},
Win32::System::Threading::{
GetCurrentProcess, OpenProcess, BELOW_NORMAL_PRIORITY_CLASS, PROCESS_DUP_HANDLE,
},
};
pub unsafe fn setup(build: &mut Build) {
// Enable the Windows Error Reporting dialog which msys disables,
// so we can JIT debug rustc
let mode = SetErrorMode(0);
let mode = SetErrorMode(THREAD_ERROR_MODE::default());
let mode = THREAD_ERROR_MODE(mode);
SetErrorMode(mode & !SEM_NOGPFAULTERRORBOX);
// Create a new job object for us to use
let job = CreateJobObjectW(ptr::null_mut(), ptr::null());
assert!(!job.is_null(), "{}", io::Error::last_os_error());
let job = CreateJobObjectW(None, PCWSTR::null()).unwrap();
// Indicate that when all handles to the job object are gone that all
// process in the object should be killed. Note that this includes our
// entire process tree by default because we've added ourselves and our
// children will reside in the job by default.
let mut info = mem::zeroed::<JOBOBJECT_EXTENDED_LIMIT_INFORMATION>();
let mut info = JOBOBJECT_EXTENDED_LIMIT_INFORMATION::default();
info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
if build.config.low_priority {
info.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_PRIORITY_CLASS;
info.BasicLimitInformation.PriorityClass = BELOW_NORMAL_PRIORITY_CLASS;
info.BasicLimitInformation.PriorityClass = BELOW_NORMAL_PRIORITY_CLASS.0;
}
let r = SetInformationJobObject(
job,
JobObjectExtendedLimitInformation,
&mut info as *mut _ as LPVOID,
mem::size_of_val(&info) as DWORD,
);
assert!(r != 0, "{}", io::Error::last_os_error());
&info as *const _ as *const c_void,
mem::size_of_val(&info) as u32,
)
.ok();
assert!(r.is_ok(), "{}", io::Error::last_os_error());
// Assign our process to this job object. Note that if this fails, one very
// likely reason is that we are ourselves already in a job object! This can
@ -83,8 +85,8 @@ pub unsafe fn setup(build: &mut Build) {
// Also note that nested jobs (why this might fail) are supported in recent
// versions of Windows, but the version of Windows that our bots are running
// at least don't support nested job objects.
let r = AssignProcessToJobObject(job, GetCurrentProcess());
if r == 0 {
let r = AssignProcessToJobObject(job, GetCurrentProcess()).ok();
if r.is_err() {
CloseHandle(job);
return;
}
@ -102,31 +104,32 @@ pub unsafe fn setup(build: &mut Build) {
Err(..) => return,
};
let parent = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid.parse().unwrap());
let parent = match OpenProcess(PROCESS_DUP_HANDLE, false, pid.parse().unwrap()).ok() {
Some(parent) => parent,
_ => {
// If we get a null parent pointer here, it is possible that either
// we have an invalid pid or the parent process has been closed.
// Since the first case rarely happens
// (only when wrongly setting the environmental variable),
// it might be better to improve the experience of the second case
// when users have interrupted the parent process and we haven't finish
// duplicating the handle yet. We just need close the job object if that occurs.
CloseHandle(job);
return;
}
};
// If we get a null parent pointer here, it is possible that either
// we have got an invalid pid or the parent process has been closed.
// Since the first case rarely happens
// (only when wrongly setting the environmental variable),
// so it might be better to improve the experience of the second case
// when users have interrupted the parent process and we don't finish
// duplicating the handle yet.
// We just need close the job object if that occurs.
if parent.is_null() {
CloseHandle(job);
return;
}
let mut parent_handle = ptr::null_mut();
let mut parent_handle = HANDLE::default();
let r = DuplicateHandle(
GetCurrentProcess(),
job,
parent,
&mut parent_handle,
0,
FALSE,
false,
DUPLICATE_SAME_ACCESS,
);
)
.ok();
// If this failed, well at least we tried! An example of DuplicateHandle
// failing in the past has been when the wrong python2 package spawned this
@ -134,7 +137,7 @@ pub unsafe fn setup(build: &mut Build) {
// `mingw-w64-x86_64-python2`. Not sure why it failed, but the "failure
// mode" here is that we only clean everything up when the build system
// dies, not when the python parent does, so not too bad.
if r != 0 {
if r.is_err() {
CloseHandle(job);
}
}

View file

@ -144,6 +144,9 @@ const EXTRA_CHECK_CFGS: &[(Option<Mode>, &'static str, Option<&[&'static str]>)]
// FIXME: Used by filetime, but we should not be triggering on external dependencies.
(Some(Mode::Rustc), "emulate_second_only_system", None),
(Some(Mode::ToolRustc), "emulate_second_only_system", None),
// Needed to avoid the need to copy windows.lib into the sysroot.
(Some(Mode::Rustc), "windows_raw_dylib", None),
(Some(Mode::ToolRustc), "windows_raw_dylib", None),
];
/// A structure representing a Rust compiler.

View file

@ -567,6 +567,8 @@ fn configure_cmake(
cfg.define("CMAKE_SYSTEM_NAME", "Haiku");
} else if target.contains("solaris") || target.contains("illumos") {
cfg.define("CMAKE_SYSTEM_NAME", "SunOS");
} else if target.contains("linux") {
cfg.define("CMAKE_SYSTEM_NAME", "Linux");
}
// When cross-compiling we should also set CMAKE_SYSTEM_VERSION, but in
// that case like CMake we cannot easily determine system version either.

View file

@ -155,29 +155,30 @@ pub fn symlink_dir(config: &Config, src: &Path, dest: &Path) -> io::Result<()> {
fn symlink_dir_inner(target: &Path, junction: &Path) -> io::Result<()> {
use std::ffi::OsStr;
use std::os::windows::ffi::OsStrExt;
use std::ptr;
use winapi::shared::minwindef::{DWORD, WORD};
use winapi::um::fileapi::{CreateFileW, OPEN_EXISTING};
use winapi::um::handleapi::CloseHandle;
use winapi::um::ioapiset::DeviceIoControl;
use winapi::um::winbase::{FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT};
use winapi::um::winioctl::FSCTL_SET_REPARSE_POINT;
use winapi::um::winnt::{
FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE, GENERIC_WRITE,
IO_REPARSE_TAG_MOUNT_POINT, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, WCHAR,
use windows::{
core::PCWSTR,
Win32::Foundation::{CloseHandle, HANDLE},
Win32::Storage::FileSystem::{
CreateFileW, FILE_ACCESS_FLAGS, FILE_FLAG_BACKUP_SEMANTICS,
FILE_FLAG_OPEN_REPARSE_POINT, FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE,
MAXIMUM_REPARSE_DATA_BUFFER_SIZE, OPEN_EXISTING,
},
Win32::System::Ioctl::FSCTL_SET_REPARSE_POINT,
Win32::System::SystemServices::{GENERIC_WRITE, IO_REPARSE_TAG_MOUNT_POINT},
Win32::System::IO::DeviceIoControl,
};
#[allow(non_snake_case)]
#[repr(C)]
struct REPARSE_MOUNTPOINT_DATA_BUFFER {
ReparseTag: DWORD,
ReparseDataLength: DWORD,
Reserved: WORD,
ReparseTargetLength: WORD,
ReparseTargetMaximumLength: WORD,
Reserved1: WORD,
ReparseTarget: WCHAR,
ReparseTag: u32,
ReparseDataLength: u32,
Reserved: u16,
ReparseTargetLength: u16,
ReparseTargetMaximumLength: u16,
Reserved1: u16,
ReparseTarget: u16,
}
fn to_u16s<S: AsRef<OsStr>>(s: S) -> io::Result<Vec<u16>> {
@ -193,17 +194,20 @@ pub fn symlink_dir(config: &Config, src: &Path, dest: &Path) -> io::Result<()> {
let path = to_u16s(junction)?;
unsafe {
let h = CreateFileW(
path.as_ptr(),
GENERIC_WRITE,
let h = unsafe {
CreateFileW(
PCWSTR(path.as_ptr()),
FILE_ACCESS_FLAGS(GENERIC_WRITE),
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
ptr::null_mut(),
None,
OPEN_EXISTING,
FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
ptr::null_mut(),
);
HANDLE::default(),
)
}
.map_err(|_| io::Error::last_os_error())?;
unsafe {
#[repr(C, align(8))]
struct Align8<T>(T);
let mut data = Align8([0u8; MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize]);
@ -219,27 +223,29 @@ pub fn symlink_dir(config: &Config, src: &Path, dest: &Path) -> io::Result<()> {
}
*buf.offset(i) = 0;
i += 1;
(*db).ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
(*db).ReparseTargetMaximumLength = (i * 2) as WORD;
(*db).ReparseTargetLength = ((i - 1) * 2) as WORD;
(*db).ReparseDataLength = (*db).ReparseTargetLength as DWORD + 12;
(*db).ReparseTargetMaximumLength = (i * 2) as u16;
(*db).ReparseTargetLength = ((i - 1) * 2) as u16;
(*db).ReparseDataLength = ((*db).ReparseTargetLength + 12) as u32;
let mut ret = 0;
let res = DeviceIoControl(
h as *mut _,
let mut ret = 0u32;
DeviceIoControl(
h,
FSCTL_SET_REPARSE_POINT,
db.cast(),
Some(db.cast()),
(*db).ReparseDataLength + 8,
ptr::null_mut(),
None,
0,
&mut ret,
ptr::null_mut(),
);
let out = if res == 0 { Err(io::Error::last_os_error()) } else { Ok(()) };
CloseHandle(h);
out
Some(&mut ret),
None,
)
.ok()
.map_err(|_| io::Error::last_os_error())?;
}
unsafe { CloseHandle(h) };
Ok(())
}
}

View file

@ -1,6 +1,8 @@
FROM ubuntu:22.04
ARG DEBIAN_FRONTEND=noninteractive
# NOTE: intentionally uses python2 for x.py so we can test it still works.
# validate-toolstate only runs in our CI, so it's ok for it to only support python3.
RUN apt-get update && apt-get install -y --no-install-recommends \
g++ \
make \
@ -8,6 +10,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
file \
curl \
ca-certificates \
python2.7 \
python3 \
python3-pip \
python3-pkg-resources \
@ -30,4 +33,4 @@ RUN pip3 install --no-deps --no-cache-dir --require-hashes -r /tmp/reuse-require
COPY host-x86_64/mingw-check/validate-toolstate.sh /scripts/
COPY host-x86_64/mingw-check/validate-error-codes.sh /scripts/
ENV SCRIPT python3 ../x.py test --stage 0 src/tools/tidy tidyselftest
ENV SCRIPT python2.7 ../x.py test --stage 0 src/tools/tidy tidyselftest

View file

@ -2,7 +2,6 @@ FROM ubuntu:22.04
ARG DEBIAN_FRONTEND=noninteractive
# NOTE: intentionally installs both python2 and python3 so we can test support for both.
RUN apt-get update && apt-get install -y --no-install-recommends \
g++ \
gcc-multilib \
@ -11,8 +10,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
file \
curl \
ca-certificates \
python2.7 \
python3 \
python3.11 \
git \
cmake \
sudo \

View file

@ -74,7 +74,7 @@ x--expand-yaml-anchors--remove:
env: {}
- &job-linux-xl
os: ubuntu-20.04-xl
os: ubuntu-20.04-16core-64gb
<<: *base-job
- &job-macos-xl
@ -82,7 +82,7 @@ x--expand-yaml-anchors--remove:
<<: *base-job
- &job-windows-xl
os: windows-latest-xl
os: windows-2019-8core-32gb
<<: *base-job
- &job-aarch64-linux

View file

@ -39,6 +39,7 @@ use std::hash::Hash;
use std::mem;
use thin_vec::ThinVec;
use crate::clean::inline::merge_attrs;
use crate::core::{self, DocContext, ImplTraitParam};
use crate::formats::item_type::ItemType;
use crate::visit_ast::Module as DocModule;
@ -2373,21 +2374,22 @@ fn clean_maybe_renamed_item<'tcx>(
_ => unreachable!("not yet converted"),
};
let mut extra_attrs = Vec::new();
let mut import_attrs = Vec::new();
let mut target_attrs = Vec::new();
if let Some(import_id) = import_id &&
let Some(hir::Node::Item(use_node)) = cx.tcx.hir().find_by_def_id(import_id)
{
let is_inline = inline::load_attrs(cx, import_id.to_def_id()).lists(sym::doc).get_word_attr(sym::inline).is_some();
// Then we get all the various imports' attributes.
get_all_import_attributes(use_node, cx.tcx, item.owner_id.def_id, &mut extra_attrs, is_inline);
add_without_unwanted_attributes(&mut extra_attrs, inline::load_attrs(cx, def_id), is_inline);
get_all_import_attributes(use_node, cx.tcx, item.owner_id.def_id, &mut import_attrs, is_inline);
add_without_unwanted_attributes(&mut target_attrs, inline::load_attrs(cx, def_id), is_inline);
} else {
// We only keep the item's attributes.
extra_attrs.extend_from_slice(inline::load_attrs(cx, def_id));
target_attrs.extend_from_slice(inline::load_attrs(cx, def_id));
}
let attrs = Attributes::from_ast(&extra_attrs);
let cfg = extra_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg);
let import_parent = import_id.map(|import_id| cx.tcx.local_parent(import_id).to_def_id());
let (attrs, cfg) = merge_attrs(cx, import_parent, &target_attrs, Some(&import_attrs));
let mut item =
Item::from_def_id_and_attrs_and_parts(def_id, Some(name), kind, Box::new(attrs), cfg);

View file

@ -1057,6 +1057,16 @@ impl Tester for Collector {
Ignore::Some(ref ignores) => ignores.iter().any(|s| target_str.contains(s)),
},
ignore_message: None,
#[cfg(not(bootstrap))]
source_file: "",
#[cfg(not(bootstrap))]
start_line: 0,
#[cfg(not(bootstrap))]
start_col: 0,
#[cfg(not(bootstrap))]
end_line: 0,
#[cfg(not(bootstrap))]
end_col: 0,
// compiler failures are test failures
should_panic: test::ShouldPanic::No,
compile_fail: config.compile_fail,

View file

@ -1480,7 +1480,7 @@ pub(crate) fn visibility_print_with_space<'a, 'tcx: 'a>(
debug!("path={:?}", path);
// modified from `resolved_path()` to work with `DefPathData`
let last_name = path.data.last().unwrap().data.get_opt_name().unwrap();
let anchor = anchor(vis_did, last_name, cx).to_string();
let anchor = anchor(vis_did, last_name, cx);
let mut s = "pub(in ".to_owned();
for seg in &path.data[..path.data.len() - 1] {

View file

@ -556,7 +556,15 @@ fn check_if_allowed_tag(t: &Tag<'_>) -> bool {
}
fn is_forbidden_tag(t: &Tag<'_>) -> bool {
matches!(t, Tag::CodeBlock(_) | Tag::Table(_) | Tag::TableHead | Tag::TableRow | Tag::TableCell)
matches!(
t,
Tag::CodeBlock(_)
| Tag::Table(_)
| Tag::TableHead
| Tag::TableRow
| Tag::TableCell
| Tag::FootnoteDefinition(_)
)
}
impl<'a, I: Iterator<Item = Event<'a>>> Iterator for SummaryLine<'a, I> {
@ -589,6 +597,10 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for SummaryLine<'a, I> {
is_start = false;
check_if_allowed_tag(c)
}
Event::FootnoteReference(_) => {
self.skipped_tags += 1;
false
}
_ => true,
};
if !is_allowed_tag {

View file

@ -352,7 +352,7 @@ impl<'tcx> Context<'tcx> {
},
);
path = href.into_inner().to_string_lossy().to_string();
path = href.into_inner().to_string_lossy().into_owned();
if let Some(c) = path.as_bytes().last() && *c != b'/' {
path.push('/');

View file

@ -1,5 +1,6 @@
use clean::AttributesExt;
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
use rustc_hir::def::CtorKind;
@ -28,8 +29,8 @@ use crate::formats::item_type::ItemType;
use crate::formats::{AssocItemRender, Impl, RenderMode};
use crate::html::escape::Escape;
use crate::html::format::{
join_with_double_colon, print_abi_with_space, print_constness_with_space, print_where_clause,
visibility_print_with_space, Buffer, Ending, PrintWithSpace,
display_fn, join_with_double_colon, print_abi_with_space, print_constness_with_space,
print_where_clause, visibility_print_with_space, Buffer, Ending, PrintWithSpace,
};
use crate::html::layout::Page;
use crate::html::markdown::{HeadingOffset, MarkdownSummaryLine};
@ -367,7 +368,7 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
..myitem.clone()
};
let stab_tags = Some(extra_info_tags(&import_item, item, cx.tcx()));
let stab_tags = Some(extra_info_tags(&import_item, item, cx.tcx()).to_string());
stab_tags
} else {
None
@ -461,42 +462,62 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
/// Render the stability, deprecation and portability tags that are displayed in the item's summary
/// at the module level.
fn extra_info_tags(item: &clean::Item, parent: &clean::Item, tcx: TyCtxt<'_>) -> String {
let mut tags = String::new();
fn extra_info_tags<'a, 'tcx: 'a>(
item: &'a clean::Item,
parent: &'a clean::Item,
tcx: TyCtxt<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
display_fn(move |f| {
fn tag_html<'a>(
class: &'a str,
title: &'a str,
contents: &'a str,
) -> impl fmt::Display + 'a {
display_fn(move |f| {
write!(
f,
r#"<span class="stab {}" title="{}">{}</span>"#,
class,
Escape(title),
contents
)
})
}
fn tag_html(class: &str, title: &str, contents: &str) -> String {
format!(r#"<span class="stab {}" title="{}">{}</span>"#, class, Escape(title), contents)
}
// The trailing space after each tag is to space it properly against the rest of the docs.
if let Some(depr) = &item.deprecation(tcx) {
let message = if stability::deprecation_in_effect(depr) {
"Deprecated"
} else {
"Deprecation planned"
};
write!(f, "{}", tag_html("deprecated", "", message))?;
}
// The trailing space after each tag is to space it properly against the rest of the docs.
if let Some(depr) = &item.deprecation(tcx) {
let message = if stability::deprecation_in_effect(depr) {
"Deprecated"
} else {
"Deprecation planned"
// The "rustc_private" crates are permanently unstable so it makes no sense
// to render "unstable" everywhere.
if item.stability(tcx).as_ref().map(|s| s.is_unstable() && s.feature != sym::rustc_private)
== Some(true)
{
write!(f, "{}", tag_html("unstable", "", "Experimental"))?;
}
let cfg = match (&item.cfg, parent.cfg.as_ref()) {
(Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg),
(cfg, _) => cfg.as_deref().cloned(),
};
tags += &tag_html("deprecated", "", message);
}
// The "rustc_private" crates are permanently unstable so it makes no sense
// to render "unstable" everywhere.
if item.stability(tcx).as_ref().map(|s| s.is_unstable() && s.feature != sym::rustc_private)
== Some(true)
{
tags += &tag_html("unstable", "", "Experimental");
}
let cfg = match (&item.cfg, parent.cfg.as_ref()) {
(Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg),
(cfg, _) => cfg.as_deref().cloned(),
};
debug!("Portability name={:?} {:?} - {:?} = {:?}", item.name, item.cfg, parent.cfg, cfg);
if let Some(ref cfg) = cfg {
tags += &tag_html("portability", &cfg.render_long_plain(), &cfg.render_short_html());
}
tags
debug!("Portability name={:?} {:?} - {:?} = {:?}", item.name, item.cfg, parent.cfg, cfg);
if let Some(ref cfg) = cfg {
write!(
f,
"{}",
tag_html("portability", &cfg.render_long_plain(), &cfg.render_short_html())
)
} else {
Ok(())
}
})
}
fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &clean::Function) {

View file

@ -85,7 +85,7 @@ impl LocalSourcesCollector<'_, '_> {
},
);
let mut href = href.into_inner().to_string_lossy().to_string();
let mut href = href.into_inner().to_string_lossy().into_owned();
if let Some(c) = href.as_bytes().last() && *c != b'/' {
href.push('/');
}

View file

@ -1202,28 +1202,42 @@ function initSearch(rawSearchIndex) {
* @param {Row} row
* @param {QueryElement} elem - The element from the parsed query.
* @param {integer} typeFilter
* @param {Array<integer>} skipPositions - Do not return one of these positions.
*
* @return {integer} - Returns an edit distance to the best match. If there is no
* match, returns `maxEditDistance + 1`.
* @return {dist: integer, position: integer} - Returns an edit distance to the best match.
* If there is no match, returns
* `maxEditDistance + 1` and position: -1.
*/
function findArg(row, elem, typeFilter, maxEditDistance) {
function findArg(row, elem, typeFilter, maxEditDistance, skipPositions) {
let dist = maxEditDistance + 1;
let position = -1;
if (row && row.type && row.type.inputs && row.type.inputs.length > 0) {
let i = 0;
for (const input of row.type.inputs) {
if (!typePassesFilter(typeFilter, input.ty)) {
if (!typePassesFilter(typeFilter, input.ty) ||
skipPositions.indexOf(i) !== -1) {
i += 1;
continue;
}
dist = Math.min(
dist,
checkType(input, elem, parsedQuery.literalSearch, maxEditDistance)
const typeDist = checkType(
input,
elem,
parsedQuery.literalSearch,
maxEditDistance
);
if (dist === 0) {
return 0;
if (typeDist === 0) {
return {dist: 0, position: i};
}
if (typeDist < dist) {
dist = typeDist;
position = i;
}
i += 1;
}
}
return parsedQuery.literalSearch ? maxEditDistance + 1 : dist;
dist = parsedQuery.literalSearch ? maxEditDistance + 1 : dist;
return {dist, position};
}
/**
@ -1232,29 +1246,43 @@ function initSearch(rawSearchIndex) {
* @param {Row} row
* @param {QueryElement} elem - The element from the parsed query.
* @param {integer} typeFilter
* @param {Array<integer>} skipPositions - Do not return one of these positions.
*
* @return {integer} - Returns an edit distance to the best match. If there is no
* match, returns `maxEditDistance + 1`.
* @return {dist: integer, position: integer} - Returns an edit distance to the best match.
* If there is no match, returns
* `maxEditDistance + 1` and position: -1.
*/
function checkReturned(row, elem, typeFilter, maxEditDistance) {
function checkReturned(row, elem, typeFilter, maxEditDistance, skipPositions) {
let dist = maxEditDistance + 1;
let position = -1;
if (row && row.type && row.type.output.length > 0) {
const ret = row.type.output;
let i = 0;
for (const ret_ty of ret) {
if (!typePassesFilter(typeFilter, ret_ty.ty)) {
if (!typePassesFilter(typeFilter, ret_ty.ty) ||
skipPositions.indexOf(i) !== -1) {
i += 1;
continue;
}
dist = Math.min(
dist,
checkType(ret_ty, elem, parsedQuery.literalSearch, maxEditDistance)
const typeDist = checkType(
ret_ty,
elem,
parsedQuery.literalSearch,
maxEditDistance
);
if (dist === 0) {
return 0;
if (typeDist === 0) {
return {dist: 0, position: i};
}
if (typeDist < dist) {
dist = typeDist;
position = i;
}
i += 1;
}
}
return parsedQuery.literalSearch ? maxEditDistance + 1 : dist;
dist = parsedQuery.literalSearch ? maxEditDistance + 1 : dist;
return {dist, position};
}
function checkPath(contains, ty, maxEditDistance) {
@ -1455,13 +1483,13 @@ function initSearch(rawSearchIndex) {
const fullId = row.id;
const searchWord = searchWords[pos];
const in_args = findArg(row, elem, parsedQuery.typeFilter, maxEditDistance);
const returned = checkReturned(row, elem, parsedQuery.typeFilter, maxEditDistance);
const in_args = findArg(row, elem, parsedQuery.typeFilter, maxEditDistance, []);
const returned = checkReturned(row, elem, parsedQuery.typeFilter, maxEditDistance, []);
// path_dist is 0 because no parent path information is currently stored
// in the search index
addIntoResults(results_in_args, fullId, pos, -1, in_args, 0, maxEditDistance);
addIntoResults(results_returned, fullId, pos, -1, returned, 0, maxEditDistance);
addIntoResults(results_in_args, fullId, pos, -1, in_args.dist, 0, maxEditDistance);
addIntoResults(results_returned, fullId, pos, -1, returned.dist, 0, maxEditDistance);
if (!typePassesFilter(parsedQuery.typeFilter, row.ty)) {
return;
@ -1534,12 +1562,20 @@ function initSearch(rawSearchIndex) {
// If the result is too "bad", we return false and it ends this search.
function checkArgs(elems, callback) {
const skipPositions = [];
for (const elem of elems) {
// There is more than one parameter to the query so all checks should be "exact"
const dist = callback(row, elem, NO_TYPE_FILTER, maxEditDistance);
const { dist, position } = callback(
row,
elem,
NO_TYPE_FILTER,
maxEditDistance,
skipPositions
);
if (dist <= 1) {
nbDist += 1;
totalDist += dist;
skipPositions.push(position);
} else {
return false;
}
@ -1597,9 +1633,17 @@ function initSearch(rawSearchIndex) {
row,
elem,
parsedQuery.typeFilter,
maxEditDistance,
[]
);
addIntoResults(
results_others,
row.id,
i,
-1,
in_returned.dist,
maxEditDistance
);
addIntoResults(results_others, row.id, i, -1, in_returned, maxEditDistance);
}
}
} else if (parsedQuery.foundElems > 0) {

View file

@ -2,7 +2,7 @@
{% when Self::Deprecation with { message } %}
<div class="stab deprecated"> {# #}
<span class="emoji">👎</span> {# #}
<span>{{message}}</span> {# #}
<span>{{message|safe}}</span> {# #}
</div> {# #}
{% when Self::Unstable with { feature, tracking } %}
<div class="stab unstable"> {# #}

View file

@ -286,7 +286,6 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
split.next().map(|f| Symbol::intern(f)).ok_or_else(no_res)?;
let path = split
.next()
.map(|f| f.to_owned())
// If there's no third component, we saw `[a::b]` before and it failed to resolve.
// So there's no partial res.
.ok_or_else(no_res)?;
@ -429,7 +428,6 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
let item_name = Symbol::intern(item_str);
let path_root = split
.next()
.map(|f| f.to_owned())
// If there's no `::`, it's not an associated item.
// So we can be sure that `rustc_resolve` was accurate when it said it wasn't resolved.
.ok_or_else(|| {

View file

@ -265,10 +265,6 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
return false;
}
if !self.view_item_stack.insert(res_did) {
return false;
}
if !please_inline &&
let mut visitor = OneLevelVisitor::new(self.cx.tcx.hir(), res_did) &&
let Some(item) = visitor.find_target(self.cx.tcx, def_id.to_def_id(), path) &&
@ -285,6 +281,10 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
return false;
}
if !self.view_item_stack.insert(res_did) {
return false;
}
let ret = match tcx.hir().get_by_def_id(res_did) {
Node::Item(&hir::Item { kind: hir::ItemKind::Mod(ref m), .. }) if glob => {
let prev = mem::replace(&mut self.inlining, true);

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