Merge from rustc
This commit is contained in:
commit
2383a00d4f
@ -3650,6 +3650,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"smallvec",
|
||||
"tempfile",
|
||||
"thin-vec",
|
||||
"thorin-dwp",
|
||||
"tracing",
|
||||
"windows",
|
||||
@ -5081,7 +5082,6 @@ version = "0.0.0"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"alloc",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"compiler_builtins",
|
||||
"core",
|
||||
|
@ -548,7 +548,11 @@ pub fn cfg_matches(
|
||||
UNEXPECTED_CFGS,
|
||||
cfg.span,
|
||||
lint_node_id,
|
||||
"unexpected `cfg` condition value",
|
||||
if let Some(value) = cfg.value {
|
||||
format!("unexpected `cfg` condition value: `{value}`")
|
||||
} else {
|
||||
format!("unexpected `cfg` condition value: (none)")
|
||||
},
|
||||
BuiltinLintDiagnostics::UnexpectedCfgValue(
|
||||
(cfg.name, cfg.name_span),
|
||||
cfg.value.map(|v| (v, cfg.value_span.unwrap())),
|
||||
@ -560,7 +564,7 @@ pub fn cfg_matches(
|
||||
UNEXPECTED_CFGS,
|
||||
cfg.span,
|
||||
lint_node_id,
|
||||
"unexpected `cfg` condition name",
|
||||
format!("unexpected `cfg` condition name: `{}`", cfg.name),
|
||||
BuiltinLintDiagnostics::UnexpectedCfgName(
|
||||
(cfg.name, cfg.name_span),
|
||||
cfg.value.map(|v| (v, cfg.value_span.unwrap())),
|
||||
|
@ -7,14 +7,15 @@
|
||||
use std::thread::JoinHandle;
|
||||
|
||||
use cranelift_object::{ObjectBuilder, ObjectModule};
|
||||
use rustc_codegen_ssa::assert_module_sources::CguReuse;
|
||||
use rustc_codegen_ssa::back::metadata::create_compressed_metadata_file;
|
||||
use rustc_codegen_ssa::base::determine_cgu_reuse;
|
||||
use rustc_codegen_ssa::{CodegenResults, CompiledModule, CrateInfo, ModuleKind};
|
||||
use rustc_data_structures::profiling::SelfProfilerRef;
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_metadata::EncodedMetadata;
|
||||
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
|
||||
use rustc_middle::mir::mono::{CodegenUnit, MonoItem};
|
||||
use rustc_session::cgu_reuse_tracker::CguReuse;
|
||||
use rustc_session::config::{DebugInfo, OutputFilenames, OutputType};
|
||||
use rustc_session::Session;
|
||||
|
||||
@ -374,43 +375,47 @@ pub(crate) fn run_aot(
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the CGU reuse
|
||||
let cgu_reuse = tcx.sess.time("find_cgu_reuse", || {
|
||||
cgus.iter().map(|cgu| determine_cgu_reuse(tcx, &cgu)).collect::<Vec<_>>()
|
||||
});
|
||||
|
||||
rustc_codegen_ssa::assert_module_sources::assert_module_sources(tcx, &|cgu_reuse_tracker| {
|
||||
for (i, cgu) in cgus.iter().enumerate() {
|
||||
let cgu_reuse = cgu_reuse[i];
|
||||
cgu_reuse_tracker.set_actual_reuse(cgu.name().as_str(), cgu_reuse);
|
||||
}
|
||||
});
|
||||
|
||||
let global_asm_config = Arc::new(crate::global_asm::GlobalAsmConfig::new(tcx));
|
||||
|
||||
let mut concurrency_limiter = ConcurrencyLimiter::new(tcx.sess, cgus.len());
|
||||
|
||||
let modules = tcx.sess.time("codegen mono items", || {
|
||||
cgus.iter()
|
||||
.map(|cgu| {
|
||||
let cgu_reuse = if backend_config.disable_incr_cache {
|
||||
CguReuse::No
|
||||
} else {
|
||||
determine_cgu_reuse(tcx, cgu)
|
||||
};
|
||||
tcx.sess.cgu_reuse_tracker.set_actual_reuse(cgu.name().as_str(), cgu_reuse);
|
||||
|
||||
match cgu_reuse {
|
||||
CguReuse::No => {
|
||||
let dep_node = cgu.codegen_dep_node(tcx);
|
||||
tcx.dep_graph
|
||||
.with_task(
|
||||
dep_node,
|
||||
tcx,
|
||||
(
|
||||
backend_config.clone(),
|
||||
global_asm_config.clone(),
|
||||
cgu.name(),
|
||||
concurrency_limiter.acquire(tcx.sess.diagnostic()),
|
||||
),
|
||||
module_codegen,
|
||||
Some(rustc_middle::dep_graph::hash_result),
|
||||
)
|
||||
.0
|
||||
}
|
||||
CguReuse::PreLto => unreachable!(),
|
||||
CguReuse::PostLto => {
|
||||
concurrency_limiter.job_already_done();
|
||||
OngoingModuleCodegen::Sync(reuse_workproduct_for_cgu(tcx, cgu))
|
||||
}
|
||||
.enumerate()
|
||||
.map(|(i, cgu)| match cgu_reuse[i] {
|
||||
CguReuse::No => {
|
||||
let dep_node = cgu.codegen_dep_node(tcx);
|
||||
tcx.dep_graph
|
||||
.with_task(
|
||||
dep_node,
|
||||
tcx,
|
||||
(
|
||||
backend_config.clone(),
|
||||
global_asm_config.clone(),
|
||||
cgu.name(),
|
||||
concurrency_limiter.acquire(tcx.sess.diagnostic()),
|
||||
),
|
||||
module_codegen,
|
||||
Some(rustc_middle::dep_graph::hash_result),
|
||||
)
|
||||
.0
|
||||
}
|
||||
CguReuse::PreLto => unreachable!("LTO not yet supported"),
|
||||
CguReuse::PostLto => {
|
||||
concurrency_limiter.job_already_done();
|
||||
OngoingModuleCodegen::Sync(reuse_workproduct_for_cgu(tcx, cgu))
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
@ -489,32 +494,3 @@ pub(crate) fn run_aot(
|
||||
concurrency_limiter,
|
||||
})
|
||||
}
|
||||
|
||||
// Adapted from https://github.com/rust-lang/rust/blob/303d8aff6092709edd4dbd35b1c88e9aa40bf6d8/src/librustc_codegen_ssa/base.rs#L922-L953
|
||||
fn determine_cgu_reuse<'tcx>(tcx: TyCtxt<'tcx>, cgu: &CodegenUnit<'tcx>) -> CguReuse {
|
||||
if !tcx.dep_graph.is_fully_enabled() {
|
||||
return CguReuse::No;
|
||||
}
|
||||
|
||||
let work_product_id = &cgu.work_product_id();
|
||||
if tcx.dep_graph.previous_work_product(work_product_id).is_none() {
|
||||
// We don't have anything cached for this CGU. This can happen
|
||||
// if the CGU did not exist in the previous session.
|
||||
return CguReuse::No;
|
||||
}
|
||||
|
||||
// Try to mark the CGU as green. If it we can do so, it means that nothing
|
||||
// affecting the LLVM module has changed and we can re-use a cached version.
|
||||
// If we compile with any kind of LTO, this means we can re-use the bitcode
|
||||
// of the Pre-LTO stage (possibly also the Post-LTO version but we'll only
|
||||
// know that later). If we are not doing LTO, there is only one optimized
|
||||
// version of each module, so we re-use that.
|
||||
let dep_node = cgu.codegen_dep_node(tcx);
|
||||
assert!(
|
||||
!tcx.dep_graph.dep_node_exists(&dep_node),
|
||||
"CompileCodegenUnit dep-node for CGU `{}` already exists before marking.",
|
||||
cgu.name()
|
||||
);
|
||||
|
||||
if tcx.try_mark_green(&dep_node) { CguReuse::PostLto } else { CguReuse::No }
|
||||
}
|
||||
|
@ -19,7 +19,6 @@
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::dep_graph::WorkProduct;
|
||||
use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel};
|
||||
use rustc_session::cgu_reuse_tracker::CguReuse;
|
||||
use rustc_session::config::{self, CrateType, Lto};
|
||||
|
||||
use std::ffi::{CStr, CString};
|
||||
@ -584,7 +583,6 @@ fn thin_lto(
|
||||
copy_jobs.push(work_product);
|
||||
info!(" - {}: re-used", module_name);
|
||||
assert!(cgcx.incr_comp_session_dir.is_some());
|
||||
cgcx.cgu_reuse_tracker.set_actual_reuse(module_name, CguReuse::PostLto);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -397,7 +397,12 @@ fn scalar_copy_llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> Option<&'a Type
|
||||
// extracts all the individual values.
|
||||
|
||||
let ety = element.llvm_type(cx);
|
||||
return Some(cx.type_vector(ety, *count));
|
||||
if *count == 1 {
|
||||
// Emitting `<1 x T>` would be silly; just use the scalar.
|
||||
return Some(ety);
|
||||
} else {
|
||||
return Some(cx.type_vector(ety, *count));
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: The above only handled integer arrays; surely more things
|
||||
|
@ -16,6 +16,7 @@ pathdiff = "0.2.0"
|
||||
serde_json = "1.0.59"
|
||||
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
|
||||
regex = "1.4"
|
||||
thin-vec = "0.2.12"
|
||||
|
||||
rustc_serialize = { path = "../rustc_serialize" }
|
||||
rustc_arena = { path = "../rustc_arena" }
|
||||
|
@ -11,6 +11,9 @@ codegen_ssa_atomic_compare_exchange = Atomic compare-exchange intrinsic missing
|
||||
|
||||
codegen_ssa_binary_output_to_tty = option `-o` or `--emit` is used to write binary output type `{$shorthand}` to stdout, but stdout is a tty
|
||||
|
||||
codegen_ssa_cgu_not_recorded =
|
||||
CGU-reuse for `{$cgu_user_name}` is (mangled: `{$cgu_name}`) was not recorded
|
||||
|
||||
codegen_ssa_check_installed_visual_studio = please ensure that Visual Studio 2017 or later, or Build Tools for Visual Studio were installed with the Visual C++ option.
|
||||
|
||||
codegen_ssa_copy_path = could not copy {$from} to {$to}: {$error}
|
||||
@ -39,6 +42,8 @@ codegen_ssa_failed_to_get_layout = failed to get layout for {$ty}: {$err}
|
||||
|
||||
codegen_ssa_failed_to_write = failed to write {$path}: {$error}
|
||||
|
||||
codegen_ssa_field_associated_value_expected = associated value expected for `{$name}`
|
||||
|
||||
codegen_ssa_ignoring_emit_path = ignoring emit path because multiple .{$extension} files were produced
|
||||
|
||||
codegen_ssa_ignoring_output = ignoring -o because multiple .{$extension} files were produced
|
||||
@ -46,6 +51,12 @@ codegen_ssa_ignoring_output = ignoring -o because multiple .{$extension} files w
|
||||
codegen_ssa_illegal_link_ordinal_format = illegal ordinal format in `link_ordinal`
|
||||
.note = an unsuffixed integer value, e.g., `1`, is expected
|
||||
|
||||
codegen_ssa_incorrect_cgu_reuse_type =
|
||||
CGU-reuse for `{$cgu_user_name}` is `{$actual_reuse}` but should be {$at_least ->
|
||||
[one] {"at least "}
|
||||
*[other] {""}
|
||||
}`{$expected_reuse}`
|
||||
|
||||
codegen_ssa_insufficient_vs_code_product = VS Code is a different product, and is not sufficient.
|
||||
|
||||
codegen_ssa_invalid_link_ordinal_nargs = incorrect number of arguments to `#[link_ordinal]`
|
||||
@ -153,12 +164,18 @@ codegen_ssa_linker_unsupported_modifier = `as-needed` modifier not supported for
|
||||
|
||||
codegen_ssa_linking_failed = linking with `{$linker_path}` failed: {$exit_status}
|
||||
|
||||
codegen_ssa_malformed_cgu_name =
|
||||
found malformed codegen unit name `{$user_path}`. codegen units names must always start with the name of the crate (`{$crate_name}` in this case).
|
||||
|
||||
codegen_ssa_metadata_object_file_write = error writing metadata object file: {$error}
|
||||
|
||||
codegen_ssa_missing_cpp_build_tool_component = or a necessary component may be missing from the "C++ build tools" workload
|
||||
|
||||
codegen_ssa_missing_memory_ordering = Atomic intrinsic missing memory ordering
|
||||
|
||||
codegen_ssa_missing_query_depgraph =
|
||||
found CGU-reuse attribute but `-Zquery-dep-graph` was not specified
|
||||
|
||||
codegen_ssa_msvc_missing_linker = the msvc targets depend on the msvc linker but `link.exe` was not found
|
||||
|
||||
codegen_ssa_multiple_external_func_decl = multiple declarations of external function `{$function}` from library `{$library_name}` have different calling conventions
|
||||
@ -166,6 +183,11 @@ codegen_ssa_multiple_external_func_decl = multiple declarations of external func
|
||||
codegen_ssa_multiple_main_functions = entry symbol `main` declared multiple times
|
||||
.help = did you use `#[no_mangle]` on `fn main`? Use `#[start]` instead
|
||||
|
||||
codegen_ssa_no_field = no field `{$name}`
|
||||
|
||||
codegen_ssa_no_module_named =
|
||||
no module named `{$user_path}` (mangled: {$cgu_name}). available modules: {$cgu_names}
|
||||
|
||||
codegen_ssa_no_natvis_directory = error enumerating natvis directory: {$error}
|
||||
|
||||
codegen_ssa_processing_dymutil_failed = processing debug info with `dsymutil` failed: {$status}
|
||||
@ -297,6 +319,8 @@ codegen_ssa_unknown_atomic_operation = unknown atomic operation
|
||||
|
||||
codegen_ssa_unknown_atomic_ordering = unknown ordering in atomic intrinsic
|
||||
|
||||
codegen_ssa_unknown_reuse_kind = unknown cgu-reuse-kind `{$kind}` specified
|
||||
|
||||
codegen_ssa_unsupported_arch = unsupported arch `{$arch}` for os `{$os}`
|
||||
|
||||
codegen_ssa_unsupported_link_self_contained = option `-C link-self-contained` is not supported on this target
|
||||
|
@ -25,16 +25,21 @@
|
||||
|
||||
use crate::errors;
|
||||
use rustc_ast as ast;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::unord::UnordSet;
|
||||
use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg};
|
||||
use rustc_hir::def_id::LOCAL_CRATE;
|
||||
use rustc_middle::mir::mono::CodegenUnitNameBuilder;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::cgu_reuse_tracker::*;
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::{Span, Symbol};
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub fn assert_module_sources(tcx: TyCtxt<'_>) {
|
||||
pub fn assert_module_sources(tcx: TyCtxt<'_>, set_reuse: &dyn Fn(&mut CguReuseTracker)) {
|
||||
tcx.dep_graph.with_ignore(|| {
|
||||
if tcx.sess.opts.incremental.is_none() {
|
||||
return;
|
||||
@ -43,21 +48,34 @@ pub fn assert_module_sources(tcx: TyCtxt<'_>) {
|
||||
let available_cgus =
|
||||
tcx.collect_and_partition_mono_items(()).1.iter().map(|cgu| cgu.name()).collect();
|
||||
|
||||
let ams = AssertModuleSource { tcx, available_cgus };
|
||||
let mut ams = AssertModuleSource {
|
||||
tcx,
|
||||
available_cgus,
|
||||
cgu_reuse_tracker: if tcx.sess.opts.unstable_opts.query_dep_graph {
|
||||
CguReuseTracker::new()
|
||||
} else {
|
||||
CguReuseTracker::new_disabled()
|
||||
},
|
||||
};
|
||||
|
||||
for attr in tcx.hir().attrs(rustc_hir::CRATE_HIR_ID) {
|
||||
ams.check_attr(attr);
|
||||
}
|
||||
})
|
||||
|
||||
set_reuse(&mut ams.cgu_reuse_tracker);
|
||||
|
||||
ams.cgu_reuse_tracker.check_expected_reuse(tcx.sess);
|
||||
});
|
||||
}
|
||||
|
||||
struct AssertModuleSource<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
available_cgus: UnordSet<Symbol>,
|
||||
cgu_reuse_tracker: CguReuseTracker,
|
||||
}
|
||||
|
||||
impl<'tcx> AssertModuleSource<'tcx> {
|
||||
fn check_attr(&self, attr: &ast::Attribute) {
|
||||
fn check_attr(&mut self, attr: &ast::Attribute) {
|
||||
let (expected_reuse, comp_kind) = if attr.has_name(sym::rustc_partition_reused) {
|
||||
(CguReuse::PreLto, ComparisonKind::AtLeast)
|
||||
} else if attr.has_name(sym::rustc_partition_codegened) {
|
||||
@ -129,7 +147,7 @@ fn check_attr(&self, attr: &ast::Attribute) {
|
||||
});
|
||||
}
|
||||
|
||||
self.tcx.sess.cgu_reuse_tracker.set_expectation(
|
||||
self.cgu_reuse_tracker.set_expectation(
|
||||
cgu_name,
|
||||
&user_path,
|
||||
attr.span,
|
||||
@ -169,3 +187,109 @@ fn check_config(&self, attr: &ast::Attribute) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
|
||||
pub enum CguReuse {
|
||||
No,
|
||||
PreLto,
|
||||
PostLto,
|
||||
}
|
||||
|
||||
impl fmt::Display for CguReuse {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
CguReuse::No => write!(f, "No"),
|
||||
CguReuse::PreLto => write!(f, "PreLto "),
|
||||
CguReuse::PostLto => write!(f, "PostLto "),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoDiagnosticArg for CguReuse {
|
||||
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
||||
DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum ComparisonKind {
|
||||
Exact,
|
||||
AtLeast,
|
||||
}
|
||||
|
||||
struct TrackerData {
|
||||
actual_reuse: FxHashMap<String, CguReuse>,
|
||||
expected_reuse: FxHashMap<String, (String, Span, CguReuse, ComparisonKind)>,
|
||||
}
|
||||
|
||||
pub struct CguReuseTracker {
|
||||
data: Option<TrackerData>,
|
||||
}
|
||||
|
||||
impl CguReuseTracker {
|
||||
fn new() -> CguReuseTracker {
|
||||
let data =
|
||||
TrackerData { actual_reuse: Default::default(), expected_reuse: Default::default() };
|
||||
|
||||
CguReuseTracker { data: Some(data) }
|
||||
}
|
||||
|
||||
fn new_disabled() -> CguReuseTracker {
|
||||
CguReuseTracker { data: None }
|
||||
}
|
||||
|
||||
pub fn set_actual_reuse(&mut self, cgu_name: &str, kind: CguReuse) {
|
||||
if let Some(data) = &mut self.data {
|
||||
debug!("set_actual_reuse({cgu_name:?}, {kind:?})");
|
||||
|
||||
let prev_reuse = data.actual_reuse.insert(cgu_name.to_string(), kind);
|
||||
assert!(prev_reuse.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
fn set_expectation(
|
||||
&mut self,
|
||||
cgu_name: Symbol,
|
||||
cgu_user_name: &str,
|
||||
error_span: Span,
|
||||
expected_reuse: CguReuse,
|
||||
comparison_kind: ComparisonKind,
|
||||
) {
|
||||
if let Some(data) = &mut self.data {
|
||||
debug!("set_expectation({cgu_name:?}, {expected_reuse:?}, {comparison_kind:?})");
|
||||
|
||||
data.expected_reuse.insert(
|
||||
cgu_name.to_string(),
|
||||
(cgu_user_name.to_string(), error_span, expected_reuse, comparison_kind),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expected_reuse(&self, sess: &Session) {
|
||||
if let Some(ref data) = self.data {
|
||||
for (cgu_name, &(ref cgu_user_name, ref error_span, expected_reuse, comparison_kind)) in
|
||||
&data.expected_reuse
|
||||
{
|
||||
if let Some(&actual_reuse) = data.actual_reuse.get(cgu_name) {
|
||||
let (error, at_least) = match comparison_kind {
|
||||
ComparisonKind::Exact => (expected_reuse != actual_reuse, false),
|
||||
ComparisonKind::AtLeast => (actual_reuse < expected_reuse, true),
|
||||
};
|
||||
|
||||
if error {
|
||||
let at_least = if at_least { 1 } else { 0 };
|
||||
errors::IncorrectCguReuseType {
|
||||
span: *error_span,
|
||||
cgu_user_name,
|
||||
actual_reuse,
|
||||
expected_reuse,
|
||||
at_least,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
sess.emit_fatal(errors::CguNotRecorded { cgu_user_name, cgu_name });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -26,7 +26,6 @@
|
||||
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
|
||||
use rustc_middle::middle::exported_symbols::SymbolExportInfo;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::cgu_reuse_tracker::CguReuseTracker;
|
||||
use rustc_session::config::{self, CrateType, Lto, OutFileName, OutputFilenames, OutputType};
|
||||
use rustc_session::config::{Passes, SwitchWithOptPath};
|
||||
use rustc_session::Session;
|
||||
@ -366,8 +365,6 @@ pub struct CodegenContext<B: WriteBackendMethods> {
|
||||
/// The incremental compilation session directory, or None if we are not
|
||||
/// compiling incrementally
|
||||
pub incr_comp_session_dir: Option<PathBuf>,
|
||||
/// Used to update CGU re-use information during the thinlto phase.
|
||||
pub cgu_reuse_tracker: CguReuseTracker,
|
||||
/// Channel back to the main control thread to send messages to
|
||||
pub coordinator_send: Sender<Box<dyn Any + Send>>,
|
||||
}
|
||||
@ -1119,7 +1116,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
|
||||
remark: sess.opts.cg.remark.clone(),
|
||||
remark_dir,
|
||||
incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()),
|
||||
cgu_reuse_tracker: sess.cgu_reuse_tracker.clone(),
|
||||
coordinator_send,
|
||||
expanded_args: tcx.sess.expanded_args.clone(),
|
||||
diag_emitter: shared_emitter.clone(),
|
||||
@ -1969,8 +1965,6 @@ pub fn join(self, sess: &Session) -> (CodegenResults, FxIndexMap<WorkProductId,
|
||||
}
|
||||
});
|
||||
|
||||
sess.cgu_reuse_tracker.check_expected_reuse(sess);
|
||||
|
||||
sess.abort_if_errors();
|
||||
|
||||
let work_products =
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::assert_module_sources::CguReuse;
|
||||
use crate::back::link::are_upstream_rust_objects_already_included;
|
||||
use crate::back::metadata::create_compressed_metadata_file;
|
||||
use crate::back::write::{
|
||||
@ -31,7 +32,6 @@
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
|
||||
use rustc_session::cgu_reuse_tracker::CguReuse;
|
||||
use rustc_session::config::{self, CrateType, EntryFnType, OutputType};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::symbol::sym;
|
||||
@ -683,6 +683,13 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
|
||||
codegen_units.iter().map(|cgu| determine_cgu_reuse(tcx, &cgu)).collect::<Vec<_>>()
|
||||
});
|
||||
|
||||
crate::assert_module_sources::assert_module_sources(tcx, &|cgu_reuse_tracker| {
|
||||
for (i, cgu) in codegen_units.iter().enumerate() {
|
||||
let cgu_reuse = cgu_reuse[i];
|
||||
cgu_reuse_tracker.set_actual_reuse(cgu.name().as_str(), cgu_reuse);
|
||||
}
|
||||
});
|
||||
|
||||
let mut total_codegen_time = Duration::new(0, 0);
|
||||
let start_rss = tcx.sess.opts.unstable_opts.time_passes.then(|| get_resident_set_size());
|
||||
|
||||
@ -727,7 +734,6 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
|
||||
ongoing_codegen.check_for_errors(tcx.sess);
|
||||
|
||||
let cgu_reuse = cgu_reuse[i];
|
||||
tcx.sess.cgu_reuse_tracker.set_actual_reuse(cgu.name().as_str(), cgu_reuse);
|
||||
|
||||
match cgu_reuse {
|
||||
CguReuse::No => {
|
||||
@ -994,7 +1000,7 @@ pub fn provide(providers: &mut Providers) {
|
||||
};
|
||||
}
|
||||
|
||||
fn determine_cgu_reuse<'tcx>(tcx: TyCtxt<'tcx>, cgu: &CodegenUnit<'tcx>) -> CguReuse {
|
||||
pub fn determine_cgu_reuse<'tcx>(tcx: TyCtxt<'tcx>, cgu: &CodegenUnit<'tcx>) -> CguReuse {
|
||||
if !tcx.dep_graph.is_fully_enabled() {
|
||||
return CguReuse::No;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
//! Errors emitted by codegen_ssa
|
||||
|
||||
use crate::assert_module_sources::CguReuse;
|
||||
use crate::back::command::Command;
|
||||
use crate::fluent_generated as fluent;
|
||||
use rustc_errors::{
|
||||
@ -16,6 +17,74 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::ExitStatus;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_ssa_incorrect_cgu_reuse_type)]
|
||||
pub struct IncorrectCguReuseType<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub cgu_user_name: &'a str,
|
||||
pub actual_reuse: CguReuse,
|
||||
pub expected_reuse: CguReuse,
|
||||
pub at_least: u8,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_ssa_cgu_not_recorded)]
|
||||
pub struct CguNotRecorded<'a> {
|
||||
pub cgu_user_name: &'a str,
|
||||
pub cgu_name: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_ssa_unknown_reuse_kind)]
|
||||
pub struct UnknownReuseKind {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub kind: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_ssa_missing_query_depgraph)]
|
||||
pub struct MissingQueryDepGraph {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_ssa_malformed_cgu_name)]
|
||||
pub struct MalformedCguName {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub user_path: String,
|
||||
pub crate_name: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_ssa_no_module_named)]
|
||||
pub struct NoModuleNamed<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub user_path: &'a str,
|
||||
pub cgu_name: Symbol,
|
||||
pub cgu_names: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_ssa_field_associated_value_expected)]
|
||||
pub struct FieldAssociatedValueExpected {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub name: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_ssa_no_field)]
|
||||
pub struct NoField {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub name: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_ssa_lib_def_write_failure)]
|
||||
pub struct LibDefWriteFailure {
|
||||
|
@ -46,6 +46,7 @@
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
pub mod assert_module_sources;
|
||||
pub mod back;
|
||||
pub mod base;
|
||||
pub mod codegen_attrs;
|
||||
|
@ -1,6 +1,3 @@
|
||||
use crate::const_eval::CheckAlignment;
|
||||
use crate::errors::ConstEvalError;
|
||||
|
||||
use either::{Left, Right};
|
||||
|
||||
use rustc_hir::def::DefKind;
|
||||
@ -15,7 +12,9 @@
|
||||
use rustc_target::abi::{self, Abi};
|
||||
|
||||
use super::{CanAccessStatics, CompileTimeEvalContext, CompileTimeInterpreter};
|
||||
use crate::const_eval::CheckAlignment;
|
||||
use crate::errors;
|
||||
use crate::errors::ConstEvalError;
|
||||
use crate::interpret::eval_nullary_intrinsic;
|
||||
use crate::interpret::{
|
||||
intern_const_alloc_recursive, CtfeValidationMode, GlobalId, Immediate, InternKind, InterpCx,
|
||||
@ -290,14 +289,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
|
||||
key.param_env,
|
||||
// Statics (and promoteds inside statics) may access other statics, because unlike consts
|
||||
// they do not have to behave "as if" they were evaluated at runtime.
|
||||
CompileTimeInterpreter::new(
|
||||
CanAccessStatics::from(is_static),
|
||||
if tcx.sess.opts.unstable_opts.extra_const_ub_checks {
|
||||
CheckAlignment::Error
|
||||
} else {
|
||||
CheckAlignment::FutureIncompat
|
||||
},
|
||||
),
|
||||
CompileTimeInterpreter::new(CanAccessStatics::from(is_static), CheckAlignment::Error),
|
||||
);
|
||||
|
||||
let res = ecx.load_mir(cid.instance.def, cid.promoted);
|
||||
|
@ -1,10 +1,9 @@
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::{LangItem, CRATE_HIR_ID};
|
||||
use rustc_hir::LangItem;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::mir::interpret::PointerArithmetic;
|
||||
use rustc_middle::ty::layout::{FnAbiOf, TyAndLayout};
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_session::lint::builtin::INVALID_ALIGNMENT;
|
||||
use std::borrow::Borrow;
|
||||
use std::hash::Hash;
|
||||
use std::ops::ControlFlow;
|
||||
@ -21,11 +20,11 @@
|
||||
use rustc_target::spec::abi::Abi as CallAbi;
|
||||
|
||||
use crate::errors::{LongRunning, LongRunningWarn};
|
||||
use crate::fluent_generated as fluent;
|
||||
use crate::interpret::{
|
||||
self, compile_time_machine, AllocId, ConstAllocation, FnArg, FnVal, Frame, ImmTy, InterpCx,
|
||||
InterpResult, OpTy, PlaceTy, Pointer, Scalar,
|
||||
};
|
||||
use crate::{errors, fluent_generated as fluent};
|
||||
|
||||
use super::error::*;
|
||||
|
||||
@ -65,22 +64,11 @@ pub struct CompileTimeInterpreter<'mir, 'tcx> {
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum CheckAlignment {
|
||||
/// Ignore alignment when following relocations.
|
||||
/// Ignore all alignment requirements.
|
||||
/// This is mainly used in interning.
|
||||
No,
|
||||
/// Hard error when dereferencing a misaligned pointer.
|
||||
Error,
|
||||
/// Emit a future incompat lint when dereferencing a misaligned pointer.
|
||||
FutureIncompat,
|
||||
}
|
||||
|
||||
impl CheckAlignment {
|
||||
pub fn should_check(&self) -> bool {
|
||||
match self {
|
||||
CheckAlignment::No => false,
|
||||
CheckAlignment::Error | CheckAlignment::FutureIncompat => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
@ -358,8 +346,8 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
|
||||
const PANIC_ON_ALLOC_FAIL: bool = false; // will be raised as a proper error
|
||||
|
||||
#[inline(always)]
|
||||
fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> CheckAlignment {
|
||||
ecx.machine.check_alignment
|
||||
fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
|
||||
matches!(ecx.machine.check_alignment, CheckAlignment::Error)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@ -367,39 +355,6 @@ fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>, layout: TyAndLayout<'tcx>)
|
||||
ecx.tcx.sess.opts.unstable_opts.extra_const_ub_checks || layout.abi.is_uninhabited()
|
||||
}
|
||||
|
||||
fn alignment_check_failed(
|
||||
ecx: &InterpCx<'mir, 'tcx, Self>,
|
||||
has: Align,
|
||||
required: Align,
|
||||
check: CheckAlignment,
|
||||
) -> InterpResult<'tcx, ()> {
|
||||
let err = err_ub!(AlignmentCheckFailed { has, required }).into();
|
||||
match check {
|
||||
CheckAlignment::Error => Err(err),
|
||||
CheckAlignment::No => span_bug!(
|
||||
ecx.cur_span(),
|
||||
"`alignment_check_failed` called when no alignment check requested"
|
||||
),
|
||||
CheckAlignment::FutureIncompat => {
|
||||
let (_, backtrace) = err.into_parts();
|
||||
backtrace.print_backtrace();
|
||||
let (span, frames) = super::get_span_and_frames(&ecx);
|
||||
|
||||
ecx.tcx.emit_spanned_lint(
|
||||
INVALID_ALIGNMENT,
|
||||
ecx.stack().iter().find_map(|frame| frame.lint_root()).unwrap_or(CRATE_HIR_ID),
|
||||
span,
|
||||
errors::AlignmentCheckFailed {
|
||||
has: has.bytes(),
|
||||
required: required.bytes(),
|
||||
frames,
|
||||
},
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_mir(
|
||||
ecx: &InterpCx<'mir, 'tcx, Self>,
|
||||
instance: ty::InstanceDef<'tcx>,
|
||||
|
@ -12,11 +12,9 @@
|
||||
use rustc_middle::ty::layout::TyAndLayout;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_target::abi::{Align, Size};
|
||||
use rustc_target::abi::Size;
|
||||
use rustc_target::spec::abi::Abi as CallAbi;
|
||||
|
||||
use crate::const_eval::CheckAlignment;
|
||||
|
||||
use super::{
|
||||
AllocBytes, AllocId, AllocRange, Allocation, ConstAllocation, FnArg, Frame, ImmTy, InterpCx,
|
||||
InterpResult, MPlaceTy, MemoryKind, OpTy, PlaceTy, Pointer, Provenance,
|
||||
@ -135,7 +133,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
|
||||
const POST_MONO_CHECKS: bool = true;
|
||||
|
||||
/// Whether memory accesses should be alignment-checked.
|
||||
fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> CheckAlignment;
|
||||
fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
|
||||
|
||||
/// Whether, when checking alignment, we should look at the actual address and thus support
|
||||
/// custom alignment logic based on whatever the integer address happens to be.
|
||||
@ -143,13 +141,6 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
|
||||
/// If this returns true, Provenance::OFFSET_IS_ADDR must be true.
|
||||
fn use_addr_for_alignment_check(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
|
||||
|
||||
fn alignment_check_failed(
|
||||
ecx: &InterpCx<'mir, 'tcx, Self>,
|
||||
has: Align,
|
||||
required: Align,
|
||||
check: CheckAlignment,
|
||||
) -> InterpResult<'tcx, ()>;
|
||||
|
||||
/// Whether to enforce the validity invariant for a specific layout.
|
||||
fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>, layout: TyAndLayout<'tcx>) -> bool;
|
||||
|
||||
|
@ -18,7 +18,6 @@
|
||||
use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TyCtxt};
|
||||
use rustc_target::abi::{Align, HasDataLayout, Size};
|
||||
|
||||
use crate::const_eval::CheckAlignment;
|
||||
use crate::fluent_generated as fluent;
|
||||
|
||||
use super::{
|
||||
@ -373,8 +372,7 @@ fn get_ptr_access(
|
||||
self.check_and_deref_ptr(
|
||||
ptr,
|
||||
size,
|
||||
align,
|
||||
M::enforce_alignment(self),
|
||||
M::enforce_alignment(self).then_some(align),
|
||||
CheckInAllocMsg::MemoryAccessTest,
|
||||
|alloc_id, offset, prov| {
|
||||
let (size, align) = self
|
||||
@ -395,17 +393,10 @@ pub fn check_ptr_access_align(
|
||||
align: Align,
|
||||
msg: CheckInAllocMsg,
|
||||
) -> InterpResult<'tcx> {
|
||||
self.check_and_deref_ptr(
|
||||
ptr,
|
||||
size,
|
||||
align,
|
||||
CheckAlignment::Error,
|
||||
msg,
|
||||
|alloc_id, _, _| {
|
||||
let (size, align) = self.get_live_alloc_size_and_align(alloc_id, msg)?;
|
||||
Ok((size, align, ()))
|
||||
},
|
||||
)?;
|
||||
self.check_and_deref_ptr(ptr, size, Some(align), msg, |alloc_id, _, _| {
|
||||
let (size, align) = self.get_live_alloc_size_and_align(alloc_id, msg)?;
|
||||
Ok((size, align, ()))
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -419,8 +410,7 @@ fn check_and_deref_ptr<T>(
|
||||
&self,
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
size: Size,
|
||||
align: Align,
|
||||
check: CheckAlignment,
|
||||
align: Option<Align>,
|
||||
msg: CheckInAllocMsg,
|
||||
alloc_size: impl FnOnce(
|
||||
AllocId,
|
||||
@ -436,8 +426,8 @@ fn check_and_deref_ptr<T>(
|
||||
throw_ub!(DanglingIntPointer(addr, msg));
|
||||
}
|
||||
// Must be aligned.
|
||||
if check.should_check() {
|
||||
self.check_offset_align(addr, align, check)?;
|
||||
if let Some(align) = align {
|
||||
self.check_offset_align(addr, align)?;
|
||||
}
|
||||
None
|
||||
}
|
||||
@ -460,16 +450,16 @@ fn check_and_deref_ptr<T>(
|
||||
}
|
||||
// Test align. Check this last; if both bounds and alignment are violated
|
||||
// we want the error to be about the bounds.
|
||||
if check.should_check() {
|
||||
if let Some(align) = align {
|
||||
if M::use_addr_for_alignment_check(self) {
|
||||
// `use_addr_for_alignment_check` can only be true if `OFFSET_IS_ADDR` is true.
|
||||
self.check_offset_align(ptr.addr().bytes(), align, check)?;
|
||||
self.check_offset_align(ptr.addr().bytes(), align)?;
|
||||
} else {
|
||||
// Check allocation alignment and offset alignment.
|
||||
if alloc_align.bytes() < align.bytes() {
|
||||
M::alignment_check_failed(self, alloc_align, align, check)?;
|
||||
throw_ub!(AlignmentCheckFailed { has: alloc_align, required: align });
|
||||
}
|
||||
self.check_offset_align(offset.bytes(), align, check)?;
|
||||
self.check_offset_align(offset.bytes(), align)?;
|
||||
}
|
||||
}
|
||||
|
||||
@ -480,18 +470,16 @@ fn check_and_deref_ptr<T>(
|
||||
})
|
||||
}
|
||||
|
||||
fn check_offset_align(
|
||||
&self,
|
||||
offset: u64,
|
||||
align: Align,
|
||||
check: CheckAlignment,
|
||||
) -> InterpResult<'tcx> {
|
||||
fn check_offset_align(&self, offset: u64, align: Align) -> InterpResult<'tcx> {
|
||||
if offset % align.bytes() == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
// The biggest power of two through which `offset` is divisible.
|
||||
let offset_pow2 = 1 << offset.trailing_zeros();
|
||||
M::alignment_check_failed(self, Align::from_bytes(offset_pow2).unwrap(), align, check)
|
||||
throw_ub!(AlignmentCheckFailed {
|
||||
has: Align::from_bytes(offset_pow2).unwrap(),
|
||||
required: align
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -609,8 +597,7 @@ pub fn get_ptr_alloc<'a>(
|
||||
let ptr_and_alloc = self.check_and_deref_ptr(
|
||||
ptr,
|
||||
size,
|
||||
align,
|
||||
M::enforce_alignment(self),
|
||||
M::enforce_alignment(self).then_some(align),
|
||||
CheckInAllocMsg::MemoryAccessTest,
|
||||
|alloc_id, offset, prov| {
|
||||
let alloc = self.get_alloc_raw(alloc_id)?;
|
||||
|
@ -500,8 +500,7 @@ pub fn check_mplace(&self, mplace: &MPlaceTy<'tcx, M::Provenance>) -> InterpResu
|
||||
.size_and_align_of_mplace(&mplace)?
|
||||
.unwrap_or((mplace.layout.size, mplace.layout.align.abi));
|
||||
// Due to packed places, only `mplace.align` matters.
|
||||
let align =
|
||||
if M::enforce_alignment(self).should_check() { mplace.align } else { Align::ONE };
|
||||
let align = if M::enforce_alignment(self) { mplace.align } else { Align::ONE };
|
||||
self.check_ptr_access_align(mplace.ptr(), size, align, CheckInAllocMsg::DerefTest)?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -8,10 +8,10 @@
|
||||
#![cfg_attr(not(bootstrap), doc(rust_logo))]
|
||||
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
|
||||
#![cfg_attr(not(bootstrap), allow(internal_features))]
|
||||
#![feature(lazy_cell)]
|
||||
#![feature(decl_macro)]
|
||||
#![feature(panic_update_hook)]
|
||||
#![feature(lazy_cell)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(panic_update_hook)]
|
||||
#![recursion_limit = "256"]
|
||||
#![allow(rustc::potential_query_instability)]
|
||||
#![deny(rustc::untranslatable_diagnostic)]
|
||||
@ -395,7 +395,7 @@ fn run_compiler(
|
||||
if ppm.needs_ast_map() {
|
||||
queries.global_ctxt()?.enter(|tcx| {
|
||||
tcx.ensure().early_lint_checks(());
|
||||
pretty::print_after_hir_lowering(tcx, *ppm);
|
||||
pretty::print(sess, *ppm, pretty::PrintExtra::NeedsAstMap { tcx });
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
@ -404,7 +404,7 @@ fn run_compiler(
|
||||
queries.global_ctxt()?.enter(|tcx| tcx.output_filenames(()));
|
||||
} else {
|
||||
let krate = queries.parse()?.steal();
|
||||
pretty::print_after_parsing(sess, &krate, *ppm);
|
||||
pretty::print(sess, *ppm, pretty::PrintExtra::AfterParsing { krate });
|
||||
}
|
||||
trace!("finished pretty-printing");
|
||||
return early_exit();
|
||||
@ -545,7 +545,7 @@ pub enum Compilation {
|
||||
}
|
||||
|
||||
impl Compilation {
|
||||
pub fn and_then<F: FnOnce() -> Compilation>(self, next: F) -> Compilation {
|
||||
fn and_then<F: FnOnce() -> Compilation>(self, next: F) -> Compilation {
|
||||
match self {
|
||||
Compilation::Stop => Compilation::Stop,
|
||||
Compilation::Continue => next(),
|
||||
@ -657,7 +657,7 @@ fn show_md_content_with_pager(content: &str, color: ColorConfig) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_process_rlink(sess: &Session, compiler: &interface::Compiler) -> Compilation {
|
||||
fn try_process_rlink(sess: &Session, compiler: &interface::Compiler) -> Compilation {
|
||||
if sess.opts.unstable_opts.link_only {
|
||||
if let Input::File(file) = &sess.io.input {
|
||||
let outputs = compiler.build_output_filenames(sess, &[]);
|
||||
@ -698,7 +698,7 @@ pub fn try_process_rlink(sess: &Session, compiler: &interface::Compiler) -> Comp
|
||||
}
|
||||
}
|
||||
|
||||
pub fn list_metadata(
|
||||
fn list_metadata(
|
||||
handler: &EarlyErrorHandler,
|
||||
sess: &Session,
|
||||
metadata_loader: &dyn MetadataLoader,
|
||||
@ -1184,7 +1184,7 @@ fn print_flag_list<T>(
|
||||
///
|
||||
/// So with all that in mind, the comments below have some more detail about the
|
||||
/// contortions done here to get things to work out correctly.
|
||||
pub fn handle_options(handler: &EarlyErrorHandler, args: &[String]) -> Option<getopts::Matches> {
|
||||
fn handle_options(handler: &EarlyErrorHandler, args: &[String]) -> Option<getopts::Matches> {
|
||||
if args.is_empty() {
|
||||
// user did not write `-v` nor `-Z unstable-options`, so do not
|
||||
// include that extra information.
|
||||
@ -1283,9 +1283,9 @@ pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
pub static ICE_PATH: OnceLock<Option<PathBuf>> = OnceLock::new();
|
||||
static ICE_PATH: OnceLock<Option<PathBuf>> = OnceLock::new();
|
||||
|
||||
pub fn ice_path() -> &'static Option<PathBuf> {
|
||||
fn ice_path() -> &'static Option<PathBuf> {
|
||||
ICE_PATH.get_or_init(|| {
|
||||
if !rustc_feature::UnstableFeatures::from_environment(None).is_nightly_build() {
|
||||
return None;
|
||||
@ -1394,7 +1394,7 @@ pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler))
|
||||
///
|
||||
/// When `install_ice_hook` is called, this function will be called as the panic
|
||||
/// hook.
|
||||
pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info: fn(&Handler)) {
|
||||
fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info: fn(&Handler)) {
|
||||
let fallback_bundle =
|
||||
rustc_errors::fallback_fluent_bundle(crate::DEFAULT_LOCALE_RESOURCES.to_vec(), false);
|
||||
let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
|
||||
|
@ -1,14 +1,13 @@
|
||||
//! The various pretty-printing routines.
|
||||
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_ast_pretty::pprust as pprust_ast;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir_pretty as pprust_hir;
|
||||
use rustc_middle::hir::map as hir_map;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::mir::{write_mir_graphviz, write_mir_pretty};
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_session::config::{OutFileName, PpAstTreeMode, PpHirMode, PpMode, PpSourceMode};
|
||||
use rustc_session::config::{OutFileName, PpHirMode, PpMode, PpSourceMode};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::FileName;
|
||||
@ -20,174 +19,57 @@
|
||||
pub use self::PpSourceMode::*;
|
||||
use crate::abort_on_err;
|
||||
|
||||
// This slightly awkward construction is to allow for each PpMode to
|
||||
// choose whether it needs to do analyses (which can consume the
|
||||
// Session) and then pass through the session (now attached to the
|
||||
// analysis results) on to the chosen pretty-printer, along with the
|
||||
// `&PpAnn` object.
|
||||
//
|
||||
// Note that since the `&PrinterSupport` is freshly constructed on each
|
||||
// call, it would not make sense to try to attach the lifetime of `self`
|
||||
// to the lifetime of the `&PrinterObject`.
|
||||
struct AstNoAnn;
|
||||
|
||||
/// Constructs a `PrinterSupport` object and passes it to `f`.
|
||||
fn call_with_pp_support<'tcx, A, F>(
|
||||
ppmode: &PpSourceMode,
|
||||
sess: &'tcx Session,
|
||||
tcx: Option<TyCtxt<'tcx>>,
|
||||
f: F,
|
||||
) -> A
|
||||
where
|
||||
F: FnOnce(&dyn PrinterSupport) -> A,
|
||||
{
|
||||
match *ppmode {
|
||||
Normal | Expanded => {
|
||||
let annotation = NoAnn { sess, tcx };
|
||||
f(&annotation)
|
||||
}
|
||||
impl pprust_ast::PpAnn for AstNoAnn {}
|
||||
|
||||
Identified | ExpandedIdentified => {
|
||||
let annotation = IdentifiedAnnotation { sess, tcx };
|
||||
f(&annotation)
|
||||
}
|
||||
ExpandedHygiene => {
|
||||
let annotation = HygieneAnnotation { sess };
|
||||
f(&annotation)
|
||||
}
|
||||
}
|
||||
}
|
||||
fn call_with_pp_support_hir<A, F>(ppmode: &PpHirMode, tcx: TyCtxt<'_>, f: F) -> A
|
||||
where
|
||||
F: FnOnce(&dyn HirPrinterSupport<'_>, hir_map::Map<'_>) -> A,
|
||||
{
|
||||
match *ppmode {
|
||||
PpHirMode::Normal => {
|
||||
let annotation = NoAnn { sess: tcx.sess, tcx: Some(tcx) };
|
||||
f(&annotation, tcx.hir())
|
||||
}
|
||||
|
||||
PpHirMode::Identified => {
|
||||
let annotation = IdentifiedAnnotation { sess: tcx.sess, tcx: Some(tcx) };
|
||||
f(&annotation, tcx.hir())
|
||||
}
|
||||
PpHirMode::Typed => {
|
||||
abort_on_err(tcx.analysis(()), tcx.sess);
|
||||
|
||||
let annotation = TypedAnnotation { tcx, maybe_typeck_results: Cell::new(None) };
|
||||
tcx.dep_graph.with_ignore(|| f(&annotation, tcx.hir()))
|
||||
}
|
||||
}
|
||||
struct HirNoAnn<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
}
|
||||
|
||||
trait PrinterSupport: pprust::PpAnn {
|
||||
/// Provides a uniform interface for re-extracting a reference to a
|
||||
/// `Session` from a value that now owns it.
|
||||
fn sess(&self) -> &Session;
|
||||
|
||||
/// Produces the pretty-print annotation object.
|
||||
///
|
||||
/// (Rust does not yet support upcasting from a trait object to
|
||||
/// an object for one of its supertraits.)
|
||||
fn pp_ann(&self) -> &dyn pprust::PpAnn;
|
||||
}
|
||||
|
||||
trait HirPrinterSupport<'hir>: pprust_hir::PpAnn {
|
||||
/// Provides a uniform interface for re-extracting a reference to a
|
||||
/// `Session` from a value that now owns it.
|
||||
fn sess(&self) -> &Session;
|
||||
|
||||
/// Provides a uniform interface for re-extracting a reference to an
|
||||
/// `hir_map::Map` from a value that now owns it.
|
||||
fn hir_map(&self) -> Option<hir_map::Map<'hir>>;
|
||||
|
||||
/// Produces the pretty-print annotation object.
|
||||
///
|
||||
/// (Rust does not yet support upcasting from a trait object to
|
||||
/// an object for one of its supertraits.)
|
||||
fn pp_ann(&self) -> &dyn pprust_hir::PpAnn;
|
||||
}
|
||||
|
||||
struct NoAnn<'hir> {
|
||||
sess: &'hir Session,
|
||||
tcx: Option<TyCtxt<'hir>>,
|
||||
}
|
||||
|
||||
impl<'hir> PrinterSupport for NoAnn<'hir> {
|
||||
fn sess(&self) -> &Session {
|
||||
self.sess
|
||||
}
|
||||
|
||||
fn pp_ann(&self) -> &dyn pprust::PpAnn {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'hir> HirPrinterSupport<'hir> for NoAnn<'hir> {
|
||||
fn sess(&self) -> &Session {
|
||||
self.sess
|
||||
}
|
||||
|
||||
fn hir_map(&self) -> Option<hir_map::Map<'hir>> {
|
||||
self.tcx.map(|tcx| tcx.hir())
|
||||
}
|
||||
|
||||
fn pp_ann(&self) -> &dyn pprust_hir::PpAnn {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'hir> pprust::PpAnn for NoAnn<'hir> {}
|
||||
impl<'hir> pprust_hir::PpAnn for NoAnn<'hir> {
|
||||
impl<'tcx> pprust_hir::PpAnn for HirNoAnn<'tcx> {
|
||||
fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) {
|
||||
if let Some(tcx) = self.tcx {
|
||||
pprust_hir::PpAnn::nested(&(&tcx.hir() as &dyn hir::intravisit::Map<'_>), state, nested)
|
||||
}
|
||||
pprust_hir::PpAnn::nested(
|
||||
&(&self.tcx.hir() as &dyn hir::intravisit::Map<'_>),
|
||||
state,
|
||||
nested,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct IdentifiedAnnotation<'hir> {
|
||||
sess: &'hir Session,
|
||||
tcx: Option<TyCtxt<'hir>>,
|
||||
}
|
||||
struct AstIdentifiedAnn;
|
||||
|
||||
impl<'hir> PrinterSupport for IdentifiedAnnotation<'hir> {
|
||||
fn sess(&self) -> &Session {
|
||||
self.sess
|
||||
}
|
||||
|
||||
fn pp_ann(&self) -> &dyn pprust::PpAnn {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'hir> pprust::PpAnn for IdentifiedAnnotation<'hir> {
|
||||
fn pre(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) {
|
||||
if let pprust::AnnNode::Expr(_) = node {
|
||||
impl pprust_ast::PpAnn for AstIdentifiedAnn {
|
||||
fn pre(&self, s: &mut pprust_ast::State<'_>, node: pprust_ast::AnnNode<'_>) {
|
||||
if let pprust_ast::AnnNode::Expr(_) = node {
|
||||
s.popen();
|
||||
}
|
||||
}
|
||||
fn post(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) {
|
||||
match node {
|
||||
pprust::AnnNode::Crate(_) | pprust::AnnNode::Ident(_) | pprust::AnnNode::Name(_) => {}
|
||||
|
||||
pprust::AnnNode::Item(item) => {
|
||||
fn post(&self, s: &mut pprust_ast::State<'_>, node: pprust_ast::AnnNode<'_>) {
|
||||
match node {
|
||||
pprust_ast::AnnNode::Crate(_)
|
||||
| pprust_ast::AnnNode::Ident(_)
|
||||
| pprust_ast::AnnNode::Name(_) => {}
|
||||
|
||||
pprust_ast::AnnNode::Item(item) => {
|
||||
s.s.space();
|
||||
s.synth_comment(item.id.to_string())
|
||||
}
|
||||
pprust::AnnNode::SubItem(id) => {
|
||||
pprust_ast::AnnNode::SubItem(id) => {
|
||||
s.s.space();
|
||||
s.synth_comment(id.to_string())
|
||||
}
|
||||
pprust::AnnNode::Block(blk) => {
|
||||
pprust_ast::AnnNode::Block(blk) => {
|
||||
s.s.space();
|
||||
s.synth_comment(format!("block {}", blk.id))
|
||||
}
|
||||
pprust::AnnNode::Expr(expr) => {
|
||||
pprust_ast::AnnNode::Expr(expr) => {
|
||||
s.s.space();
|
||||
s.synth_comment(expr.id.to_string());
|
||||
s.pclose()
|
||||
}
|
||||
pprust::AnnNode::Pat(pat) => {
|
||||
pprust_ast::AnnNode::Pat(pat) => {
|
||||
s.s.space();
|
||||
s.synth_comment(format!("pat {}", pat.id));
|
||||
}
|
||||
@ -195,31 +77,25 @@ fn post(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'hir> HirPrinterSupport<'hir> for IdentifiedAnnotation<'hir> {
|
||||
fn sess(&self) -> &Session {
|
||||
self.sess
|
||||
}
|
||||
|
||||
fn hir_map(&self) -> Option<hir_map::Map<'hir>> {
|
||||
self.tcx.map(|tcx| tcx.hir())
|
||||
}
|
||||
|
||||
fn pp_ann(&self) -> &dyn pprust_hir::PpAnn {
|
||||
self
|
||||
}
|
||||
struct HirIdentifiedAnn<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
}
|
||||
|
||||
impl<'hir> pprust_hir::PpAnn for IdentifiedAnnotation<'hir> {
|
||||
impl<'tcx> pprust_hir::PpAnn for HirIdentifiedAnn<'tcx> {
|
||||
fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) {
|
||||
if let Some(ref tcx) = self.tcx {
|
||||
pprust_hir::PpAnn::nested(&(&tcx.hir() as &dyn hir::intravisit::Map<'_>), state, nested)
|
||||
}
|
||||
pprust_hir::PpAnn::nested(
|
||||
&(&self.tcx.hir() as &dyn hir::intravisit::Map<'_>),
|
||||
state,
|
||||
nested,
|
||||
)
|
||||
}
|
||||
|
||||
fn pre(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
|
||||
if let pprust_hir::AnnNode::Expr(_) = node {
|
||||
s.popen();
|
||||
}
|
||||
}
|
||||
|
||||
fn post(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
|
||||
match node {
|
||||
pprust_hir::AnnNode::Name(_) => {}
|
||||
@ -252,32 +128,22 @@ fn post(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
|
||||
}
|
||||
}
|
||||
|
||||
struct HygieneAnnotation<'a> {
|
||||
struct AstHygieneAnn<'a> {
|
||||
sess: &'a Session,
|
||||
}
|
||||
|
||||
impl<'a> PrinterSupport for HygieneAnnotation<'a> {
|
||||
fn sess(&self) -> &Session {
|
||||
self.sess
|
||||
}
|
||||
|
||||
fn pp_ann(&self) -> &dyn pprust::PpAnn {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> pprust::PpAnn for HygieneAnnotation<'a> {
|
||||
fn post(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) {
|
||||
impl<'a> pprust_ast::PpAnn for AstHygieneAnn<'a> {
|
||||
fn post(&self, s: &mut pprust_ast::State<'_>, node: pprust_ast::AnnNode<'_>) {
|
||||
match node {
|
||||
pprust::AnnNode::Ident(&Ident { name, span }) => {
|
||||
pprust_ast::AnnNode::Ident(&Ident { name, span }) => {
|
||||
s.s.space();
|
||||
s.synth_comment(format!("{}{:?}", name.as_u32(), span.ctxt()))
|
||||
}
|
||||
pprust::AnnNode::Name(&name) => {
|
||||
pprust_ast::AnnNode::Name(&name) => {
|
||||
s.s.space();
|
||||
s.synth_comment(name.as_u32().to_string())
|
||||
}
|
||||
pprust::AnnNode::Crate(_) => {
|
||||
pprust_ast::AnnNode::Crate(_) => {
|
||||
s.s.hardbreak();
|
||||
let verbose = self.sess.verbose();
|
||||
s.synth_comment(rustc_span::hygiene::debug_hygiene_data(verbose));
|
||||
@ -288,26 +154,12 @@ fn post(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) {
|
||||
}
|
||||
}
|
||||
|
||||
struct TypedAnnotation<'tcx> {
|
||||
struct HirTypedAnn<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
maybe_typeck_results: Cell<Option<&'tcx ty::TypeckResults<'tcx>>>,
|
||||
}
|
||||
|
||||
impl<'tcx> HirPrinterSupport<'tcx> for TypedAnnotation<'tcx> {
|
||||
fn sess(&self) -> &Session {
|
||||
self.tcx.sess
|
||||
}
|
||||
|
||||
fn hir_map(&self) -> Option<hir_map::Map<'tcx>> {
|
||||
Some(self.tcx.hir())
|
||||
}
|
||||
|
||||
fn pp_ann(&self) -> &dyn pprust_hir::PpAnn {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> pprust_hir::PpAnn for TypedAnnotation<'tcx> {
|
||||
impl<'tcx> pprust_hir::PpAnn for HirTypedAnn<'tcx> {
|
||||
fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) {
|
||||
let old_maybe_typeck_results = self.maybe_typeck_results.get();
|
||||
if let pprust_hir::Nested::Body(id) = nested {
|
||||
@ -317,11 +169,13 @@ fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested)
|
||||
pprust_hir::PpAnn::nested(pp_ann, state, nested);
|
||||
self.maybe_typeck_results.set(old_maybe_typeck_results);
|
||||
}
|
||||
|
||||
fn pre(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
|
||||
if let pprust_hir::AnnNode::Expr(_) = node {
|
||||
s.popen();
|
||||
}
|
||||
}
|
||||
|
||||
fn post(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
|
||||
if let pprust_hir::AnnNode::Expr(expr) = node {
|
||||
let typeck_results = self.maybe_typeck_results.get().or_else(|| {
|
||||
@ -360,119 +214,119 @@ fn write_or_print(out: &str, sess: &Session) {
|
||||
sess.io.output_file.as_ref().unwrap_or(&OutFileName::Stdout).overwrite(out, sess);
|
||||
}
|
||||
|
||||
pub fn print_after_parsing(sess: &Session, krate: &ast::Crate, ppm: PpMode) {
|
||||
// Extra data for pretty-printing, the form of which depends on what kind of
|
||||
// pretty-printing we are doing.
|
||||
pub enum PrintExtra<'tcx> {
|
||||
AfterParsing { krate: ast::Crate },
|
||||
NeedsAstMap { tcx: TyCtxt<'tcx> },
|
||||
}
|
||||
|
||||
impl<'tcx> PrintExtra<'tcx> {
|
||||
fn with_krate<F, R>(&self, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&ast::Crate) -> R,
|
||||
{
|
||||
match self {
|
||||
PrintExtra::AfterParsing { krate, .. } => f(krate),
|
||||
PrintExtra::NeedsAstMap { tcx } => f(&tcx.resolver_for_lowering(()).borrow().1),
|
||||
}
|
||||
}
|
||||
|
||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
match self {
|
||||
PrintExtra::AfterParsing { .. } => bug!("PrintExtra::tcx"),
|
||||
PrintExtra::NeedsAstMap { tcx } => *tcx,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) {
|
||||
if ppm.needs_analysis() {
|
||||
abort_on_err(ex.tcx().analysis(()), sess);
|
||||
}
|
||||
|
||||
let (src, src_name) = get_source(sess);
|
||||
|
||||
let out = match ppm {
|
||||
Source(s) => {
|
||||
// Silently ignores an identified node.
|
||||
call_with_pp_support(&s, sess, None, move |annotation| {
|
||||
debug!("pretty printing source code {:?}", s);
|
||||
let sess = annotation.sess();
|
||||
let parse = &sess.parse_sess;
|
||||
pprust::print_crate(
|
||||
debug!("pretty printing source code {:?}", s);
|
||||
let annotation: Box<dyn pprust_ast::PpAnn> = match s {
|
||||
Normal => Box::new(AstNoAnn),
|
||||
Expanded => Box::new(AstNoAnn),
|
||||
Identified => Box::new(AstIdentifiedAnn),
|
||||
ExpandedIdentified => Box::new(AstIdentifiedAnn),
|
||||
ExpandedHygiene => Box::new(AstHygieneAnn { sess }),
|
||||
};
|
||||
let parse = &sess.parse_sess;
|
||||
let is_expanded = ppm.needs_ast_map();
|
||||
ex.with_krate(|krate| {
|
||||
pprust_ast::print_crate(
|
||||
sess.source_map(),
|
||||
krate,
|
||||
src_name,
|
||||
src,
|
||||
annotation.pp_ann(),
|
||||
false,
|
||||
&*annotation,
|
||||
is_expanded,
|
||||
parse.edition,
|
||||
&sess.parse_sess.attr_id_generator,
|
||||
)
|
||||
})
|
||||
}
|
||||
AstTree(PpAstTreeMode::Normal) => {
|
||||
AstTree => {
|
||||
debug!("pretty printing AST tree");
|
||||
format!("{krate:#?}")
|
||||
ex.with_krate(|krate| format!("{krate:#?}"))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
write_or_print(&out, sess);
|
||||
}
|
||||
|
||||
pub fn print_after_hir_lowering<'tcx>(tcx: TyCtxt<'tcx>, ppm: PpMode) {
|
||||
if ppm.needs_analysis() {
|
||||
abort_on_err(print_with_analysis(tcx, ppm), tcx.sess);
|
||||
return;
|
||||
}
|
||||
|
||||
let (src, src_name) = get_source(tcx.sess);
|
||||
|
||||
let out = match ppm {
|
||||
Source(s) => {
|
||||
// Silently ignores an identified node.
|
||||
call_with_pp_support(&s, tcx.sess, Some(tcx), move |annotation| {
|
||||
debug!("pretty printing source code {:?}", s);
|
||||
let sess = annotation.sess();
|
||||
let parse = &sess.parse_sess;
|
||||
pprust::print_crate(
|
||||
sess.source_map(),
|
||||
&tcx.resolver_for_lowering(()).borrow().1,
|
||||
AstTreeExpanded => {
|
||||
debug!("pretty-printing expanded AST");
|
||||
format!("{:#?}", ex.tcx().resolver_for_lowering(()).borrow().1)
|
||||
}
|
||||
Hir(s) => {
|
||||
debug!("pretty printing HIR {:?}", s);
|
||||
let tcx = ex.tcx();
|
||||
let f = |annotation: &dyn pprust_hir::PpAnn| {
|
||||
let sm = sess.source_map();
|
||||
let hir_map = tcx.hir();
|
||||
let attrs = |id| hir_map.attrs(id);
|
||||
pprust_hir::print_crate(
|
||||
sm,
|
||||
hir_map.root_module(),
|
||||
src_name,
|
||||
src,
|
||||
annotation.pp_ann(),
|
||||
true,
|
||||
parse.edition,
|
||||
&sess.parse_sess.attr_id_generator,
|
||||
&attrs,
|
||||
annotation,
|
||||
)
|
||||
})
|
||||
};
|
||||
match s {
|
||||
PpHirMode::Normal => {
|
||||
let annotation = HirNoAnn { tcx };
|
||||
f(&annotation)
|
||||
}
|
||||
PpHirMode::Identified => {
|
||||
let annotation = HirIdentifiedAnn { tcx };
|
||||
f(&annotation)
|
||||
}
|
||||
PpHirMode::Typed => {
|
||||
let annotation = HirTypedAnn { tcx, maybe_typeck_results: Cell::new(None) };
|
||||
tcx.dep_graph.with_ignore(|| f(&annotation))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AstTree(PpAstTreeMode::Expanded) => {
|
||||
debug!("pretty-printing expanded AST");
|
||||
format!("{:#?}", tcx.resolver_for_lowering(()).borrow().1)
|
||||
}
|
||||
|
||||
Hir(s) => call_with_pp_support_hir(&s, tcx, move |annotation, hir_map| {
|
||||
debug!("pretty printing HIR {:?}", s);
|
||||
let sess = annotation.sess();
|
||||
let sm = sess.source_map();
|
||||
let attrs = |id| hir_map.attrs(id);
|
||||
pprust_hir::print_crate(
|
||||
sm,
|
||||
hir_map.root_module(),
|
||||
src_name,
|
||||
src,
|
||||
&attrs,
|
||||
annotation.pp_ann(),
|
||||
)
|
||||
}),
|
||||
|
||||
HirTree => {
|
||||
call_with_pp_support_hir(&PpHirMode::Normal, tcx, move |_annotation, hir_map| {
|
||||
debug!("pretty printing HIR tree");
|
||||
format!("{:#?}", hir_map.krate())
|
||||
})
|
||||
debug!("pretty printing HIR tree");
|
||||
format!("{:#?}", ex.tcx().hir().krate())
|
||||
}
|
||||
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
write_or_print(&out, tcx.sess);
|
||||
}
|
||||
|
||||
// In an ideal world, this would be a public function called by the driver after
|
||||
// analysis is performed. However, we want to call `phase_3_run_analysis_passes`
|
||||
// with a different callback than the standard driver, so that isn't easy.
|
||||
// Instead, we call that function ourselves.
|
||||
fn print_with_analysis(tcx: TyCtxt<'_>, ppm: PpMode) -> Result<(), ErrorGuaranteed> {
|
||||
tcx.analysis(())?;
|
||||
let out = match ppm {
|
||||
Mir => {
|
||||
let mut out = Vec::new();
|
||||
write_mir_pretty(tcx, None, &mut out).unwrap();
|
||||
write_mir_pretty(ex.tcx(), None, &mut out).unwrap();
|
||||
String::from_utf8(out).unwrap()
|
||||
}
|
||||
|
||||
MirCFG => {
|
||||
let mut out = Vec::new();
|
||||
write_mir_graphviz(tcx, None, &mut out).unwrap();
|
||||
write_mir_graphviz(ex.tcx(), None, &mut out).unwrap();
|
||||
String::from_utf8(out).unwrap()
|
||||
}
|
||||
|
||||
ThirTree => {
|
||||
let tcx = ex.tcx();
|
||||
let mut out = String::new();
|
||||
abort_on_err(rustc_hir_analysis::check_crate(tcx), tcx.sess);
|
||||
debug!("pretty printing THIR tree");
|
||||
@ -481,8 +335,8 @@ fn print_with_analysis(tcx: TyCtxt<'_>, ppm: PpMode) -> Result<(), ErrorGuarante
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
ThirFlat => {
|
||||
let tcx = ex.tcx();
|
||||
let mut out = String::new();
|
||||
abort_on_err(rustc_hir_analysis::check_crate(tcx), tcx.sess);
|
||||
debug!("pretty printing THIR flat");
|
||||
@ -491,11 +345,7 @@ fn print_with_analysis(tcx: TyCtxt<'_>, ppm: PpMode) -> Result<(), ErrorGuarante
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
write_or_print(&out, tcx.sess);
|
||||
|
||||
Ok(())
|
||||
write_or_print(&out, sess);
|
||||
}
|
||||
|
@ -659,6 +659,7 @@ pub fn span_labels(
|
||||
msg: impl Into<SubdiagnosticMessage>,
|
||||
) -> &mut Self);
|
||||
forward!(pub fn help(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self);
|
||||
forward!(pub fn help_once(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self);
|
||||
forward!(pub fn span_help(
|
||||
&mut self,
|
||||
sp: impl Into<MultiSpan>,
|
||||
|
@ -52,8 +52,6 @@ fn post(&self, _state: &mut State<'_>, _node: AnnNode<'_>) {}
|
||||
impl PpAnn for NoAnn {}
|
||||
pub const NO_ANN: &dyn PpAnn = &NoAnn;
|
||||
|
||||
/// Identical to the `PpAnn` implementation for `hir::Crate`,
|
||||
/// except it avoids creating a dependency on the whole crate.
|
||||
impl PpAnn for &dyn rustc_hir::intravisit::Map<'_> {
|
||||
fn nested(&self, state: &mut State<'_>, nested: Nested) {
|
||||
match nested {
|
||||
@ -75,7 +73,11 @@ pub struct State<'a> {
|
||||
}
|
||||
|
||||
impl<'a> State<'a> {
|
||||
pub fn print_node(&mut self, node: Node<'_>) {
|
||||
fn attrs(&self, id: hir::HirId) -> &'a [ast::Attribute] {
|
||||
(self.attrs)(id)
|
||||
}
|
||||
|
||||
fn print_node(&mut self, node: Node<'_>) {
|
||||
match node {
|
||||
Node::Param(a) => self.print_param(a),
|
||||
Node::Item(a) => self.print_item(a),
|
||||
@ -144,7 +146,7 @@ fn print_generic_args(&mut self, _: &ast::GenericArgs, _colons_before_params: bo
|
||||
}
|
||||
}
|
||||
|
||||
pub const INDENT_UNIT: isize = 4;
|
||||
const INDENT_UNIT: isize = 4;
|
||||
|
||||
/// Requires you to pass an input filename and reader so that
|
||||
/// it can scan the input text for comments to copy forward.
|
||||
@ -156,7 +158,12 @@ pub fn print_crate<'a>(
|
||||
attrs: &'a dyn Fn(hir::HirId) -> &'a [ast::Attribute],
|
||||
ann: &'a dyn PpAnn,
|
||||
) -> String {
|
||||
let mut s = State::new_from_input(sm, filename, input, attrs, ann);
|
||||
let mut s = State {
|
||||
s: pp::Printer::new(),
|
||||
comments: Some(Comments::new(sm, filename, input)),
|
||||
attrs,
|
||||
ann,
|
||||
};
|
||||
|
||||
// When printing the AST, we sometimes need to inject `#[no_std]` here.
|
||||
// Since you can't compile the HIR, it's not necessary.
|
||||
@ -166,28 +173,7 @@ pub fn print_crate<'a>(
|
||||
s.s.eof()
|
||||
}
|
||||
|
||||
impl<'a> State<'a> {
|
||||
pub fn new_from_input(
|
||||
sm: &'a SourceMap,
|
||||
filename: FileName,
|
||||
input: String,
|
||||
attrs: &'a dyn Fn(hir::HirId) -> &'a [ast::Attribute],
|
||||
ann: &'a dyn PpAnn,
|
||||
) -> State<'a> {
|
||||
State {
|
||||
s: pp::Printer::new(),
|
||||
comments: Some(Comments::new(sm, filename, input)),
|
||||
attrs,
|
||||
ann,
|
||||
}
|
||||
}
|
||||
|
||||
fn attrs(&self, id: hir::HirId) -> &'a [ast::Attribute] {
|
||||
(self.attrs)(id)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_string<F>(ann: &dyn PpAnn, f: F) -> String
|
||||
fn to_string<F>(ann: &dyn PpAnn, f: F) -> String
|
||||
where
|
||||
F: FnOnce(&mut State<'_>),
|
||||
{
|
||||
@ -196,52 +182,20 @@ pub fn to_string<F>(ann: &dyn PpAnn, f: F) -> String
|
||||
printer.s.eof()
|
||||
}
|
||||
|
||||
pub fn generic_params_to_string(generic_params: &[GenericParam<'_>]) -> String {
|
||||
to_string(NO_ANN, |s| s.print_generic_params(generic_params))
|
||||
}
|
||||
|
||||
pub fn bounds_to_string<'b>(bounds: impl IntoIterator<Item = &'b hir::GenericBound<'b>>) -> String {
|
||||
to_string(NO_ANN, |s| s.print_bounds("", bounds))
|
||||
}
|
||||
|
||||
pub fn ty_to_string(ty: &hir::Ty<'_>) -> String {
|
||||
to_string(NO_ANN, |s| s.print_type(ty))
|
||||
}
|
||||
|
||||
pub fn path_segment_to_string(segment: &hir::PathSegment<'_>) -> String {
|
||||
to_string(NO_ANN, |s| s.print_path_segment(segment))
|
||||
}
|
||||
|
||||
pub fn path_to_string(segment: &hir::Path<'_>) -> String {
|
||||
to_string(NO_ANN, |s| s.print_path(segment, false))
|
||||
}
|
||||
|
||||
pub fn qpath_to_string(segment: &hir::QPath<'_>) -> String {
|
||||
to_string(NO_ANN, |s| s.print_qpath(segment, false))
|
||||
}
|
||||
|
||||
pub fn fn_to_string(
|
||||
decl: &hir::FnDecl<'_>,
|
||||
header: hir::FnHeader,
|
||||
name: Option<Symbol>,
|
||||
generics: &hir::Generics<'_>,
|
||||
arg_names: &[Ident],
|
||||
body_id: Option<hir::BodyId>,
|
||||
) -> String {
|
||||
to_string(NO_ANN, |s| s.print_fn(decl, header, name, generics, arg_names, body_id))
|
||||
}
|
||||
|
||||
pub fn enum_def_to_string(
|
||||
enum_definition: &hir::EnumDef<'_>,
|
||||
generics: &hir::Generics<'_>,
|
||||
name: Symbol,
|
||||
span: rustc_span::Span,
|
||||
) -> String {
|
||||
to_string(NO_ANN, |s| s.print_enum_def(enum_definition, generics, name, span))
|
||||
pub fn pat_to_string(pat: &hir::Pat<'_>) -> String {
|
||||
to_string(NO_ANN, |s| s.print_pat(pat))
|
||||
}
|
||||
|
||||
impl<'a> State<'a> {
|
||||
pub fn bclose_maybe_open(&mut self, span: rustc_span::Span, close_box: bool) {
|
||||
fn bclose_maybe_open(&mut self, span: rustc_span::Span, close_box: bool) {
|
||||
self.maybe_print_comment(span.hi());
|
||||
self.break_offset_if_not_bol(1, -INDENT_UNIT);
|
||||
self.word("}");
|
||||
@ -250,11 +204,11 @@ pub fn bclose_maybe_open(&mut self, span: rustc_span::Span, close_box: bool) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bclose(&mut self, span: rustc_span::Span) {
|
||||
fn bclose(&mut self, span: rustc_span::Span) {
|
||||
self.bclose_maybe_open(span, true)
|
||||
}
|
||||
|
||||
pub fn commasep_cmnt<T, F, G>(&mut self, b: Breaks, elts: &[T], mut op: F, mut get_span: G)
|
||||
fn commasep_cmnt<T, F, G>(&mut self, b: Breaks, elts: &[T], mut op: F, mut get_span: G)
|
||||
where
|
||||
F: FnMut(&mut State<'_>, &T),
|
||||
G: FnMut(&T) -> rustc_span::Span,
|
||||
@ -275,25 +229,25 @@ pub fn commasep_cmnt<T, F, G>(&mut self, b: Breaks, elts: &[T], mut op: F, mut g
|
||||
self.end();
|
||||
}
|
||||
|
||||
pub fn commasep_exprs(&mut self, b: Breaks, exprs: &[hir::Expr<'_>]) {
|
||||
fn commasep_exprs(&mut self, b: Breaks, exprs: &[hir::Expr<'_>]) {
|
||||
self.commasep_cmnt(b, exprs, |s, e| s.print_expr(e), |e| e.span);
|
||||
}
|
||||
|
||||
pub fn print_mod(&mut self, _mod: &hir::Mod<'_>, attrs: &[ast::Attribute]) {
|
||||
fn print_mod(&mut self, _mod: &hir::Mod<'_>, attrs: &[ast::Attribute]) {
|
||||
self.print_inner_attributes(attrs);
|
||||
for &item_id in _mod.item_ids {
|
||||
self.ann.nested(self, Nested::Item(item_id));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_opt_lifetime(&mut self, lifetime: &hir::Lifetime) {
|
||||
fn print_opt_lifetime(&mut self, lifetime: &hir::Lifetime) {
|
||||
if !lifetime.is_elided() {
|
||||
self.print_lifetime(lifetime);
|
||||
self.nbsp();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_type(&mut self, ty: &hir::Ty<'_>) {
|
||||
fn print_type(&mut self, ty: &hir::Ty<'_>) {
|
||||
self.maybe_print_comment(ty.span.lo());
|
||||
self.ibox(0);
|
||||
match ty.kind {
|
||||
@ -371,7 +325,7 @@ pub fn print_type(&mut self, ty: &hir::Ty<'_>) {
|
||||
self.end()
|
||||
}
|
||||
|
||||
pub fn print_foreign_item(&mut self, item: &hir::ForeignItem<'_>) {
|
||||
fn print_foreign_item(&mut self, item: &hir::ForeignItem<'_>) {
|
||||
self.hardbreak_if_not_bol();
|
||||
self.maybe_print_comment(item.span.lo());
|
||||
self.print_outer_attributes(self.attrs(item.hir_id()));
|
||||
@ -478,8 +432,7 @@ fn print_item_type(
|
||||
self.end(); // end the outer ibox
|
||||
}
|
||||
|
||||
/// Pretty-print an item
|
||||
pub fn print_item(&mut self, item: &hir::Item<'_>) {
|
||||
fn print_item(&mut self, item: &hir::Item<'_>) {
|
||||
self.hardbreak_if_not_bol();
|
||||
self.maybe_print_comment(item.span.lo());
|
||||
let attrs = self.attrs(item.hir_id());
|
||||
@ -704,7 +657,7 @@ pub fn print_item(&mut self, item: &hir::Item<'_>) {
|
||||
self.ann.post(self, AnnNode::Item(item))
|
||||
}
|
||||
|
||||
pub fn print_trait_ref(&mut self, t: &hir::TraitRef<'_>) {
|
||||
fn print_trait_ref(&mut self, t: &hir::TraitRef<'_>) {
|
||||
self.print_path(t.path, false);
|
||||
}
|
||||
|
||||
@ -721,7 +674,7 @@ fn print_poly_trait_ref(&mut self, t: &hir::PolyTraitRef<'_>) {
|
||||
self.print_trait_ref(&t.trait_ref);
|
||||
}
|
||||
|
||||
pub fn print_enum_def(
|
||||
fn print_enum_def(
|
||||
&mut self,
|
||||
enum_definition: &hir::EnumDef<'_>,
|
||||
generics: &hir::Generics<'_>,
|
||||
@ -736,7 +689,7 @@ pub fn print_enum_def(
|
||||
self.print_variants(enum_definition.variants, span);
|
||||
}
|
||||
|
||||
pub fn print_variants(&mut self, variants: &[hir::Variant<'_>], span: rustc_span::Span) {
|
||||
fn print_variants(&mut self, variants: &[hir::Variant<'_>], span: rustc_span::Span) {
|
||||
self.bopen();
|
||||
for v in variants {
|
||||
self.space_if_not_bol();
|
||||
@ -751,14 +704,14 @@ pub fn print_variants(&mut self, variants: &[hir::Variant<'_>], span: rustc_span
|
||||
self.bclose(span)
|
||||
}
|
||||
|
||||
pub fn print_defaultness(&mut self, defaultness: hir::Defaultness) {
|
||||
fn print_defaultness(&mut self, defaultness: hir::Defaultness) {
|
||||
match defaultness {
|
||||
hir::Defaultness::Default { .. } => self.word_nbsp("default"),
|
||||
hir::Defaultness::Final => (),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_struct(
|
||||
fn print_struct(
|
||||
&mut self,
|
||||
struct_def: &hir::VariantData<'_>,
|
||||
generics: &hir::Generics<'_>,
|
||||
@ -807,7 +760,7 @@ pub fn print_struct(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_variant(&mut self, v: &hir::Variant<'_>) {
|
||||
fn print_variant(&mut self, v: &hir::Variant<'_>) {
|
||||
self.head("");
|
||||
let generics = hir::Generics::empty();
|
||||
self.print_struct(&v.data, generics, v.ident.name, v.span, false);
|
||||
@ -817,7 +770,8 @@ pub fn print_variant(&mut self, v: &hir::Variant<'_>) {
|
||||
self.print_anon_const(d);
|
||||
}
|
||||
}
|
||||
pub fn print_method_sig(
|
||||
|
||||
fn print_method_sig(
|
||||
&mut self,
|
||||
ident: Ident,
|
||||
m: &hir::FnSig<'_>,
|
||||
@ -828,7 +782,7 @@ pub fn print_method_sig(
|
||||
self.print_fn(m.decl, m.header, Some(ident.name), generics, arg_names, body_id);
|
||||
}
|
||||
|
||||
pub fn print_trait_item(&mut self, ti: &hir::TraitItem<'_>) {
|
||||
fn print_trait_item(&mut self, ti: &hir::TraitItem<'_>) {
|
||||
self.ann.pre(self, AnnNode::SubItem(ti.hir_id()));
|
||||
self.hardbreak_if_not_bol();
|
||||
self.maybe_print_comment(ti.span.lo());
|
||||
@ -856,7 +810,7 @@ pub fn print_trait_item(&mut self, ti: &hir::TraitItem<'_>) {
|
||||
self.ann.post(self, AnnNode::SubItem(ti.hir_id()))
|
||||
}
|
||||
|
||||
pub fn print_impl_item(&mut self, ii: &hir::ImplItem<'_>) {
|
||||
fn print_impl_item(&mut self, ii: &hir::ImplItem<'_>) {
|
||||
self.ann.pre(self, AnnNode::SubItem(ii.hir_id()));
|
||||
self.hardbreak_if_not_bol();
|
||||
self.maybe_print_comment(ii.span.lo());
|
||||
@ -881,7 +835,7 @@ pub fn print_impl_item(&mut self, ii: &hir::ImplItem<'_>) {
|
||||
self.ann.post(self, AnnNode::SubItem(ii.hir_id()))
|
||||
}
|
||||
|
||||
pub fn print_local(
|
||||
fn print_local(
|
||||
&mut self,
|
||||
init: Option<&hir::Expr<'_>>,
|
||||
els: Option<&hir::Block<'_>>,
|
||||
@ -914,7 +868,7 @@ pub fn print_local(
|
||||
self.end()
|
||||
}
|
||||
|
||||
pub fn print_stmt(&mut self, st: &hir::Stmt<'_>) {
|
||||
fn print_stmt(&mut self, st: &hir::Stmt<'_>) {
|
||||
self.maybe_print_comment(st.span.lo());
|
||||
match st.kind {
|
||||
hir::StmtKind::Local(loc) => {
|
||||
@ -937,19 +891,19 @@ pub fn print_stmt(&mut self, st: &hir::Stmt<'_>) {
|
||||
self.maybe_print_trailing_comment(st.span, None)
|
||||
}
|
||||
|
||||
pub fn print_block(&mut self, blk: &hir::Block<'_>) {
|
||||
fn print_block(&mut self, blk: &hir::Block<'_>) {
|
||||
self.print_block_with_attrs(blk, &[])
|
||||
}
|
||||
|
||||
pub fn print_block_unclosed(&mut self, blk: &hir::Block<'_>) {
|
||||
fn print_block_unclosed(&mut self, blk: &hir::Block<'_>) {
|
||||
self.print_block_maybe_unclosed(blk, &[], false)
|
||||
}
|
||||
|
||||
pub fn print_block_with_attrs(&mut self, blk: &hir::Block<'_>, attrs: &[ast::Attribute]) {
|
||||
fn print_block_with_attrs(&mut self, blk: &hir::Block<'_>, attrs: &[ast::Attribute]) {
|
||||
self.print_block_maybe_unclosed(blk, attrs, true)
|
||||
}
|
||||
|
||||
pub fn print_block_maybe_unclosed(
|
||||
fn print_block_maybe_unclosed(
|
||||
&mut self,
|
||||
blk: &hir::Block<'_>,
|
||||
attrs: &[ast::Attribute],
|
||||
@ -1005,7 +959,7 @@ fn print_else(&mut self, els: Option<&hir::Expr<'_>>) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_if(
|
||||
fn print_if(
|
||||
&mut self,
|
||||
test: &hir::Expr<'_>,
|
||||
blk: &hir::Expr<'_>,
|
||||
@ -1018,14 +972,14 @@ pub fn print_if(
|
||||
self.print_else(elseopt)
|
||||
}
|
||||
|
||||
pub fn print_array_length(&mut self, len: &hir::ArrayLen) {
|
||||
fn print_array_length(&mut self, len: &hir::ArrayLen) {
|
||||
match len {
|
||||
hir::ArrayLen::Infer(_, _) => self.word("_"),
|
||||
hir::ArrayLen::Body(ct) => self.print_anon_const(ct),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_anon_const(&mut self, constant: &hir::AnonConst) {
|
||||
fn print_anon_const(&mut self, constant: &hir::AnonConst) {
|
||||
self.ann.nested(self, Nested::Body(constant.body))
|
||||
}
|
||||
|
||||
@ -1041,7 +995,7 @@ fn print_expr_maybe_paren(&mut self, expr: &hir::Expr<'_>, prec: i8) {
|
||||
|
||||
/// Prints an expr using syntax that's acceptable in a condition position, such as the `cond` in
|
||||
/// `if cond { ... }`.
|
||||
pub fn print_expr_as_cond(&mut self, expr: &hir::Expr<'_>) {
|
||||
fn print_expr_as_cond(&mut self, expr: &hir::Expr<'_>) {
|
||||
self.print_expr_cond_paren(expr, Self::cond_needs_par(expr))
|
||||
}
|
||||
|
||||
@ -1360,7 +1314,7 @@ enum AsmArg<'a> {
|
||||
self.pclose();
|
||||
}
|
||||
|
||||
pub fn print_expr(&mut self, expr: &hir::Expr<'_>) {
|
||||
fn print_expr(&mut self, expr: &hir::Expr<'_>) {
|
||||
self.maybe_print_comment(expr.span.lo());
|
||||
self.print_outer_attributes(self.attrs(expr.hir_id));
|
||||
self.ibox(INDENT_UNIT);
|
||||
@ -1593,7 +1547,7 @@ pub fn print_expr(&mut self, expr: &hir::Expr<'_>) {
|
||||
self.end()
|
||||
}
|
||||
|
||||
pub fn print_local_decl(&mut self, loc: &hir::Local<'_>) {
|
||||
fn print_local_decl(&mut self, loc: &hir::Local<'_>) {
|
||||
self.print_pat(loc.pat);
|
||||
if let Some(ty) = loc.ty {
|
||||
self.word_space(":");
|
||||
@ -1601,11 +1555,11 @@ pub fn print_local_decl(&mut self, loc: &hir::Local<'_>) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_name(&mut self, name: Symbol) {
|
||||
fn print_name(&mut self, name: Symbol) {
|
||||
self.print_ident(Ident::with_dummy_span(name))
|
||||
}
|
||||
|
||||
pub fn print_path<R>(&mut self, path: &hir::Path<'_, R>, colons_before_params: bool) {
|
||||
fn print_path<R>(&mut self, path: &hir::Path<'_, R>, colons_before_params: bool) {
|
||||
self.maybe_print_comment(path.span.lo());
|
||||
|
||||
for (i, segment) in path.segments.iter().enumerate() {
|
||||
@ -1619,14 +1573,14 @@ pub fn print_path<R>(&mut self, path: &hir::Path<'_, R>, colons_before_params: b
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_path_segment(&mut self, segment: &hir::PathSegment<'_>) {
|
||||
fn print_path_segment(&mut self, segment: &hir::PathSegment<'_>) {
|
||||
if segment.ident.name != kw::PathRoot {
|
||||
self.print_ident(segment.ident);
|
||||
self.print_generic_args(segment.args(), false);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_qpath(&mut self, qpath: &hir::QPath<'_>, colons_before_params: bool) {
|
||||
fn print_qpath(&mut self, qpath: &hir::QPath<'_>, colons_before_params: bool) {
|
||||
match *qpath {
|
||||
hir::QPath::Resolved(None, path) => self.print_path(path, colons_before_params),
|
||||
hir::QPath::Resolved(Some(qself), path) => {
|
||||
@ -1743,7 +1697,7 @@ fn print_generic_args(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_type_binding(&mut self, binding: &hir::TypeBinding<'_>) {
|
||||
fn print_type_binding(&mut self, binding: &hir::TypeBinding<'_>) {
|
||||
self.print_ident(binding.ident);
|
||||
self.print_generic_args(binding.gen_args, false);
|
||||
self.space();
|
||||
@ -1761,7 +1715,7 @@ pub fn print_type_binding(&mut self, binding: &hir::TypeBinding<'_>) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_pat(&mut self, pat: &hir::Pat<'_>) {
|
||||
fn print_pat(&mut self, pat: &hir::Pat<'_>) {
|
||||
self.maybe_print_comment(pat.span.lo());
|
||||
self.ann.pre(self, AnnNode::Pat(pat));
|
||||
// Pat isn't normalized, but the beauty of it
|
||||
@ -1905,7 +1859,7 @@ pub fn print_pat(&mut self, pat: &hir::Pat<'_>) {
|
||||
self.ann.post(self, AnnNode::Pat(pat))
|
||||
}
|
||||
|
||||
pub fn print_patfield(&mut self, field: &hir::PatField<'_>) {
|
||||
fn print_patfield(&mut self, field: &hir::PatField<'_>) {
|
||||
if self.attrs(field.hir_id).is_empty() {
|
||||
self.space();
|
||||
}
|
||||
@ -1919,12 +1873,12 @@ pub fn print_patfield(&mut self, field: &hir::PatField<'_>) {
|
||||
self.end();
|
||||
}
|
||||
|
||||
pub fn print_param(&mut self, arg: &hir::Param<'_>) {
|
||||
fn print_param(&mut self, arg: &hir::Param<'_>) {
|
||||
self.print_outer_attributes(self.attrs(arg.hir_id));
|
||||
self.print_pat(arg.pat);
|
||||
}
|
||||
|
||||
pub fn print_arm(&mut self, arm: &hir::Arm<'_>) {
|
||||
fn print_arm(&mut self, arm: &hir::Arm<'_>) {
|
||||
// I have no idea why this check is necessary, but here it
|
||||
// is :(
|
||||
if self.attrs(arm.hir_id).is_empty() {
|
||||
@ -1976,7 +1930,7 @@ pub fn print_arm(&mut self, arm: &hir::Arm<'_>) {
|
||||
self.end() // close enclosing cbox
|
||||
}
|
||||
|
||||
pub fn print_fn(
|
||||
fn print_fn(
|
||||
&mut self,
|
||||
decl: &hir::FnDecl<'_>,
|
||||
header: hir::FnHeader,
|
||||
@ -2056,14 +2010,14 @@ fn print_closure_params(&mut self, decl: &hir::FnDecl<'_>, body_id: hir::BodyId)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_capture_clause(&mut self, capture_clause: hir::CaptureBy) {
|
||||
fn print_capture_clause(&mut self, capture_clause: hir::CaptureBy) {
|
||||
match capture_clause {
|
||||
hir::CaptureBy::Value => self.word_space("move"),
|
||||
hir::CaptureBy::Ref => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_closure_binder(
|
||||
fn print_closure_binder(
|
||||
&mut self,
|
||||
binder: hir::ClosureBinder,
|
||||
generic_params: &[GenericParam<'_>],
|
||||
@ -2083,7 +2037,8 @@ pub fn print_closure_binder(
|
||||
|
||||
match binder {
|
||||
hir::ClosureBinder::Default => {}
|
||||
// we need to distinguish `|...| {}` from `for<> |...| {}` as `for<>` adds additional restrictions
|
||||
// We need to distinguish `|...| {}` from `for<> |...| {}` as `for<>` adds additional
|
||||
// restrictions.
|
||||
hir::ClosureBinder::For { .. } if generic_params.is_empty() => self.word("for<>"),
|
||||
hir::ClosureBinder::For { .. } => {
|
||||
self.word("for");
|
||||
@ -2099,7 +2054,7 @@ pub fn print_closure_binder(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_bounds<'b>(
|
||||
fn print_bounds<'b>(
|
||||
&mut self,
|
||||
prefix: &'static str,
|
||||
bounds: impl IntoIterator<Item = &'b hir::GenericBound<'b>>,
|
||||
@ -2137,7 +2092,7 @@ pub fn print_bounds<'b>(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_generic_params(&mut self, generic_params: &[GenericParam<'_>]) {
|
||||
fn print_generic_params(&mut self, generic_params: &[GenericParam<'_>]) {
|
||||
if !generic_params.is_empty() {
|
||||
self.word("<");
|
||||
|
||||
@ -2147,7 +2102,7 @@ pub fn print_generic_params(&mut self, generic_params: &[GenericParam<'_>]) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_generic_param(&mut self, param: &GenericParam<'_>) {
|
||||
fn print_generic_param(&mut self, param: &GenericParam<'_>) {
|
||||
if let GenericParamKind::Const { .. } = param.kind {
|
||||
self.word_space("const");
|
||||
}
|
||||
@ -2175,11 +2130,11 @@ pub fn print_generic_param(&mut self, param: &GenericParam<'_>) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_lifetime(&mut self, lifetime: &hir::Lifetime) {
|
||||
fn print_lifetime(&mut self, lifetime: &hir::Lifetime) {
|
||||
self.print_ident(lifetime.ident)
|
||||
}
|
||||
|
||||
pub fn print_where_clause(&mut self, generics: &hir::Generics<'_>) {
|
||||
fn print_where_clause(&mut self, generics: &hir::Generics<'_>) {
|
||||
if generics.predicates.is_empty() {
|
||||
return;
|
||||
}
|
||||
@ -2236,7 +2191,7 @@ pub fn print_where_clause(&mut self, generics: &hir::Generics<'_>) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_mutability(&mut self, mutbl: hir::Mutability, print_const: bool) {
|
||||
fn print_mutability(&mut self, mutbl: hir::Mutability, print_const: bool) {
|
||||
match mutbl {
|
||||
hir::Mutability::Mut => self.word_nbsp("mut"),
|
||||
hir::Mutability::Not => {
|
||||
@ -2247,12 +2202,12 @@ pub fn print_mutability(&mut self, mutbl: hir::Mutability, print_const: bool) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_mt(&mut self, mt: &hir::MutTy<'_>, print_const: bool) {
|
||||
fn print_mt(&mut self, mt: &hir::MutTy<'_>, print_const: bool) {
|
||||
self.print_mutability(mt.mutbl, print_const);
|
||||
self.print_type(mt.ty);
|
||||
}
|
||||
|
||||
pub fn print_fn_output(&mut self, decl: &hir::FnDecl<'_>) {
|
||||
fn print_fn_output(&mut self, decl: &hir::FnDecl<'_>) {
|
||||
if let hir::FnRetTy::DefaultReturn(..) = decl.output {
|
||||
return;
|
||||
}
|
||||
@ -2271,7 +2226,7 @@ pub fn print_fn_output(&mut self, decl: &hir::FnDecl<'_>) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_ty_fn(
|
||||
fn print_ty_fn(
|
||||
&mut self,
|
||||
abi: Abi,
|
||||
unsafety: hir::Unsafety,
|
||||
@ -2299,7 +2254,7 @@ pub fn print_ty_fn(
|
||||
self.end();
|
||||
}
|
||||
|
||||
pub fn print_fn_header_info(&mut self, header: hir::FnHeader) {
|
||||
fn print_fn_header_info(&mut self, header: hir::FnHeader) {
|
||||
self.print_constness(header.constness);
|
||||
|
||||
match header.asyncness {
|
||||
@ -2317,21 +2272,21 @@ pub fn print_fn_header_info(&mut self, header: hir::FnHeader) {
|
||||
self.word("fn")
|
||||
}
|
||||
|
||||
pub fn print_constness(&mut self, s: hir::Constness) {
|
||||
fn print_constness(&mut self, s: hir::Constness) {
|
||||
match s {
|
||||
hir::Constness::NotConst => {}
|
||||
hir::Constness::Const => self.word_nbsp("const"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_unsafety(&mut self, s: hir::Unsafety) {
|
||||
fn print_unsafety(&mut self, s: hir::Unsafety) {
|
||||
match s {
|
||||
hir::Unsafety::Normal => {}
|
||||
hir::Unsafety::Unsafe => self.word_nbsp("unsafe"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_is_auto(&mut self, s: hir::IsAuto) {
|
||||
fn print_is_auto(&mut self, s: hir::IsAuto) {
|
||||
match s {
|
||||
hir::IsAuto::Yes => self.word_nbsp("auto"),
|
||||
hir::IsAuto::No => {}
|
||||
|
@ -2036,7 +2036,8 @@ fn label_fn_like(
|
||||
}
|
||||
let typeck = self.typeck_results.borrow();
|
||||
for (rcvr, args) in call_finder.calls {
|
||||
if let Some(rcvr_ty) = typeck.node_type_opt(rcvr.hir_id)
|
||||
if rcvr.hir_id.owner == typeck.hir_owner
|
||||
&& let Some(rcvr_ty) = typeck.node_type_opt(rcvr.hir_id)
|
||||
&& let ty::Closure(call_def_id, _) = rcvr_ty.kind()
|
||||
&& def_id == *call_def_id
|
||||
&& let Some(idx) = expected_idx
|
||||
|
@ -1504,9 +1504,7 @@ fn error_tuple_variant_index_shorthand(
|
||||
{
|
||||
let has_shorthand_field_name = field_patterns.iter().any(|field| field.is_shorthand);
|
||||
if has_shorthand_field_name {
|
||||
let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
|
||||
s.print_qpath(qpath, false)
|
||||
});
|
||||
let path = rustc_hir_pretty::qpath_to_string(qpath);
|
||||
let mut err = struct_span_err!(
|
||||
self.tcx.sess,
|
||||
pat.span,
|
||||
@ -1688,9 +1686,7 @@ fn error_tuple_variant_as_struct_pat(
|
||||
return None;
|
||||
}
|
||||
|
||||
let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
|
||||
s.print_qpath(qpath, false)
|
||||
});
|
||||
let path = rustc_hir_pretty::qpath_to_string(qpath);
|
||||
let mut err = struct_span_err!(
|
||||
self.tcx.sess,
|
||||
pat.span,
|
||||
@ -1740,9 +1736,7 @@ fn get_suggested_tuple_struct_pattern(
|
||||
f
|
||||
}
|
||||
}
|
||||
Err(_) => rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
|
||||
s.print_pat(field.pat)
|
||||
}),
|
||||
Err(_) => rustc_hir_pretty::pat_to_string(field.pat),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
|
@ -46,8 +46,6 @@ incremental_delete_partial = failed to delete partly initialized session dir `{$
|
||||
|
||||
incremental_delete_workproduct = file-system error deleting outdated file `{$path}`: {$err}
|
||||
|
||||
incremental_field_associated_value_expected = associated value expected for `{$name}`
|
||||
|
||||
incremental_finalize = error finalizing incremental compilation session directory `{$path}`: {$err}
|
||||
|
||||
incremental_finalized_gc_failed =
|
||||
@ -63,25 +61,15 @@ incremental_load_dep_graph = could not load dep-graph from `{$path}`: {$err}
|
||||
|
||||
incremental_lock_unsupported =
|
||||
the filesystem for the incremental path at {$session_dir} does not appear to support locking, consider changing the incremental path to a filesystem that supports locking or disable incremental compilation
|
||||
incremental_malformed_cgu_name =
|
||||
found malformed codegen unit name `{$user_path}`. codegen units names must always start with the name of the crate (`{$crate_name}` in this case).
|
||||
|
||||
incremental_missing_depnode = missing `DepNode` variant
|
||||
|
||||
incremental_missing_if_this_changed = no `#[rustc_if_this_changed]` annotation detected
|
||||
|
||||
incremental_missing_query_depgraph =
|
||||
found CGU-reuse attribute but `-Zquery-dep-graph` was not specified
|
||||
|
||||
incremental_move_dep_graph = failed to move dependency graph from `{$from}` to `{$to}`: {$err}
|
||||
|
||||
incremental_no_cfg = no cfg attribute
|
||||
|
||||
incremental_no_field = no field `{$name}`
|
||||
|
||||
incremental_no_module_named =
|
||||
no module named `{$user_path}` (mangled: {$cgu_name}). available modules: {$cgu_names}
|
||||
|
||||
incremental_no_path = no path from `{$source}` to `{$target}`
|
||||
|
||||
incremental_not_clean = `{$dep_node_str}` should be clean but is not
|
||||
@ -107,8 +95,6 @@ incremental_undefined_clean_dirty_assertions_item =
|
||||
|
||||
incremental_unknown_item = unknown item `{$name}`
|
||||
|
||||
incremental_unknown_reuse_kind = unknown cgu-reuse-kind `{$kind}` specified
|
||||
|
||||
incremental_unrecognized_depnode = unrecognized `DepNode` variant: {$name}
|
||||
|
||||
incremental_unrecognized_depnode_label = dep-node label `{$label}` not recognized
|
||||
|
@ -40,56 +40,6 @@ pub struct NoPath {
|
||||
pub source: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(incremental_unknown_reuse_kind)]
|
||||
pub struct UnknownReuseKind {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub kind: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(incremental_missing_query_depgraph)]
|
||||
pub struct MissingQueryDepGraph {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(incremental_malformed_cgu_name)]
|
||||
pub struct MalformedCguName {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub user_path: String,
|
||||
pub crate_name: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(incremental_no_module_named)]
|
||||
pub struct NoModuleNamed<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub user_path: &'a str,
|
||||
pub cgu_name: Symbol,
|
||||
pub cgu_names: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(incremental_field_associated_value_expected)]
|
||||
pub struct FieldAssociatedValueExpected {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub name: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(incremental_no_field)]
|
||||
pub struct NoField {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub name: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(incremental_assertion_auto)]
|
||||
pub struct AssertionAuto<'a> {
|
||||
|
@ -16,7 +16,6 @@
|
||||
extern crate tracing;
|
||||
|
||||
mod assert_dep_graph;
|
||||
pub mod assert_module_sources;
|
||||
mod errors;
|
||||
mod persist;
|
||||
|
||||
|
@ -957,10 +957,9 @@ pub fn start_codegen<'tcx>(
|
||||
codegen_backend.codegen_crate(tcx, metadata, need_metadata_module)
|
||||
});
|
||||
|
||||
// Don't run these test assertions when not doing codegen. Compiletest tries to build
|
||||
// Don't run this test assertions when not doing codegen. Compiletest tries to build
|
||||
// build-fail tests in check mode first and expects it to not give an error in that case.
|
||||
if tcx.sess.opts.output_types.should_codegen() {
|
||||
rustc_incremental::assert_module_sources::assert_module_sources(tcx);
|
||||
rustc_symbol_mangling::test::report_symbol_names(tcx);
|
||||
}
|
||||
|
||||
|
@ -727,11 +727,14 @@ fn lookup_with_diagnostics(
|
||||
.collect::<Vec<_>>();
|
||||
possibilities.sort();
|
||||
|
||||
let mut should_print_possibilities = true;
|
||||
if let Some((value, value_span)) = value {
|
||||
if best_match_values.contains(&Some(value)) {
|
||||
db.span_suggestion(name_span, "there is a config with a similar name and value", best_match, Applicability::MaybeIncorrect);
|
||||
should_print_possibilities = false;
|
||||
} else if best_match_values.contains(&None) {
|
||||
db.span_suggestion(name_span.to(value_span), "there is a config with a similar name and no value", best_match, Applicability::MaybeIncorrect);
|
||||
should_print_possibilities = false;
|
||||
} else if let Some(first_value) = possibilities.first() {
|
||||
db.span_suggestion(name_span.to(value_span), "there is a config with a similar name and different values", format!("{best_match} = \"{first_value}\""), Applicability::MaybeIncorrect);
|
||||
} else {
|
||||
@ -741,13 +744,25 @@ fn lookup_with_diagnostics(
|
||||
db.span_suggestion(name_span, "there is a config with a similar name", best_match, Applicability::MaybeIncorrect);
|
||||
}
|
||||
|
||||
if !possibilities.is_empty() {
|
||||
if !possibilities.is_empty() && should_print_possibilities {
|
||||
let possibilities = possibilities.join("`, `");
|
||||
db.help(format!("expected values for `{best_match}` are: `{possibilities}`"));
|
||||
}
|
||||
} else {
|
||||
db.span_suggestion(name_span, "there is a config with a similar name", best_match, Applicability::MaybeIncorrect);
|
||||
}
|
||||
} else if !possibilities.is_empty() {
|
||||
let mut possibilities = possibilities.iter()
|
||||
.map(Symbol::as_str)
|
||||
.collect::<Vec<_>>();
|
||||
possibilities.sort();
|
||||
let possibilities = possibilities.join("`, `");
|
||||
|
||||
// The list of expected names can be long (even by default) and
|
||||
// so the diagnostic produced can take a lot of space. To avoid
|
||||
// cloging the user output we only want to print that diagnostic
|
||||
// once.
|
||||
db.help_once(format!("expected names are: `{possibilities}`"));
|
||||
}
|
||||
},
|
||||
BuiltinLintDiagnostics::UnexpectedCfgValue((name, name_span), value) => {
|
||||
|
@ -506,6 +506,11 @@ macro_rules! add_lint_group {
|
||||
"replaced with another group of lints, see RFC \
|
||||
<https://rust-lang.github.io/rfcs/2145-type-privacy.html> for more information",
|
||||
);
|
||||
store.register_removed(
|
||||
"invalid_alignment",
|
||||
"converted into hard error, see PR #104616 \
|
||||
<https://github.com/rust-lang/rust/pull/104616> for more information",
|
||||
);
|
||||
}
|
||||
|
||||
fn register_internals(store: &mut LintStore) {
|
||||
|
@ -986,45 +986,6 @@
|
||||
"detects trivial casts of numeric types which could be removed"
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `invalid_alignment` lint detects dereferences of misaligned pointers during
|
||||
/// constant evaluation.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust,compile_fail
|
||||
/// #![feature(const_mut_refs)]
|
||||
/// const FOO: () = unsafe {
|
||||
/// let x = &[0_u8; 4];
|
||||
/// let y = x.as_ptr().cast::<u32>();
|
||||
/// let mut z = 123;
|
||||
/// y.copy_to_nonoverlapping(&mut z, 1); // the address of a `u8` array is unknown
|
||||
/// // and thus we don't know if it is aligned enough for copying a `u32`.
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// {{produces}}
|
||||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// The compiler allowed dereferencing raw pointers irrespective of alignment
|
||||
/// during const eval due to the const evaluator at the time not making it easy
|
||||
/// or cheap to check. Now that it is both, this is not accepted anymore.
|
||||
///
|
||||
/// Since it was undefined behaviour to begin with, this breakage does not violate
|
||||
/// Rust's stability guarantees. Using undefined behaviour can cause arbitrary
|
||||
/// behaviour, including failure to build.
|
||||
///
|
||||
/// [future-incompatible]: ../index.md#future-incompatible-lints
|
||||
pub INVALID_ALIGNMENT,
|
||||
Deny,
|
||||
"raw pointers must be aligned before dereferencing",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps,
|
||||
reference: "issue #68585 <https://github.com/rust-lang/rust/issues/104616>",
|
||||
};
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `exported_private_dependencies` lint detects private dependencies
|
||||
/// that are exposed in a public interface.
|
||||
@ -3430,7 +3391,6 @@
|
||||
INDIRECT_STRUCTURAL_MATCH,
|
||||
INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
|
||||
INLINE_NO_SANITIZE,
|
||||
INVALID_ALIGNMENT,
|
||||
INVALID_DOC_ATTRIBUTES,
|
||||
INVALID_MACRO_EXPORT_ARGUMENTS,
|
||||
INVALID_TYPE_PARAM_DEFAULT,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -307,7 +307,7 @@
|
||||
|
||||
use self::ArmType::*;
|
||||
use self::Usefulness::*;
|
||||
use super::deconstruct_pat::{Constructor, DeconstructedPat, Fields, SplitWildcard};
|
||||
use super::deconstruct_pat::{Constructor, ConstructorSet, DeconstructedPat, Fields};
|
||||
use crate::errors::{NonExhaustiveOmittedPattern, Uncovered};
|
||||
|
||||
use rustc_data_structures::captures::Captures;
|
||||
@ -368,8 +368,6 @@ pub(super) struct PatCtxt<'a, 'p, 'tcx> {
|
||||
/// Whether the current pattern is the whole pattern as found in a match arm, or if it's a
|
||||
/// subpattern.
|
||||
pub(super) is_top_level: bool,
|
||||
/// Whether the current pattern is from a `non_exhaustive` enum.
|
||||
pub(super) is_non_exhaustive: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'p, 'tcx> fmt::Debug for PatCtxt<'a, 'p, 'tcx> {
|
||||
@ -616,62 +614,41 @@ fn apply_constructor(
|
||||
WithWitnesses(ref witnesses) if witnesses.is_empty() => self,
|
||||
WithWitnesses(witnesses) => {
|
||||
let new_witnesses = if let Constructor::Missing { .. } = ctor {
|
||||
// We got the special `Missing` constructor, so each of the missing constructors
|
||||
// gives a new pattern that is not caught by the match. We list those patterns.
|
||||
if pcx.is_non_exhaustive {
|
||||
witnesses
|
||||
.into_iter()
|
||||
// Here we don't want the user to try to list all variants, we want them to add
|
||||
// a wildcard, so we only suggest that.
|
||||
.map(|witness| {
|
||||
witness.apply_constructor(pcx, &Constructor::NonExhaustive)
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
let mut split_wildcard = SplitWildcard::new(pcx);
|
||||
split_wildcard.split(pcx, matrix.heads().map(DeconstructedPat::ctor));
|
||||
|
||||
// This lets us know if we skipped any variants because they are marked
|
||||
// `doc(hidden)` or they are unstable feature gate (only stdlib types).
|
||||
let mut hide_variant_show_wild = false;
|
||||
// Construct for each missing constructor a "wild" version of this
|
||||
// constructor, that matches everything that can be built with
|
||||
// it. For example, if `ctor` is a `Constructor::Variant` for
|
||||
// `Option::Some`, we get the pattern `Some(_)`.
|
||||
let mut new_patterns: Vec<DeconstructedPat<'_, '_>> = split_wildcard
|
||||
.iter_missing(pcx)
|
||||
.filter_map(|missing_ctor| {
|
||||
// Check if this variant is marked `doc(hidden)`
|
||||
if missing_ctor.is_doc_hidden_variant(pcx)
|
||||
|| missing_ctor.is_unstable_variant(pcx)
|
||||
{
|
||||
hide_variant_show_wild = true;
|
||||
return None;
|
||||
}
|
||||
Some(DeconstructedPat::wild_from_ctor(pcx, missing_ctor.clone()))
|
||||
})
|
||||
.collect();
|
||||
|
||||
if hide_variant_show_wild {
|
||||
new_patterns.push(DeconstructedPat::wildcard(pcx.ty, pcx.span));
|
||||
}
|
||||
|
||||
witnesses
|
||||
.into_iter()
|
||||
.flat_map(|witness| {
|
||||
new_patterns.iter().map(move |pat| {
|
||||
Witness(
|
||||
witness
|
||||
.0
|
||||
.iter()
|
||||
.chain(once(pat))
|
||||
.map(DeconstructedPat::clone_and_forget_reachability)
|
||||
.collect(),
|
||||
)
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
let mut missing = ConstructorSet::for_ty(pcx.cx, pcx.ty)
|
||||
.compute_missing(pcx, matrix.heads().map(DeconstructedPat::ctor));
|
||||
if missing.iter().any(|c| c.is_non_exhaustive()) {
|
||||
// We only report `_` here; listing other constructors would be redundant.
|
||||
missing = vec![Constructor::NonExhaustive];
|
||||
}
|
||||
|
||||
// We got the special `Missing` constructor, so each of the missing constructors
|
||||
// gives a new pattern that is not caught by the match.
|
||||
// We construct for each missing constructor a version of this constructor with
|
||||
// wildcards for fields, i.e. that matches everything that can be built with it.
|
||||
// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get
|
||||
// the pattern `Some(_)`.
|
||||
let new_patterns: Vec<DeconstructedPat<'_, '_>> = missing
|
||||
.into_iter()
|
||||
.map(|missing_ctor| {
|
||||
DeconstructedPat::wild_from_ctor(pcx, missing_ctor.clone())
|
||||
})
|
||||
.collect();
|
||||
|
||||
witnesses
|
||||
.into_iter()
|
||||
.flat_map(|witness| {
|
||||
new_patterns.iter().map(move |pat| {
|
||||
Witness(
|
||||
witness
|
||||
.0
|
||||
.iter()
|
||||
.chain(once(pat))
|
||||
.map(DeconstructedPat::clone_and_forget_reachability)
|
||||
.collect(),
|
||||
)
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
witnesses
|
||||
.into_iter()
|
||||
@ -844,9 +821,8 @@ fn is_useful<'p, 'tcx>(
|
||||
ty = row.head().ty();
|
||||
}
|
||||
}
|
||||
let is_non_exhaustive = cx.is_foreign_non_exhaustive_enum(ty);
|
||||
debug!("v.head: {:?}, v.span: {:?}", v.head(), v.head().span());
|
||||
let pcx = &PatCtxt { cx, ty, span: v.head().span(), is_top_level, is_non_exhaustive };
|
||||
let pcx = &PatCtxt { cx, ty, span: v.head().span(), is_top_level };
|
||||
|
||||
let v_ctor = v.head().ctor();
|
||||
debug!(?v_ctor);
|
||||
@ -861,7 +837,8 @@ fn is_useful<'p, 'tcx>(
|
||||
}
|
||||
// We split the head constructor of `v`.
|
||||
let split_ctors = v_ctor.split(pcx, matrix.heads().map(DeconstructedPat::ctor));
|
||||
let is_non_exhaustive_and_wild = is_non_exhaustive && v_ctor.is_wildcard();
|
||||
let is_non_exhaustive_and_wild =
|
||||
cx.is_foreign_non_exhaustive_enum(ty) && v_ctor.is_wildcard();
|
||||
// For each constructor, we compute whether there's a value that starts with it that would
|
||||
// witness the usefulness of `v`.
|
||||
let start_matrix = &matrix;
|
||||
@ -895,27 +872,21 @@ fn is_useful<'p, 'tcx>(
|
||||
&& usefulness.is_useful() && matches!(witness_preference, RealArm)
|
||||
&& matches!(
|
||||
&ctor,
|
||||
Constructor::Missing { nonexhaustive_enum_missing_real_variants: true }
|
||||
Constructor::Missing { nonexhaustive_enum_missing_visible_variants: true }
|
||||
)
|
||||
{
|
||||
let patterns = {
|
||||
let mut split_wildcard = SplitWildcard::new(pcx);
|
||||
split_wildcard.split(pcx, matrix.heads().map(DeconstructedPat::ctor));
|
||||
// Construct for each missing constructor a "wild" version of this
|
||||
// constructor, that matches everything that can be built with
|
||||
// it. For example, if `ctor` is a `Constructor::Variant` for
|
||||
// `Option::Some`, we get the pattern `Some(_)`.
|
||||
split_wildcard
|
||||
.iter_missing(pcx)
|
||||
// Filter out the `NonExhaustive` because we want to list only real
|
||||
// variants. Also remove any unstable feature gated variants.
|
||||
// Because of how we computed `nonexhaustive_enum_missing_real_variants`,
|
||||
// this will not return an empty `Vec`.
|
||||
.filter(|c| !(c.is_non_exhaustive() || c.is_unstable_variant(pcx)))
|
||||
.cloned()
|
||||
.map(|missing_ctor| DeconstructedPat::wild_from_ctor(pcx, missing_ctor))
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
let missing = ConstructorSet::for_ty(pcx.cx, pcx.ty)
|
||||
.compute_missing(pcx, matrix.heads().map(DeconstructedPat::ctor));
|
||||
// Construct for each missing constructor a "wild" version of this constructor, that
|
||||
// matches everything that can be built with it. For example, if `ctor` is a
|
||||
// `Constructor::Variant` for `Option::Some`, we get the pattern `Some(_)`.
|
||||
let patterns = missing
|
||||
.into_iter()
|
||||
// Because of how we computed `nonexhaustive_enum_missing_visible_variants`,
|
||||
// this will not return an empty `Vec`.
|
||||
.filter(|c| !(matches!(c, Constructor::NonExhaustive | Constructor::Hidden)))
|
||||
.map(|missing_ctor| DeconstructedPat::wild_from_ctor(pcx, missing_ctor))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns`
|
||||
// is not exhaustive enough.
|
||||
|
@ -2,8 +2,6 @@
|
||||
//! assertion failures
|
||||
|
||||
use either::Right;
|
||||
|
||||
use rustc_const_eval::const_eval::CheckAlignment;
|
||||
use rustc_const_eval::ReportErrorExt;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir::def::DefKind;
|
||||
@ -16,7 +14,7 @@
|
||||
use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout};
|
||||
use rustc_middle::ty::{self, GenericArgs, Instance, ParamEnv, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_span::{def_id::DefId, Span};
|
||||
use rustc_target::abi::{self, Align, HasDataLayout, Size, TargetDataLayout};
|
||||
use rustc_target::abi::{self, HasDataLayout, Size, TargetDataLayout};
|
||||
use rustc_target::spec::abi::Abi as CallAbi;
|
||||
|
||||
use crate::dataflow_const_prop::Patch;
|
||||
@ -141,27 +139,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
|
||||
type MemoryKind = !;
|
||||
|
||||
#[inline(always)]
|
||||
fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> CheckAlignment {
|
||||
// We do not check for alignment to avoid having to carry an `Align`
|
||||
// in `ConstValue::Indirect`.
|
||||
CheckAlignment::No
|
||||
fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
|
||||
false // no reason to enforce alignment
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>, _layout: TyAndLayout<'tcx>) -> bool {
|
||||
false // for now, we don't enforce validity
|
||||
}
|
||||
fn alignment_check_failed(
|
||||
ecx: &InterpCx<'mir, 'tcx, Self>,
|
||||
_has: Align,
|
||||
_required: Align,
|
||||
_check: CheckAlignment,
|
||||
) -> InterpResult<'tcx, ()> {
|
||||
span_bug!(
|
||||
ecx.cur_span(),
|
||||
"`alignment_check_failed` called when no alignment check requested"
|
||||
)
|
||||
}
|
||||
|
||||
fn load_mir(
|
||||
_ecx: &InterpCx<'mir, 'tcx, Self>,
|
||||
|
@ -245,13 +245,13 @@ fn make_bcb_counters(
|
||||
// the loop. The `traversal` state includes a `context_stack`, providing a way to know if
|
||||
// the current BCB is in one or more nested loops or not.
|
||||
let mut traversal = TraverseCoverageGraphWithLoops::new(&self.basic_coverage_blocks);
|
||||
while let Some(bcb) = traversal.next(self.basic_coverage_blocks) {
|
||||
while let Some(bcb) = traversal.next() {
|
||||
if bcb_has_coverage_spans(bcb) {
|
||||
debug!("{:?} has at least one coverage span. Get or make its counter", bcb);
|
||||
let branching_counter_operand = self.get_or_make_counter_operand(bcb)?;
|
||||
|
||||
if self.bcb_needs_branch_counters(bcb) {
|
||||
self.make_branch_counters(&mut traversal, bcb, branching_counter_operand)?;
|
||||
self.make_branch_counters(&traversal, bcb, branching_counter_operand)?;
|
||||
}
|
||||
} else {
|
||||
debug!(
|
||||
@ -274,7 +274,7 @@ fn make_bcb_counters(
|
||||
|
||||
fn make_branch_counters(
|
||||
&mut self,
|
||||
traversal: &mut TraverseCoverageGraphWithLoops,
|
||||
traversal: &TraverseCoverageGraphWithLoops<'_>,
|
||||
branching_bcb: BasicCoverageBlock,
|
||||
branching_counter_operand: Operand,
|
||||
) -> Result<(), Error> {
|
||||
@ -507,21 +507,14 @@ fn recursive_get_or_make_edge_counter_operand(
|
||||
/// found, select any branch.
|
||||
fn choose_preferred_expression_branch(
|
||||
&self,
|
||||
traversal: &TraverseCoverageGraphWithLoops,
|
||||
traversal: &TraverseCoverageGraphWithLoops<'_>,
|
||||
branches: &[BcbBranch],
|
||||
) -> BcbBranch {
|
||||
let branch_needs_a_counter = |branch: &BcbBranch| self.branch_has_no_counter(branch);
|
||||
|
||||
let some_reloop_branch = self.find_some_reloop_branch(traversal, &branches);
|
||||
if let Some(reloop_branch_without_counter) =
|
||||
some_reloop_branch.filter(branch_needs_a_counter)
|
||||
{
|
||||
debug!(
|
||||
"Selecting reloop_branch={:?} that still needs a counter, to get the \
|
||||
`Expression`",
|
||||
reloop_branch_without_counter
|
||||
);
|
||||
reloop_branch_without_counter
|
||||
let good_reloop_branch = self.find_good_reloop_branch(traversal, &branches);
|
||||
if let Some(reloop_branch) = good_reloop_branch {
|
||||
assert!(self.branch_has_no_counter(&reloop_branch));
|
||||
debug!("Selecting reloop branch {reloop_branch:?} to get an expression");
|
||||
reloop_branch
|
||||
} else {
|
||||
let &branch_without_counter =
|
||||
branches.iter().find(|&branch| self.branch_has_no_counter(branch)).expect(
|
||||
@ -538,75 +531,52 @@ fn choose_preferred_expression_branch(
|
||||
}
|
||||
}
|
||||
|
||||
/// At most, one of the branches (or its edge, from the branching_bcb, if the branch has
|
||||
/// multiple incoming edges) can have a counter computed by expression.
|
||||
///
|
||||
/// If at least one of the branches leads outside of a loop (`found_loop_exit` is
|
||||
/// true), and at least one other branch does not exit the loop (the first of which
|
||||
/// is captured in `some_reloop_branch`), it's likely any reloop branch will be
|
||||
/// executed far more often than loop exit branch, making the reloop branch a better
|
||||
/// candidate for an expression.
|
||||
fn find_some_reloop_branch(
|
||||
/// Tries to find a branch that leads back to the top of a loop, and that
|
||||
/// doesn't already have a counter. Such branches are good candidates to
|
||||
/// be given an expression (instead of a physical counter), because they
|
||||
/// will tend to be executed more times than a loop-exit branch.
|
||||
fn find_good_reloop_branch(
|
||||
&self,
|
||||
traversal: &TraverseCoverageGraphWithLoops,
|
||||
traversal: &TraverseCoverageGraphWithLoops<'_>,
|
||||
branches: &[BcbBranch],
|
||||
) -> Option<BcbBranch> {
|
||||
let branch_needs_a_counter = |branch: &BcbBranch| self.branch_has_no_counter(branch);
|
||||
// Consider each loop on the current traversal context stack, top-down.
|
||||
for reloop_bcbs in traversal.reloop_bcbs_per_loop() {
|
||||
let mut all_branches_exit_this_loop = true;
|
||||
|
||||
let mut some_reloop_branch: Option<BcbBranch> = None;
|
||||
for context in traversal.context_stack.iter().rev() {
|
||||
if let Some((backedge_from_bcbs, _)) = &context.loop_backedges {
|
||||
let mut found_loop_exit = false;
|
||||
for &branch in branches.iter() {
|
||||
if backedge_from_bcbs.iter().any(|&backedge_from_bcb| {
|
||||
self.bcb_dominates(branch.target_bcb, backedge_from_bcb)
|
||||
}) {
|
||||
if let Some(reloop_branch) = some_reloop_branch {
|
||||
if self.branch_has_no_counter(&reloop_branch) {
|
||||
// we already found a candidate reloop_branch that still
|
||||
// needs a counter
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// The path from branch leads back to the top of the loop. Set this
|
||||
// branch as the `reloop_branch`. If this branch already has a
|
||||
// counter, and we find another reloop branch that doesn't have a
|
||||
// counter yet, that branch will be selected as the `reloop_branch`
|
||||
// instead.
|
||||
some_reloop_branch = Some(branch);
|
||||
} else {
|
||||
// The path from branch leads outside this loop
|
||||
found_loop_exit = true;
|
||||
}
|
||||
if found_loop_exit
|
||||
&& some_reloop_branch.filter(branch_needs_a_counter).is_some()
|
||||
{
|
||||
// Found both a branch that exits the loop and a branch that returns
|
||||
// to the top of the loop (`reloop_branch`), and the `reloop_branch`
|
||||
// doesn't already have a counter.
|
||||
break;
|
||||
// Try to find a branch that doesn't exit this loop and doesn't
|
||||
// already have a counter.
|
||||
for &branch in branches {
|
||||
// A branch is a reloop branch if it dominates any BCB that has
|
||||
// an edge back to the loop header. (Other branches are exits.)
|
||||
let is_reloop_branch = reloop_bcbs.iter().any(|&reloop_bcb| {
|
||||
self.basic_coverage_blocks.dominates(branch.target_bcb, reloop_bcb)
|
||||
});
|
||||
|
||||
if is_reloop_branch {
|
||||
all_branches_exit_this_loop = false;
|
||||
if self.branch_has_no_counter(&branch) {
|
||||
// We found a good branch to be given an expression.
|
||||
return Some(branch);
|
||||
}
|
||||
// Keep looking for another reloop branch without a counter.
|
||||
} else {
|
||||
// This branch exits the loop.
|
||||
}
|
||||
if !found_loop_exit {
|
||||
debug!(
|
||||
"No branches exit the loop, so any branch without an existing \
|
||||
counter can have the `Expression`."
|
||||
);
|
||||
break;
|
||||
}
|
||||
if some_reloop_branch.is_some() {
|
||||
debug!(
|
||||
"Found a branch that exits the loop and a branch the loops back to \
|
||||
the top of the loop (`reloop_branch`). The `reloop_branch` will \
|
||||
get the `Expression`, as long as it still needs a counter."
|
||||
);
|
||||
break;
|
||||
}
|
||||
// else all branches exited this loop context, so run the same checks with
|
||||
// the outer loop(s)
|
||||
}
|
||||
|
||||
if !all_branches_exit_this_loop {
|
||||
// We found one or more reloop branches, but all of them already
|
||||
// have counters. Let the caller choose one of the exit branches.
|
||||
debug!("All reloop branches had counters; skip checking the other loops");
|
||||
return None;
|
||||
}
|
||||
|
||||
// All of the branches exit this loop, so keep looking for a good
|
||||
// reloop branch for one of the outer loops.
|
||||
}
|
||||
some_reloop_branch
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -652,9 +622,4 @@ fn branch_counter(&self, branch: &BcbBranch) -> Option<&BcbCounter> {
|
||||
fn bcb_has_one_path_to_target(&self, bcb: BasicCoverageBlock) -> bool {
|
||||
self.bcb_predecessors(bcb).len() <= 1
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn bcb_dominates(&self, dom: BasicCoverageBlock, node: BasicCoverageBlock) -> bool {
|
||||
self.basic_coverage_blocks.dominates(dom, node)
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
use rustc_middle::mir::{self, BasicBlock, TerminatorKind};
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::VecDeque;
|
||||
use std::ops::{Index, IndexMut};
|
||||
|
||||
/// A coverage-specific simplification of the MIR control flow graph (CFG). The `CoverageGraph`s
|
||||
@ -385,57 +386,72 @@ fn bcb_filtered_successors<'a, 'tcx>(
|
||||
/// ensures a loop is completely traversed before processing Blocks after the end of the loop.
|
||||
#[derive(Debug)]
|
||||
pub(super) struct TraversalContext {
|
||||
/// From one or more backedges returning to a loop header.
|
||||
pub loop_backedges: Option<(Vec<BasicCoverageBlock>, BasicCoverageBlock)>,
|
||||
/// BCB with one or more incoming loop backedges, indicating which loop
|
||||
/// this context is for.
|
||||
///
|
||||
/// If `None`, this is the non-loop context for the function as a whole.
|
||||
loop_header: Option<BasicCoverageBlock>,
|
||||
|
||||
/// worklist, to be traversed, of CoverageGraph in the loop with the given loop
|
||||
/// backedges, such that the loop is the inner inner-most loop containing these
|
||||
/// CoverageGraph
|
||||
pub worklist: Vec<BasicCoverageBlock>,
|
||||
/// Worklist of BCBs to be processed in this context.
|
||||
worklist: VecDeque<BasicCoverageBlock>,
|
||||
}
|
||||
|
||||
pub(super) struct TraverseCoverageGraphWithLoops {
|
||||
pub backedges: IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>>,
|
||||
pub context_stack: Vec<TraversalContext>,
|
||||
pub(super) struct TraverseCoverageGraphWithLoops<'a> {
|
||||
basic_coverage_blocks: &'a CoverageGraph,
|
||||
|
||||
backedges: IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>>,
|
||||
context_stack: Vec<TraversalContext>,
|
||||
visited: BitSet<BasicCoverageBlock>,
|
||||
}
|
||||
|
||||
impl TraverseCoverageGraphWithLoops {
|
||||
pub fn new(basic_coverage_blocks: &CoverageGraph) -> Self {
|
||||
let start_bcb = basic_coverage_blocks.start_node();
|
||||
impl<'a> TraverseCoverageGraphWithLoops<'a> {
|
||||
pub(super) fn new(basic_coverage_blocks: &'a CoverageGraph) -> Self {
|
||||
let backedges = find_loop_backedges(basic_coverage_blocks);
|
||||
let context_stack =
|
||||
vec![TraversalContext { loop_backedges: None, worklist: vec![start_bcb] }];
|
||||
|
||||
let worklist = VecDeque::from([basic_coverage_blocks.start_node()]);
|
||||
let context_stack = vec![TraversalContext { loop_header: None, worklist }];
|
||||
|
||||
// `context_stack` starts with a `TraversalContext` for the main function context (beginning
|
||||
// with the `start` BasicCoverageBlock of the function). New worklists are pushed to the top
|
||||
// of the stack as loops are entered, and popped off of the stack when a loop's worklist is
|
||||
// exhausted.
|
||||
let visited = BitSet::new_empty(basic_coverage_blocks.num_nodes());
|
||||
Self { backedges, context_stack, visited }
|
||||
Self { basic_coverage_blocks, backedges, context_stack, visited }
|
||||
}
|
||||
|
||||
pub fn next(&mut self, basic_coverage_blocks: &CoverageGraph) -> Option<BasicCoverageBlock> {
|
||||
/// For each loop on the loop context stack (top-down), yields a list of BCBs
|
||||
/// within that loop that have an outgoing edge back to the loop header.
|
||||
pub(super) fn reloop_bcbs_per_loop(&self) -> impl Iterator<Item = &[BasicCoverageBlock]> {
|
||||
self.context_stack
|
||||
.iter()
|
||||
.rev()
|
||||
.filter_map(|context| context.loop_header)
|
||||
.map(|header_bcb| self.backedges[header_bcb].as_slice())
|
||||
}
|
||||
|
||||
pub(super) fn next(&mut self) -> Option<BasicCoverageBlock> {
|
||||
debug!(
|
||||
"TraverseCoverageGraphWithLoops::next - context_stack: {:?}",
|
||||
self.context_stack.iter().rev().collect::<Vec<_>>()
|
||||
);
|
||||
|
||||
while let Some(context) = self.context_stack.last_mut() {
|
||||
if let Some(next_bcb) = context.worklist.pop() {
|
||||
if !self.visited.insert(next_bcb) {
|
||||
debug!("Already visited: {:?}", next_bcb);
|
||||
if let Some(bcb) = context.worklist.pop_front() {
|
||||
if !self.visited.insert(bcb) {
|
||||
debug!("Already visited: {bcb:?}");
|
||||
continue;
|
||||
}
|
||||
debug!("Visiting {:?}", next_bcb);
|
||||
if self.backedges[next_bcb].len() > 0 {
|
||||
debug!("{:?} is a loop header! Start a new TraversalContext...", next_bcb);
|
||||
debug!("Visiting {bcb:?}");
|
||||
|
||||
if self.backedges[bcb].len() > 0 {
|
||||
debug!("{bcb:?} is a loop header! Start a new TraversalContext...");
|
||||
self.context_stack.push(TraversalContext {
|
||||
loop_backedges: Some((self.backedges[next_bcb].clone(), next_bcb)),
|
||||
worklist: Vec::new(),
|
||||
loop_header: Some(bcb),
|
||||
worklist: VecDeque::new(),
|
||||
});
|
||||
}
|
||||
self.extend_worklist(basic_coverage_blocks, next_bcb);
|
||||
return Some(next_bcb);
|
||||
self.add_successors_to_worklists(bcb);
|
||||
return Some(bcb);
|
||||
} else {
|
||||
// Strip contexts with empty worklists from the top of the stack
|
||||
self.context_stack.pop();
|
||||
@ -445,13 +461,10 @@ pub fn next(&mut self, basic_coverage_blocks: &CoverageGraph) -> Option<BasicCov
|
||||
None
|
||||
}
|
||||
|
||||
pub fn extend_worklist(
|
||||
&mut self,
|
||||
basic_coverage_blocks: &CoverageGraph,
|
||||
bcb: BasicCoverageBlock,
|
||||
) {
|
||||
let successors = &basic_coverage_blocks.successors[bcb];
|
||||
pub fn add_successors_to_worklists(&mut self, bcb: BasicCoverageBlock) {
|
||||
let successors = &self.basic_coverage_blocks.successors[bcb];
|
||||
debug!("{:?} has {} successors:", bcb, successors.len());
|
||||
|
||||
for &successor in successors {
|
||||
if successor == bcb {
|
||||
debug!(
|
||||
@ -460,56 +473,44 @@ pub fn extend_worklist(
|
||||
bcb
|
||||
);
|
||||
// Don't re-add this successor to the worklist. We are already processing it.
|
||||
// FIXME: This claims to skip just the self-successor, but it actually skips
|
||||
// all other successors as well. Does that matter?
|
||||
break;
|
||||
}
|
||||
for context in self.context_stack.iter_mut().rev() {
|
||||
// Add successors of the current BCB to the appropriate context. Successors that
|
||||
// stay within a loop are added to the BCBs context worklist. Successors that
|
||||
// exit the loop (they are not dominated by the loop header) must be reachable
|
||||
// from other BCBs outside the loop, and they will be added to a different
|
||||
// worklist.
|
||||
//
|
||||
// Branching blocks (with more than one successor) must be processed before
|
||||
// blocks with only one successor, to prevent unnecessarily complicating
|
||||
// `Expression`s by creating a Counter in a `BasicCoverageBlock` that the
|
||||
// branching block would have given an `Expression` (or vice versa).
|
||||
let (some_successor_to_add, some_loop_header) =
|
||||
if let Some((_, loop_header)) = context.loop_backedges {
|
||||
if basic_coverage_blocks.dominates(loop_header, successor) {
|
||||
(Some(successor), Some(loop_header))
|
||||
} else {
|
||||
(None, None)
|
||||
}
|
||||
} else {
|
||||
(Some(successor), None)
|
||||
};
|
||||
if let Some(successor_to_add) = some_successor_to_add {
|
||||
if basic_coverage_blocks.successors[successor_to_add].len() > 1 {
|
||||
debug!(
|
||||
"{:?} successor is branching. Prioritize it at the beginning of \
|
||||
the {}",
|
||||
successor_to_add,
|
||||
if let Some(loop_header) = some_loop_header {
|
||||
format!("worklist for the loop headed by {loop_header:?}")
|
||||
} else {
|
||||
String::from("non-loop worklist")
|
||||
},
|
||||
);
|
||||
context.worklist.insert(0, successor_to_add);
|
||||
} else {
|
||||
debug!(
|
||||
"{:?} successor is non-branching. Defer it to the end of the {}",
|
||||
successor_to_add,
|
||||
if let Some(loop_header) = some_loop_header {
|
||||
format!("worklist for the loop headed by {loop_header:?}")
|
||||
} else {
|
||||
String::from("non-loop worklist")
|
||||
},
|
||||
);
|
||||
context.worklist.push(successor_to_add);
|
||||
|
||||
// Add successors of the current BCB to the appropriate context. Successors that
|
||||
// stay within a loop are added to the BCBs context worklist. Successors that
|
||||
// exit the loop (they are not dominated by the loop header) must be reachable
|
||||
// from other BCBs outside the loop, and they will be added to a different
|
||||
// worklist.
|
||||
//
|
||||
// Branching blocks (with more than one successor) must be processed before
|
||||
// blocks with only one successor, to prevent unnecessarily complicating
|
||||
// `Expression`s by creating a Counter in a `BasicCoverageBlock` that the
|
||||
// branching block would have given an `Expression` (or vice versa).
|
||||
|
||||
let context = self
|
||||
.context_stack
|
||||
.iter_mut()
|
||||
.rev()
|
||||
.find(|context| match context.loop_header {
|
||||
Some(loop_header) => {
|
||||
self.basic_coverage_blocks.dominates(loop_header, successor)
|
||||
}
|
||||
break;
|
||||
}
|
||||
None => true,
|
||||
})
|
||||
.unwrap_or_else(|| bug!("should always fall back to the root non-loop context"));
|
||||
debug!("adding to worklist for {:?}", context.loop_header);
|
||||
|
||||
// FIXME: The code below had debug messages claiming to add items to a
|
||||
// particular end of the worklist, but was confused about which end was
|
||||
// which. The existing behaviour has been preserved for now, but it's
|
||||
// unclear what the intended behaviour was.
|
||||
|
||||
if self.basic_coverage_blocks.successors[successor].len() > 1 {
|
||||
context.worklist.push_back(successor);
|
||||
} else {
|
||||
context.worklist.push_front(successor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -628,7 +628,7 @@ fn test_traverse_coverage_with_loops() {
|
||||
let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
|
||||
let mut traversed_in_order = Vec::new();
|
||||
let mut traversal = graph::TraverseCoverageGraphWithLoops::new(&basic_coverage_blocks);
|
||||
while let Some(bcb) = traversal.next(&basic_coverage_blocks) {
|
||||
while let Some(bcb) = traversal.next() {
|
||||
traversed_in_order.push(bcb);
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,6 @@
|
||||
//!
|
||||
//! Currently, this pass only propagates scalar values.
|
||||
|
||||
use rustc_const_eval::const_eval::CheckAlignment;
|
||||
use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, Projectable};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir::def::DefKind;
|
||||
@ -17,7 +16,7 @@
|
||||
use rustc_mir_dataflow::{lattice::FlatSet, Analysis, Results, ResultsVisitor};
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::DUMMY_SP;
|
||||
use rustc_target::abi::{Align, FieldIdx, VariantIdx};
|
||||
use rustc_target::abi::{FieldIdx, VariantIdx};
|
||||
|
||||
use crate::MirPass;
|
||||
|
||||
@ -709,23 +708,13 @@ impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for Dumm
|
||||
const PANIC_ON_ALLOC_FAIL: bool = true;
|
||||
|
||||
#[inline(always)]
|
||||
fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> CheckAlignment {
|
||||
// We do not check for alignment to avoid having to carry an `Align`
|
||||
// in `ConstValue::ByRef`.
|
||||
CheckAlignment::No
|
||||
fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
|
||||
false // no reason to enforce alignment
|
||||
}
|
||||
|
||||
fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>, _layout: TyAndLayout<'tcx>) -> bool {
|
||||
unimplemented!()
|
||||
}
|
||||
fn alignment_check_failed(
|
||||
_ecx: &InterpCx<'mir, 'tcx, Self>,
|
||||
_has: Align,
|
||||
_required: Align,
|
||||
_check: CheckAlignment,
|
||||
) -> interpret::InterpResult<'tcx, ()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn before_access_global(
|
||||
_tcx: TyCtxt<'tcx>,
|
||||
|
@ -827,6 +827,65 @@ pub fn maybe_suggest_struct_literal(
|
||||
None
|
||||
}
|
||||
|
||||
pub(super) fn recover_closure_body(
|
||||
&mut self,
|
||||
mut err: DiagnosticBuilder<'a, ErrorGuaranteed>,
|
||||
before: token::Token,
|
||||
prev: token::Token,
|
||||
token: token::Token,
|
||||
lo: Span,
|
||||
decl_hi: Span,
|
||||
) -> PResult<'a, P<Expr>> {
|
||||
err.span_label(lo.to(decl_hi), "while parsing the body of this closure");
|
||||
match before.kind {
|
||||
token::OpenDelim(Delimiter::Brace)
|
||||
if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) =>
|
||||
{
|
||||
// `{ || () }` should have been `|| { () }`
|
||||
err.multipart_suggestion(
|
||||
"you might have meant to open the body of the closure, instead of enclosing \
|
||||
the closure in a block",
|
||||
vec![
|
||||
(before.span, String::new()),
|
||||
(prev.span.shrink_to_hi(), " {".to_string()),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
err.emit();
|
||||
self.eat_to_tokens(&[&token::CloseDelim(Delimiter::Brace)]);
|
||||
}
|
||||
token::OpenDelim(Delimiter::Parenthesis)
|
||||
if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) =>
|
||||
{
|
||||
// We are within a function call or tuple, we can emit the error
|
||||
// and recover.
|
||||
self.eat_to_tokens(&[&token::CloseDelim(Delimiter::Parenthesis), &token::Comma]);
|
||||
|
||||
err.multipart_suggestion_verbose(
|
||||
"you might have meant to open the body of the closure",
|
||||
vec![
|
||||
(prev.span.shrink_to_hi(), " {".to_string()),
|
||||
(self.token.span.shrink_to_lo(), "}".to_string()),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
err.emit();
|
||||
}
|
||||
_ if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) => {
|
||||
// We don't have a heuristic to correctly identify where the block
|
||||
// should be closed.
|
||||
err.multipart_suggestion_verbose(
|
||||
"you might have meant to open the body of the closure",
|
||||
vec![(prev.span.shrink_to_hi(), " {".to_string())],
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
return Err(err);
|
||||
}
|
||||
_ => return Err(err),
|
||||
}
|
||||
Ok(self.mk_expr_err(lo.to(self.token.span)))
|
||||
}
|
||||
|
||||
/// Eats and discards tokens until one of `kets` is encountered. Respects token trees,
|
||||
/// passes through any errors encountered. Used for error recovery.
|
||||
pub(super) fn eat_to_tokens(&mut self, kets: &[&TokenKind]) {
|
||||
|
@ -2209,6 +2209,7 @@ fn parse_simple_block(&mut self) -> PResult<'a, P<Expr>> {
|
||||
fn parse_expr_closure(&mut self) -> PResult<'a, P<Expr>> {
|
||||
let lo = self.token.span;
|
||||
|
||||
let before = self.prev_token.clone();
|
||||
let binder = if self.check_keyword(kw::For) {
|
||||
let lo = self.token.span;
|
||||
let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
|
||||
@ -2239,7 +2240,12 @@ fn parse_expr_closure(&mut self) -> PResult<'a, P<Expr>> {
|
||||
FnRetTy::Default(_) => {
|
||||
let restrictions =
|
||||
self.restrictions - Restrictions::STMT_EXPR - Restrictions::ALLOW_LET;
|
||||
self.parse_expr_res(restrictions, None)?
|
||||
let prev = self.prev_token.clone();
|
||||
let token = self.token.clone();
|
||||
match self.parse_expr_res(restrictions, None) {
|
||||
Ok(expr) => expr,
|
||||
Err(err) => self.recover_closure_body(err, before, prev, token, lo, decl_hi)?,
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// If an explicit return type is given, require a block to appear (RFC 968).
|
||||
@ -2459,10 +2465,16 @@ fn parse_expr_cond(&mut self) -> PResult<'a, P<Expr>> {
|
||||
/// Parses a `let $pat = $expr` pseudo-expression.
|
||||
fn parse_expr_let(&mut self, restrictions: Restrictions) -> PResult<'a, P<Expr>> {
|
||||
let is_recovered = if !restrictions.contains(Restrictions::ALLOW_LET) {
|
||||
Some(self.sess.emit_err(errors::ExpectedExpressionFoundLet {
|
||||
let err = errors::ExpectedExpressionFoundLet {
|
||||
span: self.token.span,
|
||||
reason: ForbiddenLetReason::OtherForbidden,
|
||||
}))
|
||||
};
|
||||
if self.prev_token.kind == token::BinOp(token::Or) {
|
||||
// This was part of a closure, the that part of the parser recover.
|
||||
return Err(err.into_diagnostic(&self.sess.span_diagnostic));
|
||||
} else {
|
||||
Some(self.sess.emit_err(err))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
@ -5,9 +5,6 @@ session_cannot_enable_crt_static_linux = sanitizer is incompatible with statical
|
||||
|
||||
session_cannot_mix_and_match_sanitizers = `-Zsanitizer={$first}` is incompatible with `-Zsanitizer={$second}`
|
||||
|
||||
session_cgu_not_recorded =
|
||||
CGU-reuse for `{$cgu_user_name}` is (mangled: `{$cgu_name}`) was not recorded
|
||||
|
||||
session_cli_feature_diagnostic_help =
|
||||
add `-Zcrate-attr="feature({$feature})"` to the command-line options to enable
|
||||
|
||||
@ -34,12 +31,6 @@ session_hexadecimal_float_literal_not_supported = hexadecimal float literal is n
|
||||
session_incompatible_linker_flavor = linker flavor `{$flavor}` is incompatible with the current target
|
||||
.note = compatible flavors are: {$compatible_list}
|
||||
|
||||
session_incorrect_cgu_reuse_type =
|
||||
CGU-reuse for `{$cgu_user_name}` is `{$actual_reuse}` but should be {$at_least ->
|
||||
[one] {"at least "}
|
||||
*[other] {""}
|
||||
}`{$expected_reuse}`
|
||||
|
||||
session_instrumentation_not_supported = {$us} instrumentation is not supported for this target
|
||||
|
||||
session_int_literal_too_large = integer literal is too large
|
||||
|
@ -1,136 +0,0 @@
|
||||
//! Some facilities for tracking how codegen-units are reused during incremental
|
||||
//! compilation. This is used for incremental compilation tests and debug
|
||||
//! output.
|
||||
|
||||
use crate::errors::{CguNotRecorded, IncorrectCguReuseType};
|
||||
use crate::Session;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg};
|
||||
use rustc_span::{Span, Symbol};
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::{self};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
|
||||
pub enum CguReuse {
|
||||
No,
|
||||
PreLto,
|
||||
PostLto,
|
||||
}
|
||||
|
||||
impl fmt::Display for CguReuse {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
CguReuse::No => write!(f, "No"),
|
||||
CguReuse::PreLto => write!(f, "PreLto "),
|
||||
CguReuse::PostLto => write!(f, "PostLto "),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoDiagnosticArg for CguReuse {
|
||||
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
||||
DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum ComparisonKind {
|
||||
Exact,
|
||||
AtLeast,
|
||||
}
|
||||
|
||||
struct TrackerData {
|
||||
actual_reuse: FxHashMap<String, CguReuse>,
|
||||
expected_reuse: FxHashMap<String, (String, SendSpan, CguReuse, ComparisonKind)>,
|
||||
}
|
||||
|
||||
// Span does not implement `Send`, so we can't just store it in the shared
|
||||
// `TrackerData` object. Instead of splitting up `TrackerData` into shared and
|
||||
// non-shared parts (which would be complicated), we just mark the `Span` here
|
||||
// explicitly as `Send`. That's safe because the span data here is only ever
|
||||
// accessed from the main thread.
|
||||
struct SendSpan(Span);
|
||||
unsafe impl Send for SendSpan {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CguReuseTracker {
|
||||
data: Option<Arc<Mutex<TrackerData>>>,
|
||||
}
|
||||
|
||||
impl CguReuseTracker {
|
||||
pub fn new() -> CguReuseTracker {
|
||||
let data =
|
||||
TrackerData { actual_reuse: Default::default(), expected_reuse: Default::default() };
|
||||
|
||||
CguReuseTracker { data: Some(Arc::new(Mutex::new(data))) }
|
||||
}
|
||||
|
||||
pub fn new_disabled() -> CguReuseTracker {
|
||||
CguReuseTracker { data: None }
|
||||
}
|
||||
|
||||
pub fn set_actual_reuse(&self, cgu_name: &str, kind: CguReuse) {
|
||||
if let Some(ref data) = self.data {
|
||||
debug!("set_actual_reuse({cgu_name:?}, {kind:?})");
|
||||
|
||||
let prev_reuse = data.lock().unwrap().actual_reuse.insert(cgu_name.to_string(), kind);
|
||||
|
||||
if let Some(prev_reuse) = prev_reuse {
|
||||
// The only time it is legal to overwrite reuse state is when
|
||||
// we discover during ThinLTO that we can actually reuse the
|
||||
// post-LTO version of a CGU.
|
||||
assert_eq!(prev_reuse, CguReuse::PreLto);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_expectation(
|
||||
&self,
|
||||
cgu_name: Symbol,
|
||||
cgu_user_name: &str,
|
||||
error_span: Span,
|
||||
expected_reuse: CguReuse,
|
||||
comparison_kind: ComparisonKind,
|
||||
) {
|
||||
if let Some(ref data) = self.data {
|
||||
debug!("set_expectation({cgu_name:?}, {expected_reuse:?}, {comparison_kind:?})");
|
||||
let mut data = data.lock().unwrap();
|
||||
|
||||
data.expected_reuse.insert(
|
||||
cgu_name.to_string(),
|
||||
(cgu_user_name.to_string(), SendSpan(error_span), expected_reuse, comparison_kind),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_expected_reuse(&self, sess: &Session) {
|
||||
if let Some(ref data) = self.data {
|
||||
let data = data.lock().unwrap();
|
||||
|
||||
for (cgu_name, &(ref cgu_user_name, ref error_span, expected_reuse, comparison_kind)) in
|
||||
&data.expected_reuse
|
||||
{
|
||||
if let Some(&actual_reuse) = data.actual_reuse.get(cgu_name) {
|
||||
let (error, at_least) = match comparison_kind {
|
||||
ComparisonKind::Exact => (expected_reuse != actual_reuse, false),
|
||||
ComparisonKind::AtLeast => (actual_reuse < expected_reuse, true),
|
||||
};
|
||||
|
||||
if error {
|
||||
let at_least = if at_least { 1 } else { 0 };
|
||||
IncorrectCguReuseType {
|
||||
span: error_span.0,
|
||||
cgu_user_name,
|
||||
actual_reuse,
|
||||
expected_reuse,
|
||||
at_least,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
sess.emit_fatal(CguNotRecorded { cgu_user_name, cgu_name });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2925,8 +2925,8 @@ fn parse_pretty(handler: &EarlyErrorHandler, unstable_opts: &UnstableOptions) ->
|
||||
"expanded" => Source(PpSourceMode::Expanded),
|
||||
"expanded,identified" => Source(PpSourceMode::ExpandedIdentified),
|
||||
"expanded,hygiene" => Source(PpSourceMode::ExpandedHygiene),
|
||||
"ast-tree" => AstTree(PpAstTreeMode::Normal),
|
||||
"ast-tree,expanded" => AstTree(PpAstTreeMode::Expanded),
|
||||
"ast-tree" => AstTree,
|
||||
"ast-tree,expanded" => AstTreeExpanded,
|
||||
"hir" => Hir(PpHirMode::Normal),
|
||||
"hir,identified" => Hir(PpHirMode::Identified),
|
||||
"hir,typed" => Hir(PpHirMode::Typed),
|
||||
@ -3083,14 +3083,6 @@ pub enum PpSourceMode {
|
||||
ExpandedHygiene,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub enum PpAstTreeMode {
|
||||
/// `-Zunpretty=ast`
|
||||
Normal,
|
||||
/// `-Zunpretty=ast,expanded`
|
||||
Expanded,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub enum PpHirMode {
|
||||
/// `-Zunpretty=hir`
|
||||
@ -3106,7 +3098,10 @@ pub enum PpMode {
|
||||
/// Options that print the source code, i.e.
|
||||
/// `-Zunpretty=normal` and `-Zunpretty=expanded`
|
||||
Source(PpSourceMode),
|
||||
AstTree(PpAstTreeMode),
|
||||
/// `-Zunpretty=ast-tree`
|
||||
AstTree,
|
||||
/// `-Zunpretty=ast-tree,expanded`
|
||||
AstTreeExpanded,
|
||||
/// Options that print the HIR, i.e. `-Zunpretty=hir`
|
||||
Hir(PpHirMode),
|
||||
/// `-Zunpretty=hir-tree`
|
||||
@ -3126,10 +3121,10 @@ pub fn needs_ast_map(&self) -> bool {
|
||||
use PpMode::*;
|
||||
use PpSourceMode::*;
|
||||
match *self {
|
||||
Source(Normal | Identified) | AstTree(PpAstTreeMode::Normal) => false,
|
||||
Source(Normal | Identified) | AstTree => false,
|
||||
|
||||
Source(Expanded | ExpandedIdentified | ExpandedHygiene)
|
||||
| AstTree(PpAstTreeMode::Expanded)
|
||||
| AstTreeExpanded
|
||||
| Hir(_)
|
||||
| HirTree
|
||||
| ThirTree
|
||||
@ -3141,7 +3136,7 @@ pub fn needs_ast_map(&self) -> bool {
|
||||
pub fn needs_hir(&self) -> bool {
|
||||
use PpMode::*;
|
||||
match *self {
|
||||
Source(_) | AstTree(_) => false,
|
||||
Source(_) | AstTree | AstTreeExpanded => false,
|
||||
|
||||
Hir(_) | HirTree | ThirTree | ThirFlat | Mir | MirCFG => true,
|
||||
}
|
||||
@ -3149,7 +3144,7 @@ pub fn needs_hir(&self) -> bool {
|
||||
|
||||
pub fn needs_analysis(&self) -> bool {
|
||||
use PpMode::*;
|
||||
matches!(*self, Mir | MirCFG | ThirTree | ThirFlat)
|
||||
matches!(*self, Hir(PpHirMode::Typed) | Mir | MirCFG | ThirTree | ThirFlat)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
use crate::cgu_reuse_tracker::CguReuse;
|
||||
use crate::parse::ParseSess;
|
||||
use rustc_ast::token;
|
||||
use rustc_ast::util::literal::LitError;
|
||||
@ -9,24 +8,6 @@
|
||||
use rustc_span::{BytePos, Span, Symbol};
|
||||
use rustc_target::spec::{SplitDebuginfo, StackProtector, TargetTriple};
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(session_incorrect_cgu_reuse_type)]
|
||||
pub struct IncorrectCguReuseType<'a> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub cgu_user_name: &'a str,
|
||||
pub actual_reuse: CguReuse,
|
||||
pub expected_reuse: CguReuse,
|
||||
pub at_least: u8,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(session_cgu_not_recorded)]
|
||||
pub struct CguNotRecorded<'a> {
|
||||
pub cgu_user_name: &'a str,
|
||||
pub cgu_name: &'a str,
|
||||
}
|
||||
|
||||
pub struct FeatureGateError {
|
||||
pub span: MultiSpan,
|
||||
pub explain: DiagnosticMessage,
|
||||
|
@ -23,7 +23,6 @@
|
||||
use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
|
||||
use rustc_fluent_macro::fluent_messages;
|
||||
|
||||
pub mod cgu_reuse_tracker;
|
||||
pub mod utils;
|
||||
pub use lint::{declare_lint, declare_lint_pass, declare_tool_lint, impl_lint_pass};
|
||||
pub use rustc_lint_defs as lint;
|
||||
|
@ -1,4 +1,3 @@
|
||||
use crate::cgu_reuse_tracker::CguReuseTracker;
|
||||
use crate::code_stats::CodeStats;
|
||||
pub use crate::code_stats::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo};
|
||||
use crate::config::{
|
||||
@ -153,9 +152,6 @@ pub struct Session {
|
||||
pub io: CompilerIO,
|
||||
|
||||
incr_comp_session: OneThread<RefCell<IncrCompSession>>,
|
||||
/// Used for incremental compilation tests. Will only be populated if
|
||||
/// `-Zquery-dep-graph` is specified.
|
||||
pub cgu_reuse_tracker: CguReuseTracker,
|
||||
|
||||
/// Used by `-Z self-profile`.
|
||||
pub prof: SelfProfilerRef,
|
||||
@ -1431,12 +1427,6 @@ pub fn build_session(
|
||||
});
|
||||
let print_fuel = AtomicU64::new(0);
|
||||
|
||||
let cgu_reuse_tracker = if sopts.unstable_opts.query_dep_graph {
|
||||
CguReuseTracker::new()
|
||||
} else {
|
||||
CguReuseTracker::new_disabled()
|
||||
};
|
||||
|
||||
let prof = SelfProfilerRef::new(
|
||||
self_profiler,
|
||||
sopts.unstable_opts.time_passes.then(|| sopts.unstable_opts.time_passes_format),
|
||||
@ -1461,7 +1451,6 @@ pub fn build_session(
|
||||
sysroot,
|
||||
io,
|
||||
incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)),
|
||||
cgu_reuse_tracker,
|
||||
prof,
|
||||
perf_stats: PerfStats {
|
||||
symbol_hash_time: Lock::new(Duration::from_secs(0)),
|
||||
|
@ -1,6 +1,6 @@
|
||||
use super::{ObligationCauseCode, PredicateObligation};
|
||||
use crate::infer::error_reporting::TypeErrCtxt;
|
||||
use rustc_ast::{MetaItem, NestedMetaItem};
|
||||
use rustc_ast::{Attribute, MetaItem, NestedMetaItem};
|
||||
use rustc_attr as attr;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::{struct_span_err, ErrorGuaranteed};
|
||||
@ -474,18 +474,40 @@ fn parse(
|
||||
}
|
||||
|
||||
pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<Option<Self>, ErrorGuaranteed> {
|
||||
let mut is_diagnostic_namespace_variant = false;
|
||||
let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented).or_else(|| {
|
||||
if tcx.features().diagnostic_namespace {
|
||||
is_diagnostic_namespace_variant = true;
|
||||
tcx.get_attrs_by_path(item_def_id, &[sym::diagnostic, sym::on_unimplemented]).next()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) else {
|
||||
return Ok(None);
|
||||
};
|
||||
if let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) {
|
||||
return Self::parse_attribute(attr, false, tcx, item_def_id);
|
||||
} else if tcx.features().diagnostic_namespace {
|
||||
tcx.get_attrs_by_path(item_def_id, &[sym::diagnostic, sym::on_unimplemented])
|
||||
.filter_map(|attr| Self::parse_attribute(attr, true, tcx, item_def_id).transpose())
|
||||
.try_fold(None, |aggr: Option<Self>, directive| {
|
||||
let directive = directive?;
|
||||
if let Some(aggr) = aggr {
|
||||
let mut subcommands = aggr.subcommands;
|
||||
subcommands.extend(directive.subcommands);
|
||||
Ok(Some(Self {
|
||||
condition: aggr.condition.or(directive.condition),
|
||||
subcommands,
|
||||
message: aggr.message.or(directive.message),
|
||||
label: aggr.label.or(directive.label),
|
||||
note: aggr.note.or(directive.note),
|
||||
parent_label: aggr.parent_label.or(directive.parent_label),
|
||||
append_const_msg: aggr.append_const_msg.or(directive.append_const_msg),
|
||||
}))
|
||||
} else {
|
||||
Ok(Some(directive))
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_attribute(
|
||||
attr: &Attribute,
|
||||
is_diagnostic_namespace_variant: bool,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
item_def_id: DefId,
|
||||
) -> Result<Option<Self>, ErrorGuaranteed> {
|
||||
let result = if let Some(items) = attr.meta_item_list() {
|
||||
Self::parse(tcx, item_def_id, &items, attr.span, true, is_diagnostic_namespace_variant)
|
||||
} else if let Some(value) = attr.value_str() {
|
||||
|
@ -686,7 +686,10 @@ pub const fn as_mut_ptr(&mut self) -> *mut T {
|
||||
/// // they both get dropped!
|
||||
/// ```
|
||||
#[stable(feature = "maybe_uninit_extra", since = "1.60.0")]
|
||||
#[rustc_const_unstable(feature = "const_maybe_uninit_assume_init_read", issue = "63567")]
|
||||
#[rustc_const_stable(
|
||||
feature = "const_maybe_uninit_assume_init_read",
|
||||
since = "CURRENT_RUSTC_VERSION"
|
||||
)]
|
||||
#[inline(always)]
|
||||
#[track_caller]
|
||||
pub const unsafe fn assume_init_read(&self) -> T {
|
||||
|
@ -319,7 +319,7 @@ pub const fn new(v: bool) -> AtomicBool {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(atomic_from_ptr, pointer_is_aligned)]
|
||||
/// #![feature(pointer_is_aligned)]
|
||||
/// use std::sync::atomic::{self, AtomicBool};
|
||||
/// use std::mem::align_of;
|
||||
///
|
||||
@ -346,13 +346,21 @@ pub const fn new(v: bool) -> AtomicBool {
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// * `ptr` must be aligned to `align_of::<AtomicBool>()` (note that on some platforms this can be bigger than `align_of::<bool>()`).
|
||||
/// * `ptr` must be aligned to `align_of::<AtomicBool>()` (note that on some platforms this can
|
||||
/// be bigger than `align_of::<bool>()`).
|
||||
/// * `ptr` must be [valid] for both reads and writes for the whole lifetime `'a`.
|
||||
/// * The value behind `ptr` must not be accessed through non-atomic operations for the whole lifetime `'a`.
|
||||
/// * Non-atomic accesses to the value behind `ptr` must have a happens-before relationship
|
||||
/// with atomic accesses via the returned value (or vice-versa).
|
||||
/// * In other words, time periods where the value is accessed atomically may not overlap
|
||||
/// with periods where the value is accessed non-atomically.
|
||||
/// * This requirement is trivially satisfied if `ptr` is never used non-atomically for the
|
||||
/// duration of lifetime `'a`. Most use cases should be able to follow this guideline.
|
||||
/// * This requirement is also trivially satisfied if all accesses (atomic or not) are done
|
||||
/// from the same thread.
|
||||
///
|
||||
/// [valid]: crate::ptr#safety
|
||||
#[unstable(feature = "atomic_from_ptr", issue = "108652")]
|
||||
#[rustc_const_unstable(feature = "atomic_from_ptr", issue = "108652")]
|
||||
#[stable(feature = "atomic_from_ptr", since = "CURRENT_RUSTC_VERSION")]
|
||||
#[rustc_const_unstable(feature = "const_atomic_from_ptr", issue = "108652")]
|
||||
pub const unsafe fn from_ptr<'a>(ptr: *mut bool) -> &'a AtomicBool {
|
||||
// SAFETY: guaranteed by the caller
|
||||
unsafe { &*ptr.cast() }
|
||||
@ -1113,7 +1121,7 @@ pub const fn new(p: *mut T) -> AtomicPtr<T> {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(atomic_from_ptr, pointer_is_aligned)]
|
||||
/// #![feature(pointer_is_aligned)]
|
||||
/// use std::sync::atomic::{self, AtomicPtr};
|
||||
/// use std::mem::align_of;
|
||||
///
|
||||
@ -1140,13 +1148,23 @@ pub const fn new(p: *mut T) -> AtomicPtr<T> {
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// * `ptr` must be aligned to `align_of::<AtomicPtr<T>>()` (note that on some platforms this can be bigger than `align_of::<*mut T>()`).
|
||||
/// * `ptr` must be aligned to `align_of::<AtomicPtr<T>>()` (note that on some platforms this
|
||||
/// can be bigger than `align_of::<*mut T>()`).
|
||||
/// * `ptr` must be [valid] for both reads and writes for the whole lifetime `'a`.
|
||||
/// * The value behind `ptr` must not be accessed through non-atomic operations for the whole lifetime `'a`.
|
||||
/// * Non-atomic accesses to the value behind `ptr` must have a happens-before relationship
|
||||
/// with atomic accesses via the returned value (or vice-versa).
|
||||
/// * In other words, time periods where the value is accessed atomically may not overlap
|
||||
/// with periods where the value is accessed non-atomically.
|
||||
/// * This requirement is trivially satisfied if `ptr` is never used non-atomically for the
|
||||
/// duration of lifetime `'a`. Most use cases should be able to follow this guideline.
|
||||
/// * This requirement is also trivially satisfied if all accesses (atomic or not) are done
|
||||
/// from the same thread.
|
||||
/// * This method should not be used to create overlapping or mixed-size atomic accesses, as
|
||||
/// these are not supported by the memory model.
|
||||
///
|
||||
/// [valid]: crate::ptr#safety
|
||||
#[unstable(feature = "atomic_from_ptr", issue = "108652")]
|
||||
#[rustc_const_unstable(feature = "atomic_from_ptr", issue = "108652")]
|
||||
#[stable(feature = "atomic_from_ptr", since = "CURRENT_RUSTC_VERSION")]
|
||||
#[rustc_const_unstable(feature = "const_atomic_from_ptr", issue = "108652")]
|
||||
pub const unsafe fn from_ptr<'a>(ptr: *mut *mut T) -> &'a AtomicPtr<T> {
|
||||
// SAFETY: guaranteed by the caller
|
||||
unsafe { &*ptr.cast() }
|
||||
@ -2083,7 +2101,7 @@ pub const fn new(v: $int_type) -> Self {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(atomic_from_ptr, pointer_is_aligned)]
|
||||
/// #![feature(pointer_is_aligned)]
|
||||
#[doc = concat!($extra_feature, "use std::sync::atomic::{self, ", stringify!($atomic_type), "};")]
|
||||
/// use std::mem::align_of;
|
||||
///
|
||||
@ -2111,14 +2129,25 @@ pub const fn new(v: $int_type) -> Self {
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// * `ptr` must be aligned to `align_of::<AtomicBool>()` (note that on some platforms this can be bigger than `align_of::<bool>()`).
|
||||
#[doc = concat!(" * `ptr` must be aligned to `align_of::<", stringify!($atomic_type), ">()` (note that on some platforms this can be bigger than `align_of::<", stringify!($int_type), ">()`).")]
|
||||
#[doc = concat!(" * `ptr` must be aligned to \
|
||||
`align_of::<", stringify!($atomic_type), ">()` (note that on some platforms this \
|
||||
can be bigger than `align_of::<", stringify!($int_type), ">()`).")]
|
||||
/// * `ptr` must be [valid] for both reads and writes for the whole lifetime `'a`.
|
||||
/// * The value behind `ptr` must not be accessed through non-atomic operations for the whole lifetime `'a`.
|
||||
/// * Non-atomic accesses to the value behind `ptr` must have a happens-before
|
||||
/// relationship with atomic accesses via the returned value (or vice-versa).
|
||||
/// * In other words, time periods where the value is accessed atomically may not
|
||||
/// overlap with periods where the value is accessed non-atomically.
|
||||
/// * This requirement is trivially satisfied if `ptr` is never used non-atomically
|
||||
/// for the duration of lifetime `'a`. Most use cases should be able to follow
|
||||
/// this guideline.
|
||||
/// * This requirement is also trivially satisfied if all accesses (atomic or not) are
|
||||
/// done from the same thread.
|
||||
/// * This method should not be used to create overlapping or mixed-size atomic
|
||||
/// accesses, as these are not supported by the memory model.
|
||||
///
|
||||
/// [valid]: crate::ptr#safety
|
||||
#[unstable(feature = "atomic_from_ptr", issue = "108652")]
|
||||
#[rustc_const_unstable(feature = "atomic_from_ptr", issue = "108652")]
|
||||
#[stable(feature = "atomic_from_ptr", since = "CURRENT_RUSTC_VERSION")]
|
||||
#[rustc_const_unstable(feature = "const_atomic_from_ptr", issue = "108652")]
|
||||
pub const unsafe fn from_ptr<'a>(ptr: *mut $int_type) -> &'a $atomic_type {
|
||||
// SAFETY: guaranteed by the caller
|
||||
unsafe { &*ptr.cast() }
|
||||
|
@ -15,7 +15,6 @@
|
||||
#![feature(const_hash)]
|
||||
#![feature(const_heap)]
|
||||
#![feature(const_maybe_uninit_as_mut_ptr)]
|
||||
#![feature(const_maybe_uninit_assume_init_read)]
|
||||
#![feature(const_nonnull_new)]
|
||||
#![feature(const_pointer_byte_offsets)]
|
||||
#![feature(const_pointer_is_aligned)]
|
||||
|
@ -36,10 +36,6 @@ object = { version = "0.32.0", default-features = false, optional = true, featur
|
||||
rand = { version = "0.8.5", default-features = false, features = ["alloc"] }
|
||||
rand_xorshift = "0.3.0"
|
||||
|
||||
[build-dependencies]
|
||||
# Dependency of the `backtrace` crate's build script
|
||||
cc = "1.0.67"
|
||||
|
||||
[target.'cfg(any(all(target_family = "wasm", target_os = "unknown"), target_os = "xous", all(target_vendor = "fortanix", target_env = "sgx")))'.dependencies]
|
||||
dlmalloc = { version = "0.2.4", features = ['rustc-dep-of-std'] }
|
||||
|
||||
|
@ -1,11 +1,5 @@
|
||||
use std::env;
|
||||
|
||||
// backtrace-rs requires a feature check on Android targets, so
|
||||
// we need to run its build.rs as well.
|
||||
#[allow(unused_extern_crates)]
|
||||
#[path = "../backtrace/build.rs"]
|
||||
mod backtrace_build_rs;
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
let target = env::var("TARGET").expect("TARGET was not set");
|
||||
@ -65,6 +59,4 @@ fn main() {
|
||||
}
|
||||
println!("cargo:rustc-env=STD_ENV_ARCH={}", env::var("CARGO_CFG_TARGET_ARCH").unwrap());
|
||||
println!("cargo:rustc-cfg=backtrace_in_libstd");
|
||||
|
||||
backtrace_build_rs::main();
|
||||
}
|
||||
|
@ -159,3 +159,36 @@ fn test_program_kind() {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Test that Rust std handles wait status values (`ExitStatus`) the way that Unix does,
|
||||
// at least for the values which represent a Unix exit status (`ExitCode`).
|
||||
// Should work on every #[cfg(unix)] platform. However:
|
||||
#[cfg(not(any(
|
||||
// Fuchsia is not Unix and has totally broken std::os::unix.
|
||||
// https://github.com/rust-lang/rust/issues/58590#issuecomment-836535609
|
||||
target_os = "fuchsia",
|
||||
)))]
|
||||
#[test]
|
||||
fn unix_exit_statuses() {
|
||||
use crate::num::NonZeroI32;
|
||||
use crate::os::unix::process::ExitStatusExt;
|
||||
use crate::process::*;
|
||||
|
||||
for exit_code in 0..=0xff {
|
||||
// FIXME impl From<ExitCode> for ExitStatus and then test that here too;
|
||||
// the two ExitStatus values should be the same
|
||||
let raw_wait_status = exit_code << 8;
|
||||
let exit_status = ExitStatus::from_raw(raw_wait_status);
|
||||
|
||||
assert_eq!(exit_status.code(), Some(exit_code));
|
||||
|
||||
if let Ok(nz) = NonZeroI32::try_from(exit_code) {
|
||||
assert!(!exit_status.success());
|
||||
let es_error = exit_status.exit_ok().unwrap_err();
|
||||
assert_eq!(es_error.code().unwrap(), i32::from(nz));
|
||||
} else {
|
||||
assert!(exit_status.success());
|
||||
assert_eq!(exit_status.exit_ok(), Ok(()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1074,3 +1074,8 @@ fn take_pidfd(&mut self) -> io::Result<PidFd> {
|
||||
#[cfg(test)]
|
||||
#[path = "process_unix/tests.rs"]
|
||||
mod tests;
|
||||
|
||||
// See [`process_unsupported_wait_status::compare_with_linux`];
|
||||
#[cfg(all(test, target_os = "linux"))]
|
||||
#[path = "process_unsupported/wait_status.rs"]
|
||||
mod process_unsupported_wait_status;
|
||||
|
@ -55,56 +55,8 @@ pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)]
|
||||
pub struct ExitStatus(c_int);
|
||||
|
||||
impl ExitStatus {
|
||||
#[cfg_attr(target_os = "horizon", allow(unused))]
|
||||
pub fn success(&self) -> bool {
|
||||
self.code() == Some(0)
|
||||
}
|
||||
|
||||
pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
|
||||
Err(ExitStatusError(1.try_into().unwrap()))
|
||||
}
|
||||
|
||||
pub fn code(&self) -> Option<i32> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn signal(&self) -> Option<i32> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn core_dumped(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn stopped_signal(&self) -> Option<i32> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn continued(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn into_raw(&self) -> c_int {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying.
|
||||
impl From<c_int> for ExitStatus {
|
||||
fn from(a: c_int) -> ExitStatus {
|
||||
ExitStatus(a as i32)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ExitStatus {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "exit code: {}", self.0)
|
||||
}
|
||||
}
|
||||
mod wait_status;
|
||||
pub use wait_status::ExitStatus;
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct ExitStatusError(NonZero_c_int);
|
||||
|
@ -0,0 +1,68 @@
|
||||
//! Emulated wait status for non-Unix #[cfg(unix) platforms
|
||||
//!
|
||||
//! Separate module to facilitate testing against a real Unix implementation.
|
||||
|
||||
use crate::ffi::c_int;
|
||||
use crate::fmt;
|
||||
|
||||
/// Emulated wait status for use by `process_unsupported.rs`
|
||||
///
|
||||
/// Uses the "traditional unix" encoding. For use on platfors which are `#[cfg(unix)]`
|
||||
/// but do not actually support subprocesses at all.
|
||||
///
|
||||
/// These platforms aren't Unix, but are simply pretending to be for porting convenience.
|
||||
/// So, we provide a faithful pretence here.
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)]
|
||||
pub struct ExitStatus {
|
||||
wait_status: c_int,
|
||||
}
|
||||
|
||||
/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it
|
||||
impl From<c_int> for ExitStatus {
|
||||
fn from(wait_status: c_int) -> ExitStatus {
|
||||
ExitStatus { wait_status }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ExitStatus {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "emulated wait status: {}", self.wait_status)
|
||||
}
|
||||
}
|
||||
|
||||
impl ExitStatus {
|
||||
pub fn code(&self) -> Option<i32> {
|
||||
// Linux and FreeBSD both agree that values linux 0x80
|
||||
// count as "WIFEXITED" even though this is quite mad.
|
||||
// Likewise the macros disregard all the high bits, so are happy to declare
|
||||
// out-of-range values to be WIFEXITED, WIFSTOPPED, etc.
|
||||
let w = self.wait_status;
|
||||
if (w & 0x7f) == 0 { Some((w & 0xff00) >> 8) } else { None }
|
||||
}
|
||||
|
||||
pub fn signal(&self) -> Option<i32> {
|
||||
let signal = self.wait_status & 0x007f;
|
||||
if signal > 0 && signal < 0x7f { Some(signal) } else { None }
|
||||
}
|
||||
|
||||
pub fn core_dumped(&self) -> bool {
|
||||
self.signal().is_some() && (self.wait_status & 0x80) != 0
|
||||
}
|
||||
|
||||
pub fn stopped_signal(&self) -> Option<i32> {
|
||||
let w = self.wait_status;
|
||||
if (w & 0xff) == 0x7f { Some((w & 0xff00) >> 8) } else { None }
|
||||
}
|
||||
|
||||
pub fn continued(&self) -> bool {
|
||||
self.wait_status == 0xffff
|
||||
}
|
||||
|
||||
pub fn into_raw(&self) -> c_int {
|
||||
self.wait_status
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "wait_status/tests.rs"] // needed because of strange layout of process_unsupported
|
||||
mod tests;
|
@ -0,0 +1,36 @@
|
||||
// Note that tests in this file are run on Linux as well as on platforms using process_unsupported
|
||||
|
||||
// Test that our emulation exactly matches Linux
|
||||
//
|
||||
// This test runs *on Linux* but it tests
|
||||
// the implementation used on non-Unix `#[cfg(unix)]` platforms.
|
||||
//
|
||||
// I.e. we're using Linux as a proxy for "trad unix".
|
||||
#[cfg(target_os = "linux")]
|
||||
#[test]
|
||||
fn compare_with_linux() {
|
||||
use super::ExitStatus as Emulated;
|
||||
use crate::os::unix::process::ExitStatusExt as _;
|
||||
use crate::process::ExitStatus as Real;
|
||||
|
||||
// Check that we handle out-of-range values similarly, too.
|
||||
for wstatus in -0xf_ffff..0xf_ffff {
|
||||
let emulated = Emulated::from(wstatus);
|
||||
let real = Real::from_raw(wstatus);
|
||||
|
||||
macro_rules! compare { { $method:ident } => {
|
||||
assert_eq!(
|
||||
emulated.$method(),
|
||||
real.$method(),
|
||||
"{wstatus:#x}.{}()",
|
||||
stringify!($method),
|
||||
);
|
||||
} }
|
||||
compare!(code);
|
||||
compare!(signal);
|
||||
compare!(core_dumped);
|
||||
compare!(stopped_signal);
|
||||
compare!(continued);
|
||||
compare!(into_raw);
|
||||
}
|
||||
}
|
@ -286,10 +286,6 @@ target | std | host | notes
|
||||
`mipsel-unknown-linux-musl` | ✓ | | MIPS (little endian) Linux with musl libc
|
||||
`mipsel-sony-psp` | * | | MIPS (LE) Sony PlayStation Portable (PSP)
|
||||
[`mipsel-sony-psx`](platform-support/mipsel-sony-psx.md) | * | | MIPS (LE) Sony PlayStation 1 (PSX)
|
||||
`mips-unknown-linux-gnu` | MIPS Linux (kernel 4.4, glibc 2.23)
|
||||
`mips64-unknown-linux-gnuabi64` | MIPS64 Linux, n64 ABI (kernel 4.4, glibc 2.23)
|
||||
`mips64el-unknown-linux-gnuabi64` | MIPS64 (LE) Linux, n64 ABI (kernel 4.4, glibc 2.23)
|
||||
`mipsel-unknown-linux-gnu` | MIPS (LE) Linux (kernel 4.4, glibc 2.23)
|
||||
`mipsel-unknown-linux-uclibc` | ✓ | | MIPS (LE) Linux with uClibc
|
||||
`mipsel-unknown-none` | * | | Bare MIPS (LE) softfloat
|
||||
[`mipsisa32r6-unknown-linux-gnu`](platform-support/mips-release-6.md) | ? | | 32-bit MIPS Release 6 Big Endian
|
||||
|
19
src/doc/unstable-book/src/compiler-flags/no-jump-tables.md
Normal file
19
src/doc/unstable-book/src/compiler-flags/no-jump-tables.md
Normal file
@ -0,0 +1,19 @@
|
||||
# `no-jump-tables`
|
||||
|
||||
The tracking issue for this feature is [#116592](https://github.com/rust-lang/rust/issues/116592)
|
||||
|
||||
---
|
||||
|
||||
This option enables the `-fno-jump-tables` flag for LLVM, which makes the
|
||||
codegen backend avoid generating jump tables when lowering switches.
|
||||
|
||||
This option adds the LLVM `no-jump-tables=true` attribute to every function.
|
||||
|
||||
The option can be used to help provide protection against
|
||||
jump-oriented-programming (JOP) attacks, such as with the linux kernel's [IBT].
|
||||
|
||||
```sh
|
||||
RUSTFLAGS="-Zno-jump-tables" cargo +nightly build -Z build-std
|
||||
```
|
||||
|
||||
[IBT]: https://www.phoronix.com/news/Linux-IBT-By-Default-Tip
|
@ -19,7 +19,7 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<'
|
||||
if is_type_diagnostic_item(cx, ex_ty, sym::Result) {
|
||||
for arm in arms {
|
||||
if let PatKind::TupleStruct(ref path, inner, _) = arm.pat.kind {
|
||||
let path_str = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false));
|
||||
let path_str = rustc_hir_pretty::qpath_to_string(path);
|
||||
if path_str == "Err" {
|
||||
let mut matching_wild = inner.iter().any(is_wild);
|
||||
let mut ident_bind_name = kw::Underscore;
|
||||
|
@ -49,7 +49,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
cx,
|
||||
arguments.iter().collect(),
|
||||
cx.typeck_results().expr_ty(fn_expr),
|
||||
&rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)),
|
||||
&rustc_hir_pretty::qpath_to_string(path),
|
||||
"function",
|
||||
);
|
||||
}
|
||||
|
@ -12,7 +12,6 @@
|
||||
use rand::SeedableRng;
|
||||
|
||||
use rustc_ast::ast::Mutability;
|
||||
use rustc_const_eval::const_eval::CheckAlignment;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
#[allow(unused)]
|
||||
use rustc_data_structures::static_assert_size;
|
||||
@ -885,12 +884,8 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
|
||||
const PANIC_ON_ALLOC_FAIL: bool = false;
|
||||
|
||||
#[inline(always)]
|
||||
fn enforce_alignment(ecx: &MiriInterpCx<'mir, 'tcx>) -> CheckAlignment {
|
||||
if ecx.machine.check_alignment == AlignmentCheck::None {
|
||||
CheckAlignment::No
|
||||
} else {
|
||||
CheckAlignment::Error
|
||||
}
|
||||
fn enforce_alignment(ecx: &MiriInterpCx<'mir, 'tcx>) -> bool {
|
||||
ecx.machine.check_alignment != AlignmentCheck::None
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@ -898,15 +893,6 @@ fn use_addr_for_alignment_check(ecx: &MiriInterpCx<'mir, 'tcx>) -> bool {
|
||||
ecx.machine.check_alignment == AlignmentCheck::Int
|
||||
}
|
||||
|
||||
fn alignment_check_failed(
|
||||
_ecx: &InterpCx<'mir, 'tcx, Self>,
|
||||
has: Align,
|
||||
required: Align,
|
||||
_check: CheckAlignment,
|
||||
) -> InterpResult<'tcx, ()> {
|
||||
throw_ub!(AlignmentCheckFailed { has, required })
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn enforce_validity(ecx: &MiriInterpCx<'mir, 'tcx>, _layout: TyAndLayout<'tcx>) -> bool {
|
||||
ecx.machine.validate
|
||||
|
20
tests/assembly/x86_64-array-pair-load-store-merge.rs
Normal file
20
tests/assembly/x86_64-array-pair-load-store-merge.rs
Normal file
@ -0,0 +1,20 @@
|
||||
// assembly-output: emit-asm
|
||||
// compile-flags: --crate-type=lib -O -C llvm-args=-x86-asm-syntax=intel
|
||||
// only-x86_64
|
||||
// ignore-sgx
|
||||
// ignore-macos (manipulates rsp too)
|
||||
|
||||
// Depending on various codegen choices, this might end up copying
|
||||
// a `<2 x i8>`, an `i16`, or two `i8`s.
|
||||
// Regardless of those choices, make sure the instructions use (2-byte) words.
|
||||
|
||||
// CHECK-LABEL: array_copy_2_elements:
|
||||
#[no_mangle]
|
||||
pub fn array_copy_2_elements(a: &[u8; 2], p: &mut [u8; 2]) {
|
||||
// CHECK-NOT: byte
|
||||
// CHECK-NOT: mov
|
||||
// CHECK: mov{{.+}}, word ptr
|
||||
// CHECK-NEXT: mov word ptr
|
||||
// CHECK-NEXT: ret
|
||||
*p = *a;
|
||||
}
|
@ -32,3 +32,25 @@
|
||||
// CHECK: store <4 x i8> %[[TEMP2]], ptr %p, align 1
|
||||
*p = *a;
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @array_copy_1_element
|
||||
#[no_mangle]
|
||||
pub fn array_copy_1_element(a: &[u8; 1], p: &mut [u8; 1]) {
|
||||
// CHECK: %[[LOCAL:.+]] = alloca [1 x i8], align 1
|
||||
// CHECK: %[[TEMP1:.+]] = load i8, ptr %a, align 1
|
||||
// CHECK: store i8 %[[TEMP1]], ptr %[[LOCAL]], align 1
|
||||
// CHECK: %[[TEMP2:.+]] = load i8, ptr %[[LOCAL]], align 1
|
||||
// CHECK: store i8 %[[TEMP2]], ptr %p, align 1
|
||||
*p = *a;
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @array_copy_2_elements
|
||||
#[no_mangle]
|
||||
pub fn array_copy_2_elements(a: &[u8; 2], p: &mut [u8; 2]) {
|
||||
// CHECK: %[[LOCAL:.+]] = alloca [2 x i8], align 1
|
||||
// CHECK: %[[TEMP1:.+]] = load <2 x i8>, ptr %a, align 1
|
||||
// CHECK: store <2 x i8> %[[TEMP1]], ptr %[[LOCAL]], align 1
|
||||
// CHECK: %[[TEMP2:.+]] = load <2 x i8>, ptr %[[LOCAL]], align 1
|
||||
// CHECK: store <2 x i8> %[[TEMP2]], ptr %p, align 1
|
||||
*p = *a;
|
||||
}
|
||||
|
33
tests/codegen/array-optimized.rs
Normal file
33
tests/codegen/array-optimized.rs
Normal file
@ -0,0 +1,33 @@
|
||||
// compile-flags: -O
|
||||
|
||||
#![crate_type = "lib"]
|
||||
|
||||
// CHECK-LABEL: @array_copy_1_element
|
||||
#[no_mangle]
|
||||
pub fn array_copy_1_element(a: &[u8; 1], p: &mut [u8; 1]) {
|
||||
// CHECK-NOT: alloca
|
||||
// CHECK: %[[TEMP:.+]] = load i8, ptr %a, align 1
|
||||
// CHECK: store i8 %[[TEMP]], ptr %p, align 1
|
||||
// CHECK: ret
|
||||
*p = *a;
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @array_copy_2_elements
|
||||
#[no_mangle]
|
||||
pub fn array_copy_2_elements(a: &[u8; 2], p: &mut [u8; 2]) {
|
||||
// CHECK-NOT: alloca
|
||||
// CHECK: %[[TEMP:.+]] = load <2 x i8>, ptr %a, align 1
|
||||
// CHECK: store <2 x i8> %[[TEMP]], ptr %p, align 1
|
||||
// CHECK: ret
|
||||
*p = *a;
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @array_copy_4_elements
|
||||
#[no_mangle]
|
||||
pub fn array_copy_4_elements(a: &[u8; 4], p: &mut [u8; 4]) {
|
||||
// CHECK-NOT: alloca
|
||||
// CHECK: %[[TEMP:.+]] = load <4 x i8>, ptr %a, align 1
|
||||
// CHECK: store <4 x i8> %[[TEMP]], ptr %p, align 1
|
||||
// CHECK: ret
|
||||
*p = *a;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
warning: unexpected `cfg` condition name
|
||||
warning: unexpected `cfg` condition name: `uniz`
|
||||
--> $DIR/check-cfg.rs:5:7
|
||||
|
|
||||
LL | #[cfg(uniz)]
|
||||
|
@ -1,4 +1,4 @@
|
||||
warning: unexpected `cfg` condition value
|
||||
warning: unexpected `cfg` condition value: `invalid`
|
||||
--> $DIR/check-cfg-test.rs:9:7
|
||||
|
|
||||
LL | #[cfg(feature = "invalid")]
|
||||
|
@ -1,9 +1,10 @@
|
||||
warning: unexpected `cfg` condition name
|
||||
warning: unexpected `cfg` condition name: `FALSE`
|
||||
--> $DIR/allow-same-level.rs:7:7
|
||||
|
|
||||
LL | #[cfg(FALSE)]
|
||||
| ^^^^^
|
||||
|
|
||||
= help: expected names are: `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `sanitize`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `unix`, `windows`
|
||||
= note: `#[warn(unexpected_cfgs)]` on by default
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
@ -1,9 +1,10 @@
|
||||
warning: unexpected `cfg` condition name
|
||||
warning: unexpected `cfg` condition name: `target_architecture`
|
||||
--> $DIR/compact-names.rs:11:28
|
||||
|
|
||||
LL | #[cfg(target(os = "linux", architecture = "arm"))]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: expected names are: `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `sanitize`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `unix`, `windows`
|
||||
= note: `#[warn(unexpected_cfgs)]` on by default
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
@ -1,4 +1,4 @@
|
||||
warning: unexpected `cfg` condition value
|
||||
warning: unexpected `cfg` condition value: `X`
|
||||
--> $DIR/compact-values.rs:11:28
|
||||
|
|
||||
LL | #[cfg(target(os = "linux", arch = "X"))]
|
||||
|
@ -1,4 +1,4 @@
|
||||
warning: unexpected `cfg` condition name
|
||||
warning: unexpected `cfg` condition name: `featur`
|
||||
--> $DIR/diagnotics.rs:4:7
|
||||
|
|
||||
LL | #[cfg(featur)]
|
||||
@ -7,19 +7,18 @@ LL | #[cfg(featur)]
|
||||
= help: expected values for `feature` are: `foo`
|
||||
= note: `#[warn(unexpected_cfgs)]` on by default
|
||||
|
||||
warning: unexpected `cfg` condition name
|
||||
warning: unexpected `cfg` condition name: `featur`
|
||||
--> $DIR/diagnotics.rs:8:7
|
||||
|
|
||||
LL | #[cfg(featur = "foo")]
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: expected values for `feature` are: `foo`
|
||||
help: there is a config with a similar name and value
|
||||
|
|
||||
LL | #[cfg(feature = "foo")]
|
||||
| ~~~~~~~
|
||||
|
||||
warning: unexpected `cfg` condition name
|
||||
warning: unexpected `cfg` condition name: `featur`
|
||||
--> $DIR/diagnotics.rs:12:7
|
||||
|
|
||||
LL | #[cfg(featur = "fo")]
|
||||
@ -31,13 +30,13 @@ help: there is a config with a similar name and different values
|
||||
LL | #[cfg(feature = "foo")]
|
||||
| ~~~~~~~~~~~~~~~
|
||||
|
||||
warning: unexpected `cfg` condition name
|
||||
warning: unexpected `cfg` condition name: `no_value`
|
||||
--> $DIR/diagnotics.rs:19:7
|
||||
|
|
||||
LL | #[cfg(no_value)]
|
||||
| ^^^^^^^^ help: there is a config with a similar name: `no_values`
|
||||
|
||||
warning: unexpected `cfg` condition name
|
||||
warning: unexpected `cfg` condition name: `no_value`
|
||||
--> $DIR/diagnotics.rs:23:7
|
||||
|
|
||||
LL | #[cfg(no_value = "foo")]
|
||||
@ -48,7 +47,7 @@ help: there is a config with a similar name and no value
|
||||
LL | #[cfg(no_values)]
|
||||
| ~~~~~~~~~
|
||||
|
||||
warning: unexpected `cfg` condition value
|
||||
warning: unexpected `cfg` condition value: `bar`
|
||||
--> $DIR/diagnotics.rs:27:7
|
||||
|
|
||||
LL | #[cfg(no_values = "bar")]
|
||||
|
@ -1,9 +1,10 @@
|
||||
warning: unexpected `cfg` condition name
|
||||
warning: unexpected `cfg` condition name: `unknown_key`
|
||||
--> $DIR/empty-names.rs:6:7
|
||||
|
|
||||
LL | #[cfg(unknown_key = "value")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: expected names are: `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `sanitize`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `unix`, `windows`
|
||||
= note: `#[warn(unexpected_cfgs)]` on by default
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
@ -1,4 +1,4 @@
|
||||
warning: unexpected `cfg` condition value
|
||||
warning: unexpected `cfg` condition value: `value`
|
||||
--> $DIR/empty-values.rs:6:7
|
||||
|
|
||||
LL | #[cfg(test = "value")]
|
||||
|
@ -1,4 +1,4 @@
|
||||
warning: unexpected `cfg` condition name
|
||||
warning: unexpected `cfg` condition name: `widnows`
|
||||
--> $DIR/invalid-cfg-name.rs:7:7
|
||||
|
|
||||
LL | #[cfg(widnows)]
|
||||
|
@ -1,4 +1,4 @@
|
||||
warning: unexpected `cfg` condition value
|
||||
warning: unexpected `cfg` condition value: `sedre`
|
||||
--> $DIR/invalid-cfg-value.rs:7:7
|
||||
|
|
||||
LL | #[cfg(feature = "sedre")]
|
||||
@ -9,7 +9,7 @@ LL | #[cfg(feature = "sedre")]
|
||||
= note: expected values for `feature` are: `full`, `serde`
|
||||
= note: `#[warn(unexpected_cfgs)]` on by default
|
||||
|
||||
warning: unexpected `cfg` condition value
|
||||
warning: unexpected `cfg` condition value: `rand`
|
||||
--> $DIR/invalid-cfg-value.rs:14:7
|
||||
|
|
||||
LL | #[cfg(feature = "rand")]
|
||||
|
@ -1,4 +1,4 @@
|
||||
warning: unexpected `cfg` condition name
|
||||
warning: unexpected `cfg` condition name: `widnows`
|
||||
--> $DIR/mix.rs:11:7
|
||||
|
|
||||
LL | #[cfg(widnows)]
|
||||
@ -6,7 +6,7 @@ LL | #[cfg(widnows)]
|
||||
|
|
||||
= note: `#[warn(unexpected_cfgs)]` on by default
|
||||
|
||||
warning: unexpected `cfg` condition value
|
||||
warning: unexpected `cfg` condition value: (none)
|
||||
--> $DIR/mix.rs:15:7
|
||||
|
|
||||
LL | #[cfg(feature)]
|
||||
@ -14,7 +14,7 @@ LL | #[cfg(feature)]
|
||||
|
|
||||
= note: expected values for `feature` are: `foo`
|
||||
|
||||
warning: unexpected `cfg` condition value
|
||||
warning: unexpected `cfg` condition value: `bar`
|
||||
--> $DIR/mix.rs:22:7
|
||||
|
|
||||
LL | #[cfg(feature = "bar")]
|
||||
@ -22,7 +22,7 @@ LL | #[cfg(feature = "bar")]
|
||||
|
|
||||
= note: expected values for `feature` are: `foo`
|
||||
|
||||
warning: unexpected `cfg` condition value
|
||||
warning: unexpected `cfg` condition value: `zebra`
|
||||
--> $DIR/mix.rs:26:7
|
||||
|
|
||||
LL | #[cfg(feature = "zebra")]
|
||||
@ -30,11 +30,13 @@ LL | #[cfg(feature = "zebra")]
|
||||
|
|
||||
= note: expected values for `feature` are: `foo`
|
||||
|
||||
warning: unexpected `cfg` condition name
|
||||
warning: unexpected `cfg` condition name: `uu`
|
||||
--> $DIR/mix.rs:30:12
|
||||
|
|
||||
LL | #[cfg_attr(uu, test)]
|
||||
| ^^
|
||||
|
|
||||
= help: expected names are: `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `sanitize`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `unix`, `windows`
|
||||
|
||||
warning: unexpected condition value `bar` for condition name `feature`
|
||||
|
|
||||
@ -44,13 +46,13 @@ warning: unexpected `unknown_name` as condition name
|
||||
|
|
||||
= help: was set with `--cfg` but isn't in the `--check-cfg` expected names
|
||||
|
||||
warning: unexpected `cfg` condition name
|
||||
warning: unexpected `cfg` condition name: `widnows`
|
||||
--> $DIR/mix.rs:39:10
|
||||
|
|
||||
LL | cfg!(widnows);
|
||||
| ^^^^^^^ help: there is a config with a similar name: `windows`
|
||||
|
||||
warning: unexpected `cfg` condition value
|
||||
warning: unexpected `cfg` condition value: `bar`
|
||||
--> $DIR/mix.rs:42:10
|
||||
|
|
||||
LL | cfg!(feature = "bar");
|
||||
@ -58,7 +60,7 @@ LL | cfg!(feature = "bar");
|
||||
|
|
||||
= note: expected values for `feature` are: `foo`
|
||||
|
||||
warning: unexpected `cfg` condition value
|
||||
warning: unexpected `cfg` condition value: `zebra`
|
||||
--> $DIR/mix.rs:44:10
|
||||
|
|
||||
LL | cfg!(feature = "zebra");
|
||||
@ -66,25 +68,25 @@ LL | cfg!(feature = "zebra");
|
||||
|
|
||||
= note: expected values for `feature` are: `foo`
|
||||
|
||||
warning: unexpected `cfg` condition name
|
||||
warning: unexpected `cfg` condition name: `xxx`
|
||||
--> $DIR/mix.rs:46:10
|
||||
|
|
||||
LL | cfg!(xxx = "foo");
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
warning: unexpected `cfg` condition name
|
||||
warning: unexpected `cfg` condition name: `xxx`
|
||||
--> $DIR/mix.rs:48:10
|
||||
|
|
||||
LL | cfg!(xxx);
|
||||
| ^^^
|
||||
|
||||
warning: unexpected `cfg` condition name
|
||||
warning: unexpected `cfg` condition name: `xxx`
|
||||
--> $DIR/mix.rs:50:14
|
||||
|
|
||||
LL | cfg!(any(xxx, windows));
|
||||
| ^^^
|
||||
|
||||
warning: unexpected `cfg` condition value
|
||||
warning: unexpected `cfg` condition value: `bad`
|
||||
--> $DIR/mix.rs:52:14
|
||||
|
|
||||
LL | cfg!(any(feature = "bad", windows));
|
||||
@ -92,43 +94,43 @@ LL | cfg!(any(feature = "bad", windows));
|
||||
|
|
||||
= note: expected values for `feature` are: `foo`
|
||||
|
||||
warning: unexpected `cfg` condition name
|
||||
warning: unexpected `cfg` condition name: `xxx`
|
||||
--> $DIR/mix.rs:54:23
|
||||
|
|
||||
LL | cfg!(any(windows, xxx));
|
||||
| ^^^
|
||||
|
||||
warning: unexpected `cfg` condition name
|
||||
warning: unexpected `cfg` condition name: `xxx`
|
||||
--> $DIR/mix.rs:56:20
|
||||
|
|
||||
LL | cfg!(all(unix, xxx));
|
||||
| ^^^
|
||||
|
||||
warning: unexpected `cfg` condition name
|
||||
warning: unexpected `cfg` condition name: `aa`
|
||||
--> $DIR/mix.rs:58:14
|
||||
|
|
||||
LL | cfg!(all(aa, bb));
|
||||
| ^^
|
||||
|
||||
warning: unexpected `cfg` condition name
|
||||
warning: unexpected `cfg` condition name: `bb`
|
||||
--> $DIR/mix.rs:58:18
|
||||
|
|
||||
LL | cfg!(all(aa, bb));
|
||||
| ^^
|
||||
|
||||
warning: unexpected `cfg` condition name
|
||||
warning: unexpected `cfg` condition name: `aa`
|
||||
--> $DIR/mix.rs:61:14
|
||||
|
|
||||
LL | cfg!(any(aa, bb));
|
||||
| ^^
|
||||
|
||||
warning: unexpected `cfg` condition name
|
||||
warning: unexpected `cfg` condition name: `bb`
|
||||
--> $DIR/mix.rs:61:18
|
||||
|
|
||||
LL | cfg!(any(aa, bb));
|
||||
| ^^
|
||||
|
||||
warning: unexpected `cfg` condition value
|
||||
warning: unexpected `cfg` condition value: `zebra`
|
||||
--> $DIR/mix.rs:64:20
|
||||
|
|
||||
LL | cfg!(any(unix, feature = "zebra"));
|
||||
@ -136,13 +138,13 @@ LL | cfg!(any(unix, feature = "zebra"));
|
||||
|
|
||||
= note: expected values for `feature` are: `foo`
|
||||
|
||||
warning: unexpected `cfg` condition name
|
||||
warning: unexpected `cfg` condition name: `xxx`
|
||||
--> $DIR/mix.rs:66:14
|
||||
|
|
||||
LL | cfg!(any(xxx, feature = "zebra"));
|
||||
| ^^^
|
||||
|
||||
warning: unexpected `cfg` condition value
|
||||
warning: unexpected `cfg` condition value: `zebra`
|
||||
--> $DIR/mix.rs:66:19
|
||||
|
|
||||
LL | cfg!(any(xxx, feature = "zebra"));
|
||||
@ -150,19 +152,19 @@ LL | cfg!(any(xxx, feature = "zebra"));
|
||||
|
|
||||
= note: expected values for `feature` are: `foo`
|
||||
|
||||
warning: unexpected `cfg` condition name
|
||||
warning: unexpected `cfg` condition name: `xxx`
|
||||
--> $DIR/mix.rs:69:14
|
||||
|
|
||||
LL | cfg!(any(xxx, unix, xxx));
|
||||
| ^^^
|
||||
|
||||
warning: unexpected `cfg` condition name
|
||||
warning: unexpected `cfg` condition name: `xxx`
|
||||
--> $DIR/mix.rs:69:25
|
||||
|
|
||||
LL | cfg!(any(xxx, unix, xxx));
|
||||
| ^^^
|
||||
|
||||
warning: unexpected `cfg` condition value
|
||||
warning: unexpected `cfg` condition value: `zebra`
|
||||
--> $DIR/mix.rs:72:14
|
||||
|
|
||||
LL | cfg!(all(feature = "zebra", feature = "zebra", feature = "zebra"));
|
||||
@ -170,7 +172,7 @@ LL | cfg!(all(feature = "zebra", feature = "zebra", feature = "zebra"));
|
||||
|
|
||||
= note: expected values for `feature` are: `foo`
|
||||
|
||||
warning: unexpected `cfg` condition value
|
||||
warning: unexpected `cfg` condition value: `zebra`
|
||||
--> $DIR/mix.rs:72:33
|
||||
|
|
||||
LL | cfg!(all(feature = "zebra", feature = "zebra", feature = "zebra"));
|
||||
@ -178,7 +180,7 @@ LL | cfg!(all(feature = "zebra", feature = "zebra", feature = "zebra"));
|
||||
|
|
||||
= note: expected values for `feature` are: `foo`
|
||||
|
||||
warning: unexpected `cfg` condition value
|
||||
warning: unexpected `cfg` condition value: `zebra`
|
||||
--> $DIR/mix.rs:72:52
|
||||
|
|
||||
LL | cfg!(all(feature = "zebra", feature = "zebra", feature = "zebra"));
|
||||
|
@ -1,4 +1,4 @@
|
||||
warning: unexpected `cfg` condition value
|
||||
warning: unexpected `cfg` condition value: `foo`
|
||||
--> $DIR/no-values.rs:6:7
|
||||
|
|
||||
LL | #[cfg(feature = "foo")]
|
||||
@ -9,7 +9,7 @@ LL | #[cfg(feature = "foo")]
|
||||
= note: no expected value for `feature`
|
||||
= note: `#[warn(unexpected_cfgs)]` on by default
|
||||
|
||||
warning: unexpected `cfg` condition value
|
||||
warning: unexpected `cfg` condition value: `foo`
|
||||
--> $DIR/no-values.rs:10:7
|
||||
|
|
||||
LL | #[cfg(test = "foo")]
|
||||
|
@ -1,4 +1,4 @@
|
||||
warning: unexpected `cfg` condition value
|
||||
warning: unexpected `cfg` condition value: (none)
|
||||
--> $DIR/order-independant.rs:8:7
|
||||
|
|
||||
LL | #[cfg(a)]
|
||||
@ -7,7 +7,7 @@ LL | #[cfg(a)]
|
||||
= note: expected values for `a` are: `b`
|
||||
= note: `#[warn(unexpected_cfgs)]` on by default
|
||||
|
||||
warning: unexpected `cfg` condition value
|
||||
warning: unexpected `cfg` condition value: `unk`
|
||||
--> $DIR/order-independant.rs:12:7
|
||||
|
|
||||
LL | #[cfg(a = "unk")]
|
||||
|
@ -1,4 +1,4 @@
|
||||
warning: unexpected `cfg` condition value
|
||||
warning: unexpected `cfg` condition value: (none)
|
||||
--> $DIR/order-independant.rs:8:7
|
||||
|
|
||||
LL | #[cfg(a)]
|
||||
@ -7,7 +7,7 @@ LL | #[cfg(a)]
|
||||
= note: expected values for `a` are: `b`
|
||||
= note: `#[warn(unexpected_cfgs)]` on by default
|
||||
|
||||
warning: unexpected `cfg` condition value
|
||||
warning: unexpected `cfg` condition value: `unk`
|
||||
--> $DIR/order-independant.rs:12:7
|
||||
|
|
||||
LL | #[cfg(a = "unk")]
|
||||
|
@ -1,9 +1,10 @@
|
||||
warning: unexpected `cfg` condition name
|
||||
warning: unexpected `cfg` condition name: `crossbeam_loom`
|
||||
--> $DIR/stmt-no-ice.rs:7:11
|
||||
|
|
||||
LL | #[cfg(crossbeam_loom)]
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: expected names are: `debug_assertions`, `doc`, `doctest`, `feature`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `sanitize`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `unix`, `windows`
|
||||
= note: `#[warn(unexpected_cfgs)]` on by default
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
@ -1,4 +1,4 @@
|
||||
warning: unexpected `cfg` condition value
|
||||
warning: unexpected `cfg` condition value: `linuz`
|
||||
--> $DIR/values-target-json.rs:13:7
|
||||
|
|
||||
LL | #[cfg(target_os = "linuz")]
|
||||
|
@ -1,4 +1,4 @@
|
||||
warning: unexpected `cfg` condition name
|
||||
warning: unexpected `cfg` condition name: `target_oz`
|
||||
--> $DIR/well-known-names.rs:6:7
|
||||
|
|
||||
LL | #[cfg(target_oz = "linux")]
|
||||
@ -8,7 +8,7 @@ LL | #[cfg(target_oz = "linux")]
|
||||
|
|
||||
= note: `#[warn(unexpected_cfgs)]` on by default
|
||||
|
||||
warning: unexpected `cfg` condition name
|
||||
warning: unexpected `cfg` condition name: `features`
|
||||
--> $DIR/well-known-names.rs:13:7
|
||||
|
|
||||
LL | #[cfg(features = "foo")]
|
||||
@ -16,7 +16,7 @@ LL | #[cfg(features = "foo")]
|
||||
| |
|
||||
| help: there is a config with a similar name: `feature`
|
||||
|
||||
warning: unexpected `cfg` condition name
|
||||
warning: unexpected `cfg` condition name: `uniw`
|
||||
--> $DIR/well-known-names.rs:20:7
|
||||
|
|
||||
LL | #[cfg(uniw)]
|
||||
|
@ -1,4 +1,4 @@
|
||||
warning: unexpected `cfg` condition value
|
||||
warning: unexpected `cfg` condition value: `linuz`
|
||||
--> $DIR/well-known-values.rs:7:7
|
||||
|
|
||||
LL | #[cfg(target_os = "linuz")]
|
||||
@ -9,7 +9,7 @@ LL | #[cfg(target_os = "linuz")]
|
||||
= note: expected values for `target_os` are: `aix`, `android`, `cuda`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `macos`, `netbsd`, `none`, `nto`, `openbsd`, `psp`, `redox`, `solaris`, `solid_asp3`, `teeos`, `tvos`, `uefi`, `unknown`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`
|
||||
= note: `#[warn(unexpected_cfgs)]` on by default
|
||||
|
||||
warning: unexpected `cfg` condition value
|
||||
warning: unexpected `cfg` condition value: `0`
|
||||
--> $DIR/well-known-values.rs:14:7
|
||||
|
|
||||
LL | #[cfg(target_has_atomic = "0")]
|
||||
@ -19,7 +19,7 @@ LL | #[cfg(target_has_atomic = "0")]
|
||||
|
|
||||
= note: expected values for `target_has_atomic` are: (none), `128`, `16`, `32`, `64`, `8`, `ptr`
|
||||
|
||||
warning: unexpected `cfg` condition value
|
||||
warning: unexpected `cfg` condition value: `aa`
|
||||
--> $DIR/well-known-values.rs:21:7
|
||||
|
|
||||
LL | #[cfg(unix = "aa")]
|
||||
@ -29,7 +29,7 @@ LL | #[cfg(unix = "aa")]
|
||||
|
|
||||
= note: no expected value for `unix`
|
||||
|
||||
warning: unexpected `cfg` condition value
|
||||
warning: unexpected `cfg` condition value: `miri`
|
||||
--> $DIR/well-known-values.rs:28:7
|
||||
|
|
||||
LL | #[cfg(miri = "miri")]
|
||||
@ -39,7 +39,7 @@ LL | #[cfg(miri = "miri")]
|
||||
|
|
||||
= note: no expected value for `miri`
|
||||
|
||||
warning: unexpected `cfg` condition value
|
||||
warning: unexpected `cfg` condition value: `linux`
|
||||
--> $DIR/well-known-values.rs:35:7
|
||||
|
|
||||
LL | #[cfg(doc = "linux")]
|
||||
|
36
tests/ui/consts/const-eval/raw-pointer-ub.rs
Normal file
36
tests/ui/consts/const-eval/raw-pointer-ub.rs
Normal file
@ -0,0 +1,36 @@
|
||||
// normalize-stderr-test "alloc\d+" -> "allocN"
|
||||
#![feature(const_pointer_byte_offsets)]
|
||||
#![feature(pointer_byte_offsets)]
|
||||
#![feature(const_mut_refs)]
|
||||
|
||||
const MISALIGNED_LOAD: () = unsafe {
|
||||
let mem = [0u32; 8];
|
||||
let ptr = mem.as_ptr().byte_add(1);
|
||||
let _val = *ptr; //~ERROR: evaluation of constant value failed
|
||||
//~^NOTE: accessing memory with alignment 1, but alignment 4 is required
|
||||
};
|
||||
|
||||
const MISALIGNED_STORE: () = unsafe {
|
||||
let mut mem = [0u32; 8];
|
||||
let ptr = mem.as_mut_ptr().byte_add(1);
|
||||
*ptr = 0; //~ERROR: evaluation of constant value failed
|
||||
//~^NOTE: accessing memory with alignment 1, but alignment 4 is required
|
||||
};
|
||||
|
||||
const MISALIGNED_COPY: () = unsafe {
|
||||
let x = &[0_u8; 4];
|
||||
let y = x.as_ptr().cast::<u32>();
|
||||
let mut z = 123;
|
||||
y.copy_to_nonoverlapping(&mut z, 1);
|
||||
//~^NOTE
|
||||
// The actual error points into the implementation of `copy_to_nonoverlapping`.
|
||||
};
|
||||
|
||||
const OOB: () = unsafe {
|
||||
let mem = [0u32; 1];
|
||||
let ptr = mem.as_ptr().cast::<u64>();
|
||||
let _val = *ptr; //~ERROR: evaluation of constant value failed
|
||||
//~^NOTE: size 4, so pointer to 8 bytes starting at offset 0 is out-of-bounds
|
||||
};
|
||||
|
||||
fn main() {}
|
36
tests/ui/consts/const-eval/raw-pointer-ub.stderr
Normal file
36
tests/ui/consts/const-eval/raw-pointer-ub.stderr
Normal file
@ -0,0 +1,36 @@
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/raw-pointer-ub.rs:9:16
|
||||
|
|
||||
LL | let _val = *ptr;
|
||||
| ^^^^ accessing memory with alignment 1, but alignment 4 is required
|
||||
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/raw-pointer-ub.rs:16:5
|
||||
|
|
||||
LL | *ptr = 0;
|
||||
| ^^^^^^^^ accessing memory with alignment 1, but alignment 4 is required
|
||||
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $SRC_DIR/core/src/intrinsics.rs:LL:COL
|
||||
|
|
||||
= note: accessing memory with alignment 1, but alignment 4 is required
|
||||
|
|
||||
note: inside `copy_nonoverlapping::<u32>`
|
||||
--> $SRC_DIR/core/src/intrinsics.rs:LL:COL
|
||||
note: inside `ptr::const_ptr::<impl *const u32>::copy_to_nonoverlapping`
|
||||
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
||||
note: inside `MISALIGNED_COPY`
|
||||
--> $DIR/raw-pointer-ub.rs:24:5
|
||||
|
|
||||
LL | y.copy_to_nonoverlapping(&mut z, 1);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/raw-pointer-ub.rs:32:16
|
||||
|
|
||||
LL | let _val = *ptr;
|
||||
| ^^^^ dereferencing pointer failed: allocN has size 4, so pointer to 8 bytes starting at offset 0 is out-of-bounds
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0080`.
|
@ -0,0 +1,22 @@
|
||||
#![feature(diagnostic_namespace)]
|
||||
|
||||
#[diagnostic::on_unimplemented(
|
||||
//~^WARN malformed `on_unimplemented` attribute
|
||||
//~|WARN malformed `on_unimplemented` attribute
|
||||
if(Self = ()),
|
||||
message = "not used yet",
|
||||
label = "not used yet",
|
||||
note = "not used yet"
|
||||
)]
|
||||
#[diagnostic::on_unimplemented(message = "fallback!!")]
|
||||
#[diagnostic::on_unimplemented(label = "fallback label")]
|
||||
#[diagnostic::on_unimplemented(note = "fallback note")]
|
||||
#[diagnostic::on_unimplemented(message = "fallback2!!")]
|
||||
trait Foo {}
|
||||
|
||||
fn takes_foo(_: impl Foo) {}
|
||||
|
||||
fn main() {
|
||||
takes_foo(());
|
||||
//~^ERROR fallback!!
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
warning: malformed `on_unimplemented` attribute
|
||||
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:3:1
|
||||
|
|
||||
LL | / #[diagnostic::on_unimplemented(
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | if(Self = ()),
|
||||
... |
|
||||
LL | | note = "not used yet"
|
||||
LL | | )]
|
||||
| |__^
|
||||
|
|
||||
= note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
|
||||
|
||||
warning: malformed `on_unimplemented` attribute
|
||||
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:3:1
|
||||
|
|
||||
LL | / #[diagnostic::on_unimplemented(
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | if(Self = ()),
|
||||
... |
|
||||
LL | | note = "not used yet"
|
||||
LL | | )]
|
||||
| |__^
|
||||
|
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error[E0277]: fallback!!
|
||||
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:20:15
|
||||
|
|
||||
LL | takes_foo(());
|
||||
| --------- ^^ fallback label
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: the trait `Foo` is not implemented for `()`
|
||||
= note: fallback note
|
||||
help: this trait has no implementations, consider adding one
|
||||
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:15:1
|
||||
|
|
||||
LL | trait Foo {}
|
||||
| ^^^^^^^^^
|
||||
note: required by a bound in `takes_foo`
|
||||
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:17:22
|
||||
|
|
||||
LL | fn takes_foo(_: impl Foo) {}
|
||||
| ^^^ required by this bound in `takes_foo`
|
||||
|
||||
error: aborting due to previous error; 2 warnings emitted
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
@ -0,0 +1,10 @@
|
||||
// run-rustfix
|
||||
fn main() {
|
||||
let _ = vec![1, 2, 3].into_iter().map(|x| {
|
||||
let y = x; //~ ERROR expected expression, found `let` statement
|
||||
y
|
||||
});
|
||||
let _: () = foo(); //~ ERROR mismatched types
|
||||
}
|
||||
|
||||
fn foo() {}
|
10
tests/ui/expr/malformed_closure/missing_block_in_fn_call.rs
Normal file
10
tests/ui/expr/malformed_closure/missing_block_in_fn_call.rs
Normal file
@ -0,0 +1,10 @@
|
||||
// run-rustfix
|
||||
fn main() {
|
||||
let _ = vec![1, 2, 3].into_iter().map(|x|
|
||||
let y = x; //~ ERROR expected expression, found `let` statement
|
||||
y
|
||||
);
|
||||
let _: () = foo; //~ ERROR mismatched types
|
||||
}
|
||||
|
||||
fn foo() {}
|
@ -0,0 +1,38 @@
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/missing_block_in_fn_call.rs:4:9
|
||||
|
|
||||
LL | let _ = vec![1, 2, 3].into_iter().map(|x|
|
||||
| --- while parsing the body of this closure
|
||||
LL | let y = x;
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
help: you might have meant to open the body of the closure
|
||||
|
|
||||
LL ~ let _ = vec![1, 2, 3].into_iter().map(|x| {
|
||||
LL | let y = x;
|
||||
LL | y
|
||||
LL ~ });
|
||||
|
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/missing_block_in_fn_call.rs:7:17
|
||||
|
|
||||
LL | let _: () = foo;
|
||||
| -- ^^^ expected `()`, found fn item
|
||||
| |
|
||||
| expected due to this
|
||||
...
|
||||
LL | fn foo() {}
|
||||
| -------- function `foo` defined here
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found fn item `fn() {foo}`
|
||||
help: use parentheses to call this function
|
||||
|
|
||||
LL | let _: () = foo();
|
||||
| ++
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
@ -0,0 +1,6 @@
|
||||
fn main() {
|
||||
let x = |x|
|
||||
let y = x; //~ ERROR expected expression, found `let` statement
|
||||
let _ = () + ();
|
||||
y
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/missing_block_in_let_binding.rs:3:9
|
||||
|
|
||||
LL | let x = |x|
|
||||
| --- while parsing the body of this closure
|
||||
LL | let y = x;
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
help: you might have meant to open the body of the closure
|
||||
|
|
||||
LL | let x = |x| {
|
||||
| +
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -0,0 +1,9 @@
|
||||
// run-rustfix
|
||||
fn main() {
|
||||
let _ = vec![1, 2, 3].into_iter().map(|x| {
|
||||
let y = x; //~ ERROR expected expression, found `let` statement
|
||||
y
|
||||
});
|
||||
let _: () = foo(); //~ ERROR mismatched types
|
||||
}
|
||||
fn foo() {}
|
@ -0,0 +1,9 @@
|
||||
// run-rustfix
|
||||
fn main() {
|
||||
let _ = vec![1, 2, 3].into_iter().map({|x|
|
||||
let y = x; //~ ERROR expected expression, found `let` statement
|
||||
y
|
||||
});
|
||||
let _: () = foo; //~ ERROR mismatched types
|
||||
}
|
||||
fn foo() {}
|
@ -0,0 +1,36 @@
|
||||
error: expected expression, found `let` statement
|
||||
--> $DIR/ruby_style_closure_parse_error.rs:4:9
|
||||
|
|
||||
LL | let _ = vec![1, 2, 3].into_iter().map({|x|
|
||||
| --- while parsing the body of this closure
|
||||
LL | let y = x;
|
||||
| ^^^
|
||||
|
|
||||
= note: only supported directly in conditions of `if` and `while` expressions
|
||||
help: you might have meant to open the body of the closure, instead of enclosing the closure in a block
|
||||
|
|
||||
LL - let _ = vec![1, 2, 3].into_iter().map({|x|
|
||||
LL + let _ = vec![1, 2, 3].into_iter().map(|x| {
|
||||
|
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/ruby_style_closure_parse_error.rs:7:17
|
||||
|
|
||||
LL | let _: () = foo;
|
||||
| -- ^^^ expected `()`, found fn item
|
||||
| |
|
||||
| expected due to this
|
||||
LL | }
|
||||
LL | fn foo() {}
|
||||
| -------- function `foo` defined here
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found fn item `fn() {foo}`
|
||||
help: use parentheses to call this function
|
||||
|
|
||||
LL | let _: () = foo();
|
||||
| ++
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
@ -2,9 +2,15 @@ error: expected identifier, found `:`
|
||||
--> $DIR/or-patterns-syntactic-fail.rs:11:19
|
||||
|
|
||||
LL | let _ = |A | B: E| ();
|
||||
| ^ expected identifier
|
||||
| ---- ^ expected identifier
|
||||
| |
|
||||
| while parsing the body of this closure
|
||||
|
|
||||
= note: type ascription syntax has been removed, see issue #101728 <https://github.com/rust-lang/rust/issues/101728>
|
||||
help: you might have meant to open the body of the closure
|
||||
|
|
||||
LL | let _ = |A | { B: E| ();
|
||||
| +
|
||||
|
||||
error: top-level or-patterns are not allowed in function parameters
|
||||
--> $DIR/or-patterns-syntactic-fail.rs:18:13
|
||||
|
@ -1,5 +1,6 @@
|
||||
pub fn test() {
|
||||
foo(|_|) //~ ERROR expected expression, found `)`
|
||||
//~^ ERROR cannot find function `foo` in this scope
|
||||
}
|
||||
|
||||
fn main() { }
|
||||
|
@ -2,7 +2,21 @@ error: expected expression, found `)`
|
||||
--> $DIR/issue-32505.rs:2:12
|
||||
|
|
||||
LL | foo(|_|)
|
||||
| ^ expected expression
|
||||
| ---^ expected expression
|
||||
| |
|
||||
| while parsing the body of this closure
|
||||
|
|
||||
help: you might have meant to open the body of the closure
|
||||
|
|
||||
LL | foo(|_| {})
|
||||
| ++
|
||||
|
||||
error: aborting due to previous error
|
||||
error[E0425]: cannot find function `foo` in this scope
|
||||
--> $DIR/issue-32505.rs:2:5
|
||||
|
|
||||
LL | foo(|_|)
|
||||
| ^^^ not found in this scope
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0425`.
|
||||
|
22
tests/ui/pattern/usefulness/slice_of_empty.rs
Normal file
22
tests/ui/pattern/usefulness/slice_of_empty.rs
Normal file
@ -0,0 +1,22 @@
|
||||
#![feature(never_type)]
|
||||
#![feature(exhaustive_patterns)]
|
||||
#![deny(unreachable_patterns)]
|
||||
|
||||
fn main() {}
|
||||
|
||||
fn foo(nevers: &[!]) {
|
||||
match nevers {
|
||||
&[] => (),
|
||||
};
|
||||
|
||||
match nevers {
|
||||
&[] => (),
|
||||
&[_] => (), //~ ERROR unreachable pattern
|
||||
&[_, _, ..] => (), //~ ERROR unreachable pattern
|
||||
};
|
||||
|
||||
match nevers {
|
||||
//~^ ERROR non-exhaustive patterns: `&[]` not covered
|
||||
&[_] => (), //~ ERROR unreachable pattern
|
||||
};
|
||||
}
|
39
tests/ui/pattern/usefulness/slice_of_empty.stderr
Normal file
39
tests/ui/pattern/usefulness/slice_of_empty.stderr
Normal file
@ -0,0 +1,39 @@
|
||||
error: unreachable pattern
|
||||
--> $DIR/slice_of_empty.rs:14:9
|
||||
|
|
||||
LL | &[_] => (),
|
||||
| ^^^^
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/slice_of_empty.rs:3:9
|
||||
|
|
||||
LL | #![deny(unreachable_patterns)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: unreachable pattern
|
||||
--> $DIR/slice_of_empty.rs:15:9
|
||||
|
|
||||
LL | &[_, _, ..] => (),
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
error: unreachable pattern
|
||||
--> $DIR/slice_of_empty.rs:20:9
|
||||
|
|
||||
LL | &[_] => (),
|
||||
| ^^^^
|
||||
|
||||
error[E0004]: non-exhaustive patterns: `&[]` not covered
|
||||
--> $DIR/slice_of_empty.rs:18:11
|
||||
|
|
||||
LL | match nevers {
|
||||
| ^^^^^^ pattern `&[]` not covered
|
||||
|
|
||||
= note: the matched value is of type `&[!]`
|
||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||
|
|
||||
LL | &[_] => (), &[] => todo!(),
|
||||
| ++++++++++++++++
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0004`.
|
@ -0,0 +1,23 @@
|
||||
fn test() {
|
||||
let x = match **x { //~ ERROR
|
||||
Some(&a) if { panic!() } => {}
|
||||
};
|
||||
let mut p = &x;
|
||||
|
||||
{
|
||||
let mut closure = expect_sig(|p, y| *p = y);
|
||||
closure(&mut p, &y); //~ ERROR
|
||||
//~^ ERROR
|
||||
}
|
||||
|
||||
deref(p); //~ ERROR
|
||||
}
|
||||
|
||||
fn expect_sig<F>(f: F) -> F
|
||||
where
|
||||
F: FnMut(&mut &i32, &i32),
|
||||
{
|
||||
f
|
||||
}
|
||||
|
||||
fn main() {}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user