Auto merge of #3785 - rust-lang:rustup-2024-08-03, r=RalfJung
Automatic Rustup
This commit is contained in:
commit
47aea6f806
88
Cargo.lock
88
Cargo.lock
@ -771,7 +771,7 @@ dependencies = [
|
||||
"tracing-subscriber",
|
||||
"unified-diff",
|
||||
"walkdir",
|
||||
"windows",
|
||||
"windows 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1713,7 +1713,7 @@ dependencies = [
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"windows-core",
|
||||
"windows-core 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2436,15 +2436,6 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ntapi"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.46.0"
|
||||
@ -3717,7 +3708,7 @@ dependencies = [
|
||||
"thorin-dwp",
|
||||
"tracing",
|
||||
"wasm-encoder 0.210.0",
|
||||
"windows",
|
||||
"windows 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3774,7 +3765,7 @@ dependencies = [
|
||||
"tempfile",
|
||||
"thin-vec",
|
||||
"tracing",
|
||||
"windows",
|
||||
"windows 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3835,7 +3826,7 @@ dependencies = [
|
||||
"shlex",
|
||||
"time",
|
||||
"tracing",
|
||||
"windows",
|
||||
"windows 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3886,7 +3877,7 @@ dependencies = [
|
||||
"termcolor",
|
||||
"termize",
|
||||
"tracing",
|
||||
"windows",
|
||||
"windows 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4355,6 +4346,7 @@ dependencies = [
|
||||
"rustc_span",
|
||||
"rustc_target",
|
||||
"rustc_trait_selection",
|
||||
"rustc_type_ir",
|
||||
"smallvec",
|
||||
"tracing",
|
||||
]
|
||||
@ -4607,7 +4599,7 @@ dependencies = [
|
||||
"smallvec",
|
||||
"termize",
|
||||
"tracing",
|
||||
"windows",
|
||||
"windows 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5365,16 +5357,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.30.12"
|
||||
version = "0.31.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "732ffa00f53e6b2af46208fba5718d9662a421049204e156328b66791ffa15ae"
|
||||
checksum = "d4115055da5f572fff541dd0c4e61b0262977f453cc9fe04be83aba25a89bdab"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"ntapi",
|
||||
"once_cell",
|
||||
"windows",
|
||||
"windows 0.57.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -6299,7 +6288,17 @@ version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-core 0.52.0",
|
||||
"windows-targets 0.52.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143"
|
||||
dependencies = [
|
||||
"windows-core 0.57.0",
|
||||
"windows-targets 0.52.5",
|
||||
]
|
||||
|
||||
@ -6326,12 +6325,55 @@ dependencies = [
|
||||
"windows-targets 0.52.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-result",
|
||||
"windows-targets 0.52.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.67",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.67",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-metadata"
|
||||
version = "0.58.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e837f3c3012cfe9e7086302a93f441a7999439be1ad4c530d55d2f6d2921809"
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
|
@ -453,11 +453,6 @@ impl<'a> AstValidator<'a> {
|
||||
item_span: span,
|
||||
block: Some(self.current_extern_span().shrink_to_lo()),
|
||||
});
|
||||
} else if !self.features.unsafe_extern_blocks {
|
||||
self.dcx().emit_err(errors::InvalidSafetyOnExtern {
|
||||
item_span: span,
|
||||
block: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1054,32 +1049,19 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
errors::VisibilityNotPermittedNote::IndividualForeignItems,
|
||||
);
|
||||
|
||||
if this.features.unsafe_extern_blocks {
|
||||
if &Safety::Default == safety {
|
||||
if item.span.at_least_rust_2024() {
|
||||
this.dcx()
|
||||
.emit_err(errors::MissingUnsafeOnExtern { span: item.span });
|
||||
} else {
|
||||
this.lint_buffer.buffer_lint(
|
||||
MISSING_UNSAFE_ON_EXTERN,
|
||||
item.id,
|
||||
item.span,
|
||||
BuiltinLintDiag::MissingUnsafeOnExtern {
|
||||
suggestion: item.span.shrink_to_lo(),
|
||||
},
|
||||
);
|
||||
}
|
||||
if &Safety::Default == safety {
|
||||
if item.span.at_least_rust_2024() {
|
||||
this.dcx().emit_err(errors::MissingUnsafeOnExtern { span: item.span });
|
||||
} else {
|
||||
this.lint_buffer.buffer_lint(
|
||||
MISSING_UNSAFE_ON_EXTERN,
|
||||
item.id,
|
||||
item.span,
|
||||
BuiltinLintDiag::MissingUnsafeOnExtern {
|
||||
suggestion: item.span.shrink_to_lo(),
|
||||
},
|
||||
);
|
||||
}
|
||||
} else if let &Safety::Unsafe(span) = safety {
|
||||
let mut diag = this
|
||||
.dcx()
|
||||
.create_err(errors::UnsafeItem { span, kind: "extern block" });
|
||||
rustc_session::parse::add_feature_diagnostics(
|
||||
&mut diag,
|
||||
self.session,
|
||||
sym::unsafe_extern_blocks,
|
||||
);
|
||||
diag.emit();
|
||||
}
|
||||
|
||||
if abi.is_none() {
|
||||
|
@ -560,10 +560,6 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
|
||||
gate_all!(precise_capturing, "precise captures on `impl Trait` are experimental");
|
||||
gate_all!(global_registration, "global registration is experimental");
|
||||
gate_all!(unsafe_attributes, "`#[unsafe()]` markers for attributes are experimental");
|
||||
gate_all!(
|
||||
unsafe_extern_blocks,
|
||||
"`unsafe extern {}` blocks and `safe` keyword are experimental"
|
||||
);
|
||||
gate_all!(return_type_notation, "return type notation is experimental");
|
||||
|
||||
if !visitor.features.never_patterns {
|
||||
|
@ -80,7 +80,7 @@ fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span
|
||||
let params = thin_vec![cx.param(span, size, ty_usize.clone()), cx.param(span, align, ty_usize)];
|
||||
let decl = cx.fn_decl(params, never);
|
||||
let header = FnHeader { safety: Safety::Unsafe(span), ..FnHeader::default() };
|
||||
let sig = FnSig { decl, header, span: span };
|
||||
let sig = FnSig { decl, header, span };
|
||||
|
||||
let body = Some(cx.block_expr(call));
|
||||
let kind = ItemKind::Fn(Box::new(Fn {
|
||||
|
@ -202,7 +202,7 @@ impl CfgEval<'_> {
|
||||
}
|
||||
|
||||
// Now that we have our re-parsed `AttrTokenStream`, recursively configuring
|
||||
// our attribute target will correctly the tokens as well.
|
||||
// our attribute target will correctly configure the tokens as well.
|
||||
flat_map_annotatable(self, annotatable)
|
||||
}
|
||||
}
|
||||
|
@ -2065,17 +2065,61 @@ fn add_local_crate_metadata_objects(
|
||||
}
|
||||
|
||||
/// Add sysroot and other globally set directories to the directory search list.
|
||||
fn add_library_search_dirs(cmd: &mut dyn Linker, sess: &Session, self_contained: bool) {
|
||||
// The default library location, we need this to find the runtime.
|
||||
// The location of crates will be determined as needed.
|
||||
let lib_path = sess.target_filesearch(PathKind::All).get_lib_path();
|
||||
cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path));
|
||||
fn add_library_search_dirs(
|
||||
cmd: &mut dyn Linker,
|
||||
sess: &Session,
|
||||
self_contained_components: LinkSelfContainedComponents,
|
||||
apple_sdk_root: Option<&Path>,
|
||||
) {
|
||||
if !sess.opts.unstable_opts.link_native_libraries {
|
||||
return;
|
||||
}
|
||||
|
||||
// Special directory with libraries used only in self-contained linkage mode
|
||||
if self_contained {
|
||||
let lib_path = sess.target_filesearch(PathKind::All).get_self_contained_lib_path();
|
||||
// Library search paths explicitly supplied by user (`-L` on the command line).
|
||||
for search_path in sess.target_filesearch(PathKind::Native).cli_search_paths() {
|
||||
cmd.include_path(&fix_windows_verbatim_for_gcc(&search_path.dir));
|
||||
}
|
||||
for search_path in sess.target_filesearch(PathKind::Framework).cli_search_paths() {
|
||||
// Contrary to the `-L` docs only framework-specific paths are considered here.
|
||||
if search_path.kind != PathKind::All {
|
||||
cmd.framework_path(&search_path.dir);
|
||||
}
|
||||
}
|
||||
|
||||
// The toolchain ships some native library components and self-contained linking was enabled.
|
||||
// Add the self-contained library directory to search paths.
|
||||
if self_contained_components.intersects(
|
||||
LinkSelfContainedComponents::LIBC
|
||||
| LinkSelfContainedComponents::UNWIND
|
||||
| LinkSelfContainedComponents::MINGW,
|
||||
) {
|
||||
let lib_path = sess.target_filesearch(PathKind::Native).get_self_contained_lib_path();
|
||||
cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path));
|
||||
}
|
||||
|
||||
// Toolchains for some targets may ship `libunwind.a`, but place it into the main sysroot
|
||||
// library directory instead of the self-contained directories.
|
||||
// Sanitizer libraries have the same issue and are also linked by name on Apple targets.
|
||||
// The targets here should be in sync with `copy_third_party_objects` in bootstrap.
|
||||
// FIXME: implement `-Clink-self-contained=+/-unwind,+/-sanitizers`, move the shipped libunwind
|
||||
// and sanitizers to self-contained directory, and stop adding this search path.
|
||||
if sess.target.vendor == "fortanix"
|
||||
|| sess.target.os == "linux"
|
||||
|| sess.target.os == "fuchsia"
|
||||
|| sess.target.is_like_osx && !sess.opts.unstable_opts.sanitizer.is_empty()
|
||||
{
|
||||
let lib_path = sess.target_filesearch(PathKind::Native).get_lib_path();
|
||||
cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path));
|
||||
}
|
||||
|
||||
// Mac Catalyst uses the macOS SDK, but to link to iOS-specific frameworks
|
||||
// we must have the support library stubs in the library search path (#121430).
|
||||
if let Some(sdk_root) = apple_sdk_root
|
||||
&& sess.target.llvm_target.contains("macabi")
|
||||
{
|
||||
cmd.include_path(&sdk_root.join("System/iOSSupport/usr/lib"));
|
||||
cmd.framework_path(&sdk_root.join("System/iOSSupport/System/Library/Frameworks"));
|
||||
}
|
||||
}
|
||||
|
||||
/// Add options making relocation sections in the produced ELF files read-only
|
||||
@ -2367,7 +2411,7 @@ fn add_order_independent_options(
|
||||
// Take care of the flavors and CLI options requesting the `lld` linker.
|
||||
add_lld_args(cmd, sess, flavor, self_contained_components);
|
||||
|
||||
add_apple_sdk(cmd, sess, flavor);
|
||||
let apple_sdk_root = add_apple_sdk(cmd, sess, flavor);
|
||||
|
||||
add_link_script(cmd, sess, tmpdir, crate_type);
|
||||
|
||||
@ -2423,7 +2467,7 @@ fn add_order_independent_options(
|
||||
|
||||
cmd.linker_plugin_lto();
|
||||
|
||||
add_library_search_dirs(cmd, sess, self_contained_components.are_any_components_enabled());
|
||||
add_library_search_dirs(cmd, sess, self_contained_components, apple_sdk_root.as_deref());
|
||||
|
||||
cmd.output_filename(out_filename);
|
||||
|
||||
@ -2637,19 +2681,6 @@ fn add_local_native_libraries(
|
||||
tmpdir: &Path,
|
||||
link_output_kind: LinkOutputKind,
|
||||
) {
|
||||
if sess.opts.unstable_opts.link_native_libraries {
|
||||
// User-supplied library search paths (-L on the command line). These are the same paths
|
||||
// used to find Rust crates, so some of them may have been added already by the previous
|
||||
// crate linking code. This only allows them to be found at compile time so it is still
|
||||
// entirely up to outside forces to make sure that library can be found at runtime.
|
||||
for search_path in sess.target_filesearch(PathKind::All).search_paths() {
|
||||
match search_path.kind {
|
||||
PathKind::Framework => cmd.framework_path(&search_path.dir),
|
||||
_ => cmd.include_path(&fix_windows_verbatim_for_gcc(&search_path.dir)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// All static and dynamic native library dependencies are linked to the local crate.
|
||||
let link_static = true;
|
||||
let link_dynamic = true;
|
||||
@ -2944,7 +2975,7 @@ pub(crate) fn are_upstream_rust_objects_already_included(sess: &Session) -> bool
|
||||
}
|
||||
}
|
||||
|
||||
fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
|
||||
fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) -> Option<PathBuf> {
|
||||
let arch = &sess.target.arch;
|
||||
let os = &sess.target.os;
|
||||
let llvm_target = &sess.target.llvm_target;
|
||||
@ -2952,11 +2983,11 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
|
||||
|| !matches!(os.as_ref(), "ios" | "tvos" | "watchos" | "visionos" | "macos")
|
||||
|| !matches!(flavor, LinkerFlavor::Darwin(..))
|
||||
{
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
|
||||
if os == "macos" && !matches!(flavor, LinkerFlavor::Darwin(Cc::No, _)) {
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
|
||||
let sdk_name = match (arch.as_ref(), os.as_ref()) {
|
||||
@ -2980,14 +3011,14 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
|
||||
(_, "macos") => "macosx",
|
||||
_ => {
|
||||
sess.dcx().emit_err(errors::UnsupportedArch { arch, os });
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
};
|
||||
let sdk_root = match get_apple_sdk_root(sdk_name) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
sess.dcx().emit_err(e);
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
@ -3007,16 +3038,7 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
if llvm_target.contains("macabi") {
|
||||
// Mac Catalyst uses the macOS SDK, but to link to iOS-specific
|
||||
// frameworks, we must have the support library stubs in the library
|
||||
// search path.
|
||||
|
||||
// The flags are called `-L` and `-F` both in Clang, ld64 and ldd.
|
||||
let sdk_root = Path::new(&sdk_root);
|
||||
cmd.include_path(&sdk_root.join("System/iOSSupport/usr/lib"));
|
||||
cmd.framework_path(&sdk_root.join("System/iOSSupport/System/Library/Frameworks"));
|
||||
}
|
||||
Some(sdk_root.into())
|
||||
}
|
||||
|
||||
fn get_apple_sdk_root(sdk_name: &str) -> Result<String, errors::AppleSdkRootError<'_>> {
|
||||
|
@ -78,6 +78,7 @@ pub fn from_target_feature(
|
||||
Some(sym::loongarch_target_feature) => rust_features.loongarch_target_feature,
|
||||
Some(sym::lahfsahf_target_feature) => rust_features.lahfsahf_target_feature,
|
||||
Some(sym::prfchw_target_feature) => rust_features.prfchw_target_feature,
|
||||
Some(sym::sha512_sm_x86) => rust_features.sha512_sm_x86,
|
||||
Some(sym::x86_amx_intrinsics) => rust_features.x86_amx_intrinsics,
|
||||
Some(sym::xop_target_feature) => rust_features.xop_target_feature,
|
||||
Some(sym::s390x_target_feature) => rust_features.s390x_target_feature,
|
||||
|
@ -316,9 +316,6 @@ const_eval_range_upper = less or equal to {$hi}
|
||||
const_eval_range_wrapping = less or equal to {$hi}, or greater or equal to {$lo}
|
||||
const_eval_raw_bytes = the raw bytes of the constant (size: {$size}, align: {$align}) {"{"}{$bytes}{"}"}
|
||||
|
||||
const_eval_raw_eq_with_provenance =
|
||||
`raw_eq` on bytes with provenance
|
||||
|
||||
const_eval_raw_ptr_comparison =
|
||||
pointers cannot be reliably compared during const eval
|
||||
.note = see issue #53020 <https://github.com/rust-lang/rust/issues/53020> for more information
|
||||
|
@ -877,7 +877,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
body: &'tcx mir::Body<'tcx>,
|
||||
) -> InterpResult<'tcx> {
|
||||
// Make sure all the constants required by this frame evaluate successfully (post-monomorphization check).
|
||||
for &const_ in &body.required_consts {
|
||||
for &const_ in body.required_consts() {
|
||||
let c =
|
||||
self.instantiate_from_current_frame_and_normalize_erasing_regions(const_.const_)?;
|
||||
c.eval(*self.tcx, self.param_env, const_.span).map_err(|err| {
|
||||
|
@ -690,9 +690,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
// zero-sized access
|
||||
return Ok(&[]);
|
||||
};
|
||||
if alloc_ref.has_provenance() {
|
||||
throw_ub_custom!(fluent::const_eval_raw_eq_with_provenance);
|
||||
}
|
||||
alloc_ref.get_bytes_strip_provenance()
|
||||
};
|
||||
|
||||
|
@ -1767,7 +1767,10 @@ impl HumanEmitter {
|
||||
debug!(?suggestions);
|
||||
|
||||
if suggestions.is_empty() {
|
||||
// Suggestions coming from macros can have malformed spans. This is a heavy handed
|
||||
// Here we check if there are suggestions that have actual code changes. We sometimes
|
||||
// suggest the same code that is already there, instead of changing how we produce the
|
||||
// suggestions and filtering there, we just don't emit the suggestion.
|
||||
// Suggestions coming from macros can also have malformed spans. This is a heavy handed
|
||||
// approach to avoid ICEs by ignoring the suggestion outright.
|
||||
return Ok(());
|
||||
}
|
||||
@ -2046,7 +2049,9 @@ impl HumanEmitter {
|
||||
assert!(underline_start >= 0 && underline_end >= 0);
|
||||
let padding: usize = max_line_num_len + 3;
|
||||
for p in underline_start..underline_end {
|
||||
if let DisplaySuggestion::Underline = show_code_change {
|
||||
if let DisplaySuggestion::Underline = show_code_change
|
||||
&& is_different(sm, &part.snippet, part.span)
|
||||
{
|
||||
// If this is a replacement, underline with `~`, if this is an addition
|
||||
// underline with `+`.
|
||||
buffer.putc(
|
||||
@ -2824,6 +2829,18 @@ impl Style {
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the original and suggested code are the same.
|
||||
pub fn is_different(sm: &SourceMap, suggested: &str, sp: Span) -> bool {
|
||||
let found = match sm.span_to_snippet(sp) {
|
||||
Ok(snippet) => snippet,
|
||||
Err(e) => {
|
||||
warn!(error = ?e, "Invalid span {:?}", sp);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
found != suggested
|
||||
}
|
||||
|
||||
/// Whether the original and suggested code are visually similar enough to warrant extra wording.
|
||||
pub fn is_case_difference(sm: &SourceMap, suggested: &str, sp: Span) -> bool {
|
||||
// FIXME: this should probably be extended to also account for `FO0` → `FOO` and unicode.
|
||||
|
@ -50,7 +50,7 @@ pub use diagnostic_impls::{
|
||||
IndicateAnonymousLifetime, SingleLabelManySpans,
|
||||
};
|
||||
pub use emitter::ColorConfig;
|
||||
use emitter::{is_case_difference, DynEmitter, Emitter};
|
||||
use emitter::{is_case_difference, is_different, DynEmitter, Emitter};
|
||||
use registry::Registry;
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
|
||||
use rustc_data_structures::stable_hasher::{Hash128, StableHasher};
|
||||
@ -357,10 +357,16 @@ impl CodeSuggestion {
|
||||
_ => 1,
|
||||
})
|
||||
.sum();
|
||||
line_highlight.push(SubstitutionHighlight {
|
||||
start: (cur_lo.col.0 as isize + acc) as usize,
|
||||
end: (cur_lo.col.0 as isize + acc + len) as usize,
|
||||
});
|
||||
if !is_different(sm, &part.snippet, part.span) {
|
||||
// Account for cases where we are suggesting the same code that's already
|
||||
// there. This shouldn't happen often, but in some cases for multipart
|
||||
// suggestions it's much easier to handle it here than in the origin.
|
||||
} else {
|
||||
line_highlight.push(SubstitutionHighlight {
|
||||
start: (cur_lo.col.0 as isize + acc) as usize,
|
||||
end: (cur_lo.col.0 as isize + acc + len) as usize,
|
||||
});
|
||||
}
|
||||
buf.push_str(&part.snippet);
|
||||
let cur_hi = sm.lookup_char_pos(part.span.hi());
|
||||
// Account for the difference between the width of the current code and the
|
||||
@ -392,7 +398,11 @@ impl CodeSuggestion {
|
||||
while buf.ends_with('\n') {
|
||||
buf.pop();
|
||||
}
|
||||
Some((buf, substitution.parts, highlights, only_capitalization))
|
||||
if highlights.iter().all(|parts| parts.is_empty()) {
|
||||
None
|
||||
} else {
|
||||
Some((buf, substitution.parts, highlights, only_capitalization))
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
@ -390,6 +390,8 @@ declare_features! (
|
||||
(accepted, unrestricted_attribute_tokens, "1.34.0", Some(55208)),
|
||||
/// The `unsafe_op_in_unsafe_fn` lint (allowed by default): no longer treat an unsafe function as an unsafe block.
|
||||
(accepted, unsafe_block_in_unsafe_fn, "1.52.0", Some(71668)),
|
||||
/// Allows unsafe on extern declarations and safety qualifiers over internal items.
|
||||
(accepted, unsafe_extern_blocks, "CURRENT_RUSTC_VERSION", Some(123743)),
|
||||
/// Allows importing and reexporting macros with `use`,
|
||||
/// enables macro modularization in general.
|
||||
(accepted, use_extern_macros, "1.30.0", Some(35896)),
|
||||
|
@ -703,21 +703,21 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
||||
EncodeCrossCrate::No, allocator_internals, experimental!(needs_allocator),
|
||||
),
|
||||
gated!(
|
||||
panic_runtime, Normal, template!(Word), WarnFollowing,
|
||||
panic_runtime, CrateLevel, template!(Word), WarnFollowing,
|
||||
EncodeCrossCrate::No, experimental!(panic_runtime)
|
||||
),
|
||||
gated!(
|
||||
needs_panic_runtime, Normal, template!(Word), WarnFollowing,
|
||||
needs_panic_runtime, CrateLevel, template!(Word), WarnFollowing,
|
||||
EncodeCrossCrate::No, experimental!(needs_panic_runtime)
|
||||
),
|
||||
gated!(
|
||||
compiler_builtins, Normal, template!(Word), WarnFollowing,
|
||||
compiler_builtins, CrateLevel, template!(Word), WarnFollowing,
|
||||
EncodeCrossCrate::No,
|
||||
"the `#[compiler_builtins]` attribute is used to identify the `compiler_builtins` crate \
|
||||
which contains compiler-rt intrinsics and will never be stable",
|
||||
),
|
||||
gated!(
|
||||
profiler_runtime, Normal, template!(Word), WarnFollowing,
|
||||
profiler_runtime, CrateLevel, template!(Word), WarnFollowing,
|
||||
EncodeCrossCrate::No,
|
||||
"the `#[profiler_runtime]` attribute is used to identify the `profiler_builtins` crate \
|
||||
which contains the profiler runtime and will never be stable",
|
||||
|
@ -591,6 +591,8 @@ declare_features! (
|
||||
(incomplete, return_type_notation, "1.70.0", Some(109417)),
|
||||
/// Allows `extern "rust-cold"`.
|
||||
(unstable, rust_cold_cc, "1.63.0", Some(97544)),
|
||||
/// Allows use of x86 SHA512, SM3 and SM4 target-features and intrinsics
|
||||
(unstable, sha512_sm_x86, "CURRENT_RUSTC_VERSION", Some(126624)),
|
||||
/// Shortern the tail expression lifetime
|
||||
(unstable, shorter_tail_lifetimes, "1.79.0", Some(123739)),
|
||||
/// Allows the use of SIMD types in functions declared in `extern` blocks.
|
||||
@ -629,8 +631,6 @@ declare_features! (
|
||||
(incomplete, unnamed_fields, "1.74.0", Some(49804)),
|
||||
/// Allows unsafe attributes.
|
||||
(unstable, unsafe_attributes, "1.80.0", Some(123757)),
|
||||
/// Allows unsafe on extern declarations and safety qualifiers over internal items.
|
||||
(unstable, unsafe_extern_blocks, "1.80.0", Some(123743)),
|
||||
/// Allows const generic parameters to be defined with types that
|
||||
/// are not `Sized`, e.g. `fn foo<const N: [u8]>() {`.
|
||||
(incomplete, unsized_const_params, "CURRENT_RUSTC_VERSION", Some(95174)),
|
||||
|
@ -1,5 +1,5 @@
|
||||
//! Migration code for the `expr_fragment_specifier_2024`
|
||||
//! rule.
|
||||
//! Migration code for the `expr_fragment_specifier_2024` rule.
|
||||
|
||||
use rustc_ast::token::{Token, TokenKind};
|
||||
use rustc_ast::tokenstream::{TokenStream, TokenTree};
|
||||
use rustc_session::lint::FutureIncompatibilityReason;
|
||||
|
@ -4934,7 +4934,6 @@ declare_lint! {
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(unsafe_extern_blocks)]
|
||||
/// #![warn(missing_unsafe_on_extern)]
|
||||
/// #![allow(dead_code)]
|
||||
///
|
||||
|
@ -383,15 +383,17 @@ pub struct Body<'tcx> {
|
||||
|
||||
/// Constants that are required to evaluate successfully for this MIR to be well-formed.
|
||||
/// We hold in this field all the constants we are not able to evaluate yet.
|
||||
/// `None` indicates that the list has not been computed yet.
|
||||
///
|
||||
/// This is soundness-critical, we make a guarantee that all consts syntactically mentioned in a
|
||||
/// function have successfully evaluated if the function ever gets executed at runtime.
|
||||
pub required_consts: Vec<ConstOperand<'tcx>>,
|
||||
pub required_consts: Option<Vec<ConstOperand<'tcx>>>,
|
||||
|
||||
/// Further items that were mentioned in this function and hence *may* become monomorphized,
|
||||
/// depending on optimizations. We use this to avoid optimization-dependent compile errors: the
|
||||
/// collector recursively traverses all "mentioned" items and evaluates all their
|
||||
/// `required_consts`.
|
||||
/// `None` indicates that the list has not been computed yet.
|
||||
///
|
||||
/// This is *not* soundness-critical and the contents of this list are *not* a stable guarantee.
|
||||
/// All that's relevant is that this set is optimization-level-independent, and that it includes
|
||||
@ -399,7 +401,7 @@ pub struct Body<'tcx> {
|
||||
/// set after drop elaboration, so some drop calls that can never be reached are not considered
|
||||
/// "mentioned".) See the documentation of `CollectionMode` in
|
||||
/// `compiler/rustc_monomorphize/src/collector.rs` for more context.
|
||||
pub mentioned_items: Vec<Spanned<MentionedItem<'tcx>>>,
|
||||
pub mentioned_items: Option<Vec<Spanned<MentionedItem<'tcx>>>>,
|
||||
|
||||
/// Does this body use generic parameters. This is used for the `ConstEvaluatable` check.
|
||||
///
|
||||
@ -477,8 +479,8 @@ impl<'tcx> Body<'tcx> {
|
||||
spread_arg: None,
|
||||
var_debug_info,
|
||||
span,
|
||||
required_consts: Vec::new(),
|
||||
mentioned_items: Vec::new(),
|
||||
required_consts: None,
|
||||
mentioned_items: None,
|
||||
is_polymorphic: false,
|
||||
injection_phase: None,
|
||||
tainted_by_errors,
|
||||
@ -507,8 +509,8 @@ impl<'tcx> Body<'tcx> {
|
||||
arg_count: 0,
|
||||
spread_arg: None,
|
||||
span: DUMMY_SP,
|
||||
required_consts: Vec::new(),
|
||||
mentioned_items: Vec::new(),
|
||||
required_consts: None,
|
||||
mentioned_items: None,
|
||||
var_debug_info: Vec::new(),
|
||||
is_polymorphic: false,
|
||||
injection_phase: None,
|
||||
@ -785,6 +787,40 @@ impl<'tcx> Body<'tcx> {
|
||||
// No inlined `SourceScope`s, or all of them were `#[track_caller]`.
|
||||
caller_location.unwrap_or_else(|| from_span(source_info.span))
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn set_required_consts(&mut self, required_consts: Vec<ConstOperand<'tcx>>) {
|
||||
assert!(
|
||||
self.required_consts.is_none(),
|
||||
"required_consts for {:?} have already been set",
|
||||
self.source.def_id()
|
||||
);
|
||||
self.required_consts = Some(required_consts);
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn required_consts(&self) -> &[ConstOperand<'tcx>] {
|
||||
match &self.required_consts {
|
||||
Some(l) => l,
|
||||
None => panic!("required_consts for {:?} have not yet been set", self.source.def_id()),
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn set_mentioned_items(&mut self, mentioned_items: Vec<Spanned<MentionedItem<'tcx>>>) {
|
||||
assert!(
|
||||
self.mentioned_items.is_none(),
|
||||
"mentioned_items for {:?} have already been set",
|
||||
self.source.def_id()
|
||||
);
|
||||
self.mentioned_items = Some(mentioned_items);
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn mentioned_items(&self) -> &[Spanned<MentionedItem<'tcx>>] {
|
||||
match &self.mentioned_items {
|
||||
Some(l) => l,
|
||||
None => panic!("mentioned_items for {:?} have not yet been set", self.source.def_id()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Index<BasicBlock> for Body<'tcx> {
|
||||
|
@ -1066,9 +1066,11 @@ macro_rules! super_body {
|
||||
|
||||
$self.visit_span($(& $mutability)? $body.span);
|
||||
|
||||
for const_ in &$($mutability)? $body.required_consts {
|
||||
let location = Location::START;
|
||||
$self.visit_const_operand(const_, location);
|
||||
if let Some(required_consts) = &$($mutability)? $body.required_consts {
|
||||
for const_ in required_consts {
|
||||
let location = Location::START;
|
||||
$self.visit_const_operand(const_, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -54,8 +54,8 @@ pub(super) fn build_custom_mir<'tcx>(
|
||||
spread_arg: None,
|
||||
var_debug_info: Vec::new(),
|
||||
span,
|
||||
required_consts: Vec::new(),
|
||||
mentioned_items: Vec::new(),
|
||||
required_consts: None,
|
||||
mentioned_items: None,
|
||||
is_polymorphic: false,
|
||||
tainted_by_errors: None,
|
||||
injection_phase: None,
|
||||
|
@ -25,6 +25,7 @@ rustc_session = { path = "../rustc_session" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
rustc_target = { path = "../rustc_target" }
|
||||
rustc_trait_selection = { path = "../rustc_trait_selection" }
|
||||
rustc_type_ir = { path = "../rustc_type_ir" }
|
||||
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
|
||||
tracing = "0.1"
|
||||
# tidy-alphabetical-end
|
||||
|
@ -744,8 +744,8 @@ impl<'tcx> Inliner<'tcx> {
|
||||
// Copy required constants from the callee_body into the caller_body. Although we are only
|
||||
// pushing unevaluated consts to `required_consts`, here they may have been evaluated
|
||||
// because we are calling `instantiate_and_normalize_erasing_regions` -- so we filter again.
|
||||
caller_body.required_consts.extend(
|
||||
callee_body.required_consts.into_iter().filter(|ct| ct.const_.is_required_const()),
|
||||
caller_body.required_consts.as_mut().unwrap().extend(
|
||||
callee_body.required_consts().into_iter().filter(|ct| ct.const_.is_required_const()),
|
||||
);
|
||||
// Now that we incorporated the callee's `required_consts`, we can remove the callee from
|
||||
// `mentioned_items` -- but we have to take their `mentioned_items` in return. This does
|
||||
@ -755,12 +755,11 @@ impl<'tcx> Inliner<'tcx> {
|
||||
// We need to reconstruct the `required_item` for the callee so that we can find and
|
||||
// remove it.
|
||||
let callee_item = MentionedItem::Fn(func.ty(caller_body, self.tcx));
|
||||
if let Some(idx) =
|
||||
caller_body.mentioned_items.iter().position(|item| item.node == callee_item)
|
||||
{
|
||||
let caller_mentioned_items = caller_body.mentioned_items.as_mut().unwrap();
|
||||
if let Some(idx) = caller_mentioned_items.iter().position(|item| item.node == callee_item) {
|
||||
// We found the callee, so remove it and add its items instead.
|
||||
caller_body.mentioned_items.remove(idx);
|
||||
caller_body.mentioned_items.extend(callee_body.mentioned_items);
|
||||
caller_mentioned_items.remove(idx);
|
||||
caller_mentioned_items.extend(callee_body.mentioned_items());
|
||||
} else {
|
||||
// If we can't find the callee, there's no point in adding its items. Probably it
|
||||
// already got removed by being inlined elsewhere in the same function, so we already
|
||||
|
@ -28,11 +28,10 @@ use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::mir::visit::Visitor as _;
|
||||
use rustc_middle::mir::{
|
||||
traversal, AnalysisPhase, Body, CallSource, ClearCrossCrate, ConstOperand, ConstQualifs,
|
||||
LocalDecl, MirPass, MirPhase, Operand, Place, ProjectionElem, Promoted, RuntimePhase, Rvalue,
|
||||
SourceInfo, Statement, StatementKind, TerminatorKind, START_BLOCK,
|
||||
AnalysisPhase, Body, CallSource, ClearCrossCrate, ConstOperand, ConstQualifs, LocalDecl,
|
||||
MirPass, MirPhase, Operand, Place, ProjectionElem, Promoted, RuntimePhase, Rvalue, SourceInfo,
|
||||
Statement, StatementKind, TerminatorKind, START_BLOCK,
|
||||
};
|
||||
use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
|
||||
use rustc_middle::util::Providers;
|
||||
@ -339,12 +338,15 @@ fn mir_promoted(
|
||||
|
||||
// Collect `required_consts` *before* promotion, so if there are any consts being promoted
|
||||
// we still add them to the list in the outer MIR body.
|
||||
let mut required_consts = Vec::new();
|
||||
let mut required_consts_visitor = RequiredConstsVisitor::new(&mut required_consts);
|
||||
for (bb, bb_data) in traversal::reverse_postorder(&body) {
|
||||
required_consts_visitor.visit_basic_block_data(bb, bb_data);
|
||||
RequiredConstsVisitor::compute_required_consts(&mut body);
|
||||
// If this has an associated by-move async closure body, that doesn't get run through these
|
||||
// passes itself, it gets "tagged along" by the pass manager. `RequiredConstsVisitor` is not
|
||||
// a regular pass so we have to also apply it manually to the other body.
|
||||
if let Some(coroutine) = body.coroutine.as_mut() {
|
||||
if let Some(by_move_body) = coroutine.by_move_body.as_mut() {
|
||||
RequiredConstsVisitor::compute_required_consts(by_move_body);
|
||||
}
|
||||
}
|
||||
body.required_consts = required_consts;
|
||||
|
||||
// What we need to run borrowck etc.
|
||||
let promote_pass = promote_consts::PromoteTemps::default();
|
||||
@ -561,9 +563,6 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||
tcx,
|
||||
body,
|
||||
&[
|
||||
// Before doing anything, remember which items are being mentioned so that the set of items
|
||||
// visited does not depend on the optimization level.
|
||||
&mentioned_items::MentionedItems,
|
||||
// Add some UB checks before any UB gets optimized away.
|
||||
&check_alignment::CheckAlignment,
|
||||
// Before inlining: trim down MIR with passes to reduce inlining work.
|
||||
@ -657,6 +656,19 @@ fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> {
|
||||
return body;
|
||||
}
|
||||
|
||||
// Before doing anything, remember which items are being mentioned so that the set of items
|
||||
// visited does not depend on the optimization level.
|
||||
// We do not use `run_passes` for this as that might skip the pass if `injection_phase` is set.
|
||||
mentioned_items::MentionedItems.run_pass(tcx, &mut body);
|
||||
// If this has an associated by-move async closure body, that doesn't get run through these
|
||||
// passes itself, it gets "tagged along" by the pass manager. Since we're not using the pass
|
||||
// manager we have to do this by hand.
|
||||
if let Some(coroutine) = body.coroutine.as_mut() {
|
||||
if let Some(by_move_body) = coroutine.by_move_body.as_mut() {
|
||||
mentioned_items::MentionedItems.run_pass(tcx, by_move_body);
|
||||
}
|
||||
}
|
||||
|
||||
// If `mir_drops_elaborated_and_const_checked` found that the current body has unsatisfiable
|
||||
// predicates, it will shrink the MIR to a single `unreachable` terminator.
|
||||
// More generally, if MIR is a lone `unreachable`, there is nothing to optimize.
|
||||
|
@ -3,8 +3,10 @@ use std::iter;
|
||||
use rustc_index::IndexSlice;
|
||||
use rustc_middle::mir::patch::MirPatch;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::layout::{IntegerExt, TyAndLayout};
|
||||
use rustc_middle::ty::{ParamEnv, ScalarInt, Ty, TyCtxt};
|
||||
use rustc_target::abi::Size;
|
||||
use rustc_target::abi::Integer;
|
||||
use rustc_type_ir::TyKind::*;
|
||||
|
||||
use super::simplify::simplify_cfg;
|
||||
|
||||
@ -42,10 +44,7 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
|
||||
should_cleanup = true;
|
||||
continue;
|
||||
}
|
||||
// unsound: https://github.com/rust-lang/rust/issues/124150
|
||||
if tcx.sess.opts.unstable_opts.unsound_mir_opts
|
||||
&& SimplifyToExp::default().simplify(tcx, body, bb_idx, param_env).is_some()
|
||||
{
|
||||
if SimplifyToExp::default().simplify(tcx, body, bb_idx, param_env).is_some() {
|
||||
should_cleanup = true;
|
||||
continue;
|
||||
}
|
||||
@ -264,33 +263,56 @@ impl<'tcx> SimplifyMatch<'tcx> for SimplifyToIf {
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if the cast constant using `IntToInt` is equal to the target constant.
|
||||
fn can_cast(
|
||||
tcx: TyCtxt<'_>,
|
||||
src_val: impl Into<u128>,
|
||||
src_layout: TyAndLayout<'_>,
|
||||
cast_ty: Ty<'_>,
|
||||
target_scalar: ScalarInt,
|
||||
) -> bool {
|
||||
let from_scalar = ScalarInt::try_from_uint(src_val.into(), src_layout.size).unwrap();
|
||||
let v = match src_layout.ty.kind() {
|
||||
Uint(_) => from_scalar.to_uint(src_layout.size),
|
||||
Int(_) => from_scalar.to_int(src_layout.size) as u128,
|
||||
_ => unreachable!("invalid int"),
|
||||
};
|
||||
let size = match *cast_ty.kind() {
|
||||
Int(t) => Integer::from_int_ty(&tcx, t).size(),
|
||||
Uint(t) => Integer::from_uint_ty(&tcx, t).size(),
|
||||
_ => unreachable!("invalid int"),
|
||||
};
|
||||
let v = size.truncate(v);
|
||||
let cast_scalar = ScalarInt::try_from_uint(v, size).unwrap();
|
||||
cast_scalar == target_scalar
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct SimplifyToExp {
|
||||
transfrom_types: Vec<TransfromType>,
|
||||
transfrom_kinds: Vec<TransfromKind>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum CompareType<'tcx, 'a> {
|
||||
enum ExpectedTransformKind<'tcx, 'a> {
|
||||
/// Identical statements.
|
||||
Same(&'a StatementKind<'tcx>),
|
||||
/// Assignment statements have the same value.
|
||||
Eq(&'a Place<'tcx>, Ty<'tcx>, ScalarInt),
|
||||
SameByEq { place: &'a Place<'tcx>, ty: Ty<'tcx>, scalar: ScalarInt },
|
||||
/// Enum variant comparison type.
|
||||
Discr { place: &'a Place<'tcx>, ty: Ty<'tcx>, is_signed: bool },
|
||||
Cast { place: &'a Place<'tcx>, ty: Ty<'tcx> },
|
||||
}
|
||||
|
||||
enum TransfromType {
|
||||
enum TransfromKind {
|
||||
Same,
|
||||
Eq,
|
||||
Discr,
|
||||
Cast,
|
||||
}
|
||||
|
||||
impl From<CompareType<'_, '_>> for TransfromType {
|
||||
fn from(compare_type: CompareType<'_, '_>) -> Self {
|
||||
impl From<ExpectedTransformKind<'_, '_>> for TransfromKind {
|
||||
fn from(compare_type: ExpectedTransformKind<'_, '_>) -> Self {
|
||||
match compare_type {
|
||||
CompareType::Same(_) => TransfromType::Same,
|
||||
CompareType::Eq(_, _, _) => TransfromType::Eq,
|
||||
CompareType::Discr { .. } => TransfromType::Discr,
|
||||
ExpectedTransformKind::Same(_) => TransfromKind::Same,
|
||||
ExpectedTransformKind::SameByEq { .. } => TransfromKind::Same,
|
||||
ExpectedTransformKind::Cast { .. } => TransfromKind::Cast,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -354,7 +376,7 @@ impl<'tcx> SimplifyMatch<'tcx> for SimplifyToExp {
|
||||
return None;
|
||||
}
|
||||
let mut target_iter = targets.iter();
|
||||
let (first_val, first_target) = target_iter.next().unwrap();
|
||||
let (first_case_val, first_target) = target_iter.next().unwrap();
|
||||
let first_terminator_kind = &bbs[first_target].terminator().kind;
|
||||
// Check that destinations are identical, and if not, then don't optimize this block
|
||||
if !targets
|
||||
@ -364,24 +386,20 @@ impl<'tcx> SimplifyMatch<'tcx> for SimplifyToExp {
|
||||
return None;
|
||||
}
|
||||
|
||||
let discr_size = tcx.layout_of(param_env.and(discr_ty)).unwrap().size;
|
||||
let discr_layout = tcx.layout_of(param_env.and(discr_ty)).unwrap();
|
||||
let first_stmts = &bbs[first_target].statements;
|
||||
let (second_val, second_target) = target_iter.next().unwrap();
|
||||
let (second_case_val, second_target) = target_iter.next().unwrap();
|
||||
let second_stmts = &bbs[second_target].statements;
|
||||
if first_stmts.len() != second_stmts.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
fn int_equal(l: ScalarInt, r: impl Into<u128>, size: Size) -> bool {
|
||||
l.to_bits_unchecked() == ScalarInt::try_from_uint(r, size).unwrap().to_bits_unchecked()
|
||||
}
|
||||
|
||||
// We first compare the two branches, and then the other branches need to fulfill the same conditions.
|
||||
let mut compare_types = Vec::new();
|
||||
let mut expected_transform_kinds = Vec::new();
|
||||
for (f, s) in iter::zip(first_stmts, second_stmts) {
|
||||
let compare_type = match (&f.kind, &s.kind) {
|
||||
// If two statements are exactly the same, we can optimize.
|
||||
(f_s, s_s) if f_s == s_s => CompareType::Same(f_s),
|
||||
(f_s, s_s) if f_s == s_s => ExpectedTransformKind::Same(f_s),
|
||||
|
||||
// If two statements are assignments with the match values to the same place, we can optimize.
|
||||
(
|
||||
@ -395,22 +413,29 @@ impl<'tcx> SimplifyMatch<'tcx> for SimplifyToExp {
|
||||
f_c.const_.try_eval_scalar_int(tcx, param_env),
|
||||
s_c.const_.try_eval_scalar_int(tcx, param_env),
|
||||
) {
|
||||
(Some(f), Some(s)) if f == s => CompareType::Eq(lhs_f, f_c.const_.ty(), f),
|
||||
// Enum variants can also be simplified to an assignment statement if their values are equal.
|
||||
// We need to consider both unsigned and signed scenarios here.
|
||||
(Some(f), Some(s)) if f == s => ExpectedTransformKind::SameByEq {
|
||||
place: lhs_f,
|
||||
ty: f_c.const_.ty(),
|
||||
scalar: f,
|
||||
},
|
||||
// Enum variants can also be simplified to an assignment statement,
|
||||
// if we can use `IntToInt` cast to get an equal value.
|
||||
(Some(f), Some(s))
|
||||
if ((f_c.const_.ty().is_signed() || discr_ty.is_signed())
|
||||
&& int_equal(f, first_val, discr_size)
|
||||
&& int_equal(s, second_val, discr_size))
|
||||
|| (Some(f) == ScalarInt::try_from_uint(first_val, f.size())
|
||||
&& Some(s)
|
||||
== ScalarInt::try_from_uint(second_val, s.size())) =>
|
||||
if (can_cast(
|
||||
tcx,
|
||||
first_case_val,
|
||||
discr_layout,
|
||||
f_c.const_.ty(),
|
||||
f,
|
||||
) && can_cast(
|
||||
tcx,
|
||||
second_case_val,
|
||||
discr_layout,
|
||||
f_c.const_.ty(),
|
||||
s,
|
||||
)) =>
|
||||
{
|
||||
CompareType::Discr {
|
||||
place: lhs_f,
|
||||
ty: f_c.const_.ty(),
|
||||
is_signed: f_c.const_.ty().is_signed() || discr_ty.is_signed(),
|
||||
}
|
||||
ExpectedTransformKind::Cast { place: lhs_f, ty: f_c.const_.ty() }
|
||||
}
|
||||
_ => {
|
||||
return None;
|
||||
@ -421,47 +446,36 @@ impl<'tcx> SimplifyMatch<'tcx> for SimplifyToExp {
|
||||
// Otherwise we cannot optimize. Try another block.
|
||||
_ => return None,
|
||||
};
|
||||
compare_types.push(compare_type);
|
||||
expected_transform_kinds.push(compare_type);
|
||||
}
|
||||
|
||||
// All remaining BBs need to fulfill the same pattern as the two BBs from the previous step.
|
||||
for (other_val, other_target) in target_iter {
|
||||
let other_stmts = &bbs[other_target].statements;
|
||||
if compare_types.len() != other_stmts.len() {
|
||||
if expected_transform_kinds.len() != other_stmts.len() {
|
||||
return None;
|
||||
}
|
||||
for (f, s) in iter::zip(&compare_types, other_stmts) {
|
||||
for (f, s) in iter::zip(&expected_transform_kinds, other_stmts) {
|
||||
match (*f, &s.kind) {
|
||||
(CompareType::Same(f_s), s_s) if f_s == s_s => {}
|
||||
(ExpectedTransformKind::Same(f_s), s_s) if f_s == s_s => {}
|
||||
(
|
||||
CompareType::Eq(lhs_f, f_ty, val),
|
||||
ExpectedTransformKind::SameByEq { place: lhs_f, ty: f_ty, scalar },
|
||||
StatementKind::Assign(box (lhs_s, Rvalue::Use(Operand::Constant(s_c)))),
|
||||
) if lhs_f == lhs_s
|
||||
&& s_c.const_.ty() == f_ty
|
||||
&& s_c.const_.try_eval_scalar_int(tcx, param_env) == Some(val) => {}
|
||||
&& s_c.const_.try_eval_scalar_int(tcx, param_env) == Some(scalar) => {}
|
||||
(
|
||||
CompareType::Discr { place: lhs_f, ty: f_ty, is_signed },
|
||||
ExpectedTransformKind::Cast { place: lhs_f, ty: f_ty },
|
||||
StatementKind::Assign(box (lhs_s, Rvalue::Use(Operand::Constant(s_c)))),
|
||||
) if lhs_f == lhs_s && s_c.const_.ty() == f_ty => {
|
||||
let Some(f) = s_c.const_.try_eval_scalar_int(tcx, param_env) else {
|
||||
return None;
|
||||
};
|
||||
if is_signed
|
||||
&& s_c.const_.ty().is_signed()
|
||||
&& int_equal(f, other_val, discr_size)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if Some(f) == ScalarInt::try_from_uint(other_val, f.size()) {
|
||||
continue;
|
||||
}
|
||||
return None;
|
||||
}
|
||||
) if let Some(f) = s_c.const_.try_eval_scalar_int(tcx, param_env)
|
||||
&& lhs_f == lhs_s
|
||||
&& s_c.const_.ty() == f_ty
|
||||
&& can_cast(tcx, other_val, discr_layout, f_ty, f) => {}
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
}
|
||||
self.transfrom_types = compare_types.into_iter().map(|c| c.into()).collect();
|
||||
self.transfrom_kinds = expected_transform_kinds.into_iter().map(|c| c.into()).collect();
|
||||
Some(())
|
||||
}
|
||||
|
||||
@ -479,13 +493,13 @@ impl<'tcx> SimplifyMatch<'tcx> for SimplifyToExp {
|
||||
let (_, first) = targets.iter().next().unwrap();
|
||||
let first = &bbs[first];
|
||||
|
||||
for (t, s) in iter::zip(&self.transfrom_types, &first.statements) {
|
||||
for (t, s) in iter::zip(&self.transfrom_kinds, &first.statements) {
|
||||
match (t, &s.kind) {
|
||||
(TransfromType::Same, _) | (TransfromType::Eq, _) => {
|
||||
(TransfromKind::Same, _) => {
|
||||
patch.add_statement(parent_end, s.kind.clone());
|
||||
}
|
||||
(
|
||||
TransfromType::Discr,
|
||||
TransfromKind::Cast,
|
||||
StatementKind::Assign(box (lhs, Rvalue::Use(Operand::Constant(f_c)))),
|
||||
) => {
|
||||
let operand = Operand::Copy(Place::from(discr_local));
|
||||
|
@ -23,10 +23,9 @@ impl<'tcx> MirPass<'tcx> for MentionedItems {
|
||||
}
|
||||
|
||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut mir::Body<'tcx>) {
|
||||
debug_assert!(body.mentioned_items.is_empty());
|
||||
let mut mentioned_items = Vec::new();
|
||||
MentionedItemsVisitor { tcx, body, mentioned_items: &mut mentioned_items }.visit_body(body);
|
||||
body.mentioned_items = mentioned_items;
|
||||
body.set_mentioned_items(mentioned_items);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -702,6 +702,9 @@ struct Promoter<'a, 'tcx> {
|
||||
temps: &'a mut IndexVec<Local, TempState>,
|
||||
extra_statements: &'a mut Vec<(Location, Statement<'tcx>)>,
|
||||
|
||||
/// Used to assemble the required_consts list while building the promoted.
|
||||
required_consts: Vec<ConstOperand<'tcx>>,
|
||||
|
||||
/// If true, all nested temps are also kept in the
|
||||
/// source MIR, not moved to the promoted MIR.
|
||||
keep_original: bool,
|
||||
@ -924,11 +927,14 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
|
||||
let span = self.promoted.span;
|
||||
self.assign(RETURN_PLACE, rvalue, span);
|
||||
|
||||
// Now that we did promotion, we know whether we'll want to add this to `required_consts`.
|
||||
// Now that we did promotion, we know whether we'll want to add this to `required_consts` of
|
||||
// the surrounding MIR body.
|
||||
if self.add_to_required {
|
||||
self.source.required_consts.push(promoted_op);
|
||||
self.source.required_consts.as_mut().unwrap().push(promoted_op);
|
||||
}
|
||||
|
||||
self.promoted.set_required_consts(self.required_consts);
|
||||
|
||||
self.promoted
|
||||
}
|
||||
}
|
||||
@ -947,7 +953,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> {
|
||||
|
||||
fn visit_const_operand(&mut self, constant: &mut ConstOperand<'tcx>, _location: Location) {
|
||||
if constant.const_.is_required_const() {
|
||||
self.promoted.required_consts.push(*constant);
|
||||
self.required_consts.push(*constant);
|
||||
}
|
||||
|
||||
// Skipping `super_constant` as the visitor is otherwise only looking for locals.
|
||||
@ -1011,9 +1017,9 @@ fn promote_candidates<'tcx>(
|
||||
extra_statements: &mut extra_statements,
|
||||
keep_original: false,
|
||||
add_to_required: false,
|
||||
required_consts: Vec::new(),
|
||||
};
|
||||
|
||||
// `required_consts` of the promoted itself gets filled while building the MIR body.
|
||||
let mut promoted = promoter.promote_candidate(candidate, promotions.len());
|
||||
promoted.source.promoted = Some(promotions.next_index());
|
||||
promotions.push(promoted);
|
||||
|
@ -1,14 +1,23 @@
|
||||
use rustc_middle::mir::visit::Visitor;
|
||||
use rustc_middle::mir::{ConstOperand, Location};
|
||||
use rustc_middle::mir::{traversal, Body, ConstOperand, Location};
|
||||
|
||||
pub struct RequiredConstsVisitor<'a, 'tcx> {
|
||||
required_consts: &'a mut Vec<ConstOperand<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> RequiredConstsVisitor<'a, 'tcx> {
|
||||
pub fn new(required_consts: &'a mut Vec<ConstOperand<'tcx>>) -> Self {
|
||||
fn new(required_consts: &'a mut Vec<ConstOperand<'tcx>>) -> Self {
|
||||
RequiredConstsVisitor { required_consts }
|
||||
}
|
||||
|
||||
pub fn compute_required_consts(body: &mut Body<'tcx>) {
|
||||
let mut required_consts = Vec::new();
|
||||
let mut required_consts_visitor = RequiredConstsVisitor::new(&mut required_consts);
|
||||
for (bb, bb_data) in traversal::reverse_postorder(&body) {
|
||||
required_consts_visitor.visit_basic_block_data(bb, bb_data);
|
||||
}
|
||||
body.set_required_consts(required_consts);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for RequiredConstsVisitor<'_, 'tcx> {
|
||||
|
@ -305,7 +305,7 @@ fn new_body<'tcx>(
|
||||
arg_count: usize,
|
||||
span: Span,
|
||||
) -> Body<'tcx> {
|
||||
Body::new(
|
||||
let mut body = Body::new(
|
||||
source,
|
||||
basic_blocks,
|
||||
IndexVec::from_elem_n(
|
||||
@ -326,7 +326,10 @@ fn new_body<'tcx>(
|
||||
None,
|
||||
// FIXME(compiler-errors): is this correct?
|
||||
None,
|
||||
)
|
||||
);
|
||||
// Shims do not directly mention any consts.
|
||||
body.set_required_consts(Vec::new());
|
||||
body
|
||||
}
|
||||
|
||||
pub struct DropShimElaborator<'a, 'tcx> {
|
||||
@ -969,13 +972,16 @@ pub fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> Body<'_> {
|
||||
};
|
||||
|
||||
let source = MirSource::item(ctor_id);
|
||||
let body = new_body(
|
||||
let mut body = new_body(
|
||||
source,
|
||||
IndexVec::from_elem_n(start_block, 1),
|
||||
local_decls,
|
||||
sig.inputs().len(),
|
||||
span,
|
||||
);
|
||||
// A constructor doesn't mention any other items (and we don't run the usual optimization passes
|
||||
// so this would otherwise not get filled).
|
||||
body.set_mentioned_items(Vec::new());
|
||||
|
||||
crate::pass_manager::dump_mir_for_phase_change(tcx, &body);
|
||||
|
||||
|
@ -1229,7 +1229,7 @@ fn collect_items_of_instance<'tcx>(
|
||||
|
||||
// Always visit all `required_consts`, so that we evaluate them and abort compilation if any of
|
||||
// them errors.
|
||||
for const_op in &body.required_consts {
|
||||
for const_op in body.required_consts() {
|
||||
if let Some(val) = collector.eval_constant(const_op) {
|
||||
collect_const_value(tcx, val, mentioned_items);
|
||||
}
|
||||
@ -1237,7 +1237,7 @@ fn collect_items_of_instance<'tcx>(
|
||||
|
||||
// Always gather mentioned items. We try to avoid processing items that we have already added to
|
||||
// `used_items` above.
|
||||
for item in &body.mentioned_items {
|
||||
for item in body.mentioned_items() {
|
||||
if !collector.used_mentioned_items.contains(&item.node) {
|
||||
let item_mono = collector.monomorphize(item.node);
|
||||
visit_mentioned_item(tcx, &item_mono, item.span, mentioned_items);
|
||||
|
@ -8,7 +8,7 @@ use rustc_span::{sym, BytePos, Span};
|
||||
use thin_vec::ThinVec;
|
||||
use tracing::debug;
|
||||
|
||||
use super::{AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, PathStyle};
|
||||
use super::{AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, ParserRange, PathStyle};
|
||||
use crate::{errors, fluent_generated as fluent, maybe_whole};
|
||||
|
||||
// Public for rustfmt usage
|
||||
@ -313,8 +313,8 @@ impl<'a> Parser<'a> {
|
||||
// inner attribute, for possible later processing in a `LazyAttrTokenStream`.
|
||||
if let Capturing::Yes = self.capture_state.capturing {
|
||||
let end_pos = self.num_bump_calls;
|
||||
let range = start_pos..end_pos;
|
||||
self.capture_state.inner_attr_ranges.insert(attr.id, range);
|
||||
let parser_range = ParserRange(start_pos..end_pos);
|
||||
self.capture_state.inner_attr_parser_ranges.insert(attr.id, parser_range);
|
||||
}
|
||||
attrs.push(attr);
|
||||
} else {
|
||||
|
@ -10,7 +10,10 @@ use rustc_errors::PResult;
|
||||
use rustc_session::parse::ParseSess;
|
||||
use rustc_span::{sym, Span, DUMMY_SP};
|
||||
|
||||
use super::{Capturing, FlatToken, ForceCollect, Parser, ReplaceRange, TokenCursor};
|
||||
use super::{
|
||||
Capturing, FlatToken, ForceCollect, NodeRange, NodeReplacement, Parser, ParserRange,
|
||||
TokenCursor,
|
||||
};
|
||||
|
||||
/// A wrapper type to ensure that the parser handles outer attributes correctly.
|
||||
/// When we parse outer attributes, we need to ensure that we capture tokens
|
||||
@ -28,8 +31,8 @@ use super::{Capturing, FlatToken, ForceCollect, Parser, ReplaceRange, TokenCurso
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AttrWrapper {
|
||||
attrs: AttrVec,
|
||||
// The start of the outer attributes in the token cursor.
|
||||
// This allows us to create a `ReplaceRange` for the entire attribute
|
||||
// The start of the outer attributes in the parser's token stream.
|
||||
// This lets us create a `NodeReplacement` for the entire attribute
|
||||
// target, including outer attributes.
|
||||
start_pos: u32,
|
||||
}
|
||||
@ -53,10 +56,9 @@ impl AttrWrapper {
|
||||
|
||||
/// Prepend `self.attrs` to `attrs`.
|
||||
// FIXME: require passing an NT to prevent misuse of this method
|
||||
pub(crate) fn prepend_to_nt_inner(self, attrs: &mut AttrVec) {
|
||||
let mut self_attrs = self.attrs;
|
||||
mem::swap(attrs, &mut self_attrs);
|
||||
attrs.extend(self_attrs);
|
||||
pub(crate) fn prepend_to_nt_inner(mut self, attrs: &mut AttrVec) {
|
||||
mem::swap(attrs, &mut self.attrs);
|
||||
attrs.extend(self.attrs);
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
@ -89,7 +91,7 @@ struct LazyAttrTokenStreamImpl {
|
||||
cursor_snapshot: TokenCursor,
|
||||
num_calls: u32,
|
||||
break_last_token: bool,
|
||||
replace_ranges: Box<[ReplaceRange]>,
|
||||
node_replacements: Box<[NodeReplacement]>,
|
||||
}
|
||||
|
||||
impl ToAttrTokenStream for LazyAttrTokenStreamImpl {
|
||||
@ -104,21 +106,24 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl {
|
||||
.chain(iter::repeat_with(|| FlatToken::Token(cursor_snapshot.next())))
|
||||
.take(self.num_calls as usize);
|
||||
|
||||
if self.replace_ranges.is_empty() {
|
||||
if self.node_replacements.is_empty() {
|
||||
make_attr_token_stream(tokens, self.break_last_token)
|
||||
} else {
|
||||
let mut tokens: Vec<_> = tokens.collect();
|
||||
let mut replace_ranges = self.replace_ranges.to_vec();
|
||||
replace_ranges.sort_by_key(|(range, _)| range.start);
|
||||
let mut node_replacements = self.node_replacements.to_vec();
|
||||
node_replacements.sort_by_key(|(range, _)| range.0.start);
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
for [(range, tokens), (next_range, next_tokens)] in replace_ranges.array_windows() {
|
||||
for [(node_range, tokens), (next_node_range, next_tokens)] in
|
||||
node_replacements.array_windows()
|
||||
{
|
||||
assert!(
|
||||
range.end <= next_range.start || range.end >= next_range.end,
|
||||
"Replace ranges should either be disjoint or nested: ({:?}, {:?}) ({:?}, {:?})",
|
||||
range,
|
||||
node_range.0.end <= next_node_range.0.start
|
||||
|| node_range.0.end >= next_node_range.0.end,
|
||||
"Node ranges should be disjoint or nested: ({:?}, {:?}) ({:?}, {:?})",
|
||||
node_range,
|
||||
tokens,
|
||||
next_range,
|
||||
next_node_range,
|
||||
next_tokens,
|
||||
);
|
||||
}
|
||||
@ -136,20 +141,23 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl {
|
||||
// start position, we ensure that any (outer) replace range which
|
||||
// encloses another (inner) replace range will fully overwrite the
|
||||
// inner range's replacement.
|
||||
for (range, target) in replace_ranges.into_iter().rev() {
|
||||
assert!(!range.is_empty(), "Cannot replace an empty range: {range:?}");
|
||||
for (node_range, target) in node_replacements.into_iter().rev() {
|
||||
assert!(
|
||||
!node_range.0.is_empty(),
|
||||
"Cannot replace an empty node range: {:?}",
|
||||
node_range.0
|
||||
);
|
||||
|
||||
// Replace the tokens in range with zero or one `FlatToken::AttrsTarget`s, plus
|
||||
// enough `FlatToken::Empty`s to fill up the rest of the range. This keeps the
|
||||
// total length of `tokens` constant throughout the replacement process, allowing
|
||||
// us to use all of the `ReplaceRanges` entries without adjusting indices.
|
||||
// us to do all replacements without adjusting indices.
|
||||
let target_len = target.is_some() as usize;
|
||||
tokens.splice(
|
||||
(range.start as usize)..(range.end as usize),
|
||||
target
|
||||
.into_iter()
|
||||
.map(|target| FlatToken::AttrsTarget(target))
|
||||
.chain(iter::repeat(FlatToken::Empty).take(range.len() - target_len)),
|
||||
(node_range.0.start as usize)..(node_range.0.end as usize),
|
||||
target.into_iter().map(|target| FlatToken::AttrsTarget(target)).chain(
|
||||
iter::repeat(FlatToken::Empty).take(node_range.0.len() - target_len),
|
||||
),
|
||||
);
|
||||
}
|
||||
make_attr_token_stream(tokens.into_iter(), self.break_last_token)
|
||||
@ -216,7 +224,7 @@ impl<'a> Parser<'a> {
|
||||
let cursor_snapshot = self.token_cursor.clone();
|
||||
let start_pos = self.num_bump_calls;
|
||||
let has_outer_attrs = !attrs.attrs.is_empty();
|
||||
let replace_ranges_start = self.capture_state.replace_ranges.len();
|
||||
let parser_replacements_start = self.capture_state.parser_replacements.len();
|
||||
|
||||
// We set and restore `Capturing::Yes` on either side of the call to
|
||||
// `f`, so we can distinguish the outermost call to
|
||||
@ -271,7 +279,7 @@ impl<'a> Parser<'a> {
|
||||
return Ok(ret);
|
||||
}
|
||||
|
||||
let replace_ranges_end = self.capture_state.replace_ranges.len();
|
||||
let parser_replacements_end = self.capture_state.parser_replacements.len();
|
||||
|
||||
assert!(
|
||||
!(self.break_last_token && capture_trailing),
|
||||
@ -288,15 +296,16 @@ impl<'a> Parser<'a> {
|
||||
|
||||
let num_calls = end_pos - start_pos;
|
||||
|
||||
// Take the captured ranges for any inner attributes that we parsed in
|
||||
// `Parser::parse_inner_attributes`, and pair them in a `ReplaceRange`
|
||||
// with `None`, which means the relevant tokens will be removed. (More
|
||||
// details below.)
|
||||
let mut inner_attr_replace_ranges = Vec::new();
|
||||
// Take the captured `ParserRange`s for any inner attributes that we parsed in
|
||||
// `Parser::parse_inner_attributes`, and pair them in a `ParserReplacement` with `None`,
|
||||
// which means the relevant tokens will be removed. (More details below.)
|
||||
let mut inner_attr_parser_replacements = Vec::new();
|
||||
for attr in ret.attrs() {
|
||||
if attr.style == ast::AttrStyle::Inner {
|
||||
if let Some(attr_range) = self.capture_state.inner_attr_ranges.remove(&attr.id) {
|
||||
inner_attr_replace_ranges.push((attr_range, None));
|
||||
if let Some(inner_attr_parser_range) =
|
||||
self.capture_state.inner_attr_parser_ranges.remove(&attr.id)
|
||||
{
|
||||
inner_attr_parser_replacements.push((inner_attr_parser_range, None));
|
||||
} else {
|
||||
self.dcx().span_delayed_bug(attr.span, "Missing token range for attribute");
|
||||
}
|
||||
@ -305,37 +314,41 @@ impl<'a> Parser<'a> {
|
||||
|
||||
// This is hot enough for `deep-vector` that checking the conditions for an empty iterator
|
||||
// is measurably faster than actually executing the iterator.
|
||||
let replace_ranges: Box<[ReplaceRange]> =
|
||||
if replace_ranges_start == replace_ranges_end && inner_attr_replace_ranges.is_empty() {
|
||||
Box::new([])
|
||||
} else {
|
||||
// Grab any replace ranges that occur *inside* the current AST node. We will
|
||||
// perform the actual replacement only when we convert the `LazyAttrTokenStream` to
|
||||
// an `AttrTokenStream`.
|
||||
self.capture_state.replace_ranges[replace_ranges_start..replace_ranges_end]
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(inner_attr_replace_ranges.iter().cloned())
|
||||
.map(|(range, data)| ((range.start - start_pos)..(range.end - start_pos), data))
|
||||
.collect()
|
||||
};
|
||||
let node_replacements: Box<[_]> = if parser_replacements_start == parser_replacements_end
|
||||
&& inner_attr_parser_replacements.is_empty()
|
||||
{
|
||||
Box::new([])
|
||||
} else {
|
||||
// Grab any replace ranges that occur *inside* the current AST node. Convert them
|
||||
// from `ParserRange` form to `NodeRange` form. We will perform the actual
|
||||
// replacement only when we convert the `LazyAttrTokenStream` to an
|
||||
// `AttrTokenStream`.
|
||||
self.capture_state.parser_replacements
|
||||
[parser_replacements_start..parser_replacements_end]
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(inner_attr_parser_replacements.iter().cloned())
|
||||
.map(|(parser_range, data)| (NodeRange::new(parser_range, start_pos), data))
|
||||
.collect()
|
||||
};
|
||||
|
||||
// What is the status here when parsing the example code at the top of this method?
|
||||
//
|
||||
// When parsing `g`:
|
||||
// - `start_pos..end_pos` is `12..33` (`fn g { ... }`, excluding the outer attr).
|
||||
// - `inner_attr_replace_ranges` has one entry (`5..15`, when counting from `fn`), to
|
||||
// - `inner_attr_parser_replacements` has one entry (`ParserRange(17..27)`), to
|
||||
// delete the inner attr's tokens.
|
||||
// - This entry is put into the lazy tokens for `g`, i.e. deleting the inner attr from
|
||||
// those tokens (if they get evaluated).
|
||||
// - This entry is converted to `NodeRange(5..15)` (relative to the `fn`) and put into
|
||||
// the lazy tokens for `g`, i.e. deleting the inner attr from those tokens (if they get
|
||||
// evaluated).
|
||||
// - Those lazy tokens are also put into an `AttrsTarget` that is appended to `self`'s
|
||||
// replace ranges at the bottom of this function, for processing when parsing `m`.
|
||||
// - `replace_ranges_start..replace_ranges_end` is empty.
|
||||
// - `parser_replacements_start..parser_replacements_end` is empty.
|
||||
//
|
||||
// When parsing `m`:
|
||||
// - `start_pos..end_pos` is `0..34` (`mod m`, excluding the `#[cfg_eval]` attribute).
|
||||
// - `inner_attr_replace_ranges` is empty.
|
||||
// - `replace_range_start..replace_ranges_end` has one entry.
|
||||
// - `inner_attr_parser_replacements` is empty.
|
||||
// - `parser_replacements_start..parser_replacements_end` has one entry.
|
||||
// - One `AttrsTarget` (added below when parsing `g`) to replace all of `g` (`3..33`,
|
||||
// including its outer attribute), with:
|
||||
// - `attrs`: includes the outer and the inner attr.
|
||||
@ -346,7 +359,7 @@ impl<'a> Parser<'a> {
|
||||
num_calls,
|
||||
cursor_snapshot,
|
||||
break_last_token: self.break_last_token,
|
||||
replace_ranges,
|
||||
node_replacements,
|
||||
});
|
||||
|
||||
// If we support tokens and don't already have them, store the newly captured tokens.
|
||||
@ -367,7 +380,7 @@ impl<'a> Parser<'a> {
|
||||
// What is the status here when parsing the example code at the top of this method?
|
||||
//
|
||||
// When parsing `g`, we add one entry:
|
||||
// - The `start_pos..end_pos` (`3..33`) entry has a new `AttrsTarget` with:
|
||||
// - The pushed entry (`ParserRange(3..33)`) has a new `AttrsTarget` with:
|
||||
// - `attrs`: includes the outer and the inner attr.
|
||||
// - `tokens`: lazy tokens for `g` (with its inner attr deleted).
|
||||
//
|
||||
@ -378,12 +391,14 @@ impl<'a> Parser<'a> {
|
||||
// cfg-expand this AST node.
|
||||
let start_pos = if has_outer_attrs { attrs.start_pos } else { start_pos };
|
||||
let target = AttrsTarget { attrs: ret.attrs().iter().cloned().collect(), tokens };
|
||||
self.capture_state.replace_ranges.push((start_pos..end_pos, Some(target)));
|
||||
self.capture_state
|
||||
.parser_replacements
|
||||
.push((ParserRange(start_pos..end_pos), Some(target)));
|
||||
} else if matches!(self.capture_state.capturing, Capturing::No) {
|
||||
// Only clear the ranges once we've finished capturing entirely, i.e. we've finished
|
||||
// the outermost call to this method.
|
||||
self.capture_state.replace_ranges.clear();
|
||||
self.capture_state.inner_attr_ranges.clear();
|
||||
self.capture_state.parser_replacements.clear();
|
||||
self.capture_state.inner_attr_parser_ranges.clear();
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
|
@ -40,14 +40,6 @@ use super::{
|
||||
};
|
||||
use crate::{errors, maybe_recover_from_interpolated_ty_qpath};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) enum LhsExpr {
|
||||
// Already parsed just the outer attributes.
|
||||
Unparsed { attrs: AttrWrapper },
|
||||
// Already parsed the expression.
|
||||
Parsed { expr: P<Expr>, starts_statement: bool },
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum DestructuredFloat {
|
||||
/// 1e2
|
||||
@ -113,30 +105,31 @@ impl<'a> Parser<'a> {
|
||||
r: Restrictions,
|
||||
attrs: AttrWrapper,
|
||||
) -> PResult<'a, P<Expr>> {
|
||||
self.with_res(r, |this| this.parse_expr_assoc_with(0, LhsExpr::Unparsed { attrs }))
|
||||
self.with_res(r, |this| this.parse_expr_assoc_with(0, attrs))
|
||||
}
|
||||
|
||||
/// Parses an associative expression with operators of at least `min_prec` precedence.
|
||||
pub(super) fn parse_expr_assoc_with(
|
||||
&mut self,
|
||||
min_prec: usize,
|
||||
lhs: LhsExpr,
|
||||
attrs: AttrWrapper,
|
||||
) -> PResult<'a, P<Expr>> {
|
||||
let mut starts_stmt = false;
|
||||
let mut lhs = match lhs {
|
||||
LhsExpr::Parsed { expr, starts_statement } => {
|
||||
starts_stmt = starts_statement;
|
||||
expr
|
||||
}
|
||||
LhsExpr::Unparsed { attrs } => {
|
||||
if self.token.is_range_separator() {
|
||||
return self.parse_expr_prefix_range(attrs);
|
||||
} else {
|
||||
self.parse_expr_prefix(attrs)?
|
||||
}
|
||||
}
|
||||
let lhs = if self.token.is_range_separator() {
|
||||
return self.parse_expr_prefix_range(attrs);
|
||||
} else {
|
||||
self.parse_expr_prefix(attrs)?
|
||||
};
|
||||
self.parse_expr_assoc_rest_with(min_prec, false, lhs)
|
||||
}
|
||||
|
||||
/// Parses the rest of an associative expression (i.e. the part after the lhs) with operators
|
||||
/// of at least `min_prec` precedence.
|
||||
pub(super) fn parse_expr_assoc_rest_with(
|
||||
&mut self,
|
||||
min_prec: usize,
|
||||
starts_stmt: bool,
|
||||
mut lhs: P<Expr>,
|
||||
) -> PResult<'a, P<Expr>> {
|
||||
if !self.should_continue_as_assoc_expr(&lhs) {
|
||||
return Ok(lhs);
|
||||
}
|
||||
@ -272,7 +265,7 @@ impl<'a> Parser<'a> {
|
||||
};
|
||||
let rhs = self.with_res(restrictions - Restrictions::STMT_EXPR, |this| {
|
||||
let attrs = this.parse_outer_attributes()?;
|
||||
this.parse_expr_assoc_with(prec + prec_adjustment, LhsExpr::Unparsed { attrs })
|
||||
this.parse_expr_assoc_with(prec + prec_adjustment, attrs)
|
||||
})?;
|
||||
|
||||
let span = self.mk_expr_sp(&lhs, lhs_span, rhs.span);
|
||||
@ -447,7 +440,7 @@ impl<'a> Parser<'a> {
|
||||
let maybe_lt = self.token.clone();
|
||||
let attrs = self.parse_outer_attributes()?;
|
||||
Some(
|
||||
self.parse_expr_assoc_with(prec + 1, LhsExpr::Unparsed { attrs })
|
||||
self.parse_expr_assoc_with(prec + 1, attrs)
|
||||
.map_err(|err| self.maybe_err_dotdotlt_syntax(maybe_lt, err))?,
|
||||
)
|
||||
} else {
|
||||
@ -504,12 +497,9 @@ impl<'a> Parser<'a> {
|
||||
let (span, opt_end) = if this.is_at_start_of_range_notation_rhs() {
|
||||
// RHS must be parsed with more associativity than the dots.
|
||||
let attrs = this.parse_outer_attributes()?;
|
||||
this.parse_expr_assoc_with(
|
||||
op.unwrap().precedence() + 1,
|
||||
LhsExpr::Unparsed { attrs },
|
||||
)
|
||||
.map(|x| (lo.to(x.span), Some(x)))
|
||||
.map_err(|err| this.maybe_err_dotdotlt_syntax(maybe_lt, err))?
|
||||
this.parse_expr_assoc_with(op.unwrap().precedence() + 1, attrs)
|
||||
.map(|x| (lo.to(x.span), Some(x)))
|
||||
.map_err(|err| this.maybe_err_dotdotlt_syntax(maybe_lt, err))?
|
||||
} else {
|
||||
(lo, None)
|
||||
};
|
||||
@ -889,7 +879,7 @@ impl<'a> Parser<'a> {
|
||||
mut e: P<Expr>,
|
||||
lo: Span,
|
||||
) -> PResult<'a, P<Expr>> {
|
||||
let res = ensure_sufficient_stack(|| {
|
||||
let mut res = ensure_sufficient_stack(|| {
|
||||
loop {
|
||||
let has_question =
|
||||
if self.prev_token.kind == TokenKind::Ident(kw::Return, IdentIsRaw::No) {
|
||||
@ -936,17 +926,13 @@ impl<'a> Parser<'a> {
|
||||
|
||||
// Stitch the list of outer attributes onto the return value. A little
|
||||
// bit ugly, but the best way given the current code structure.
|
||||
if attrs.is_empty() {
|
||||
res
|
||||
} else {
|
||||
res.map(|expr| {
|
||||
expr.map(|mut expr| {
|
||||
attrs.extend(expr.attrs);
|
||||
expr.attrs = attrs;
|
||||
expr
|
||||
})
|
||||
})
|
||||
if !attrs.is_empty()
|
||||
&& let Ok(expr) = &mut res
|
||||
{
|
||||
mem::swap(&mut expr.attrs, &mut attrs);
|
||||
expr.attrs.extend(attrs)
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
pub(super) fn parse_dot_suffix_expr(
|
||||
@ -2647,10 +2633,7 @@ impl<'a> Parser<'a> {
|
||||
self.expect(&token::Eq)?;
|
||||
}
|
||||
let attrs = self.parse_outer_attributes()?;
|
||||
let expr = self.parse_expr_assoc_with(
|
||||
1 + prec_let_scrutinee_needs_par(),
|
||||
LhsExpr::Unparsed { attrs },
|
||||
)?;
|
||||
let expr = self.parse_expr_assoc_with(1 + prec_let_scrutinee_needs_par(), attrs)?;
|
||||
let span = lo.to(expr.span);
|
||||
Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span, recovered)))
|
||||
}
|
||||
|
@ -192,24 +192,54 @@ struct ClosureSpans {
|
||||
body: Span,
|
||||
}
|
||||
|
||||
/// Indicates a range of tokens that should be replaced by
|
||||
/// the tokens in the provided `AttrsTarget`. This is used in two
|
||||
/// places during token collection:
|
||||
/// A token range within a `Parser`'s full token stream.
|
||||
#[derive(Clone, Debug)]
|
||||
struct ParserRange(Range<u32>);
|
||||
|
||||
/// A token range within an individual AST node's (lazy) token stream, i.e.
|
||||
/// relative to that node's first token. Distinct from `ParserRange` so the two
|
||||
/// kinds of range can't be mixed up.
|
||||
#[derive(Clone, Debug)]
|
||||
struct NodeRange(Range<u32>);
|
||||
|
||||
/// Indicates a range of tokens that should be replaced by an `AttrsTarget`
|
||||
/// (replacement) or be replaced by nothing (deletion). This is used in two
|
||||
/// places during token collection.
|
||||
///
|
||||
/// 1. During the parsing of an AST node that may have a `#[derive]`
|
||||
/// attribute, we parse a nested AST node that has `#[cfg]` or `#[cfg_attr]`
|
||||
/// In this case, we use a `ReplaceRange` to replace the entire inner AST node
|
||||
/// with `FlatToken::AttrsTarget`, allowing us to perform eager cfg-expansion
|
||||
/// on an `AttrTokenStream`.
|
||||
/// 1. Replacement. During the parsing of an AST node that may have a
|
||||
/// `#[derive]` attribute, when we parse a nested AST node that has `#[cfg]`
|
||||
/// or `#[cfg_attr]`, we replace the entire inner AST node with
|
||||
/// `FlatToken::AttrsTarget`. This lets us perform eager cfg-expansion on an
|
||||
/// `AttrTokenStream`.
|
||||
///
|
||||
/// 2. When we parse an inner attribute while collecting tokens. We
|
||||
/// remove inner attributes from the token stream entirely, and
|
||||
/// instead track them through the `attrs` field on the AST node.
|
||||
/// This allows us to easily manipulate them (for example, removing
|
||||
/// the first macro inner attribute to invoke a proc-macro).
|
||||
/// When create a `TokenStream`, the inner attributes get inserted
|
||||
/// into the proper place in the token stream.
|
||||
type ReplaceRange = (Range<u32>, Option<AttrsTarget>);
|
||||
/// 2. Deletion. We delete inner attributes from all collected token streams,
|
||||
/// and instead track them through the `attrs` field on the AST node. This
|
||||
/// lets us manipulate them similarly to outer attributes. When we create a
|
||||
/// `TokenStream`, the inner attributes are inserted into the proper place
|
||||
/// in the token stream.
|
||||
///
|
||||
/// Each replacement starts off in `ParserReplacement` form but is converted to
|
||||
/// `NodeReplacement` form when it is attached to a single AST node, via
|
||||
/// `LazyAttrTokenStreamImpl`.
|
||||
type ParserReplacement = (ParserRange, Option<AttrsTarget>);
|
||||
|
||||
/// See the comment on `ParserReplacement`.
|
||||
type NodeReplacement = (NodeRange, Option<AttrsTarget>);
|
||||
|
||||
impl NodeRange {
|
||||
// Converts a range within a parser's tokens to a range within a
|
||||
// node's tokens beginning at `start_pos`.
|
||||
//
|
||||
// For example, imagine a parser with 50 tokens in its token stream, a
|
||||
// function that spans `ParserRange(20..40)` and an inner attribute within
|
||||
// that function that spans `ParserRange(30..35)`. We would find the inner
|
||||
// attribute's range within the function's tokens by subtracting 20, which
|
||||
// is the position of the function's start token. This gives
|
||||
// `NodeRange(10..15)`.
|
||||
fn new(ParserRange(parser_range): ParserRange, start_pos: u32) -> NodeRange {
|
||||
NodeRange((parser_range.start - start_pos)..(parser_range.end - start_pos))
|
||||
}
|
||||
}
|
||||
|
||||
/// Controls how we capture tokens. Capturing can be expensive,
|
||||
/// so we try to avoid performing capturing in cases where
|
||||
@ -226,8 +256,8 @@ enum Capturing {
|
||||
#[derive(Clone, Debug)]
|
||||
struct CaptureState {
|
||||
capturing: Capturing,
|
||||
replace_ranges: Vec<ReplaceRange>,
|
||||
inner_attr_ranges: FxHashMap<AttrId, Range<u32>>,
|
||||
parser_replacements: Vec<ParserReplacement>,
|
||||
inner_attr_parser_ranges: FxHashMap<AttrId, ParserRange>,
|
||||
}
|
||||
|
||||
/// Iterator over a `TokenStream` that produces `Token`s. It's a bit odd that
|
||||
@ -417,8 +447,8 @@ impl<'a> Parser<'a> {
|
||||
subparser_name,
|
||||
capture_state: CaptureState {
|
||||
capturing: Capturing::No,
|
||||
replace_ranges: Vec::new(),
|
||||
inner_attr_ranges: Default::default(),
|
||||
parser_replacements: Vec::new(),
|
||||
inner_attr_parser_ranges: Default::default(),
|
||||
},
|
||||
current_closure: None,
|
||||
recovery: Recovery::Allowed,
|
||||
@ -1220,9 +1250,6 @@ impl<'a> Parser<'a> {
|
||||
if self.eat_keyword_case(kw::Unsafe, case) {
|
||||
Safety::Unsafe(self.prev_token.uninterpolated_span())
|
||||
} else if self.eat_keyword_case(kw::Safe, case) {
|
||||
self.psess
|
||||
.gated_spans
|
||||
.gate(sym::unsafe_extern_blocks, self.prev_token.uninterpolated_span());
|
||||
Safety::Safe(self.prev_token.uninterpolated_span())
|
||||
} else {
|
||||
Safety::Default
|
||||
|
@ -25,7 +25,7 @@ use crate::errors::{
|
||||
UnexpectedParenInRangePat, UnexpectedParenInRangePatSugg,
|
||||
UnexpectedVertVertBeforeFunctionParam, UnexpectedVertVertInPattern, WrapInParens,
|
||||
};
|
||||
use crate::parser::expr::{could_be_unclosed_char_literal, LhsExpr};
|
||||
use crate::parser::expr::could_be_unclosed_char_literal;
|
||||
use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
|
||||
|
||||
#[derive(PartialEq, Copy, Clone)]
|
||||
@ -403,8 +403,9 @@ impl<'a> Parser<'a> {
|
||||
|
||||
// Parse an associative expression such as `+ expr`, `% expr`, ...
|
||||
// Assignements, ranges and `|` are disabled by [`Restrictions::IS_PAT`].
|
||||
let lhs = LhsExpr::Parsed { expr, starts_statement: false };
|
||||
if let Ok(expr) = snapshot.parse_expr_assoc_with(0, lhs).map_err(|err| err.cancel()) {
|
||||
if let Ok(expr) =
|
||||
snapshot.parse_expr_assoc_rest_with(0, false, expr).map_err(|err| err.cancel())
|
||||
{
|
||||
// We got a valid expression.
|
||||
self.restore_snapshot(snapshot);
|
||||
self.restrictions.remove(Restrictions::IS_PAT);
|
||||
|
@ -17,7 +17,6 @@ use thin_vec::{thin_vec, ThinVec};
|
||||
|
||||
use super::attr::InnerAttrForbiddenReason;
|
||||
use super::diagnostics::AttemptLocalParseRecovery;
|
||||
use super::expr::LhsExpr;
|
||||
use super::pat::{PatternLocation, RecoverComma};
|
||||
use super::path::PathStyle;
|
||||
use super::{
|
||||
@ -66,7 +65,12 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
|
||||
Ok(Some(if self.token.is_keyword(kw::Let) {
|
||||
self.parse_local_mk(lo, attrs, capture_semi, force_collect)?
|
||||
self.collect_tokens_trailing_token(attrs, force_collect, |this, attrs| {
|
||||
this.expect_keyword(kw::Let)?;
|
||||
let local = this.parse_local(attrs)?;
|
||||
let trailing = capture_semi && this.token.kind == token::Semi;
|
||||
Ok((this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)), trailing))
|
||||
})?
|
||||
} else if self.is_kw_followed_by_ident(kw::Mut) && self.may_recover() {
|
||||
self.recover_stmt_local_after_let(
|
||||
lo,
|
||||
@ -112,13 +116,12 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
}
|
||||
} else if let Some(item) = self.parse_item_common(
|
||||
attrs.clone(),
|
||||
attrs.clone(), // FIXME: unwanted clone of attrs
|
||||
false,
|
||||
true,
|
||||
FnParseMode { req_name: |_| true, req_body: true },
|
||||
force_collect,
|
||||
)? {
|
||||
// FIXME: Bad copy of attrs
|
||||
self.mk_stmt(lo.to(item.span), StmtKind::Item(P(item)))
|
||||
} else if self.eat(&token::Semi) {
|
||||
// Do not attempt to parse an expression if we're done here.
|
||||
@ -173,7 +176,7 @@ impl<'a> Parser<'a> {
|
||||
// Perform this outside of the `collect_tokens_trailing_token` closure,
|
||||
// since our outer attributes do not apply to this part of the expression
|
||||
let expr = self.with_res(Restrictions::STMT_EXPR, |this| {
|
||||
this.parse_expr_assoc_with(0, LhsExpr::Parsed { expr, starts_statement: true })
|
||||
this.parse_expr_assoc_rest_with(0, true, expr)
|
||||
})?;
|
||||
Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Expr(expr)))
|
||||
} else {
|
||||
@ -206,8 +209,7 @@ impl<'a> Parser<'a> {
|
||||
let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac));
|
||||
let e = self.maybe_recover_from_bad_qpath(e)?;
|
||||
let e = self.parse_expr_dot_or_call_with(attrs, e, lo)?;
|
||||
let e = self
|
||||
.parse_expr_assoc_with(0, LhsExpr::Parsed { expr: e, starts_statement: false })?;
|
||||
let e = self.parse_expr_assoc_rest_with(0, false, e)?;
|
||||
StmtKind::Expr(e)
|
||||
};
|
||||
Ok(self.mk_stmt(lo.to(hi), kind))
|
||||
@ -247,21 +249,6 @@ impl<'a> Parser<'a> {
|
||||
Ok(stmt)
|
||||
}
|
||||
|
||||
fn parse_local_mk(
|
||||
&mut self,
|
||||
lo: Span,
|
||||
attrs: AttrWrapper,
|
||||
capture_semi: bool,
|
||||
force_collect: ForceCollect,
|
||||
) -> PResult<'a, Stmt> {
|
||||
self.collect_tokens_trailing_token(attrs, force_collect, |this, attrs| {
|
||||
this.expect_keyword(kw::Let)?;
|
||||
let local = this.parse_local(attrs)?;
|
||||
let trailing = capture_semi && this.token.kind == token::Semi;
|
||||
Ok((this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)), trailing))
|
||||
})
|
||||
}
|
||||
|
||||
/// Parses a local variable declaration.
|
||||
fn parse_local(&mut self, attrs: AttrVec) -> PResult<'a, P<Local>> {
|
||||
let lo = self.prev_token.span;
|
||||
|
@ -100,6 +100,10 @@ passes_continue_labeled_block =
|
||||
.label = labeled blocks cannot be `continue`'d
|
||||
.block_label = labeled block the `continue` points to
|
||||
|
||||
passes_coroutine_on_non_closure =
|
||||
attribute should be applied to closures
|
||||
.label = not a closure
|
||||
|
||||
passes_coverage_not_fn_or_closure =
|
||||
attribute should be applied to a function definition or closure
|
||||
.label = not a function or closure
|
||||
|
@ -20,13 +20,13 @@ use rustc_hir::{
|
||||
TraitItem, CRATE_HIR_ID, CRATE_OWNER_ID,
|
||||
};
|
||||
use rustc_macros::LintDiagnostic;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault;
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::traits::ObligationCause;
|
||||
use rustc_middle::ty::error::{ExpectedFound, TypeError};
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_session::lint::builtin::{
|
||||
CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, INVALID_MACRO_EXPORT_ARGUMENTS,
|
||||
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES,
|
||||
@ -116,130 +116,184 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
||||
let attrs = self.tcx.hir().attrs(hir_id);
|
||||
for attr in attrs {
|
||||
match attr.path().as_slice() {
|
||||
[sym::diagnostic, sym::do_not_recommend] => {
|
||||
[sym::diagnostic, sym::do_not_recommend, ..] => {
|
||||
self.check_do_not_recommend(attr.span, hir_id, target)
|
||||
}
|
||||
[sym::diagnostic, sym::on_unimplemented] => {
|
||||
[sym::diagnostic, sym::on_unimplemented, ..] => {
|
||||
self.check_diagnostic_on_unimplemented(attr.span, hir_id, target)
|
||||
}
|
||||
[sym::inline] => self.check_inline(hir_id, attr, span, target),
|
||||
[sym::coverage] => self.check_coverage(attr, span, target),
|
||||
[sym::optimize] => self.check_optimize(hir_id, attr, target),
|
||||
[sym::non_exhaustive] => self.check_non_exhaustive(hir_id, attr, span, target),
|
||||
[sym::marker] => self.check_marker(hir_id, attr, span, target),
|
||||
[sym::target_feature] => {
|
||||
[sym::inline, ..] => self.check_inline(hir_id, attr, span, target),
|
||||
[sym::coverage, ..] => self.check_coverage(attr, span, target),
|
||||
[sym::optimize, ..] => self.check_optimize(hir_id, attr, target),
|
||||
[sym::non_exhaustive, ..] => self.check_non_exhaustive(hir_id, attr, span, target),
|
||||
[sym::marker, ..] => self.check_marker(hir_id, attr, span, target),
|
||||
[sym::target_feature, ..] => {
|
||||
self.check_target_feature(hir_id, attr, span, target, attrs)
|
||||
}
|
||||
[sym::thread_local] => self.check_thread_local(attr, span, target),
|
||||
[sym::track_caller] => {
|
||||
[sym::thread_local, ..] => self.check_thread_local(attr, span, target),
|
||||
[sym::track_caller, ..] => {
|
||||
self.check_track_caller(hir_id, attr.span, attrs, span, target)
|
||||
}
|
||||
[sym::doc] => self.check_doc_attrs(
|
||||
[sym::doc, ..] => self.check_doc_attrs(
|
||||
attr,
|
||||
hir_id,
|
||||
target,
|
||||
&mut specified_inline,
|
||||
&mut doc_aliases,
|
||||
),
|
||||
[sym::no_link] => self.check_no_link(hir_id, attr, span, target),
|
||||
[sym::export_name] => self.check_export_name(hir_id, attr, span, target),
|
||||
[sym::rustc_layout_scalar_valid_range_start]
|
||||
| [sym::rustc_layout_scalar_valid_range_end] => {
|
||||
[sym::no_link, ..] => self.check_no_link(hir_id, attr, span, target),
|
||||
[sym::export_name, ..] => self.check_export_name(hir_id, attr, span, target),
|
||||
[sym::rustc_layout_scalar_valid_range_start, ..]
|
||||
| [sym::rustc_layout_scalar_valid_range_end, ..] => {
|
||||
self.check_rustc_layout_scalar_valid_range(attr, span, target)
|
||||
}
|
||||
[sym::allow_internal_unstable] => {
|
||||
[sym::allow_internal_unstable, ..] => {
|
||||
self.check_allow_internal_unstable(hir_id, attr, span, target, attrs)
|
||||
}
|
||||
[sym::debugger_visualizer] => self.check_debugger_visualizer(attr, target),
|
||||
[sym::rustc_allow_const_fn_unstable] => {
|
||||
[sym::debugger_visualizer, ..] => self.check_debugger_visualizer(attr, target),
|
||||
[sym::rustc_allow_const_fn_unstable, ..] => {
|
||||
self.check_rustc_allow_const_fn_unstable(hir_id, attr, span, target)
|
||||
}
|
||||
[sym::rustc_std_internal_symbol] => {
|
||||
[sym::rustc_std_internal_symbol, ..] => {
|
||||
self.check_rustc_std_internal_symbol(attr, span, target)
|
||||
}
|
||||
[sym::naked] => self.check_naked(hir_id, attr, span, target, attrs),
|
||||
[sym::rustc_never_returns_null_ptr] => {
|
||||
[sym::naked, ..] => self.check_naked(hir_id, attr, span, target, attrs),
|
||||
[sym::rustc_never_returns_null_ptr, ..] => {
|
||||
self.check_applied_to_fn_or_method(hir_id, attr, span, target)
|
||||
}
|
||||
[sym::rustc_legacy_const_generics] => {
|
||||
[sym::rustc_legacy_const_generics, ..] => {
|
||||
self.check_rustc_legacy_const_generics(hir_id, attr, span, target, item)
|
||||
}
|
||||
[sym::rustc_lint_query_instability] => {
|
||||
[sym::rustc_lint_query_instability, ..] => {
|
||||
self.check_rustc_lint_query_instability(hir_id, attr, span, target)
|
||||
}
|
||||
[sym::rustc_lint_diagnostics] => {
|
||||
[sym::rustc_lint_diagnostics, ..] => {
|
||||
self.check_rustc_lint_diagnostics(hir_id, attr, span, target)
|
||||
}
|
||||
[sym::rustc_lint_opt_ty] => self.check_rustc_lint_opt_ty(attr, span, target),
|
||||
[sym::rustc_lint_opt_deny_field_access] => {
|
||||
[sym::rustc_lint_opt_ty, ..] => self.check_rustc_lint_opt_ty(attr, span, target),
|
||||
[sym::rustc_lint_opt_deny_field_access, ..] => {
|
||||
self.check_rustc_lint_opt_deny_field_access(attr, span, target)
|
||||
}
|
||||
[sym::rustc_clean]
|
||||
| [sym::rustc_dirty]
|
||||
| [sym::rustc_if_this_changed]
|
||||
| [sym::rustc_then_this_would_need] => self.check_rustc_dirty_clean(attr),
|
||||
[sym::rustc_coinductive]
|
||||
| [sym::rustc_must_implement_one_of]
|
||||
| [sym::rustc_deny_explicit_impl]
|
||||
| [sym::const_trait] => self.check_must_be_applied_to_trait(attr, span, target),
|
||||
[sym::cmse_nonsecure_entry] => {
|
||||
[sym::rustc_clean, ..]
|
||||
| [sym::rustc_dirty, ..]
|
||||
| [sym::rustc_if_this_changed, ..]
|
||||
| [sym::rustc_then_this_would_need, ..] => self.check_rustc_dirty_clean(attr),
|
||||
[sym::rustc_coinductive, ..]
|
||||
| [sym::rustc_must_implement_one_of, ..]
|
||||
| [sym::rustc_deny_explicit_impl, ..]
|
||||
| [sym::const_trait, ..] => self.check_must_be_applied_to_trait(attr, span, target),
|
||||
[sym::cmse_nonsecure_entry, ..] => {
|
||||
self.check_cmse_nonsecure_entry(hir_id, attr, span, target)
|
||||
}
|
||||
[sym::collapse_debuginfo] => self.check_collapse_debuginfo(attr, span, target),
|
||||
[sym::must_not_suspend] => self.check_must_not_suspend(attr, span, target),
|
||||
[sym::must_use] => self.check_must_use(hir_id, attr, target),
|
||||
[sym::rustc_pass_by_value] => self.check_pass_by_value(attr, span, target),
|
||||
[sym::rustc_allow_incoherent_impl] => {
|
||||
[sym::collapse_debuginfo, ..] => self.check_collapse_debuginfo(attr, span, target),
|
||||
[sym::must_not_suspend, ..] => self.check_must_not_suspend(attr, span, target),
|
||||
[sym::must_use, ..] => self.check_must_use(hir_id, attr, target),
|
||||
[sym::rustc_pass_by_value, ..] => self.check_pass_by_value(attr, span, target),
|
||||
[sym::rustc_allow_incoherent_impl, ..] => {
|
||||
self.check_allow_incoherent_impl(attr, span, target)
|
||||
}
|
||||
[sym::rustc_has_incoherent_inherent_impls] => {
|
||||
[sym::rustc_has_incoherent_inherent_impls, ..] => {
|
||||
self.check_has_incoherent_inherent_impls(attr, span, target)
|
||||
}
|
||||
[sym::ffi_pure] => self.check_ffi_pure(attr.span, attrs, target),
|
||||
[sym::ffi_const] => self.check_ffi_const(attr.span, target),
|
||||
[sym::rustc_const_unstable]
|
||||
| [sym::rustc_const_stable]
|
||||
| [sym::unstable]
|
||||
| [sym::stable]
|
||||
| [sym::rustc_allowed_through_unstable_modules]
|
||||
| [sym::rustc_promotable] => self.check_stability_promotable(attr, target),
|
||||
[sym::link_ordinal] => self.check_link_ordinal(attr, span, target),
|
||||
[sym::rustc_confusables] => self.check_confusables(attr, target),
|
||||
[sym::rustc_safe_intrinsic] => {
|
||||
[sym::ffi_pure, ..] => self.check_ffi_pure(attr.span, attrs, target),
|
||||
[sym::ffi_const, ..] => self.check_ffi_const(attr.span, target),
|
||||
[sym::rustc_const_unstable, ..]
|
||||
| [sym::rustc_const_stable, ..]
|
||||
| [sym::unstable, ..]
|
||||
| [sym::stable, ..]
|
||||
| [sym::rustc_allowed_through_unstable_modules, ..]
|
||||
| [sym::rustc_promotable, ..] => self.check_stability_promotable(attr, target),
|
||||
[sym::link_ordinal, ..] => self.check_link_ordinal(attr, span, target),
|
||||
[sym::rustc_confusables, ..] => self.check_confusables(attr, target),
|
||||
[sym::rustc_safe_intrinsic, ..] => {
|
||||
self.check_rustc_safe_intrinsic(hir_id, attr, span, target)
|
||||
}
|
||||
[sym::cold] => self.check_cold(hir_id, attr, span, target),
|
||||
[sym::link] => self.check_link(hir_id, attr, span, target),
|
||||
[sym::link_name] => self.check_link_name(hir_id, attr, span, target),
|
||||
[sym::link_section] => self.check_link_section(hir_id, attr, span, target),
|
||||
[sym::no_mangle] => self.check_no_mangle(hir_id, attr, span, target),
|
||||
[sym::deprecated] => self.check_deprecated(hir_id, attr, span, target),
|
||||
[sym::macro_use] | [sym::macro_escape] => {
|
||||
[sym::cold, ..] => self.check_cold(hir_id, attr, span, target),
|
||||
[sym::link, ..] => self.check_link(hir_id, attr, span, target),
|
||||
[sym::link_name, ..] => self.check_link_name(hir_id, attr, span, target),
|
||||
[sym::link_section, ..] => self.check_link_section(hir_id, attr, span, target),
|
||||
[sym::no_mangle, ..] => self.check_no_mangle(hir_id, attr, span, target),
|
||||
[sym::deprecated, ..] => self.check_deprecated(hir_id, attr, span, target),
|
||||
[sym::macro_use, ..] | [sym::macro_escape, ..] => {
|
||||
self.check_macro_use(hir_id, attr, target)
|
||||
}
|
||||
[sym::path] => self.check_generic_attr(hir_id, attr, target, Target::Mod),
|
||||
[sym::macro_export] => self.check_macro_export(hir_id, attr, target),
|
||||
[sym::ignore] | [sym::should_panic] => {
|
||||
[sym::path, ..] => self.check_generic_attr(hir_id, attr, target, Target::Mod),
|
||||
[sym::macro_export, ..] => self.check_macro_export(hir_id, attr, target),
|
||||
[sym::ignore, ..] | [sym::should_panic, ..] => {
|
||||
self.check_generic_attr(hir_id, attr, target, Target::Fn)
|
||||
}
|
||||
[sym::automatically_derived] => {
|
||||
[sym::automatically_derived, ..] => {
|
||||
self.check_generic_attr(hir_id, attr, target, Target::Impl)
|
||||
}
|
||||
[sym::no_implicit_prelude] => {
|
||||
[sym::no_implicit_prelude, ..] => {
|
||||
self.check_generic_attr(hir_id, attr, target, Target::Mod)
|
||||
}
|
||||
[sym::rustc_object_lifetime_default] => self.check_object_lifetime_default(hir_id),
|
||||
[sym::proc_macro] => {
|
||||
[sym::rustc_object_lifetime_default, ..] => self.check_object_lifetime_default(hir_id),
|
||||
[sym::proc_macro, ..] => {
|
||||
self.check_proc_macro(hir_id, target, ProcMacroKind::FunctionLike)
|
||||
}
|
||||
[sym::proc_macro_attribute] => {
|
||||
[sym::proc_macro_attribute, ..] => {
|
||||
self.check_proc_macro(hir_id, target, ProcMacroKind::Attribute);
|
||||
}
|
||||
[sym::proc_macro_derive] => {
|
||||
[sym::proc_macro_derive, ..] => {
|
||||
self.check_generic_attr(hir_id, attr, target, Target::Fn);
|
||||
self.check_proc_macro(hir_id, target, ProcMacroKind::Derive)
|
||||
}
|
||||
_ => {}
|
||||
[sym::coroutine, ..] => {
|
||||
self.check_coroutine(attr, target);
|
||||
}
|
||||
[
|
||||
// ok
|
||||
sym::allow
|
||||
| sym::expect
|
||||
| sym::warn
|
||||
| sym::deny
|
||||
| sym::forbid
|
||||
| sym::cfg
|
||||
// need to be fixed
|
||||
| sym::cfi_encoding // FIXME(cfi_encoding)
|
||||
| sym::may_dangle // FIXME(dropck_eyepatch)
|
||||
| sym::pointee // FIXME(derive_smart_pointer)
|
||||
| sym::linkage // FIXME(linkage)
|
||||
| sym::no_sanitize // FIXME(no_sanitize)
|
||||
| sym::omit_gdb_pretty_printer_section // FIXME(omit_gdb_pretty_printer_section)
|
||||
| sym::used // handled elsewhere to restrict to static items
|
||||
| sym::repr // handled elsewhere to restrict to type decls items
|
||||
| sym::instruction_set // broken on stable!!!
|
||||
| sym::windows_subsystem // broken on stable!!!
|
||||
| sym::patchable_function_entry // FIXME(patchable_function_entry)
|
||||
| sym::deprecated_safe // FIXME(deprecated_safe)
|
||||
// internal
|
||||
| sym::prelude_import
|
||||
| sym::panic_handler
|
||||
| sym::allow_internal_unsafe
|
||||
| sym::fundamental
|
||||
| sym::lang
|
||||
| sym::needs_allocator
|
||||
| sym::default_lib_allocator
|
||||
| sym::start
|
||||
| sym::custom_mir,
|
||||
..
|
||||
] => {}
|
||||
[name, ..] => {
|
||||
match BUILTIN_ATTRIBUTE_MAP.get(name) {
|
||||
// checked below
|
||||
Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) => {}
|
||||
Some(_) => {
|
||||
// FIXME: differentiate between unstable and internal attributes just
|
||||
// like we do with features instead of just accepting `rustc_`
|
||||
// attributes by name. That should allow trimming the above list, too.
|
||||
if !name.as_str().starts_with("rustc_") {
|
||||
span_bug!(
|
||||
attr.span,
|
||||
"builtin attribute {name:?} not handled by `CheckAttrVisitor`"
|
||||
)
|
||||
}
|
||||
}
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
[] => unreachable!(),
|
||||
}
|
||||
|
||||
let builtin = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name));
|
||||
@ -376,6 +430,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
||||
|
||||
/// Checks that `#[optimize(..)]` is applied to a function/closure/method,
|
||||
/// or to an impl block or module.
|
||||
// FIXME(#128488): this should probably be elevated to an error?
|
||||
fn check_optimize(&self, hir_id: HirId, attr: &Attribute, target: Target) {
|
||||
match target {
|
||||
Target::Fn
|
||||
@ -2279,6 +2334,15 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
||||
self.abort.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_coroutine(&self, attr: &Attribute, target: Target) {
|
||||
match target {
|
||||
Target::Closure => return,
|
||||
_ => {
|
||||
self.dcx().emit_err(errors::CoroutineOnNonClosure { span: attr.span });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
|
||||
|
@ -17,7 +17,7 @@ use rustc_hir::{Node, PatKind, TyKind};
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
use rustc_middle::middle::privacy::Level;
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::ty::{self, AssocItemContainer, TyCtxt};
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_session::lint;
|
||||
use rustc_session::lint::builtin::DEAD_CODE;
|
||||
@ -44,81 +44,18 @@ fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
|
||||
)
|
||||
}
|
||||
|
||||
struct Publicness {
|
||||
ty_is_public: bool,
|
||||
ty_and_all_fields_are_public: bool,
|
||||
}
|
||||
|
||||
impl Publicness {
|
||||
fn new(ty_is_public: bool, ty_and_all_fields_are_public: bool) -> Self {
|
||||
Self { ty_is_public, ty_and_all_fields_are_public }
|
||||
fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> bool {
|
||||
if let TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind
|
||||
&& let Res::Def(def_kind, def_id) = path.res
|
||||
&& def_id.is_local()
|
||||
&& matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union)
|
||||
{
|
||||
tcx.visibility(def_id).is_public()
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn adt_of<'tcx>(ty: &hir::Ty<'tcx>) -> Option<(LocalDefId, DefKind)> {
|
||||
match ty.kind {
|
||||
TyKind::Path(hir::QPath::Resolved(_, path)) => {
|
||||
if let Res::Def(def_kind, def_id) = path.res
|
||||
&& let Some(local_def_id) = def_id.as_local()
|
||||
{
|
||||
Some((local_def_id, def_kind))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
TyKind::Slice(ty) | TyKind::Array(ty, _) => adt_of(ty),
|
||||
TyKind::Ptr(ty) | TyKind::Ref(_, ty) => adt_of(ty.ty),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: LocalDefId) -> bool {
|
||||
let adt_def = tcx.adt_def(id);
|
||||
|
||||
// skip types contain fields of unit and never type,
|
||||
// it's usually intentional to make the type not constructible
|
||||
let not_require_constructor = adt_def.all_fields().any(|field| {
|
||||
let field_type = tcx.type_of(field.did).instantiate_identity();
|
||||
field_type.is_unit() || field_type.is_never()
|
||||
});
|
||||
|
||||
not_require_constructor
|
||||
|| adt_def.all_fields().all(|field| {
|
||||
let field_type = tcx.type_of(field.did).instantiate_identity();
|
||||
// skip fields of PhantomData,
|
||||
// cause it's a common way to check things like well-formedness
|
||||
if field_type.is_phantom_data() {
|
||||
return true;
|
||||
}
|
||||
|
||||
field.vis.is_public()
|
||||
})
|
||||
}
|
||||
|
||||
/// check struct and its fields are public or not,
|
||||
/// for enum and union, just check they are public,
|
||||
/// and doesn't solve types like &T for now, just skip them
|
||||
fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> Publicness {
|
||||
if let Some((def_id, def_kind)) = adt_of(ty) {
|
||||
return match def_kind {
|
||||
DefKind::Enum | DefKind::Union => {
|
||||
let ty_is_public = tcx.visibility(def_id).is_public();
|
||||
Publicness::new(ty_is_public, ty_is_public)
|
||||
}
|
||||
DefKind::Struct => {
|
||||
let ty_is_public = tcx.visibility(def_id).is_public();
|
||||
Publicness::new(
|
||||
ty_is_public,
|
||||
ty_is_public && struct_all_fields_are_public(tcx, def_id),
|
||||
)
|
||||
}
|
||||
_ => Publicness::new(true, true),
|
||||
};
|
||||
}
|
||||
|
||||
Publicness::new(true, true)
|
||||
}
|
||||
|
||||
/// Determine if a work from the worklist is coming from a `#[allow]`
|
||||
/// or a `#[expect]` of `dead_code`
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
@ -172,10 +109,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
|
||||
|
||||
fn handle_res(&mut self, res: Res) {
|
||||
match res {
|
||||
Res::Def(
|
||||
DefKind::Const | DefKind::AssocConst | DefKind::AssocTy | DefKind::TyAlias,
|
||||
def_id,
|
||||
) => {
|
||||
Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::TyAlias, def_id) => {
|
||||
self.check_def_id(def_id);
|
||||
}
|
||||
_ if self.in_pat => {}
|
||||
@ -294,10 +228,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
|
||||
pats: &[hir::PatField<'_>],
|
||||
) {
|
||||
let variant = match self.typeck_results().node_type(lhs.hir_id).kind() {
|
||||
ty::Adt(adt, _) => {
|
||||
self.check_def_id(adt.did());
|
||||
adt.variant_of_res(res)
|
||||
}
|
||||
ty::Adt(adt, _) => adt.variant_of_res(res),
|
||||
_ => span_bug!(lhs.span, "non-ADT in struct pattern"),
|
||||
};
|
||||
for pat in pats {
|
||||
@ -317,10 +248,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
|
||||
dotdot: hir::DotDotPos,
|
||||
) {
|
||||
let variant = match self.typeck_results().node_type(lhs.hir_id).kind() {
|
||||
ty::Adt(adt, _) => {
|
||||
self.check_def_id(adt.did());
|
||||
adt.variant_of_res(res)
|
||||
}
|
||||
ty::Adt(adt, _) => adt.variant_of_res(res),
|
||||
_ => {
|
||||
self.tcx.dcx().span_delayed_bug(lhs.span, "non-ADT in tuple struct pattern");
|
||||
return;
|
||||
@ -425,6 +353,31 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
|
||||
return false;
|
||||
}
|
||||
|
||||
// don't ignore impls for Enums and pub Structs whose methods don't have self receiver,
|
||||
// cause external crate may call such methods to construct values of these types
|
||||
if let Some(local_impl_of) = impl_of.as_local()
|
||||
&& let Some(local_def_id) = def_id.as_local()
|
||||
&& let Some(fn_sig) =
|
||||
self.tcx.hir().fn_sig_by_hir_id(self.tcx.local_def_id_to_hir_id(local_def_id))
|
||||
&& matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None)
|
||||
&& let TyKind::Path(hir::QPath::Resolved(_, path)) =
|
||||
self.tcx.hir().expect_item(local_impl_of).expect_impl().self_ty.kind
|
||||
&& let Res::Def(def_kind, did) = path.res
|
||||
{
|
||||
match def_kind {
|
||||
// for example, #[derive(Default)] pub struct T(i32);
|
||||
// external crate can call T::default() to construct T,
|
||||
// so that don't ignore impl Default for pub Enum and Structs
|
||||
DefKind::Struct | DefKind::Union if self.tcx.visibility(did).is_public() => {
|
||||
return false;
|
||||
}
|
||||
// don't ignore impl Default for Enums,
|
||||
// cause we don't know which variant is constructed
|
||||
DefKind::Enum => return false,
|
||||
_ => (),
|
||||
};
|
||||
}
|
||||
|
||||
if let Some(trait_of) = self.tcx.trait_id_of_impl(impl_of)
|
||||
&& self.tcx.has_attr(trait_of, sym::rustc_trivial_field_reads)
|
||||
{
|
||||
@ -467,7 +420,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
|
||||
intravisit::walk_item(self, item)
|
||||
}
|
||||
hir::ItemKind::ForeignMod { .. } => {}
|
||||
hir::ItemKind::Trait(_, _, _, _, trait_item_refs) => {
|
||||
hir::ItemKind::Trait(..) => {
|
||||
for impl_def_id in self.tcx.all_impls(item.owner_id.to_def_id()) {
|
||||
if let Some(local_def_id) = impl_def_id.as_local()
|
||||
&& let ItemKind::Impl(impl_ref) =
|
||||
@ -480,12 +433,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
|
||||
intravisit::walk_path(self, impl_ref.of_trait.unwrap().path);
|
||||
}
|
||||
}
|
||||
// mark assoc ty live if the trait is live
|
||||
for trait_item in trait_item_refs {
|
||||
if let hir::AssocItemKind::Type = trait_item.kind {
|
||||
self.check_def_id(trait_item.id.owner_id.to_def_id());
|
||||
}
|
||||
}
|
||||
|
||||
intravisit::walk_item(self, item)
|
||||
}
|
||||
_ => intravisit::walk_item(self, item),
|
||||
@ -502,12 +450,11 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
|
||||
&& let ItemKind::Impl(impl_ref) =
|
||||
self.tcx.hir().expect_item(local_impl_id).kind
|
||||
{
|
||||
if !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty)
|
||||
.ty_and_all_fields_are_public
|
||||
if !matches!(trait_item.kind, hir::TraitItemKind::Type(..))
|
||||
&& !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty)
|
||||
{
|
||||
// skip impl-items of non pure pub ty,
|
||||
// cause we don't know the ty is constructed or not,
|
||||
// check these later in `solve_rest_impl_items`
|
||||
// skip methods of private ty,
|
||||
// they would be solved in `solve_rest_impl_items`
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -582,25 +529,28 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
|
||||
}
|
||||
|
||||
fn impl_item_with_used_self(&mut self, impl_id: hir::ItemId, impl_item_id: LocalDefId) -> bool {
|
||||
if let Some((local_def_id, def_kind)) =
|
||||
adt_of(self.tcx.hir().item(impl_id).expect_impl().self_ty)
|
||||
if let TyKind::Path(hir::QPath::Resolved(_, path)) =
|
||||
self.tcx.hir().item(impl_id).expect_impl().self_ty.kind
|
||||
&& let Res::Def(def_kind, def_id) = path.res
|
||||
&& let Some(local_def_id) = def_id.as_local()
|
||||
&& matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union)
|
||||
{
|
||||
if self.tcx.visibility(impl_item_id).is_public() {
|
||||
// for the public method, we don't know the trait item is used or not,
|
||||
// so we mark the method live if the self is used
|
||||
return self.live_symbols.contains(&local_def_id);
|
||||
}
|
||||
|
||||
if let Some(trait_item_id) = self.tcx.associated_item(impl_item_id).trait_item_def_id
|
||||
&& let Some(local_id) = trait_item_id.as_local()
|
||||
{
|
||||
// for the local impl item, we can know the trait item is used or not,
|
||||
// for the private method, we can know the trait item is used or not,
|
||||
// so we mark the method live if the self is used and the trait item is used
|
||||
self.live_symbols.contains(&local_id) && self.live_symbols.contains(&local_def_id)
|
||||
} else {
|
||||
// for the foreign method and inherent pub method,
|
||||
// we don't know the trait item or the method is used or not,
|
||||
// so we mark the method live if the self is used
|
||||
self.live_symbols.contains(&local_def_id)
|
||||
return self.live_symbols.contains(&local_id)
|
||||
&& self.live_symbols.contains(&local_def_id);
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@ -686,9 +636,6 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
|
||||
self.handle_field_pattern_match(pat, res, fields);
|
||||
}
|
||||
PatKind::Path(ref qpath) => {
|
||||
if let ty::Adt(adt, _) = self.typeck_results().node_type(pat.hir_id).kind() {
|
||||
self.check_def_id(adt.did());
|
||||
}
|
||||
let res = self.typeck_results().qpath_res(qpath, pat.hir_id);
|
||||
self.handle_res(res);
|
||||
}
|
||||
@ -825,9 +772,7 @@ fn check_item<'tcx>(
|
||||
.iter()
|
||||
.filter_map(|def_id| def_id.as_local());
|
||||
|
||||
let self_ty = tcx.hir().item(id).expect_impl().self_ty;
|
||||
let Publicness { ty_is_public, ty_and_all_fields_are_public } =
|
||||
ty_ref_to_pub_struct(tcx, self_ty);
|
||||
let ty_is_pub = ty_ref_to_pub_struct(tcx, tcx.hir().item(id).expect_impl().self_ty);
|
||||
|
||||
// And we access the Map here to get HirId from LocalDefId
|
||||
for local_def_id in local_def_ids {
|
||||
@ -843,19 +788,18 @@ fn check_item<'tcx>(
|
||||
// for trait impl blocks,
|
||||
// mark the method live if the self_ty is public,
|
||||
// or the method is public and may construct self
|
||||
if tcx.visibility(local_def_id).is_public()
|
||||
&& (ty_and_all_fields_are_public || (ty_is_public && may_construct_self))
|
||||
if of_trait
|
||||
&& (!matches!(tcx.def_kind(local_def_id), DefKind::AssocFn)
|
||||
|| tcx.visibility(local_def_id).is_public()
|
||||
&& (ty_is_pub || may_construct_self))
|
||||
{
|
||||
// if the impl item is public,
|
||||
// and the ty may be constructed or can be constructed in foreign crates,
|
||||
// mark the impl item live
|
||||
worklist.push((local_def_id, ComesFromAllowExpect::No));
|
||||
} else if let Some(comes_from_allow) =
|
||||
has_allow_dead_code_or_lang_attr(tcx, local_def_id)
|
||||
{
|
||||
worklist.push((local_def_id, comes_from_allow));
|
||||
} else if of_trait || tcx.visibility(local_def_id).is_public() && ty_is_public {
|
||||
// private impl items of traits || public impl items not constructs self
|
||||
} else if of_trait {
|
||||
// private method || public method not constructs self
|
||||
unsolved_impl_items.push((id, local_def_id));
|
||||
}
|
||||
}
|
||||
@ -881,13 +825,10 @@ fn check_trait_item(
|
||||
worklist: &mut Vec<(LocalDefId, ComesFromAllowExpect)>,
|
||||
id: hir::TraitItemId,
|
||||
) {
|
||||
use hir::TraitItemKind::{Const, Fn, Type};
|
||||
if matches!(
|
||||
tcx.def_kind(id.owner_id),
|
||||
DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn
|
||||
) {
|
||||
use hir::TraitItemKind::{Const, Fn};
|
||||
if matches!(tcx.def_kind(id.owner_id), DefKind::AssocConst | DefKind::AssocFn) {
|
||||
let trait_item = tcx.hir().trait_item(id);
|
||||
if matches!(trait_item.kind, Const(_, Some(_)) | Type(_, Some(_)) | Fn(..))
|
||||
if matches!(trait_item.kind, Const(_, Some(_)) | Fn(..))
|
||||
&& let Some(comes_from_allow) =
|
||||
has_allow_dead_code_or_lang_attr(tcx, trait_item.owner_id.def_id)
|
||||
{
|
||||
@ -925,14 +866,6 @@ fn create_and_seed_worklist(
|
||||
effective_vis
|
||||
.is_public_at_level(Level::Reachable)
|
||||
.then_some(id)
|
||||
.filter(|&id|
|
||||
// checks impls, impl-items and pub structs with all public fields later
|
||||
match tcx.def_kind(id) {
|
||||
DefKind::Impl { .. } => false,
|
||||
DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn => !matches!(tcx.associated_item(id).container, AssocItemContainer::ImplContainer),
|
||||
DefKind::Struct => struct_all_fields_are_public(tcx, id) || has_allow_dead_code_or_lang_attr(tcx, id).is_some(),
|
||||
_ => true
|
||||
})
|
||||
.map(|id| (id, ComesFromAllowExpect::No))
|
||||
})
|
||||
// Seed entry point
|
||||
@ -1216,7 +1149,6 @@ impl<'tcx> DeadVisitor<'tcx> {
|
||||
}
|
||||
match self.tcx.def_kind(def_id) {
|
||||
DefKind::AssocConst
|
||||
| DefKind::AssocTy
|
||||
| DefKind::AssocFn
|
||||
| DefKind::Fn
|
||||
| DefKind::Static { .. }
|
||||
@ -1258,14 +1190,10 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
|
||||
|| (def_kind == DefKind::Trait && live_symbols.contains(&item.owner_id.def_id))
|
||||
{
|
||||
for &def_id in tcx.associated_item_def_ids(item.owner_id.def_id) {
|
||||
// We have diagnosed unused assocs in traits
|
||||
// We have diagnosed unused methods in traits
|
||||
if matches!(def_kind, DefKind::Impl { of_trait: true })
|
||||
&& matches!(tcx.def_kind(def_id), DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn)
|
||||
// skip unused public inherent methods,
|
||||
// cause we have diagnosed unconstructed struct
|
||||
|| matches!(def_kind, DefKind::Impl { of_trait: false })
|
||||
&& tcx.visibility(def_id).is_public()
|
||||
&& ty_ref_to_pub_struct(tcx, tcx.hir().item(item).expect_impl().self_ty).ty_is_public
|
||||
&& tcx.def_kind(def_id) == DefKind::AssocFn
|
||||
|| def_kind == DefKind::Trait && tcx.def_kind(def_id) != DefKind::AssocFn
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
@ -636,6 +636,13 @@ pub struct Confusables {
|
||||
pub attr_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_coroutine_on_non_closure)]
|
||||
pub struct CoroutineOnNonClosure {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_empty_confusables)]
|
||||
pub(crate) struct EmptyConfusables {
|
||||
|
@ -588,7 +588,7 @@ pub fn report_cycle<'a>(
|
||||
cycle_stack,
|
||||
stack_bottom: stack[0].query.description.to_owned(),
|
||||
alias,
|
||||
cycle_usage: cycle_usage,
|
||||
cycle_usage,
|
||||
stack_count,
|
||||
note_span: (),
|
||||
};
|
||||
|
@ -19,6 +19,11 @@ pub struct FileSearch<'a> {
|
||||
}
|
||||
|
||||
impl<'a> FileSearch<'a> {
|
||||
pub fn cli_search_paths(&self) -> impl Iterator<Item = &'a SearchPath> {
|
||||
let kind = self.kind;
|
||||
self.cli_search_paths.iter().filter(move |sp| sp.kind.matches(kind))
|
||||
}
|
||||
|
||||
pub fn search_paths(&self) -> impl Iterator<Item = &'a SearchPath> {
|
||||
let kind = self.kind;
|
||||
self.cli_search_paths
|
||||
|
@ -1705,6 +1705,7 @@ symbols! {
|
||||
self_in_typedefs,
|
||||
self_struct_ctor,
|
||||
semitransparent,
|
||||
sha512_sm_x86,
|
||||
shadow_call_stack,
|
||||
shl,
|
||||
shl_assign,
|
||||
|
@ -238,6 +238,9 @@ const X86_ALLOWED_FEATURES: &[(&str, Stability)] = &[
|
||||
("rdseed", Stable),
|
||||
("rtm", Unstable(sym::rtm_target_feature)),
|
||||
("sha", Stable),
|
||||
("sha512", Unstable(sym::sha512_sm_x86)),
|
||||
("sm3", Unstable(sym::sha512_sm_x86)),
|
||||
("sm4", Unstable(sym::sha512_sm_x86)),
|
||||
("sse", Stable),
|
||||
("sse2", Stable),
|
||||
("sse3", Stable),
|
||||
|
@ -1173,6 +1173,7 @@ impl<T: ?Sized, A: Allocator> Box<T, A> {
|
||||
/// ```
|
||||
///
|
||||
/// [memory layout]: self#memory-layout
|
||||
#[must_use = "losing the pointer will leak memory"]
|
||||
#[stable(feature = "box_raw", since = "1.4.0")]
|
||||
#[inline]
|
||||
pub fn into_raw(b: Self) -> *mut T {
|
||||
@ -1226,6 +1227,7 @@ impl<T: ?Sized, A: Allocator> Box<T, A> {
|
||||
/// ```
|
||||
///
|
||||
/// [memory layout]: self#memory-layout
|
||||
#[must_use = "losing the pointer will leak memory"]
|
||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||
#[inline]
|
||||
pub fn into_raw_with_allocator(b: Self) -> (*mut T, A) {
|
||||
|
@ -1372,6 +1372,7 @@ impl<T: ?Sized, A: Allocator> Rc<T, A> {
|
||||
/// let x = unsafe { Rc::from_raw_in(ptr, alloc) };
|
||||
/// assert_eq!(&*x, "hello");
|
||||
/// ```
|
||||
#[must_use = "losing the pointer will leak memory"]
|
||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||
pub fn into_raw_with_allocator(this: Self) -> (*const T, A) {
|
||||
let this = mem::ManuallyDrop::new(this);
|
||||
@ -3107,6 +3108,7 @@ impl<T: ?Sized, A: Allocator> Weak<T, A> {
|
||||
///
|
||||
/// [`from_raw_in`]: Weak::from_raw_in
|
||||
/// [`as_ptr`]: Weak::as_ptr
|
||||
#[must_use = "losing the pointer will leak memory"]
|
||||
#[inline]
|
||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||
pub fn into_raw_with_allocator(self) -> (*const T, A) {
|
||||
|
@ -900,7 +900,7 @@ impl String {
|
||||
/// let rebuilt = unsafe { String::from_raw_parts(ptr, len, cap) };
|
||||
/// assert_eq!(rebuilt, "hello");
|
||||
/// ```
|
||||
#[must_use = "`self` will be dropped if the result is not used"]
|
||||
#[must_use = "losing the pointer will leak memory"]
|
||||
#[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")]
|
||||
pub fn into_raw_parts(self) -> (*mut u8, usize, usize) {
|
||||
self.vec.into_raw_parts()
|
||||
|
@ -878,6 +878,7 @@ impl<T, A: Allocator> Vec<T, A> {
|
||||
/// };
|
||||
/// assert_eq!(rebuilt, [4294967295, 0, 1]);
|
||||
/// ```
|
||||
#[must_use = "losing the pointer will leak memory"]
|
||||
#[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")]
|
||||
pub fn into_raw_parts(self) -> (*mut T, usize, usize) {
|
||||
let mut me = ManuallyDrop::new(self);
|
||||
@ -921,6 +922,7 @@ impl<T, A: Allocator> Vec<T, A> {
|
||||
/// };
|
||||
/// assert_eq!(rebuilt, [4294967295, 0, 1]);
|
||||
/// ```
|
||||
#[must_use = "losing the pointer will leak memory"]
|
||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||
// #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")]
|
||||
pub fn into_raw_parts_with_alloc(self) -> (*mut T, usize, usize, A) {
|
||||
|
@ -4,6 +4,15 @@
|
||||
#[stable(feature = "simd_arch", since = "1.27.0")]
|
||||
pub use crate::core_arch::arch::*;
|
||||
|
||||
#[cfg(bootstrap)]
|
||||
#[allow(dead_code)]
|
||||
#[unstable(feature = "sha512_sm_x86", issue = "126624")]
|
||||
fn dummy() {
|
||||
// AArch64 also has a target feature named `sm4`, so we need `#![feature(sha512_sm_x86)]` in lib.rs
|
||||
// But as the bootstrap compiler doesn't know about this feature yet, we need to convert it to a
|
||||
// library feature until bootstrap gets bumped
|
||||
}
|
||||
|
||||
/// Inline assembly.
|
||||
///
|
||||
/// Refer to [Rust By Example] for a usage guide and the [reference] for
|
||||
|
@ -103,6 +103,7 @@ use crate::ascii::Char as AsciiChar;
|
||||
/// ```
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "Default")]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[cfg_attr(not(bootstrap), rustc_trivial_field_reads)]
|
||||
pub trait Default: Sized {
|
||||
/// Returns the "default value" for a type.
|
||||
///
|
||||
|
@ -2436,11 +2436,13 @@ extern "rust-intrinsic" {
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// It's UB to call this if any of the *bytes* in `*a` or `*b` are uninitialized or carry a
|
||||
/// pointer value.
|
||||
/// It's UB to call this if any of the *bytes* in `*a` or `*b` are uninitialized.
|
||||
/// Note that this is a stricter criterion than just the *values* being
|
||||
/// fully-initialized: if `T` has padding, it's UB to call this intrinsic.
|
||||
///
|
||||
/// At compile-time, it is furthermore UB to call this if any of the bytes
|
||||
/// in `*a` or `*b` have provenance.
|
||||
///
|
||||
/// (The implementation is allowed to branch on the results of comparisons,
|
||||
/// which is UB if any of their inputs are `undef`.)
|
||||
#[rustc_const_unstable(feature = "const_intrinsic_raw_eq", issue = "none")]
|
||||
|
@ -114,19 +114,12 @@ impl<A: Clone> Iterator for RepeatN<A> {
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<A> {
|
||||
if self.count == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
self.count -= 1;
|
||||
Some(if self.count == 0 {
|
||||
// SAFETY: the check above ensured that the count used to be non-zero,
|
||||
// so element hasn't been dropped yet, and we just lowered the count to
|
||||
// zero so it won't be dropped later, and thus it's okay to take it here.
|
||||
unsafe { ManuallyDrop::take(&mut self.element) }
|
||||
if self.count > 0 {
|
||||
// SAFETY: Just checked it's not empty
|
||||
unsafe { Some(self.next_unchecked()) }
|
||||
} else {
|
||||
A::clone(&self.element)
|
||||
})
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -194,4 +187,18 @@ impl<A: Clone> FusedIterator for RepeatN<A> {}
|
||||
#[unstable(feature = "trusted_len", issue = "37572")]
|
||||
unsafe impl<A: Clone> TrustedLen for RepeatN<A> {}
|
||||
#[unstable(feature = "trusted_len_next_unchecked", issue = "37572")]
|
||||
impl<A: Clone> UncheckedIterator for RepeatN<A> {}
|
||||
impl<A: Clone> UncheckedIterator for RepeatN<A> {
|
||||
#[inline]
|
||||
unsafe fn next_unchecked(&mut self) -> Self::Item {
|
||||
// SAFETY: The caller promised the iterator isn't empty
|
||||
self.count = unsafe { self.count.unchecked_sub(1) };
|
||||
if self.count == 0 {
|
||||
// SAFETY: the check above ensured that the count used to be non-zero,
|
||||
// so element hasn't been dropped yet, and we just lowered the count to
|
||||
// zero so it won't be dropped later, and thus it's okay to take it here.
|
||||
unsafe { ManuallyDrop::take(&mut self.element) }
|
||||
} else {
|
||||
A::clone(&self.element)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -260,6 +260,7 @@
|
||||
#![feature(powerpc_target_feature)]
|
||||
#![feature(riscv_target_feature)]
|
||||
#![feature(rtm_target_feature)]
|
||||
#![feature(sha512_sm_x86)]
|
||||
#![feature(sse4a_target_feature)]
|
||||
#![feature(tbm_target_feature)]
|
||||
#![feature(wasm_target_feature)]
|
||||
|
@ -7,7 +7,7 @@
|
||||
#![stable(feature = "rust1", since = "1.0.0")]
|
||||
|
||||
use crate::cmp::Ordering::{self, Equal, Greater, Less};
|
||||
use crate::intrinsics::{exact_div, unchecked_sub};
|
||||
use crate::intrinsics::{exact_div, select_unpredictable, unchecked_sub};
|
||||
use crate::mem::{self, SizedTypeProperties};
|
||||
use crate::num::NonZero;
|
||||
use crate::ops::{Bound, OneSidedRange, Range, RangeBounds};
|
||||
@ -2770,41 +2770,54 @@ impl<T> [T] {
|
||||
where
|
||||
F: FnMut(&'a T) -> Ordering,
|
||||
{
|
||||
// INVARIANTS:
|
||||
// - 0 <= left <= left + size = right <= self.len()
|
||||
// - f returns Less for everything in self[..left]
|
||||
// - f returns Greater for everything in self[right..]
|
||||
let mut size = self.len();
|
||||
let mut left = 0;
|
||||
let mut right = size;
|
||||
while left < right {
|
||||
let mid = left + size / 2;
|
||||
if size == 0 {
|
||||
return Err(0);
|
||||
}
|
||||
let mut base = 0usize;
|
||||
|
||||
// SAFETY: the while condition means `size` is strictly positive, so
|
||||
// `size/2 < size`. Thus `left + size/2 < left + size`, which
|
||||
// coupled with the `left + size <= self.len()` invariant means
|
||||
// we have `left + size/2 < self.len()`, and this is in-bounds.
|
||||
// This loop intentionally doesn't have an early exit if the comparison
|
||||
// returns Equal. We want the number of loop iterations to depend *only*
|
||||
// on the size of the input slice so that the CPU can reliably predict
|
||||
// the loop count.
|
||||
while size > 1 {
|
||||
let half = size / 2;
|
||||
let mid = base + half;
|
||||
|
||||
// SAFETY: the call is made safe by the following inconstants:
|
||||
// - `mid >= 0`: by definition
|
||||
// - `mid < size`: `mid = size / 2 + size / 4 + size / 8 ...`
|
||||
let cmp = f(unsafe { self.get_unchecked(mid) });
|
||||
|
||||
// This control flow produces conditional moves, which results in
|
||||
// fewer branches and instructions than if/else or matching on
|
||||
// cmp::Ordering.
|
||||
// This is x86 asm for u8: https://rust.godbolt.org/z/698eYffTx.
|
||||
left = if cmp == Less { mid + 1 } else { left };
|
||||
right = if cmp == Greater { mid } else { right };
|
||||
if cmp == Equal {
|
||||
// SAFETY: same as the `get_unchecked` above
|
||||
unsafe { hint::assert_unchecked(mid < self.len()) };
|
||||
return Ok(mid);
|
||||
}
|
||||
// Binary search interacts poorly with branch prediction, so force
|
||||
// the compiler to use conditional moves if supported by the target
|
||||
// architecture.
|
||||
base = select_unpredictable(cmp == Greater, base, mid);
|
||||
|
||||
size = right - left;
|
||||
// This is imprecise in the case where `size` is odd and the
|
||||
// comparison returns Greater: the mid element still gets included
|
||||
// by `size` even though it's known to be larger than the element
|
||||
// being searched for.
|
||||
//
|
||||
// This is fine though: we gain more performance by keeping the
|
||||
// loop iteration count invariant (and thus predictable) than we
|
||||
// lose from considering one additional element.
|
||||
size -= half;
|
||||
}
|
||||
|
||||
// SAFETY: directly true from the overall invariant.
|
||||
// Note that this is `<=`, unlike the assume in the `Ok` path.
|
||||
unsafe { hint::assert_unchecked(left <= self.len()) };
|
||||
Err(left)
|
||||
// SAFETY: base is always in [0, size) because base <= mid.
|
||||
let cmp = f(unsafe { self.get_unchecked(base) });
|
||||
if cmp == Equal {
|
||||
// SAFETY: same as the `get_unchecked` above.
|
||||
unsafe { hint::assert_unchecked(base < self.len()) };
|
||||
Ok(base)
|
||||
} else {
|
||||
let result = base + (cmp == Less) as usize;
|
||||
// SAFETY: same as the `get_unchecked` above.
|
||||
// Note that this is `<=`, unlike the assume in the `Ok` path.
|
||||
unsafe { hint::assert_unchecked(result <= self.len()) };
|
||||
Err(result)
|
||||
}
|
||||
}
|
||||
|
||||
/// Binary searches this slice with a key extraction function.
|
||||
|
@ -69,13 +69,13 @@ fn test_binary_search() {
|
||||
assert_eq!(b.binary_search(&8), Err(5));
|
||||
|
||||
let b = [(); usize::MAX];
|
||||
assert_eq!(b.binary_search(&()), Ok(usize::MAX / 2));
|
||||
assert_eq!(b.binary_search(&()), Ok(usize::MAX - 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_binary_search_by_overflow() {
|
||||
let b = [(); usize::MAX];
|
||||
assert_eq!(b.binary_search_by(|_| Ordering::Equal), Ok(usize::MAX / 2));
|
||||
assert_eq!(b.binary_search_by(|_| Ordering::Equal), Ok(usize::MAX - 1));
|
||||
assert_eq!(b.binary_search_by(|_| Ordering::Greater), Err(0));
|
||||
assert_eq!(b.binary_search_by(|_| Ordering::Less), Err(usize::MAX));
|
||||
}
|
||||
@ -87,13 +87,13 @@ fn test_binary_search_implementation_details() {
|
||||
let b = [1, 1, 2, 2, 3, 3, 3];
|
||||
assert_eq!(b.binary_search(&1), Ok(1));
|
||||
assert_eq!(b.binary_search(&2), Ok(3));
|
||||
assert_eq!(b.binary_search(&3), Ok(5));
|
||||
assert_eq!(b.binary_search(&3), Ok(6));
|
||||
let b = [1, 1, 1, 1, 1, 3, 3, 3, 3];
|
||||
assert_eq!(b.binary_search(&1), Ok(4));
|
||||
assert_eq!(b.binary_search(&3), Ok(7));
|
||||
assert_eq!(b.binary_search(&3), Ok(8));
|
||||
let b = [1, 1, 1, 1, 3, 3, 3, 3, 3];
|
||||
assert_eq!(b.binary_search(&1), Ok(2));
|
||||
assert_eq!(b.binary_search(&3), Ok(4));
|
||||
assert_eq!(b.binary_search(&1), Ok(3));
|
||||
assert_eq!(b.binary_search(&3), Ok(8));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -269,10 +269,6 @@
|
||||
#![cfg_attr(any(windows, target_os = "uefi"), feature(round_char_boundary))]
|
||||
#![cfg_attr(target_family = "wasm", feature(stdarch_wasm_atomic_wait))]
|
||||
#![cfg_attr(target_arch = "wasm64", feature(simd_wasm64))]
|
||||
#![cfg_attr(
|
||||
all(any(target_arch = "x86_64", target_arch = "x86"), target_os = "uefi"),
|
||||
feature(stdarch_x86_has_cpuid)
|
||||
)]
|
||||
//
|
||||
// Language features:
|
||||
// tidy-alphabetical-start
|
||||
@ -302,6 +298,7 @@
|
||||
#![feature(let_chains)]
|
||||
#![feature(link_cfg)]
|
||||
#![feature(linkage)]
|
||||
#![feature(macro_metavar_expr_concat)]
|
||||
#![feature(min_exhaustive_patterns)]
|
||||
#![feature(min_specialization)]
|
||||
#![feature(must_not_suspend)]
|
||||
@ -670,7 +667,6 @@ mod panicking;
|
||||
#[allow(dead_code, unused_attributes, fuzzy_provenance_casts, unsafe_op_in_unsafe_fn)]
|
||||
mod backtrace_rs;
|
||||
|
||||
// Re-export macros defined in core.
|
||||
#[unstable(feature = "cfg_match", issue = "115585")]
|
||||
pub use core::cfg_match;
|
||||
#[unstable(
|
||||
@ -689,6 +685,7 @@ pub use core::{
|
||||
env, file, format_args, format_args_nl, include, include_bytes, include_str, line, log_syntax,
|
||||
module_path, option_env, stringify, trace_macros,
|
||||
};
|
||||
// Re-export macros defined in core.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[allow(deprecated, deprecated_in_future)]
|
||||
pub use core::{
|
||||
|
@ -138,6 +138,7 @@ pub trait IntoRawFd {
|
||||
/// let raw_fd: RawFd = f.into_raw_fd();
|
||||
/// # Ok::<(), io::Error>(())
|
||||
/// ```
|
||||
#[must_use = "losing the raw file descriptor may leak resources"]
|
||||
#[stable(feature = "into_raw_os", since = "1.4.0")]
|
||||
fn into_raw_fd(self) -> RawFd;
|
||||
}
|
||||
|
@ -342,6 +342,7 @@ pub trait IntoRawFd {
|
||||
/// This function **transfers ownership** of the underlying file descriptor
|
||||
/// to the caller. Callers are then the unique owners of the file descriptor
|
||||
/// and must close the descriptor once it's no longer needed.
|
||||
#[must_use = "losing the raw file descriptor may leak resources"]
|
||||
fn into_raw_fd(self) -> RawFd;
|
||||
}
|
||||
|
||||
|
@ -85,6 +85,7 @@ pub trait IntoRawHandle {
|
||||
/// However, transferring ownership is not strictly required. Use a
|
||||
/// `Into<OwnedHandle>::into` implementation for an API which strictly
|
||||
/// transfers ownership.
|
||||
#[must_use = "losing the raw handle may leak resources"]
|
||||
#[stable(feature = "into_raw_os", since = "1.4.0")]
|
||||
fn into_raw_handle(self) -> RawHandle;
|
||||
}
|
||||
@ -228,6 +229,7 @@ pub trait IntoRawSocket {
|
||||
/// However, transferring ownership is not strictly required. Use a
|
||||
/// `Into<OwnedSocket>::into` implementation for an API which strictly
|
||||
/// transfers ownership.
|
||||
#[must_use = "losing the raw socket may leak resources"]
|
||||
#[stable(feature = "into_raw_os", since = "1.4.0")]
|
||||
fn into_raw_socket(self) -> RawSocket;
|
||||
}
|
||||
|
@ -463,11 +463,10 @@ static SHOULD_CAPTURE: AtomicU8 = AtomicU8::new(0);
|
||||
/// environment variable; see the details in [`get_backtrace_style`].
|
||||
#[unstable(feature = "panic_backtrace_config", issue = "93346")]
|
||||
pub fn set_backtrace_style(style: BacktraceStyle) {
|
||||
if !cfg!(feature = "backtrace") {
|
||||
// If the `backtrace` feature of this crate isn't enabled, skip setting.
|
||||
return;
|
||||
if cfg!(feature = "backtrace") {
|
||||
// If the `backtrace` feature of this crate is enabled, set the backtrace style.
|
||||
SHOULD_CAPTURE.store(style.as_u8(), Ordering::Release);
|
||||
}
|
||||
SHOULD_CAPTURE.store(style.as_u8(), Ordering::Release);
|
||||
}
|
||||
|
||||
/// Checks whether the standard library's panic hook will capture and print a
|
||||
@ -503,21 +502,13 @@ pub fn get_backtrace_style() -> Option<BacktraceStyle> {
|
||||
return Some(style);
|
||||
}
|
||||
|
||||
let format = crate::env::var_os("RUST_BACKTRACE")
|
||||
.map(|x| {
|
||||
if &x == "0" {
|
||||
BacktraceStyle::Off
|
||||
} else if &x == "full" {
|
||||
BacktraceStyle::Full
|
||||
} else {
|
||||
BacktraceStyle::Short
|
||||
}
|
||||
})
|
||||
.unwrap_or(if crate::sys::FULL_BACKTRACE_DEFAULT {
|
||||
BacktraceStyle::Full
|
||||
} else {
|
||||
BacktraceStyle::Off
|
||||
});
|
||||
let format = match crate::env::var_os("RUST_BACKTRACE") {
|
||||
Some(x) if &x == "0" => BacktraceStyle::Off,
|
||||
Some(x) if &x == "full" => BacktraceStyle::Full,
|
||||
Some(_) => BacktraceStyle::Short,
|
||||
None if crate::sys::FULL_BACKTRACE_DEFAULT => BacktraceStyle::Full,
|
||||
None => BacktraceStyle::Off,
|
||||
};
|
||||
set_backtrace_style(format);
|
||||
Some(format)
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
#![cfg_attr(test, allow(dead_code))] // why is this necessary?
|
||||
|
||||
use super::abi::usercalls;
|
||||
use super::unsupported;
|
||||
use crate::ffi::CStr;
|
||||
|
@ -175,10 +175,6 @@ pub(crate) mod instant_internal {
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn timestamp_rdtsc() -> Option<Duration> {
|
||||
if !crate::arch::x86_64::has_cpuid() {
|
||||
return None;
|
||||
}
|
||||
|
||||
static FREQUENCY: crate::sync::OnceLock<u64> = crate::sync::OnceLock::new();
|
||||
|
||||
// Get Frequency in Mhz
|
||||
@ -200,10 +196,6 @@ pub(crate) mod instant_internal {
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
fn timestamp_rdtsc() -> Option<Duration> {
|
||||
if !crate::arch::x86::has_cpuid() {
|
||||
return None;
|
||||
}
|
||||
|
||||
static FREQUENCY: crate::sync::OnceLock<u64> = crate::sync::OnceLock::new();
|
||||
|
||||
let freq = FREQUENCY
|
||||
|
@ -60,6 +60,7 @@ use crate::net::TcpStream;
|
||||
use crate::os::unix::fs::FileTypeExt;
|
||||
use crate::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||
use crate::os::unix::net::UnixStream;
|
||||
use crate::pipe::{PipeReader, PipeWriter};
|
||||
use crate::process::{ChildStderr, ChildStdin, ChildStdout};
|
||||
use crate::ptr;
|
||||
use crate::sync::atomic::{AtomicBool, AtomicU8, Ordering};
|
||||
@ -405,6 +406,30 @@ impl CopyWrite for &UnixStream {
|
||||
}
|
||||
}
|
||||
|
||||
impl CopyRead for PipeReader {
|
||||
fn properties(&self) -> CopyParams {
|
||||
CopyParams(FdMeta::Pipe, Some(self.as_raw_fd()))
|
||||
}
|
||||
}
|
||||
|
||||
impl CopyRead for &PipeReader {
|
||||
fn properties(&self) -> CopyParams {
|
||||
CopyParams(FdMeta::Pipe, Some(self.as_raw_fd()))
|
||||
}
|
||||
}
|
||||
|
||||
impl CopyWrite for PipeWriter {
|
||||
fn properties(&self) -> CopyParams {
|
||||
CopyParams(FdMeta::Pipe, Some(self.as_raw_fd()))
|
||||
}
|
||||
}
|
||||
|
||||
impl CopyWrite for &PipeWriter {
|
||||
fn properties(&self) -> CopyParams {
|
||||
CopyParams(FdMeta::Pipe, Some(self.as_raw_fd()))
|
||||
}
|
||||
}
|
||||
|
||||
impl CopyWrite for ChildStdin {
|
||||
fn properties(&self) -> CopyParams {
|
||||
CopyParams(FdMeta::Pipe, Some(self.as_raw_fd()))
|
||||
|
@ -47,6 +47,8 @@ pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> {
|
||||
}
|
||||
|
||||
impl AnonPipe {
|
||||
#[allow(dead_code)]
|
||||
// FIXME: This function seems legitimately unused.
|
||||
pub fn try_clone(&self) -> io::Result<Self> {
|
||||
self.0.duplicate().map(Self)
|
||||
}
|
||||
@ -85,6 +87,8 @@ impl AnonPipe {
|
||||
self.0.is_write_vectored()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
// FIXME: This function seems legitimately unused.
|
||||
pub fn as_file_desc(&self) -> &FileDesc {
|
||||
&self.0
|
||||
}
|
||||
|
@ -168,14 +168,7 @@ pub(crate) macro syscall {
|
||||
if let Some(fun) = $name.get() {
|
||||
fun($($arg_name),*)
|
||||
} else {
|
||||
// This looks like a hack, but concat_idents only accepts idents
|
||||
// (not paths).
|
||||
use libc::*;
|
||||
|
||||
syscall(
|
||||
concat_idents!(SYS_, $name),
|
||||
$($arg_name),*
|
||||
) as $ret
|
||||
libc::syscall(libc::${concat(SYS_, $name)}, $($arg_name),*) as $ret
|
||||
}
|
||||
}
|
||||
)
|
||||
@ -185,14 +178,7 @@ pub(crate) macro syscall {
|
||||
pub(crate) macro raw_syscall {
|
||||
(fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => (
|
||||
unsafe fn $name($($arg_name:$t),*) -> $ret {
|
||||
// This looks like a hack, but concat_idents only accepts idents
|
||||
// (not paths).
|
||||
use libc::*;
|
||||
|
||||
syscall(
|
||||
concat_idents!(SYS_, $name),
|
||||
$($arg_name),*
|
||||
) as $ret
|
||||
libc::syscall(libc::${concat(SYS_, $name)}, $($arg_name),*) as $ret
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ pub mod netc {
|
||||
//!
|
||||
//! Some Windows API types are not quite what's expected by our cross-platform
|
||||
//! net code. E.g. naming differences or different pointer types.
|
||||
|
||||
use core::ffi::{c_char, c_int, c_uint, c_ulong, c_ushort, c_void};
|
||||
|
||||
use crate::sys::c::{self, ADDRESS_FAMILY, ADDRINFOA, SOCKADDR, SOCKET};
|
||||
|
@ -564,7 +564,7 @@ impl Stdio {
|
||||
Ok(io) => unsafe {
|
||||
let io = Handle::from_raw_handle(io);
|
||||
let ret = io.duplicate(0, true, c::DUPLICATE_SAME_ACCESS);
|
||||
io.into_raw_handle();
|
||||
let _ = io.into_raw_handle(); // Don't close the handle
|
||||
ret
|
||||
},
|
||||
// If no stdio handle is available, then propagate the null value.
|
||||
|
@ -94,7 +94,7 @@ fn write(handle_id: u32, data: &[u8], incomplete_utf8: &mut IncompleteUtf8) -> i
|
||||
unsafe {
|
||||
let handle = Handle::from_raw_handle(handle);
|
||||
let ret = handle.write(data);
|
||||
handle.into_raw_handle(); // Don't close the handle
|
||||
let _ = handle.into_raw_handle(); // Don't close the handle
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@ -243,7 +243,7 @@ impl io::Read for Stdin {
|
||||
unsafe {
|
||||
let handle = Handle::from_raw_handle(handle);
|
||||
let ret = handle.read(buf);
|
||||
handle.into_raw_handle(); // Don't close the handle
|
||||
let _ = handle.into_raw_handle(); // Don't close the handle
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ dependencies = [
|
||||
"termcolor",
|
||||
"toml",
|
||||
"walkdir",
|
||||
"windows",
|
||||
"windows 0.52.0",
|
||||
"xz2",
|
||||
]
|
||||
|
||||
@ -378,12 +378,6 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "opener"
|
||||
version = "0.5.2"
|
||||
@ -549,16 +543,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.30.5"
|
||||
version = "0.31.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fb4f3438c8f6389c864e61221cbc97e9bca98b4daf39a5beb7bea660f528bb2"
|
||||
checksum = "d4115055da5f572fff541dd0c4e61b0262977f453cc9fe04be83aba25a89bdab"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"memchr",
|
||||
"ntapi",
|
||||
"once_cell",
|
||||
"windows",
|
||||
"windows 0.57.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -655,7 +648,17 @@ version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-core 0.52.0",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143"
|
||||
dependencies = [
|
||||
"windows-core 0.57.0",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
@ -668,6 +671,49 @@ dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-result",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
@ -679,13 +725,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
@ -694,45 +741,51 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.0"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "xattr"
|
||||
|
@ -63,7 +63,7 @@ walkdir = "2.4"
|
||||
xz2 = "0.1"
|
||||
|
||||
# Dependencies needed by the build-metrics feature
|
||||
sysinfo = { version = "0.30", default-features = false, optional = true }
|
||||
sysinfo = { version = "0.31.2", default-features = false, optional = true, features = ["system"] }
|
||||
|
||||
[target.'cfg(windows)'.dependencies.junction]
|
||||
version = "1.0.0"
|
||||
|
@ -31,10 +31,6 @@ pub struct Std {
|
||||
}
|
||||
|
||||
impl Std {
|
||||
pub fn new(target: TargetSelection) -> Self {
|
||||
Self::new_with_build_kind(target, None)
|
||||
}
|
||||
|
||||
pub fn new_with_build_kind(target: TargetSelection, kind: Option<Kind>) -> Self {
|
||||
Self { target, crates: vec![], override_build_kind: kind }
|
||||
}
|
||||
|
@ -512,6 +512,11 @@ impl TargetSelection {
|
||||
pub fn is_windows(&self) -> bool {
|
||||
self.contains("windows")
|
||||
}
|
||||
|
||||
/// Path to the file defining the custom target, if any.
|
||||
pub fn filepath(&self) -> Option<&Path> {
|
||||
self.file.as_ref().map(Path::new)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TargetSelection {
|
||||
|
@ -260,7 +260,9 @@ than building it.
|
||||
|
||||
if !has_target {
|
||||
// This might also be a custom target, so check the target file that could have been specified by the user.
|
||||
if let Some(custom_target_path) = env::var_os("RUST_TARGET_PATH") {
|
||||
if target.filepath().is_some_and(|p| p.exists()) {
|
||||
has_target = true;
|
||||
} else if let Some(custom_target_path) = env::var_os("RUST_TARGET_PATH") {
|
||||
let mut target_filename = OsString::from(&target_str);
|
||||
// Target filename ends with `.json`.
|
||||
target_filename.push(".json");
|
||||
@ -275,8 +277,12 @@ than building it.
|
||||
|
||||
if !has_target {
|
||||
panic!(
|
||||
"No such target exists in the target list,
|
||||
specify a correct location of the JSON specification file for custom targets!"
|
||||
"No such target exists in the target list,\n\
|
||||
make sure to correctly specify the location \
|
||||
of the JSON specification file \
|
||||
for custom targets!\n\
|
||||
Use BOOTSTRAP_SKIP_TARGET_SANITY=1 to \
|
||||
bypass this check."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ use build_helper::metrics::{
|
||||
JsonInvocation, JsonInvocationSystemStats, JsonNode, JsonRoot, JsonStepSystemStats, Test,
|
||||
TestOutcome, TestSuite, TestSuiteMetadata,
|
||||
};
|
||||
use sysinfo::System;
|
||||
use sysinfo::{CpuRefreshKind, RefreshKind, System};
|
||||
|
||||
use crate::core::builder::{Builder, Step};
|
||||
use crate::utils::helpers::t;
|
||||
@ -55,7 +55,9 @@ impl BuildMetrics {
|
||||
finished_steps: Vec::new(),
|
||||
running_steps: Vec::new(),
|
||||
|
||||
system_info: System::new(),
|
||||
system_info: System::new_with_specifics(
|
||||
RefreshKind::new().with_cpu(CpuRefreshKind::everything()),
|
||||
),
|
||||
timer_start: None,
|
||||
invocation_timer_start: Instant::now(),
|
||||
invocation_start: SystemTime::now(),
|
||||
@ -77,7 +79,7 @@ impl BuildMetrics {
|
||||
self.collect_stats(&mut *state);
|
||||
}
|
||||
|
||||
state.system_info.refresh_cpu();
|
||||
state.system_info.refresh_cpu_usage();
|
||||
state.timer_start = Some(Instant::now());
|
||||
|
||||
state.running_steps.push(StepMetrics {
|
||||
@ -110,7 +112,7 @@ impl BuildMetrics {
|
||||
state.running_steps.last_mut().unwrap().children.push(step);
|
||||
|
||||
// Start collecting again for the parent step.
|
||||
state.system_info.refresh_cpu();
|
||||
state.system_info.refresh_cpu_usage();
|
||||
state.timer_start = Some(Instant::now());
|
||||
}
|
||||
}
|
||||
@ -148,7 +150,7 @@ impl BuildMetrics {
|
||||
let elapsed = state.timer_start.unwrap().elapsed();
|
||||
step.duration_excluding_children_sec += elapsed;
|
||||
|
||||
state.system_info.refresh_cpu();
|
||||
state.system_info.refresh_cpu_usage();
|
||||
let cpu = state.system_info.cpus().iter().map(|p| p.cpu_usage()).sum::<f32>();
|
||||
step.cpu_usage_time_sec += cpu as f64 / 100.0 * elapsed.as_secs_f64();
|
||||
}
|
||||
@ -159,8 +161,9 @@ impl BuildMetrics {
|
||||
|
||||
let dest = build.out.join("metrics.json");
|
||||
|
||||
let mut system = System::new();
|
||||
system.refresh_cpu();
|
||||
let mut system =
|
||||
System::new_with_specifics(RefreshKind::new().with_cpu(CpuRefreshKind::everything()));
|
||||
system.refresh_cpu_usage();
|
||||
system.refresh_memory();
|
||||
|
||||
let system_stats = JsonInvocationSystemStats {
|
||||
|
@ -332,7 +332,6 @@ struct AllTypes {
|
||||
macros: FxHashSet<ItemEntry>,
|
||||
functions: FxHashSet<ItemEntry>,
|
||||
type_aliases: FxHashSet<ItemEntry>,
|
||||
opaque_tys: FxHashSet<ItemEntry>,
|
||||
statics: FxHashSet<ItemEntry>,
|
||||
constants: FxHashSet<ItemEntry>,
|
||||
attribute_macros: FxHashSet<ItemEntry>,
|
||||
@ -352,7 +351,6 @@ impl AllTypes {
|
||||
macros: new_set(100),
|
||||
functions: new_set(100),
|
||||
type_aliases: new_set(100),
|
||||
opaque_tys: new_set(100),
|
||||
statics: new_set(100),
|
||||
constants: new_set(100),
|
||||
attribute_macros: new_set(100),
|
||||
@ -415,9 +413,6 @@ impl AllTypes {
|
||||
if !self.type_aliases.is_empty() {
|
||||
sections.insert(ItemSection::TypeAliases);
|
||||
}
|
||||
if !self.opaque_tys.is_empty() {
|
||||
sections.insert(ItemSection::OpaqueTypes);
|
||||
}
|
||||
if !self.statics.is_empty() {
|
||||
sections.insert(ItemSection::Statics);
|
||||
}
|
||||
@ -471,7 +466,6 @@ impl AllTypes {
|
||||
print_entries(f, &self.functions, ItemSection::Functions);
|
||||
print_entries(f, &self.type_aliases, ItemSection::TypeAliases);
|
||||
print_entries(f, &self.trait_aliases, ItemSection::TraitAliases);
|
||||
print_entries(f, &self.opaque_tys, ItemSection::OpaqueTypes);
|
||||
print_entries(f, &self.statics, ItemSection::Statics);
|
||||
print_entries(f, &self.constants, ItemSection::Constants);
|
||||
}
|
||||
@ -2174,7 +2168,6 @@ pub(crate) enum ItemSection {
|
||||
AssociatedConstants,
|
||||
ForeignTypes,
|
||||
Keywords,
|
||||
OpaqueTypes,
|
||||
AttributeMacros,
|
||||
DeriveMacros,
|
||||
TraitAliases,
|
||||
@ -2207,7 +2200,6 @@ impl ItemSection {
|
||||
AssociatedConstants,
|
||||
ForeignTypes,
|
||||
Keywords,
|
||||
OpaqueTypes,
|
||||
AttributeMacros,
|
||||
DeriveMacros,
|
||||
TraitAliases,
|
||||
@ -2237,7 +2229,6 @@ impl ItemSection {
|
||||
Self::AssociatedConstants => "associated-consts",
|
||||
Self::ForeignTypes => "foreign-types",
|
||||
Self::Keywords => "keywords",
|
||||
Self::OpaqueTypes => "opaque-types",
|
||||
Self::AttributeMacros => "attributes",
|
||||
Self::DeriveMacros => "derives",
|
||||
Self::TraitAliases => "trait-aliases",
|
||||
@ -2267,7 +2258,6 @@ impl ItemSection {
|
||||
Self::AssociatedConstants => "Associated Constants",
|
||||
Self::ForeignTypes => "Foreign Types",
|
||||
Self::Keywords => "Keywords",
|
||||
Self::OpaqueTypes => "Opaque Types",
|
||||
Self::AttributeMacros => "Attribute Macros",
|
||||
Self::DeriveMacros => "Derive Macros",
|
||||
Self::TraitAliases => "Trait Aliases",
|
||||
|
@ -568,7 +568,6 @@ function preLoadCss(cssUrl) {
|
||||
//block("associatedconstant", "associated-consts", "Associated Constants");
|
||||
block("foreigntype", "foreign-types", "Foreign Types");
|
||||
block("keyword", "keywords", "Keywords");
|
||||
block("opaque", "opaque-types", "Opaque Types");
|
||||
block("attr", "attributes", "Attribute Macros");
|
||||
block("derive", "derives", "Derive Macros");
|
||||
block("traitalias", "trait-aliases", "Trait Aliases");
|
||||
@ -1115,8 +1114,7 @@ function preLoadCss(cssUrl) {
|
||||
wrapper.style.left = 0;
|
||||
wrapper.style.right = "auto";
|
||||
wrapper.style.visibility = "hidden";
|
||||
const body = document.getElementsByTagName("body")[0];
|
||||
body.appendChild(wrapper);
|
||||
document.body.appendChild(wrapper);
|
||||
const wrapperPos = wrapper.getBoundingClientRect();
|
||||
// offset so that the arrow points at the center of the "(i)"
|
||||
const finalPos = pos.left + window.scrollX - wrapperPos.width + 24;
|
||||
@ -1235,8 +1233,7 @@ function preLoadCss(cssUrl) {
|
||||
}
|
||||
window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE = false;
|
||||
}
|
||||
const body = document.getElementsByTagName("body")[0];
|
||||
body.removeChild(window.CURRENT_TOOLTIP_ELEMENT);
|
||||
document.body.removeChild(window.CURRENT_TOOLTIP_ELEMENT);
|
||||
clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);
|
||||
window.CURRENT_TOOLTIP_ELEMENT = null;
|
||||
}
|
||||
@ -1833,7 +1830,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm
|
||||
let elem = event.target;
|
||||
while (!hasClass(elem, "example-wrap")) {
|
||||
elem = elem.parentElement;
|
||||
if (elem.tagName === "body" || hasClass(elem, "docblock")) {
|
||||
if (elem === document.body || hasClass(elem, "docblock")) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 257b72b8adfb1f2aa9916cefca67285c21666276
|
||||
Subproject commit fa646583675d7c140482bd906145c71b7fb4fc2b
|
@ -145,7 +145,9 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
|
||||
(
|
||||
"this function's return value is unnecessary".to_string(),
|
||||
"remove the return type...".to_string(),
|
||||
snippet(cx, fn_decl.output.span(), "..").to_string(),
|
||||
// FIXME: we should instead get the span including the `->` and suggest an
|
||||
// empty string for this case.
|
||||
"()".to_string(),
|
||||
"...and then remove returned values",
|
||||
)
|
||||
} else {
|
||||
|
@ -63,7 +63,7 @@ LL | let _val = None::<()>.expect("this always happens");
|
||||
help: remove the `None` and `expect()`
|
||||
|
|
||||
LL | let _val = panic!("this always happens");
|
||||
| ~~~~~~~ ~
|
||||
| ~~~~~~~
|
||||
|
||||
error: used `unwrap_or_default()` on `None` value
|
||||
--> tests/ui/unnecessary_literal_unwrap.rs:22:24
|
||||
@ -134,7 +134,7 @@ LL | None::<()>.expect("this always happens");
|
||||
help: remove the `None` and `expect()`
|
||||
|
|
||||
LL | panic!("this always happens");
|
||||
| ~~~~~~~ ~
|
||||
| ~~~~~~~
|
||||
|
||||
error: used `unwrap_or_default()` on `None` value
|
||||
--> tests/ui/unnecessary_literal_unwrap.rs:30:5
|
||||
|
@ -118,8 +118,8 @@ LL | | }
|
||||
|
|
||||
help: remove the return type...
|
||||
|
|
||||
LL | fn issue_6640_1(a: bool, b: bool) -> Option<()> {
|
||||
| ~~~~~~~~~~
|
||||
LL | fn issue_6640_1(a: bool, b: bool) -> () {
|
||||
| ~~
|
||||
help: ...and then remove returned values
|
||||
|
|
||||
LL ~ return ;
|
||||
@ -145,8 +145,8 @@ LL | | }
|
||||
|
|
||||
help: remove the return type...
|
||||
|
|
||||
LL | fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> {
|
||||
| ~~~~~~~~~~~~~~~
|
||||
LL | fn issue_6640_2(a: bool, b: bool) -> () {
|
||||
| ~~
|
||||
help: ...and then remove returned values
|
||||
|
|
||||
LL ~ return ;
|
||||
|
@ -18,6 +18,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
|
||||
"check-test-line-numbers-match",
|
||||
"compare-output-lines-by-subset",
|
||||
"compile-flags",
|
||||
"doc-flags",
|
||||
"dont-check-compiler-stderr",
|
||||
"dont-check-compiler-stdout",
|
||||
"dont-check-failure-status",
|
||||
@ -226,6 +227,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
|
||||
"should-ice",
|
||||
"stderr-per-bitwidth",
|
||||
"test-mir-pass",
|
||||
"unique-doc-out-dir",
|
||||
"unset-exec-env",
|
||||
"unset-rustc-env",
|
||||
// Used by the tidy check `unknown_revision`.
|
||||
|
@ -95,6 +95,8 @@ pub struct TestProps {
|
||||
pub compile_flags: Vec<String>,
|
||||
// Extra flags to pass when the compiled code is run (such as --bench)
|
||||
pub run_flags: Vec<String>,
|
||||
/// Extra flags to pass to rustdoc but not the compiler.
|
||||
pub doc_flags: Vec<String>,
|
||||
// If present, the name of a file that this test should match when
|
||||
// pretty-printed
|
||||
pub pp_exact: Option<PathBuf>,
|
||||
@ -122,6 +124,9 @@ pub struct TestProps {
|
||||
pub unset_exec_env: Vec<String>,
|
||||
// Build documentation for all specified aux-builds as well
|
||||
pub build_aux_docs: bool,
|
||||
/// Build the documentation for each crate in a unique output directory.
|
||||
/// Uses <root output directory>/docs/<test name>/doc
|
||||
pub unique_doc_out_dir: bool,
|
||||
// Flag to force a crate to be built with the host architecture
|
||||
pub force_host: bool,
|
||||
// Check stdout for error-pattern output as well as stderr
|
||||
@ -220,8 +225,10 @@ mod directives {
|
||||
pub const REGEX_ERROR_PATTERN: &'static str = "regex-error-pattern";
|
||||
pub const COMPILE_FLAGS: &'static str = "compile-flags";
|
||||
pub const RUN_FLAGS: &'static str = "run-flags";
|
||||
pub const DOC_FLAGS: &'static str = "doc-flags";
|
||||
pub const SHOULD_ICE: &'static str = "should-ice";
|
||||
pub const BUILD_AUX_DOCS: &'static str = "build-aux-docs";
|
||||
pub const UNIQUE_DOC_OUT_DIR: &'static str = "unique-doc-out-dir";
|
||||
pub const FORCE_HOST: &'static str = "force-host";
|
||||
pub const CHECK_STDOUT: &'static str = "check-stdout";
|
||||
pub const CHECK_RUN_RESULTS: &'static str = "check-run-results";
|
||||
@ -267,6 +274,7 @@ impl TestProps {
|
||||
regex_error_patterns: vec![],
|
||||
compile_flags: vec![],
|
||||
run_flags: vec![],
|
||||
doc_flags: vec![],
|
||||
pp_exact: None,
|
||||
aux_builds: vec![],
|
||||
aux_bins: vec![],
|
||||
@ -281,6 +289,7 @@ impl TestProps {
|
||||
exec_env: vec![],
|
||||
unset_exec_env: vec![],
|
||||
build_aux_docs: false,
|
||||
unique_doc_out_dir: false,
|
||||
force_host: false,
|
||||
check_stdout: false,
|
||||
check_run_results: false,
|
||||
@ -378,6 +387,8 @@ impl TestProps {
|
||||
|r| r,
|
||||
);
|
||||
|
||||
config.push_name_value_directive(ln, DOC_FLAGS, &mut self.doc_flags, |r| r);
|
||||
|
||||
fn split_flags(flags: &str) -> Vec<String> {
|
||||
// Individual flags can be single-quoted to preserve spaces; see
|
||||
// <https://github.com/rust-lang/rust/pull/115948/commits/957c5db6>.
|
||||
@ -415,6 +426,8 @@ impl TestProps {
|
||||
|
||||
config.set_name_directive(ln, SHOULD_ICE, &mut self.should_ice);
|
||||
config.set_name_directive(ln, BUILD_AUX_DOCS, &mut self.build_aux_docs);
|
||||
config.set_name_directive(ln, UNIQUE_DOC_OUT_DIR, &mut self.unique_doc_out_dir);
|
||||
|
||||
config.set_name_directive(ln, FORCE_HOST, &mut self.force_host);
|
||||
config.set_name_directive(ln, CHECK_STDOUT, &mut self.check_stdout);
|
||||
config.set_name_directive(ln, CHECK_RUN_RESULTS, &mut self.check_run_results);
|
||||
|
@ -1,5 +1,6 @@
|
||||
// ignore-tidy-filelength
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::fs::{self, create_dir_all, File, OpenOptions};
|
||||
@ -723,7 +724,7 @@ impl<'test> TestCx<'test> {
|
||||
self.maybe_add_external_args(&mut rustc, &self.config.target_rustcflags);
|
||||
rustc.args(&self.props.compile_flags);
|
||||
|
||||
self.compose_and_run_compiler(rustc, Some(src))
|
||||
self.compose_and_run_compiler(rustc, Some(src), self.testpaths)
|
||||
}
|
||||
|
||||
fn run_debuginfo_test(&self) {
|
||||
@ -1579,13 +1580,15 @@ impl<'test> TestCx<'test> {
|
||||
passes,
|
||||
);
|
||||
|
||||
self.compose_and_run_compiler(rustc, None)
|
||||
self.compose_and_run_compiler(rustc, None, self.testpaths)
|
||||
}
|
||||
|
||||
fn document(&self, out_dir: &Path) -> ProcRes {
|
||||
/// `root_out_dir` and `root_testpaths` refer to the parameters of the actual test being run.
|
||||
/// Auxiliaries, no matter how deep, have the same root_out_dir and root_testpaths.
|
||||
fn document(&self, root_out_dir: &Path, root_testpaths: &TestPaths) -> ProcRes {
|
||||
if self.props.build_aux_docs {
|
||||
for rel_ab in &self.props.aux_builds {
|
||||
let aux_testpaths = self.compute_aux_test_paths(&self.testpaths, rel_ab);
|
||||
let aux_testpaths = self.compute_aux_test_paths(root_testpaths, rel_ab);
|
||||
let aux_props =
|
||||
self.props.from_aux_file(&aux_testpaths.file, self.revision, self.config);
|
||||
let aux_cx = TestCx {
|
||||
@ -1596,7 +1599,9 @@ impl<'test> TestCx<'test> {
|
||||
};
|
||||
// Create the directory for the stdout/stderr files.
|
||||
create_dir_all(aux_cx.output_base_dir()).unwrap();
|
||||
let auxres = aux_cx.document(out_dir);
|
||||
// use root_testpaths here, because aux-builds should have the
|
||||
// same --out-dir and auxiliary directory.
|
||||
let auxres = aux_cx.document(&root_out_dir, root_testpaths);
|
||||
if !auxres.status.success() {
|
||||
return auxres;
|
||||
}
|
||||
@ -1606,21 +1611,40 @@ impl<'test> TestCx<'test> {
|
||||
let aux_dir = self.aux_output_dir_name();
|
||||
|
||||
let rustdoc_path = self.config.rustdoc_path.as_ref().expect("--rustdoc-path not passed");
|
||||
let mut rustdoc = Command::new(rustdoc_path);
|
||||
|
||||
// actual --out-dir given to the auxiliary or test, as opposed to the root out dir for the entire
|
||||
// test
|
||||
let out_dir: Cow<'_, Path> = if self.props.unique_doc_out_dir {
|
||||
let file_name = self.testpaths.file.file_stem().expect("file name should not be empty");
|
||||
let out_dir = PathBuf::from_iter([
|
||||
root_out_dir,
|
||||
Path::new("docs"),
|
||||
Path::new(file_name),
|
||||
Path::new("doc"),
|
||||
]);
|
||||
create_dir_all(&out_dir).unwrap();
|
||||
Cow::Owned(out_dir)
|
||||
} else {
|
||||
Cow::Borrowed(root_out_dir)
|
||||
};
|
||||
|
||||
let mut rustdoc = Command::new(rustdoc_path);
|
||||
let current_dir = output_base_dir(self.config, root_testpaths, self.safe_revision());
|
||||
rustdoc.current_dir(current_dir);
|
||||
rustdoc
|
||||
.arg("-L")
|
||||
.arg(self.config.run_lib_path.to_str().unwrap())
|
||||
.arg("-L")
|
||||
.arg(aux_dir)
|
||||
.arg("-o")
|
||||
.arg(out_dir)
|
||||
.arg(out_dir.as_ref())
|
||||
.arg("--deny")
|
||||
.arg("warnings")
|
||||
.arg(&self.testpaths.file)
|
||||
.arg("-A")
|
||||
.arg("internal_features")
|
||||
.args(&self.props.compile_flags);
|
||||
.args(&self.props.compile_flags)
|
||||
.args(&self.props.doc_flags);
|
||||
|
||||
if self.config.mode == RustdocJson {
|
||||
rustdoc.arg("--output-format").arg("json").arg("-Zunstable-options");
|
||||
@ -1630,7 +1654,7 @@ impl<'test> TestCx<'test> {
|
||||
rustdoc.arg(format!("-Clinker={}", linker));
|
||||
}
|
||||
|
||||
self.compose_and_run_compiler(rustdoc, None)
|
||||
self.compose_and_run_compiler(rustdoc, None, root_testpaths)
|
||||
}
|
||||
|
||||
fn exec_compiled_test(&self) -> ProcRes {
|
||||
@ -1828,9 +1852,16 @@ impl<'test> TestCx<'test> {
|
||||
}
|
||||
}
|
||||
|
||||
fn compose_and_run_compiler(&self, mut rustc: Command, input: Option<String>) -> ProcRes {
|
||||
/// `root_testpaths` refers to the path of the original test.
|
||||
/// the auxiliary and the test with an aux-build have the same `root_testpaths`.
|
||||
fn compose_and_run_compiler(
|
||||
&self,
|
||||
mut rustc: Command,
|
||||
input: Option<String>,
|
||||
root_testpaths: &TestPaths,
|
||||
) -> ProcRes {
|
||||
let aux_dir = self.aux_output_dir();
|
||||
self.build_all_auxiliary(&self.testpaths, &aux_dir, &mut rustc);
|
||||
self.build_all_auxiliary(root_testpaths, &aux_dir, &mut rustc);
|
||||
|
||||
rustc.envs(self.props.rustc_env.clone());
|
||||
self.props.unset_rustc_env.iter().fold(&mut rustc, Command::env_remove);
|
||||
@ -2545,7 +2576,7 @@ impl<'test> TestCx<'test> {
|
||||
Vec::new(),
|
||||
);
|
||||
|
||||
let proc_res = self.compose_and_run_compiler(rustc, None);
|
||||
let proc_res = self.compose_and_run_compiler(rustc, None, self.testpaths);
|
||||
let output_path = self.get_filecheck_file("ll");
|
||||
(proc_res, output_path)
|
||||
}
|
||||
@ -2581,7 +2612,7 @@ impl<'test> TestCx<'test> {
|
||||
Vec::new(),
|
||||
);
|
||||
|
||||
let proc_res = self.compose_and_run_compiler(rustc, None);
|
||||
let proc_res = self.compose_and_run_compiler(rustc, None, self.testpaths);
|
||||
let output_path = self.get_filecheck_file("s");
|
||||
(proc_res, output_path)
|
||||
}
|
||||
@ -2664,7 +2695,7 @@ impl<'test> TestCx<'test> {
|
||||
let out_dir = self.output_base_dir();
|
||||
remove_and_create_dir_all(&out_dir);
|
||||
|
||||
let proc_res = self.document(&out_dir);
|
||||
let proc_res = self.document(&out_dir, &self.testpaths);
|
||||
if !proc_res.status.success() {
|
||||
self.fatal_proc_rec("rustdoc failed!", &proc_res);
|
||||
}
|
||||
@ -2723,7 +2754,7 @@ impl<'test> TestCx<'test> {
|
||||
let aux_dir = new_rustdoc.aux_output_dir();
|
||||
new_rustdoc.build_all_auxiliary(&new_rustdoc.testpaths, &aux_dir, &mut rustc);
|
||||
|
||||
let proc_res = new_rustdoc.document(&compare_dir);
|
||||
let proc_res = new_rustdoc.document(&compare_dir, &new_rustdoc.testpaths);
|
||||
if !proc_res.status.success() {
|
||||
eprintln!("failed to run nightly rustdoc");
|
||||
return;
|
||||
@ -2847,7 +2878,7 @@ impl<'test> TestCx<'test> {
|
||||
let out_dir = self.output_base_dir();
|
||||
remove_and_create_dir_all(&out_dir);
|
||||
|
||||
let proc_res = self.document(&out_dir);
|
||||
let proc_res = self.document(&out_dir, &self.testpaths);
|
||||
if !proc_res.status.success() {
|
||||
self.fatal_proc_rec("rustdoc failed!", &proc_res);
|
||||
}
|
||||
@ -2923,31 +2954,24 @@ impl<'test> TestCx<'test> {
|
||||
fn check_rustdoc_test_option(&self, res: ProcRes) {
|
||||
let mut other_files = Vec::new();
|
||||
let mut files: HashMap<String, Vec<usize>> = HashMap::new();
|
||||
let cwd = env::current_dir().unwrap();
|
||||
files.insert(
|
||||
self.testpaths
|
||||
.file
|
||||
.strip_prefix(&cwd)
|
||||
.unwrap_or(&self.testpaths.file)
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.replace('\\', "/"),
|
||||
self.get_lines(&self.testpaths.file, Some(&mut other_files)),
|
||||
);
|
||||
let normalized = fs::canonicalize(&self.testpaths.file).expect("failed to canonicalize");
|
||||
let normalized = normalized.to_str().unwrap().replace('\\', "/");
|
||||
files.insert(normalized, self.get_lines(&self.testpaths.file, Some(&mut other_files)));
|
||||
for other_file in other_files {
|
||||
let mut path = self.testpaths.file.clone();
|
||||
path.set_file_name(&format!("{}.rs", other_file));
|
||||
files.insert(
|
||||
path.strip_prefix(&cwd).unwrap_or(&path).to_str().unwrap().replace('\\', "/"),
|
||||
self.get_lines(&path, None),
|
||||
);
|
||||
let path = fs::canonicalize(path).expect("failed to canonicalize");
|
||||
let normalized = path.to_str().unwrap().replace('\\', "/");
|
||||
files.insert(normalized, self.get_lines(&path, None));
|
||||
}
|
||||
|
||||
let mut tested = 0;
|
||||
for _ in res.stdout.split('\n').filter(|s| s.starts_with("test ")).inspect(|s| {
|
||||
if let Some((left, right)) = s.split_once(" - ") {
|
||||
let path = left.rsplit("test ").next().unwrap();
|
||||
if let Some(ref mut v) = files.get_mut(&path.replace('\\', "/")) {
|
||||
let path = fs::canonicalize(&path).expect("failed to canonicalize");
|
||||
let path = path.to_str().unwrap().replace('\\', "/");
|
||||
if let Some(ref mut v) = files.get_mut(&path) {
|
||||
tested += 1;
|
||||
let mut iter = right.split("(line ");
|
||||
iter.next();
|
||||
@ -3779,7 +3803,7 @@ impl<'test> TestCx<'test> {
|
||||
if let Some(nodejs) = &self.config.nodejs {
|
||||
let out_dir = self.output_base_dir();
|
||||
|
||||
self.document(&out_dir);
|
||||
self.document(&out_dir, &self.testpaths);
|
||||
|
||||
let root = self.config.find_rust_src_root().unwrap();
|
||||
let file_stem =
|
||||
@ -4095,7 +4119,7 @@ impl<'test> TestCx<'test> {
|
||||
rustc.arg(crate_name);
|
||||
}
|
||||
|
||||
let res = self.compose_and_run_compiler(rustc, None);
|
||||
let res = self.compose_and_run_compiler(rustc, None, self.testpaths);
|
||||
if !res.status.success() {
|
||||
self.fatal_proc_rec("failed to compile fixed code", &res);
|
||||
}
|
||||
|
@ -191,7 +191,7 @@ impl<'test> TestCx<'test> {
|
||||
|
||||
rustdoc_cmd.arg(&self.testpaths.file);
|
||||
|
||||
let proc_res = self.compose_and_run_compiler(rustdoc_cmd, None);
|
||||
let proc_res = self.compose_and_run_compiler(rustdoc_cmd, None, self.testpaths);
|
||||
if !proc_res.status.success() {
|
||||
self.fatal_proc_rec("rustdoc --test failed!", &proc_res)
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
05e692ae025fd4650c601e1e7ab51bdc5e19c35b
|
||||
1df0458781d6fd753a68c4cdc4de5313b1635dbd
|
||||
|
@ -1,10 +0,0 @@
|
||||
#![feature(intrinsics)]
|
||||
|
||||
extern "rust-intrinsic" {
|
||||
fn raw_eq<T>(a: &T, b: &T) -> bool;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = &0;
|
||||
unsafe { raw_eq(&x, &x) }; //~ERROR: `raw_eq` on bytes with provenance
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
error: Undefined Behavior: `raw_eq` on bytes with provenance
|
||||
--> $DIR/raw_eq_on_ptr.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { raw_eq(&x, &x) };
|
||||
| ^^^^^^^^^^^^^^ `raw_eq` on bytes with provenance
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= note: BACKTRACE:
|
||||
= note: inside `main` at $DIR/raw_eq_on_ptr.rs:LL:CC
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
@ -10,7 +10,7 @@ log = "0.4"
|
||||
anyhow = { version = "1", features = ["backtrace"] }
|
||||
humantime = "2"
|
||||
humansize = "2"
|
||||
sysinfo = { version = "0.30", default-features = false }
|
||||
sysinfo = { version = "0.31.2", default-features = false, features = ["disk"] }
|
||||
fs_extra = "1"
|
||||
camino = "1"
|
||||
tar = "0.4"
|
||||
|
@ -2,7 +2,7 @@ use std::path::PathBuf;
|
||||
|
||||
use super::cygpath::get_windows_path;
|
||||
use crate::artifact_names::{dynamic_lib_name, static_lib_name};
|
||||
use crate::external_deps::cc::cc;
|
||||
use crate::external_deps::cc::{cc, cxx};
|
||||
use crate::external_deps::llvm::llvm_ar;
|
||||
use crate::path_helpers::path;
|
||||
use crate::targets::{is_darwin, is_msvc, is_windows};
|
||||
@ -10,6 +10,7 @@ use crate::targets::{is_darwin, is_msvc, is_windows};
|
||||
// FIXME(Oneirical): These native build functions should take a Path-based generic.
|
||||
|
||||
/// Builds a static lib (`.lib` on Windows MSVC and `.a` for the rest) with the given name.
|
||||
/// Built from a C file.
|
||||
#[track_caller]
|
||||
pub fn build_native_static_lib(lib_name: &str) -> PathBuf {
|
||||
let obj_file = if is_msvc() { format!("{lib_name}") } else { format!("{lib_name}.o") };
|
||||
@ -58,3 +59,24 @@ pub fn build_native_dynamic_lib(lib_name: &str) -> PathBuf {
|
||||
}
|
||||
path(lib_path)
|
||||
}
|
||||
|
||||
/// Builds a static lib (`.lib` on Windows MSVC and `.a` for the rest) with the given name.
|
||||
/// Built from a C++ file.
|
||||
#[track_caller]
|
||||
pub fn build_native_static_lib_cxx(lib_name: &str) -> PathBuf {
|
||||
let obj_file = if is_msvc() { format!("{lib_name}") } else { format!("{lib_name}.o") };
|
||||
let src = format!("{lib_name}.cpp");
|
||||
let lib_path = static_lib_name(lib_name);
|
||||
if is_msvc() {
|
||||
cxx().arg("-EHs").arg("-c").out_exe(&obj_file).input(src).run();
|
||||
} else {
|
||||
cxx().arg("-c").out_exe(&obj_file).input(src).run();
|
||||
};
|
||||
let obj_file = if is_msvc() {
|
||||
PathBuf::from(format!("{lib_name}.obj"))
|
||||
} else {
|
||||
PathBuf::from(format!("{lib_name}.o"))
|
||||
};
|
||||
llvm_ar().obj_to_ar().output_input(&lib_path, &obj_file).run();
|
||||
path(lib_path)
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::path::Path;
|
||||
|
||||
use crate::command::Command;
|
||||
use crate::{bin_name, env_var};
|
||||
use crate::{bin_name, cwd, env_var};
|
||||
|
||||
/// Construct a new `clang` invocation. `clang` is not always available for all targets.
|
||||
#[track_caller]
|
||||
@ -23,7 +23,8 @@ impl Clang {
|
||||
#[track_caller]
|
||||
pub fn new() -> Self {
|
||||
let clang = env_var("CLANG");
|
||||
let cmd = Command::new(clang);
|
||||
let mut cmd = Command::new(clang);
|
||||
cmd.arg("-L").arg(cwd());
|
||||
Self { cmd }
|
||||
}
|
||||
|
||||
|
@ -42,6 +42,12 @@ pub fn llvm_nm() -> LlvmNm {
|
||||
LlvmNm::new()
|
||||
}
|
||||
|
||||
/// Construct a new `llvm-bcanalyzer` invocation. This assumes that `llvm-bcanalyzer` is available
|
||||
/// at `$LLVM_BIN_DIR/llvm-bcanalyzer`.
|
||||
pub fn llvm_bcanalyzer() -> LlvmBcanalyzer {
|
||||
LlvmBcanalyzer::new()
|
||||
}
|
||||
|
||||
/// A `llvm-readobj` invocation builder.
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
@ -84,12 +90,20 @@ pub struct LlvmNm {
|
||||
cmd: Command,
|
||||
}
|
||||
|
||||
/// A `llvm-bcanalyzer` invocation builder.
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
pub struct LlvmBcanalyzer {
|
||||
cmd: Command,
|
||||
}
|
||||
|
||||
crate::macros::impl_common_helpers!(LlvmReadobj);
|
||||
crate::macros::impl_common_helpers!(LlvmProfdata);
|
||||
crate::macros::impl_common_helpers!(LlvmFilecheck);
|
||||
crate::macros::impl_common_helpers!(LlvmObjdump);
|
||||
crate::macros::impl_common_helpers!(LlvmAr);
|
||||
crate::macros::impl_common_helpers!(LlvmNm);
|
||||
crate::macros::impl_common_helpers!(LlvmBcanalyzer);
|
||||
|
||||
/// Generate the path to the bin directory of LLVM.
|
||||
#[must_use]
|
||||
@ -232,6 +246,12 @@ impl LlvmObjdump {
|
||||
self.cmd.arg(path.as_ref());
|
||||
self
|
||||
}
|
||||
|
||||
/// Disassemble all executable sections found in the input files.
|
||||
pub fn disassemble(&mut self) -> &mut Self {
|
||||
self.cmd.arg("-d");
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl LlvmAr {
|
||||
@ -250,6 +270,12 @@ impl LlvmAr {
|
||||
self
|
||||
}
|
||||
|
||||
/// Extract archive members back to files.
|
||||
pub fn extract(&mut self) -> &mut Self {
|
||||
self.cmd.arg("x");
|
||||
self
|
||||
}
|
||||
|
||||
/// Provide an output, then an input file. Bundled in one function, as llvm-ar has
|
||||
/// no "--output"-style flag.
|
||||
pub fn output_input(&mut self, out: impl AsRef<Path>, input: impl AsRef<Path>) -> &mut Self {
|
||||
@ -274,3 +300,19 @@ impl LlvmNm {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl LlvmBcanalyzer {
|
||||
/// Construct a new `llvm-bcanalyzer` invocation. This assumes that `llvm-bcanalyzer` is available
|
||||
/// at `$LLVM_BIN_DIR/llvm-bcanalyzer`.
|
||||
pub fn new() -> Self {
|
||||
let llvm_bcanalyzer = llvm_bin_dir().join("llvm-bcanalyzer");
|
||||
let cmd = Command::new(llvm_bcanalyzer);
|
||||
Self { cmd }
|
||||
}
|
||||
|
||||
/// Provide an input file.
|
||||
pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
|
||||
self.cmd.arg(path.as_ref());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ use crate::command::Command;
|
||||
use crate::env::env_var;
|
||||
use crate::path_helpers::cwd;
|
||||
use crate::util::set_host_rpath;
|
||||
use crate::{is_darwin, is_msvc, is_windows, uname};
|
||||
|
||||
/// Construct a new `rustc` invocation. This will automatically set the library
|
||||
/// search path as `-L cwd()`. Use [`bare_rustc`] to avoid this.
|
||||
@ -314,4 +315,62 @@ impl Rustc {
|
||||
self.cmd.arg(format!("-Clinker-flavor={linker_flavor}"));
|
||||
self
|
||||
}
|
||||
|
||||
/// `EXTRARSCXXFLAGS`
|
||||
pub fn extra_rs_cxx_flags(&mut self) -> &mut Self {
|
||||
// Adapted from tools.mk (trimmed):
|
||||
//
|
||||
// ```makefile
|
||||
// ifdef IS_WINDOWS
|
||||
// ifdef IS_MSVC
|
||||
// else
|
||||
// EXTRARSCXXFLAGS := -lstatic:-bundle=stdc++
|
||||
// endif
|
||||
// else
|
||||
// ifeq ($(UNAME),Darwin)
|
||||
// EXTRARSCXXFLAGS := -lc++
|
||||
// else
|
||||
// ifeq ($(UNAME),FreeBSD)
|
||||
// else
|
||||
// ifeq ($(UNAME),SunOS)
|
||||
// else
|
||||
// ifeq ($(UNAME),OpenBSD)
|
||||
// else
|
||||
// EXTRARSCXXFLAGS := -lstdc++
|
||||
// endif
|
||||
// endif
|
||||
// endif
|
||||
// endif
|
||||
// endif
|
||||
// ```
|
||||
let flag = if is_windows() {
|
||||
// So this is a bit hacky: we can't use the DLL version of libstdc++ because
|
||||
// it pulls in the DLL version of libgcc, which means that we end up with 2
|
||||
// instances of the DW2 unwinding implementation. This is a problem on
|
||||
// i686-pc-windows-gnu because each module (DLL/EXE) needs to register its
|
||||
// unwind information with the unwinding implementation, and libstdc++'s
|
||||
// __cxa_throw won't see the unwinding info we registered with our statically
|
||||
// linked libgcc.
|
||||
//
|
||||
// Now, simply statically linking libstdc++ would fix this problem, except
|
||||
// that it is compiled with the expectation that pthreads is dynamically
|
||||
// linked as a DLL and will fail to link with a statically linked libpthread.
|
||||
//
|
||||
// So we end up with the following hack: we link use static:-bundle to only
|
||||
// link the parts of libstdc++ that we actually use, which doesn't include
|
||||
// the dependency on the pthreads DLL.
|
||||
if is_msvc() { None } else { Some("-lstatic:-bundle=stdc++") }
|
||||
} else if is_darwin() {
|
||||
Some("-lc++")
|
||||
} else {
|
||||
match &uname()[..] {
|
||||
"FreeBSD" | "SunOS" | "OpenBSD" => None,
|
||||
_ => Some("-lstdc++"),
|
||||
}
|
||||
};
|
||||
if let Some(flag) = flag {
|
||||
self.cmd.arg(flag);
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -44,13 +44,13 @@ pub use wasmparser;
|
||||
pub use external_deps::{c_build, cc, clang, htmldocck, llvm, python, rustc, rustdoc};
|
||||
|
||||
// These rely on external dependencies.
|
||||
pub use c_build::{build_native_dynamic_lib, build_native_static_lib};
|
||||
pub use cc::{cc, cxx, extra_c_flags, extra_cxx_flags, Cc};
|
||||
pub use c_build::{build_native_dynamic_lib, build_native_static_lib, build_native_static_lib_cxx};
|
||||
pub use clang::{clang, Clang};
|
||||
pub use htmldocck::htmldocck;
|
||||
pub use llvm::{
|
||||
llvm_ar, llvm_filecheck, llvm_nm, llvm_objdump, llvm_profdata, llvm_readobj, LlvmAr,
|
||||
LlvmFilecheck, LlvmNm, LlvmObjdump, LlvmProfdata, LlvmReadobj,
|
||||
llvm_ar, llvm_bcanalyzer, llvm_filecheck, llvm_nm, llvm_objdump, llvm_profdata, llvm_readobj,
|
||||
LlvmAr, LlvmBcanalyzer, LlvmFilecheck, LlvmNm, LlvmObjdump, LlvmProfdata, LlvmReadobj,
|
||||
};
|
||||
pub use python::python_command;
|
||||
pub use rustc::{aux_build, bare_rustc, rustc, Rustc};
|
||||
|
@ -34,15 +34,6 @@ pub fn check_for(x: Feature) -> bool {
|
||||
fn detect_features() -> cache::Initializer {
|
||||
let mut value = cache::Initializer::default();
|
||||
|
||||
// If the x86 CPU does not support the CPUID instruction then it is too
|
||||
// old to support any of the currently-detectable features.
|
||||
if !has_cpuid() {
|
||||
return value;
|
||||
}
|
||||
|
||||
// Calling `__cpuid`/`__cpuid_count` from here on is safe because the CPU
|
||||
// has `cpuid` support.
|
||||
|
||||
// 0. EAX = 0: Basic Information:
|
||||
// - EAX returns the "Highest Function Parameter", that is, the maximum
|
||||
// leaf value for subsequent calls of `cpuinfo` in range [0,
|
||||
|
@ -34,15 +34,6 @@ pub fn check_for(x: Feature) -> bool {
|
||||
fn detect_features() -> cache::Initializer {
|
||||
let mut value = cache::Initializer::default();
|
||||
|
||||
// If the x86 CPU does not support the CPUID instruction then it is too
|
||||
// old to support any of the currently-detectable features.
|
||||
if !has_cpuid() {
|
||||
return value;
|
||||
}
|
||||
|
||||
// Calling `__cpuid`/`__cpuid_count` from here on is safe because the CPU
|
||||
// has `cpuid` support.
|
||||
|
||||
// 0. EAX = 0: Basic Information:
|
||||
// - EAX returns the "Highest Function Parameter", that is, the maximum
|
||||
// leaf value for subsequent calls of `cpuinfo` in range [0,
|
||||
|
@ -1,27 +1,19 @@
|
||||
run-make/branch-protection-check-IBT/Makefile
|
||||
run-make/cat-and-grep-sanity-check/Makefile
|
||||
run-make/cdylib-dylib-linkage/Makefile
|
||||
run-make/cross-lang-lto-clang/Makefile
|
||||
run-make/cross-lang-lto-pgo-smoketest/Makefile
|
||||
run-make/cross-lang-lto-upstream-rlibs/Makefile
|
||||
run-make/cross-lang-lto/Makefile
|
||||
run-make/dep-info-doesnt-run-much/Makefile
|
||||
run-make/dep-info-spaces/Makefile
|
||||
run-make/dep-info/Makefile
|
||||
run-make/emit-to-stdout/Makefile
|
||||
run-make/extern-fn-reachable/Makefile
|
||||
run-make/foreign-double-unwind/Makefile
|
||||
run-make/foreign-exceptions/Makefile
|
||||
run-make/incr-add-rust-src-component/Makefile
|
||||
run-make/issue-36710/Makefile
|
||||
run-make/issue-84395-lto-embed-bitcode/Makefile
|
||||
run-make/issue-88756-default-output/Makefile
|
||||
run-make/jobserver-error/Makefile
|
||||
run-make/libs-through-symlinks/Makefile
|
||||
run-make/libtest-json/Makefile
|
||||
run-make/libtest-junit/Makefile
|
||||
run-make/libtest-thread-limit/Makefile
|
||||
run-make/link-cfg/Makefile
|
||||
run-make/long-linker-command-lines-cmd-exe/Makefile
|
||||
run-make/long-linker-command-lines/Makefile
|
||||
run-make/macos-deployment-target/Makefile
|
||||
|
@ -22,16 +22,16 @@ impl<T> Struct<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct _LifeTimeOnly<'a> {
|
||||
pub struct LifeTimeOnly<'a> {
|
||||
_a: &'a u32,
|
||||
}
|
||||
|
||||
impl<'a> _LifeTimeOnly<'a> {
|
||||
//~ MONO_ITEM fn _LifeTimeOnly::<'_>::foo
|
||||
impl<'a> LifeTimeOnly<'a> {
|
||||
//~ MONO_ITEM fn LifeTimeOnly::<'_>::foo
|
||||
pub fn foo(&self) {}
|
||||
//~ MONO_ITEM fn _LifeTimeOnly::<'_>::bar
|
||||
//~ MONO_ITEM fn LifeTimeOnly::<'_>::bar
|
||||
pub fn bar(&'a self) {}
|
||||
//~ MONO_ITEM fn _LifeTimeOnly::<'_>::baz
|
||||
//~ MONO_ITEM fn LifeTimeOnly::<'_>::baz
|
||||
pub fn baz<'b>(&'b self) {}
|
||||
|
||||
pub fn non_instantiated<T>(&self) {}
|
||||
|
@ -5,44 +5,44 @@
|
||||
|
||||
use std::ops::{Add, Deref, Index, IndexMut};
|
||||
|
||||
pub struct _Indexable {
|
||||
pub struct Indexable {
|
||||
data: [u8; 3],
|
||||
}
|
||||
|
||||
impl Index<usize> for _Indexable {
|
||||
impl Index<usize> for Indexable {
|
||||
type Output = u8;
|
||||
|
||||
//~ MONO_ITEM fn <_Indexable as std::ops::Index<usize>>::index
|
||||
//~ MONO_ITEM fn <Indexable as std::ops::Index<usize>>::index
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
if index >= 3 { &self.data[0] } else { &self.data[index] }
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<usize> for _Indexable {
|
||||
//~ MONO_ITEM fn <_Indexable as std::ops::IndexMut<usize>>::index_mut
|
||||
impl IndexMut<usize> for Indexable {
|
||||
//~ MONO_ITEM fn <Indexable as std::ops::IndexMut<usize>>::index_mut
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
if index >= 3 { &mut self.data[0] } else { &mut self.data[index] }
|
||||
}
|
||||
}
|
||||
|
||||
//~ MONO_ITEM fn <_Equatable as std::cmp::PartialEq>::eq
|
||||
//~ MONO_ITEM fn <_Equatable as std::cmp::PartialEq>::ne
|
||||
//~ MONO_ITEM fn <Equatable as std::cmp::PartialEq>::eq
|
||||
//~ MONO_ITEM fn <Equatable as std::cmp::PartialEq>::ne
|
||||
#[derive(PartialEq)]
|
||||
pub struct _Equatable(u32);
|
||||
pub struct Equatable(u32);
|
||||
|
||||
impl Add<u32> for _Equatable {
|
||||
impl Add<u32> for Equatable {
|
||||
type Output = u32;
|
||||
|
||||
//~ MONO_ITEM fn <_Equatable as std::ops::Add<u32>>::add
|
||||
//~ MONO_ITEM fn <Equatable as std::ops::Add<u32>>::add
|
||||
fn add(self, rhs: u32) -> u32 {
|
||||
self.0 + rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for _Equatable {
|
||||
impl Deref for Equatable {
|
||||
type Target = u32;
|
||||
|
||||
//~ MONO_ITEM fn <_Equatable as std::ops::Deref>::deref
|
||||
//~ MONO_ITEM fn <Equatable as std::ops::Deref>::deref
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
//@ compile-flags: -O
|
||||
//@ compile-flags: -C opt-level=3
|
||||
//@ only-x86_64
|
||||
|
||||
#![crate_type = "lib"]
|
||||
#![feature(iter_repeat_n)]
|
||||
#![feature(array_repeat)]
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct NotCopy(u16);
|
||||
@ -54,3 +55,15 @@ pub fn vec_extend_via_iter_repeat_n() -> Vec<u8> {
|
||||
v.extend(std::iter::repeat_n(42_u8, n));
|
||||
v
|
||||
}
|
||||
|
||||
// Array repeat uses `RepeatN::next_unchecked` internally,
|
||||
// so also check that the distinction disappears there.
|
||||
|
||||
#[no_mangle]
|
||||
// CHECK-LABEL: @array_repeat_not_copy
|
||||
pub unsafe fn array_repeat_not_copy(item: NotCopy) -> [NotCopy; 8] {
|
||||
// CHECK: insertelement {{.+}} i16 %item
|
||||
// CHECK: shufflevector <8 x i16> {{.+}} zeroinitializer
|
||||
// CHECK: store <8 x i16>
|
||||
std::array::repeat(item)
|
||||
}
|
||||
|
@ -5,37 +5,42 @@
|
||||
debug i => _1;
|
||||
let mut _0: u128;
|
||||
let mut _2: i128;
|
||||
+ let mut _3: i128;
|
||||
|
||||
bb0: {
|
||||
_2 = discriminant(_1);
|
||||
switchInt(move _2) -> [1: bb5, 2: bb4, 3: bb3, 340282366920938463463374607431768211455: bb2, otherwise: bb1];
|
||||
}
|
||||
|
||||
bb1: {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
bb2: {
|
||||
_0 = const core::num::<impl u128>::MAX;
|
||||
goto -> bb6;
|
||||
}
|
||||
|
||||
bb3: {
|
||||
_0 = const 3_u128;
|
||||
goto -> bb6;
|
||||
}
|
||||
|
||||
bb4: {
|
||||
_0 = const 2_u128;
|
||||
goto -> bb6;
|
||||
}
|
||||
|
||||
bb5: {
|
||||
_0 = const 1_u128;
|
||||
goto -> bb6;
|
||||
}
|
||||
|
||||
bb6: {
|
||||
- switchInt(move _2) -> [1: bb5, 2: bb4, 3: bb3, 340282366920938463463374607431768211455: bb2, otherwise: bb1];
|
||||
- }
|
||||
-
|
||||
- bb1: {
|
||||
- unreachable;
|
||||
- }
|
||||
-
|
||||
- bb2: {
|
||||
- _0 = const core::num::<impl u128>::MAX;
|
||||
- goto -> bb6;
|
||||
- }
|
||||
-
|
||||
- bb3: {
|
||||
- _0 = const 3_u128;
|
||||
- goto -> bb6;
|
||||
- }
|
||||
-
|
||||
- bb4: {
|
||||
- _0 = const 2_u128;
|
||||
- goto -> bb6;
|
||||
- }
|
||||
-
|
||||
- bb5: {
|
||||
- _0 = const 1_u128;
|
||||
- goto -> bb6;
|
||||
- }
|
||||
-
|
||||
- bb6: {
|
||||
+ StorageLive(_3);
|
||||
+ _3 = move _2;
|
||||
+ _0 = _3 as u128 (IntToInt);
|
||||
+ StorageDead(_3);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1,37 +0,0 @@
|
||||
- // MIR for `match_i16_i8` before MatchBranchSimplification
|
||||
+ // MIR for `match_i16_i8` after MatchBranchSimplification
|
||||
|
||||
fn match_i16_i8(_1: EnumAi16) -> i8 {
|
||||
debug i => _1;
|
||||
let mut _0: i8;
|
||||
let mut _2: i16;
|
||||
|
||||
bb0: {
|
||||
_2 = discriminant(_1);
|
||||
switchInt(move _2) -> [65535: bb4, 2: bb3, 65533: bb2, otherwise: bb1];
|
||||
}
|
||||
|
||||
bb1: {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
bb2: {
|
||||
_0 = const -3_i8;
|
||||
goto -> bb5;
|
||||
}
|
||||
|
||||
bb3: {
|
||||
_0 = const 2_i8;
|
||||
goto -> bb5;
|
||||
}
|
||||
|
||||
bb4: {
|
||||
_0 = const -1_i8;
|
||||
goto -> bb5;
|
||||
}
|
||||
|
||||
bb5: {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user