Auto merge of #3785 - rust-lang:rustup-2024-08-03, r=RalfJung

Automatic Rustup
This commit is contained in:
bors 2024-08-03 08:32:17 +00:00
commit 47aea6f806
298 changed files with 4159 additions and 2329 deletions

View File

@ -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"

View File

@ -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() {

View File

@ -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 {

View File

@ -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 {

View File

@ -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)
}
}

View File

@ -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<'_>> {

View File

@ -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,

View File

@ -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

View File

@ -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| {

View File

@ -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()
};

View File

@ -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.

View File

@ -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()
}

View File

@ -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)),

View File

@ -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",

View File

@ -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)),

View File

@ -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;

View File

@ -4934,7 +4934,6 @@ declare_lint! {
/// ### Example
///
/// ```rust
/// #![feature(unsafe_extern_blocks)]
/// #![warn(missing_unsafe_on_extern)]
/// #![allow(dead_code)]
///

View File

@ -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> {

View File

@ -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);
}
}
}
}

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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));

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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> {

View File

@ -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);

View File

@ -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);

View File

@ -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 {

View File

@ -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)
}

View File

@ -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)))
}

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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> {

View File

@ -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;
}

View File

@ -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 {

View File

@ -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: (),
};

View File

@ -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

View File

@ -1705,6 +1705,7 @@ symbols! {
self_in_typedefs,
self_struct_ctor,
semitransparent,
sha512_sm_x86,
shadow_call_stack,
shl,
shl_assign,

View File

@ -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),

View File

@ -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) {

View File

@ -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) {

View File

@ -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()

View File

@ -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) {

View File

@ -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

View File

@ -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.
///

View File

@ -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")]

View File

@ -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)
}
}
}

View File

@ -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)]

View File

@ -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.

View File

@ -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]

View File

@ -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::{

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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)
}

View File

@ -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;

View File

@ -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

View File

@ -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()))

View File

@ -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
}

View File

@ -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
}
)
}

View File

@ -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};

View File

@ -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.

View File

@ -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;
}
}

View File

@ -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"

View File

@ -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"

View File

@ -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 }
}

View File

@ -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 {

View File

@ -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."
);
}
}

View File

@ -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 {

View File

@ -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",

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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 ;

View File

@ -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`.

View File

@ -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);

View File

@ -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);
}

View File

@ -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)
}

View File

@ -1 +1 @@
05e692ae025fd4650c601e1e7ab51bdc5e19c35b
1df0458781d6fd753a68c4cdc4de5313b1635dbd

View File

@ -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
}

View File

@ -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

View File

@ -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"

View File

@ -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)
}

View File

@ -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 }
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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};

View File

@ -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,

View File

@ -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,

View File

@ -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

View File

@ -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) {}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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;
}
}

View File

@ -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