2020-04-26 15:58:53 +02:00
|
|
|
use std::collections::BTreeSet;
|
2023-09-15 08:49:37 +02:00
|
|
|
use std::fmt::{self, Debug, Display, Write as _};
|
2020-04-26 15:58:53 +02:00
|
|
|
use std::fs;
|
2023-09-15 08:49:37 +02:00
|
|
|
use std::io::{self, Write as _};
|
2020-04-26 15:58:53 +02:00
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
|
2017-10-24 14:48:39 +03:00
|
|
|
use super::graphviz::write_mir_fn_graphviz;
|
2020-08-29 10:55:46 -07:00
|
|
|
use super::spanview::write_mir_fn_spanview;
|
2020-03-11 11:49:00 +01:00
|
|
|
use either::Either;
|
2023-09-15 08:49:37 +02:00
|
|
|
use rustc_ast::InlineAsmTemplatePiece;
|
2016-11-08 14:02:55 +11:00
|
|
|
use rustc_data_structures::fx::FxHashMap;
|
2021-05-11 12:26:53 +02:00
|
|
|
use rustc_hir::def_id::DefId;
|
2023-04-19 10:57:17 +00:00
|
|
|
use rustc_index::Idx;
|
2020-03-29 17:19:48 +02:00
|
|
|
use rustc_middle::mir::interpret::{
|
2023-09-16 09:36:22 +02:00
|
|
|
alloc_range, read_target_uint, AllocBytes, AllocId, Allocation, ConstAllocation, GlobalAlloc,
|
|
|
|
Pointer, Provenance,
|
2020-03-29 17:19:48 +02:00
|
|
|
};
|
|
|
|
use rustc_middle::mir::visit::Visitor;
|
2023-08-27 17:11:13 +10:00
|
|
|
use rustc_middle::mir::{self, *};
|
2022-03-07 13:51:59 +01:00
|
|
|
use rustc_middle::ty::{self, TyCtxt};
|
2020-03-31 18:16:47 +02:00
|
|
|
use rustc_target::abi::Size;
|
2015-12-29 20:06:19 -06:00
|
|
|
|
2018-12-04 12:46:10 +01:00
|
|
|
const INDENT: &str = " ";
|
2016-05-13 00:20:59 +02:00
|
|
|
/// Alignment for lining up comments following MIR statements
|
2017-11-20 16:45:12 -05:00
|
|
|
pub(crate) const ALIGN: usize = 40;
|
2015-12-29 20:06:19 -06:00
|
|
|
|
2017-09-28 00:14:34 -04:00
|
|
|
/// An indication of where we are in the control flow graph. Used for printing
|
|
|
|
/// extra information in `dump_mir`
|
|
|
|
pub enum PassWhere {
|
|
|
|
/// We have not started dumping the control flow graph, but we are about to.
|
|
|
|
BeforeCFG,
|
|
|
|
|
|
|
|
/// We just finished dumping the control flow graph. This is right before EOF
|
|
|
|
AfterCFG,
|
|
|
|
|
|
|
|
/// We are about to start dumping the given basic block.
|
|
|
|
BeforeBlock(BasicBlock),
|
|
|
|
|
2017-11-20 16:45:12 -05:00
|
|
|
/// We are just about to dump the given statement or terminator.
|
|
|
|
BeforeLocation(Location),
|
|
|
|
|
|
|
|
/// We just dumped the given statement or terminator.
|
|
|
|
AfterLocation(Location),
|
2018-06-29 06:47:12 -04:00
|
|
|
|
|
|
|
/// We just dumped the terminator for a block but not the closing `}`.
|
|
|
|
AfterTerminator(BasicBlock),
|
2017-09-28 00:14:34 -04:00
|
|
|
}
|
|
|
|
|
2016-03-22 16:05:28 -04:00
|
|
|
/// If the session is properly configured, dumps a human-readable
|
|
|
|
/// representation of the mir into:
|
|
|
|
///
|
2016-04-13 16:16:14 +05:30
|
|
|
/// ```text
|
2017-04-25 18:23:33 -04:00
|
|
|
/// rustc.node<node_id>.<pass_num>.<pass_name>.<disambiguator>
|
2016-03-22 16:05:28 -04:00
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// Output from this function is controlled by passing `-Z dump-mir=<filter>`,
|
|
|
|
/// where `<filter>` takes the following forms:
|
|
|
|
///
|
|
|
|
/// - `all` -- dump MIR for all fns, all passes, all everything
|
2017-11-17 14:08:47 -05:00
|
|
|
/// - a filter defined by a set of substrings combined with `&` and `|`
|
|
|
|
/// (`&` has higher precedence). At least one of the `|`-separated groups
|
|
|
|
/// must match; an `|`-separated group matches if all of its `&`-separated
|
|
|
|
/// substrings are matched.
|
|
|
|
///
|
|
|
|
/// Example:
|
|
|
|
///
|
|
|
|
/// - `nll` == match if `nll` appears in the name
|
|
|
|
/// - `foo & nll` == match if `foo` and `nll` both appear in the name
|
|
|
|
/// - `foo & nll | typeck` == match if `foo` and `nll` both appear in the name
|
|
|
|
/// or `typeck` appears in the name.
|
|
|
|
/// - `foo & nll | bar & typeck` == match if `foo` and `nll` both appear in the name
|
|
|
|
/// or `typeck` and `bar` both appear in the name.
|
2021-10-14 15:26:59 -05:00
|
|
|
#[inline]
|
2019-06-14 00:48:52 +03:00
|
|
|
pub fn dump_mir<'tcx, F>(
|
|
|
|
tcx: TyCtxt<'tcx>,
|
2022-12-01 08:49:51 +00:00
|
|
|
pass_num: bool,
|
2017-11-20 16:45:12 -05:00
|
|
|
pass_name: &str,
|
2018-02-23 10:15:26 -08:00
|
|
|
disambiguator: &dyn Display,
|
2019-06-03 18:26:48 -04:00
|
|
|
body: &Body<'tcx>,
|
2017-11-20 16:45:12 -05:00
|
|
|
extra_data: F,
|
|
|
|
) where
|
2023-09-15 08:49:37 +02:00
|
|
|
F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
|
2017-09-28 00:14:34 -04:00
|
|
|
{
|
2020-10-04 11:01:38 -07:00
|
|
|
if !dump_enabled(tcx, pass_name, body.source.def_id()) {
|
2016-03-22 16:05:28 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-10-04 11:01:38 -07:00
|
|
|
dump_matched_mir_node(tcx, pass_num, pass_name, disambiguator, body, extra_data);
|
2017-04-25 18:23:33 -04:00
|
|
|
}
|
|
|
|
|
2022-12-20 22:10:40 +01:00
|
|
|
pub fn dump_enabled(tcx: TyCtxt<'_>, pass_name: &str, def_id: DefId) -> bool {
|
2022-07-06 07:44:47 -05:00
|
|
|
let Some(ref filters) = tcx.sess.opts.unstable_opts.dump_mir else {
|
2022-02-19 00:48:49 +01:00
|
|
|
return false;
|
2017-04-27 16:48:48 -04:00
|
|
|
};
|
2022-02-16 13:04:48 -05:00
|
|
|
// see notes on #41697 below
|
|
|
|
let node_path = ty::print::with_forced_impl_filename_line!(tcx.def_path_str(def_id));
|
2018-07-23 15:32:57 +02:00
|
|
|
filters.split('|').any(|or_filter| {
|
|
|
|
or_filter.split('&').all(|and_filter| {
|
2021-05-31 20:15:52 +02:00
|
|
|
let and_filter_trimmed = and_filter.trim();
|
|
|
|
and_filter_trimmed == "all"
|
|
|
|
|| pass_name.contains(and_filter_trimmed)
|
|
|
|
|| node_path.contains(and_filter_trimmed)
|
2017-11-17 14:08:47 -05:00
|
|
|
})
|
2017-11-20 16:45:12 -05:00
|
|
|
})
|
2017-04-27 16:48:48 -04:00
|
|
|
}
|
|
|
|
|
2017-05-05 21:13:44 -04:00
|
|
|
// #41697 -- we use `with_forced_impl_filename_line()` because
|
2018-12-19 12:31:35 +02:00
|
|
|
// `def_path_str()` would otherwise trigger `type_of`, and this can
|
2017-05-05 21:13:44 -04:00
|
|
|
// run while we are already attempting to evaluate `type_of`.
|
|
|
|
|
2019-06-14 00:48:52 +03:00
|
|
|
fn dump_matched_mir_node<'tcx, F>(
|
|
|
|
tcx: TyCtxt<'tcx>,
|
2022-12-01 08:49:51 +00:00
|
|
|
pass_num: bool,
|
2017-11-20 16:45:12 -05:00
|
|
|
pass_name: &str,
|
2018-02-23 10:15:26 -08:00
|
|
|
disambiguator: &dyn Display,
|
2019-06-03 18:26:48 -04:00
|
|
|
body: &Body<'tcx>,
|
2017-11-20 16:45:12 -05:00
|
|
|
mut extra_data: F,
|
|
|
|
) where
|
2023-09-15 08:49:37 +02:00
|
|
|
F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
|
2017-09-28 00:14:34 -04:00
|
|
|
{
|
2019-02-08 20:08:08 +09:00
|
|
|
let _: io::Result<()> = try {
|
2022-12-01 08:49:51 +00:00
|
|
|
let mut file = create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, body)?;
|
2022-02-16 13:04:48 -05:00
|
|
|
// see notes on #41697 above
|
|
|
|
let def_path =
|
|
|
|
ty::print::with_forced_impl_filename_line!(tcx.def_path_str(body.source.def_id()));
|
2023-03-04 04:25:36 +01:00
|
|
|
// ignore-tidy-odd-backticks the literal below is fine
|
2023-07-25 22:00:13 +02:00
|
|
|
write!(file, "// MIR for `{def_path}")?;
|
2020-10-04 11:01:38 -07:00
|
|
|
match body.source.promoted {
|
2020-03-11 11:49:00 +01:00
|
|
|
None => write!(file, "`")?,
|
2023-07-25 22:00:13 +02:00
|
|
|
Some(promoted) => write!(file, "::{promoted:?}`")?,
|
2020-03-11 11:49:00 +01:00
|
|
|
}
|
2023-07-25 22:00:13 +02:00
|
|
|
writeln!(file, " {disambiguator} {pass_name}")?;
|
2021-01-17 13:27:05 +01:00
|
|
|
if let Some(ref layout) = body.generator_layout() {
|
2023-07-25 22:00:13 +02:00
|
|
|
writeln!(file, "/* generator_layout = {layout:#?} */")?;
|
2017-11-20 16:45:12 -05:00
|
|
|
}
|
2020-03-07 00:37:51 +01:00
|
|
|
writeln!(file)?;
|
2017-11-20 16:45:12 -05:00
|
|
|
extra_data(PassWhere::BeforeCFG, &mut file)?;
|
2020-06-04 23:05:28 -04:00
|
|
|
write_user_type_annotations(tcx, body, &mut file)?;
|
2020-10-04 11:01:38 -07:00
|
|
|
write_mir_fn(tcx, body, &mut extra_data, &mut file)?;
|
2017-11-20 16:45:12 -05:00
|
|
|
extra_data(PassWhere::AfterCFG, &mut file)?;
|
2018-05-10 12:02:19 -06:00
|
|
|
};
|
2017-11-20 16:45:12 -05:00
|
|
|
|
2022-07-06 07:44:47 -05:00
|
|
|
if tcx.sess.opts.unstable_opts.dump_mir_graphviz {
|
2019-02-08 20:08:08 +09:00
|
|
|
let _: io::Result<()> = try {
|
2022-12-01 08:49:51 +00:00
|
|
|
let mut file = create_dump_file(tcx, "dot", pass_num, pass_name, disambiguator, body)?;
|
2020-10-04 11:01:38 -07:00
|
|
|
write_mir_fn_graphviz(tcx, body, false, &mut file)?;
|
2018-05-10 12:02:19 -06:00
|
|
|
};
|
2017-11-20 16:45:12 -05:00
|
|
|
}
|
2020-08-29 10:55:46 -07:00
|
|
|
|
2022-07-06 07:44:47 -05:00
|
|
|
if let Some(spanview) = tcx.sess.opts.unstable_opts.dump_mir_spanview {
|
2020-08-29 10:55:46 -07:00
|
|
|
let _: io::Result<()> = try {
|
2022-12-01 08:49:51 +00:00
|
|
|
let file_basename = dump_file_basename(tcx, pass_num, pass_name, disambiguator, body);
|
Updates to experimental coverage counter injection
This is a combination of 18 commits.
Commit #2:
Additional examples and some small improvements.
Commit #3:
fixed mir-opt non-mir extensions and spanview title elements
Corrected a fairly recent assumption in runtest.rs that all MIR dump
files end in .mir. (It was appending .mir to the graphviz .dot and
spanview .html file names when generating blessed output files. That
also left outdated files in the baseline alongside the files with the
incorrect names, which I've now removed.)
Updated spanview HTML title elements to match their content, replacing a
hardcoded and incorrect name that was left in accidentally when
originally submitted.
Commit #4:
added more test examples
also improved Makefiles with support for non-zero exit status and to
force validation of tests unless a specific test overrides it with a
specific comment.
Commit #5:
Fixed rare issues after testing on real-world crate
Commit #6:
Addressed PR feedback, and removed temporary -Zexperimental-coverage
-Zinstrument-coverage once again supports the latest capabilities of
LLVM instrprof coverage instrumentation.
Also fixed a bug in spanview.
Commit #7:
Fix closure handling, add tests for closures and inner items
And cleaned up other tests for consistency, and to make it more clear
where spans start/end by breaking up lines.
Commit #8:
renamed "typical" test results "expected"
Now that the `llvm-cov show` tests are improved to normally expect
matching actuals, and to allow individual tests to override that
expectation.
Commit #9:
test coverage of inline generic struct function
Commit #10:
Addressed review feedback
* Removed unnecessary Unreachable filter.
* Replaced a match wildcard with remining variants.
* Added more comments to help clarify the role of successors() in the
CFG traversal
Commit #11:
refactoring based on feedback
* refactored `fn coverage_spans()`.
* changed the way I expand an empty coverage span to improve performance
* fixed a typo that I had accidently left in, in visit.rs
Commit #12:
Optimized use of SourceMap and SourceFile
Commit #13:
Fixed a regression, and synched with upstream
Some generated test file names changed due to some new change upstream.
Commit #14:
Stripping out crate disambiguators from demangled names
These can vary depending on the test platform.
Commit #15:
Ignore llvm-cov show diff on test with generics, expand IO error message
Tests with generics produce llvm-cov show results with demangled names
that can include an unstable "crate disambiguator" (hex value). The
value changes when run in the Rust CI Windows environment. I added a sed
filter to strip them out (in a prior commit), but sed also appears to
fail in the same environment. Until I can figure out a workaround, I'm
just going to ignore this specific test result. I added a FIXME to
follow up later, but it's not that critical.
I also saw an error with Windows GNU, but the IO error did not
specify a path for the directory or file that triggered the error. I
updated the error messages to provide more info for next, time but also
noticed some other tests with similar steps did not fail. Looks
spurious.
Commit #16:
Modify rust-demangler to strip disambiguators by default
Commit #17:
Remove std::process::exit from coverage tests
Due to Issue #77553, programs that call std::process::exit() do not
generate coverage results on Windows MSVC.
Commit #18:
fix: test file paths exceeding Windows max path len
2020-09-01 16:15:17 -07:00
|
|
|
let mut file = create_dump_file_with_basename(tcx, &file_basename, "html")?;
|
2020-10-04 11:01:38 -07:00
|
|
|
if body.source.def_id().is_local() {
|
2020-10-05 09:26:30 -07:00
|
|
|
write_mir_fn_spanview(tcx, body, spanview, &file_basename, &mut file)?;
|
2020-08-29 10:55:46 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
2017-11-20 16:45:12 -05:00
|
|
|
}
|
|
|
|
|
Updates to experimental coverage counter injection
This is a combination of 18 commits.
Commit #2:
Additional examples and some small improvements.
Commit #3:
fixed mir-opt non-mir extensions and spanview title elements
Corrected a fairly recent assumption in runtest.rs that all MIR dump
files end in .mir. (It was appending .mir to the graphviz .dot and
spanview .html file names when generating blessed output files. That
also left outdated files in the baseline alongside the files with the
incorrect names, which I've now removed.)
Updated spanview HTML title elements to match their content, replacing a
hardcoded and incorrect name that was left in accidentally when
originally submitted.
Commit #4:
added more test examples
also improved Makefiles with support for non-zero exit status and to
force validation of tests unless a specific test overrides it with a
specific comment.
Commit #5:
Fixed rare issues after testing on real-world crate
Commit #6:
Addressed PR feedback, and removed temporary -Zexperimental-coverage
-Zinstrument-coverage once again supports the latest capabilities of
LLVM instrprof coverage instrumentation.
Also fixed a bug in spanview.
Commit #7:
Fix closure handling, add tests for closures and inner items
And cleaned up other tests for consistency, and to make it more clear
where spans start/end by breaking up lines.
Commit #8:
renamed "typical" test results "expected"
Now that the `llvm-cov show` tests are improved to normally expect
matching actuals, and to allow individual tests to override that
expectation.
Commit #9:
test coverage of inline generic struct function
Commit #10:
Addressed review feedback
* Removed unnecessary Unreachable filter.
* Replaced a match wildcard with remining variants.
* Added more comments to help clarify the role of successors() in the
CFG traversal
Commit #11:
refactoring based on feedback
* refactored `fn coverage_spans()`.
* changed the way I expand an empty coverage span to improve performance
* fixed a typo that I had accidently left in, in visit.rs
Commit #12:
Optimized use of SourceMap and SourceFile
Commit #13:
Fixed a regression, and synched with upstream
Some generated test file names changed due to some new change upstream.
Commit #14:
Stripping out crate disambiguators from demangled names
These can vary depending on the test platform.
Commit #15:
Ignore llvm-cov show diff on test with generics, expand IO error message
Tests with generics produce llvm-cov show results with demangled names
that can include an unstable "crate disambiguator" (hex value). The
value changes when run in the Rust CI Windows environment. I added a sed
filter to strip them out (in a prior commit), but sed also appears to
fail in the same environment. Until I can figure out a workaround, I'm
just going to ignore this specific test result. I added a FIXME to
follow up later, but it's not that critical.
I also saw an error with Windows GNU, but the IO error did not
specify a path for the directory or file that triggered the error. I
updated the error messages to provide more info for next, time but also
noticed some other tests with similar steps did not fail. Looks
spurious.
Commit #16:
Modify rust-demangler to strip disambiguators by default
Commit #17:
Remove std::process::exit from coverage tests
Due to Issue #77553, programs that call std::process::exit() do not
generate coverage results on Windows MSVC.
Commit #18:
fix: test file paths exceeding Windows max path len
2020-09-01 16:15:17 -07:00
|
|
|
/// Returns the file basename portion (without extension) of a filename path
|
|
|
|
/// where we should dump a MIR representation output files.
|
2021-12-15 19:32:30 -05:00
|
|
|
fn dump_file_basename<'tcx>(
|
|
|
|
tcx: TyCtxt<'tcx>,
|
2022-12-01 08:49:51 +00:00
|
|
|
pass_num: bool,
|
2017-11-20 16:45:12 -05:00
|
|
|
pass_name: &str,
|
2018-02-23 10:15:26 -08:00
|
|
|
disambiguator: &dyn Display,
|
2022-12-01 08:49:51 +00:00
|
|
|
body: &Body<'tcx>,
|
Updates to experimental coverage counter injection
This is a combination of 18 commits.
Commit #2:
Additional examples and some small improvements.
Commit #3:
fixed mir-opt non-mir extensions and spanview title elements
Corrected a fairly recent assumption in runtest.rs that all MIR dump
files end in .mir. (It was appending .mir to the graphviz .dot and
spanview .html file names when generating blessed output files. That
also left outdated files in the baseline alongside the files with the
incorrect names, which I've now removed.)
Updated spanview HTML title elements to match their content, replacing a
hardcoded and incorrect name that was left in accidentally when
originally submitted.
Commit #4:
added more test examples
also improved Makefiles with support for non-zero exit status and to
force validation of tests unless a specific test overrides it with a
specific comment.
Commit #5:
Fixed rare issues after testing on real-world crate
Commit #6:
Addressed PR feedback, and removed temporary -Zexperimental-coverage
-Zinstrument-coverage once again supports the latest capabilities of
LLVM instrprof coverage instrumentation.
Also fixed a bug in spanview.
Commit #7:
Fix closure handling, add tests for closures and inner items
And cleaned up other tests for consistency, and to make it more clear
where spans start/end by breaking up lines.
Commit #8:
renamed "typical" test results "expected"
Now that the `llvm-cov show` tests are improved to normally expect
matching actuals, and to allow individual tests to override that
expectation.
Commit #9:
test coverage of inline generic struct function
Commit #10:
Addressed review feedback
* Removed unnecessary Unreachable filter.
* Replaced a match wildcard with remining variants.
* Added more comments to help clarify the role of successors() in the
CFG traversal
Commit #11:
refactoring based on feedback
* refactored `fn coverage_spans()`.
* changed the way I expand an empty coverage span to improve performance
* fixed a typo that I had accidently left in, in visit.rs
Commit #12:
Optimized use of SourceMap and SourceFile
Commit #13:
Fixed a regression, and synched with upstream
Some generated test file names changed due to some new change upstream.
Commit #14:
Stripping out crate disambiguators from demangled names
These can vary depending on the test platform.
Commit #15:
Ignore llvm-cov show diff on test with generics, expand IO error message
Tests with generics produce llvm-cov show results with demangled names
that can include an unstable "crate disambiguator" (hex value). The
value changes when run in the Rust CI Windows environment. I added a sed
filter to strip them out (in a prior commit), but sed also appears to
fail in the same environment. Until I can figure out a workaround, I'm
just going to ignore this specific test result. I added a FIXME to
follow up later, but it's not that critical.
I also saw an error with Windows GNU, but the IO error did not
specify a path for the directory or file that triggered the error. I
updated the error messages to provide more info for next, time but also
noticed some other tests with similar steps did not fail. Looks
spurious.
Commit #16:
Modify rust-demangler to strip disambiguators by default
Commit #17:
Remove std::process::exit from coverage tests
Due to Issue #77553, programs that call std::process::exit() do not
generate coverage results on Windows MSVC.
Commit #18:
fix: test file paths exceeding Windows max path len
2020-09-01 16:15:17 -07:00
|
|
|
) -> String {
|
2022-12-01 08:49:51 +00:00
|
|
|
let source = body.source;
|
2017-11-10 19:20:35 +02:00
|
|
|
let promotion_id = match source.promoted {
|
2023-07-25 22:00:13 +02:00
|
|
|
Some(id) => format!("-{id:?}"),
|
2017-11-20 16:45:12 -05:00
|
|
|
None => String::new(),
|
2016-06-16 16:52:20 +03:00
|
|
|
};
|
|
|
|
|
2022-07-06 07:44:47 -05:00
|
|
|
let pass_num = if tcx.sess.opts.unstable_opts.dump_mir_exclude_pass_number {
|
2018-07-28 14:40:32 +02:00
|
|
|
String::new()
|
2017-04-25 18:23:33 -04:00
|
|
|
} else {
|
2022-12-01 08:49:51 +00:00
|
|
|
if pass_num {
|
|
|
|
format!(".{:03}-{:03}", body.phase.phase_index(), body.pass_count)
|
|
|
|
} else {
|
|
|
|
".-------".to_string()
|
2017-04-27 16:48:48 -04:00
|
|
|
}
|
2017-04-25 18:23:33 -04:00
|
|
|
};
|
|
|
|
|
2020-07-27 21:22:43 +02:00
|
|
|
let crate_name = tcx.crate_name(source.def_id().krate);
|
2017-11-20 16:45:12 -05:00
|
|
|
let item_name = tcx.def_path(source.def_id()).to_filename_friendly_no_crate();
|
2019-02-03 13:31:50 +01:00
|
|
|
// All drop shims have the same DefId, so we have to add the type
|
|
|
|
// to get unique file names.
|
|
|
|
let shim_disambiguator = match source.instance {
|
|
|
|
ty::InstanceDef::DropGlue(_, Some(ty)) => {
|
|
|
|
// Unfortunately, pretty-printed typed are not very filename-friendly.
|
|
|
|
// We dome some filtering.
|
|
|
|
let mut s = ".".to_owned();
|
|
|
|
s.extend(ty.to_string().chars().filter_map(|c| match c {
|
|
|
|
' ' => None,
|
2019-02-10 15:44:24 +00:00
|
|
|
':' | '<' | '>' => Some('_'),
|
2019-02-03 13:31:50 +01:00
|
|
|
c => Some(c),
|
|
|
|
}));
|
|
|
|
s
|
|
|
|
}
|
|
|
|
_ => String::new(),
|
|
|
|
};
|
2017-11-20 16:45:12 -05:00
|
|
|
|
Updates to experimental coverage counter injection
This is a combination of 18 commits.
Commit #2:
Additional examples and some small improvements.
Commit #3:
fixed mir-opt non-mir extensions and spanview title elements
Corrected a fairly recent assumption in runtest.rs that all MIR dump
files end in .mir. (It was appending .mir to the graphviz .dot and
spanview .html file names when generating blessed output files. That
also left outdated files in the baseline alongside the files with the
incorrect names, which I've now removed.)
Updated spanview HTML title elements to match their content, replacing a
hardcoded and incorrect name that was left in accidentally when
originally submitted.
Commit #4:
added more test examples
also improved Makefiles with support for non-zero exit status and to
force validation of tests unless a specific test overrides it with a
specific comment.
Commit #5:
Fixed rare issues after testing on real-world crate
Commit #6:
Addressed PR feedback, and removed temporary -Zexperimental-coverage
-Zinstrument-coverage once again supports the latest capabilities of
LLVM instrprof coverage instrumentation.
Also fixed a bug in spanview.
Commit #7:
Fix closure handling, add tests for closures and inner items
And cleaned up other tests for consistency, and to make it more clear
where spans start/end by breaking up lines.
Commit #8:
renamed "typical" test results "expected"
Now that the `llvm-cov show` tests are improved to normally expect
matching actuals, and to allow individual tests to override that
expectation.
Commit #9:
test coverage of inline generic struct function
Commit #10:
Addressed review feedback
* Removed unnecessary Unreachable filter.
* Replaced a match wildcard with remining variants.
* Added more comments to help clarify the role of successors() in the
CFG traversal
Commit #11:
refactoring based on feedback
* refactored `fn coverage_spans()`.
* changed the way I expand an empty coverage span to improve performance
* fixed a typo that I had accidently left in, in visit.rs
Commit #12:
Optimized use of SourceMap and SourceFile
Commit #13:
Fixed a regression, and synched with upstream
Some generated test file names changed due to some new change upstream.
Commit #14:
Stripping out crate disambiguators from demangled names
These can vary depending on the test platform.
Commit #15:
Ignore llvm-cov show diff on test with generics, expand IO error message
Tests with generics produce llvm-cov show results with demangled names
that can include an unstable "crate disambiguator" (hex value). The
value changes when run in the Rust CI Windows environment. I added a sed
filter to strip them out (in a prior commit), but sed also appears to
fail in the same environment. Until I can figure out a workaround, I'm
just going to ignore this specific test result. I added a FIXME to
follow up later, but it's not that critical.
I also saw an error with Windows GNU, but the IO error did not
specify a path for the directory or file that triggered the error. I
updated the error messages to provide more info for next, time but also
noticed some other tests with similar steps did not fail. Looks
spurious.
Commit #16:
Modify rust-demangler to strip disambiguators by default
Commit #17:
Remove std::process::exit from coverage tests
Due to Issue #77553, programs that call std::process::exit() do not
generate coverage results on Windows MSVC.
Commit #18:
fix: test file paths exceeding Windows max path len
2020-09-01 16:15:17 -07:00
|
|
|
format!(
|
2023-07-25 22:00:13 +02:00
|
|
|
"{crate_name}.{item_name}{shim_disambiguator}{promotion_id}{pass_num}.{pass_name}.{disambiguator}",
|
Updates to experimental coverage counter injection
This is a combination of 18 commits.
Commit #2:
Additional examples and some small improvements.
Commit #3:
fixed mir-opt non-mir extensions and spanview title elements
Corrected a fairly recent assumption in runtest.rs that all MIR dump
files end in .mir. (It was appending .mir to the graphviz .dot and
spanview .html file names when generating blessed output files. That
also left outdated files in the baseline alongside the files with the
incorrect names, which I've now removed.)
Updated spanview HTML title elements to match their content, replacing a
hardcoded and incorrect name that was left in accidentally when
originally submitted.
Commit #4:
added more test examples
also improved Makefiles with support for non-zero exit status and to
force validation of tests unless a specific test overrides it with a
specific comment.
Commit #5:
Fixed rare issues after testing on real-world crate
Commit #6:
Addressed PR feedback, and removed temporary -Zexperimental-coverage
-Zinstrument-coverage once again supports the latest capabilities of
LLVM instrprof coverage instrumentation.
Also fixed a bug in spanview.
Commit #7:
Fix closure handling, add tests for closures and inner items
And cleaned up other tests for consistency, and to make it more clear
where spans start/end by breaking up lines.
Commit #8:
renamed "typical" test results "expected"
Now that the `llvm-cov show` tests are improved to normally expect
matching actuals, and to allow individual tests to override that
expectation.
Commit #9:
test coverage of inline generic struct function
Commit #10:
Addressed review feedback
* Removed unnecessary Unreachable filter.
* Replaced a match wildcard with remining variants.
* Added more comments to help clarify the role of successors() in the
CFG traversal
Commit #11:
refactoring based on feedback
* refactored `fn coverage_spans()`.
* changed the way I expand an empty coverage span to improve performance
* fixed a typo that I had accidently left in, in visit.rs
Commit #12:
Optimized use of SourceMap and SourceFile
Commit #13:
Fixed a regression, and synched with upstream
Some generated test file names changed due to some new change upstream.
Commit #14:
Stripping out crate disambiguators from demangled names
These can vary depending on the test platform.
Commit #15:
Ignore llvm-cov show diff on test with generics, expand IO error message
Tests with generics produce llvm-cov show results with demangled names
that can include an unstable "crate disambiguator" (hex value). The
value changes when run in the Rust CI Windows environment. I added a sed
filter to strip them out (in a prior commit), but sed also appears to
fail in the same environment. Until I can figure out a workaround, I'm
just going to ignore this specific test result. I added a FIXME to
follow up later, but it's not that critical.
I also saw an error with Windows GNU, but the IO error did not
specify a path for the directory or file that triggered the error. I
updated the error messages to provide more info for next, time but also
noticed some other tests with similar steps did not fail. Looks
spurious.
Commit #16:
Modify rust-demangler to strip disambiguators by default
Commit #17:
Remove std::process::exit from coverage tests
Due to Issue #77553, programs that call std::process::exit() do not
generate coverage results on Windows MSVC.
Commit #18:
fix: test file paths exceeding Windows max path len
2020-09-01 16:15:17 -07:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the path to the filename where we should dump a given MIR.
|
|
|
|
/// Also used by other bits of code (e.g., NLL inference) that dump
|
|
|
|
/// graphviz data or other things.
|
|
|
|
fn dump_path(tcx: TyCtxt<'_>, basename: &str, extension: &str) -> PathBuf {
|
|
|
|
let mut file_path = PathBuf::new();
|
2022-07-06 07:44:47 -05:00
|
|
|
file_path.push(Path::new(&tcx.sess.opts.unstable_opts.dump_mir_dir));
|
Updates to experimental coverage counter injection
This is a combination of 18 commits.
Commit #2:
Additional examples and some small improvements.
Commit #3:
fixed mir-opt non-mir extensions and spanview title elements
Corrected a fairly recent assumption in runtest.rs that all MIR dump
files end in .mir. (It was appending .mir to the graphviz .dot and
spanview .html file names when generating blessed output files. That
also left outdated files in the baseline alongside the files with the
incorrect names, which I've now removed.)
Updated spanview HTML title elements to match their content, replacing a
hardcoded and incorrect name that was left in accidentally when
originally submitted.
Commit #4:
added more test examples
also improved Makefiles with support for non-zero exit status and to
force validation of tests unless a specific test overrides it with a
specific comment.
Commit #5:
Fixed rare issues after testing on real-world crate
Commit #6:
Addressed PR feedback, and removed temporary -Zexperimental-coverage
-Zinstrument-coverage once again supports the latest capabilities of
LLVM instrprof coverage instrumentation.
Also fixed a bug in spanview.
Commit #7:
Fix closure handling, add tests for closures and inner items
And cleaned up other tests for consistency, and to make it more clear
where spans start/end by breaking up lines.
Commit #8:
renamed "typical" test results "expected"
Now that the `llvm-cov show` tests are improved to normally expect
matching actuals, and to allow individual tests to override that
expectation.
Commit #9:
test coverage of inline generic struct function
Commit #10:
Addressed review feedback
* Removed unnecessary Unreachable filter.
* Replaced a match wildcard with remining variants.
* Added more comments to help clarify the role of successors() in the
CFG traversal
Commit #11:
refactoring based on feedback
* refactored `fn coverage_spans()`.
* changed the way I expand an empty coverage span to improve performance
* fixed a typo that I had accidently left in, in visit.rs
Commit #12:
Optimized use of SourceMap and SourceFile
Commit #13:
Fixed a regression, and synched with upstream
Some generated test file names changed due to some new change upstream.
Commit #14:
Stripping out crate disambiguators from demangled names
These can vary depending on the test platform.
Commit #15:
Ignore llvm-cov show diff on test with generics, expand IO error message
Tests with generics produce llvm-cov show results with demangled names
that can include an unstable "crate disambiguator" (hex value). The
value changes when run in the Rust CI Windows environment. I added a sed
filter to strip them out (in a prior commit), but sed also appears to
fail in the same environment. Until I can figure out a workaround, I'm
just going to ignore this specific test result. I added a FIXME to
follow up later, but it's not that critical.
I also saw an error with Windows GNU, but the IO error did not
specify a path for the directory or file that triggered the error. I
updated the error messages to provide more info for next, time but also
noticed some other tests with similar steps did not fail. Looks
spurious.
Commit #16:
Modify rust-demangler to strip disambiguators by default
Commit #17:
Remove std::process::exit from coverage tests
Due to Issue #77553, programs that call std::process::exit() do not
generate coverage results on Windows MSVC.
Commit #18:
fix: test file paths exceeding Windows max path len
2020-09-01 16:15:17 -07:00
|
|
|
|
2023-07-25 22:00:13 +02:00
|
|
|
let file_name = format!("{basename}.{extension}",);
|
2017-11-20 16:45:12 -05:00
|
|
|
|
2016-07-07 16:40:01 -07:00
|
|
|
file_path.push(&file_name);
|
2017-10-24 14:48:39 +03:00
|
|
|
|
2017-11-20 16:45:12 -05:00
|
|
|
file_path
|
|
|
|
}
|
|
|
|
|
Updates to experimental coverage counter injection
This is a combination of 18 commits.
Commit #2:
Additional examples and some small improvements.
Commit #3:
fixed mir-opt non-mir extensions and spanview title elements
Corrected a fairly recent assumption in runtest.rs that all MIR dump
files end in .mir. (It was appending .mir to the graphviz .dot and
spanview .html file names when generating blessed output files. That
also left outdated files in the baseline alongside the files with the
incorrect names, which I've now removed.)
Updated spanview HTML title elements to match their content, replacing a
hardcoded and incorrect name that was left in accidentally when
originally submitted.
Commit #4:
added more test examples
also improved Makefiles with support for non-zero exit status and to
force validation of tests unless a specific test overrides it with a
specific comment.
Commit #5:
Fixed rare issues after testing on real-world crate
Commit #6:
Addressed PR feedback, and removed temporary -Zexperimental-coverage
-Zinstrument-coverage once again supports the latest capabilities of
LLVM instrprof coverage instrumentation.
Also fixed a bug in spanview.
Commit #7:
Fix closure handling, add tests for closures and inner items
And cleaned up other tests for consistency, and to make it more clear
where spans start/end by breaking up lines.
Commit #8:
renamed "typical" test results "expected"
Now that the `llvm-cov show` tests are improved to normally expect
matching actuals, and to allow individual tests to override that
expectation.
Commit #9:
test coverage of inline generic struct function
Commit #10:
Addressed review feedback
* Removed unnecessary Unreachable filter.
* Replaced a match wildcard with remining variants.
* Added more comments to help clarify the role of successors() in the
CFG traversal
Commit #11:
refactoring based on feedback
* refactored `fn coverage_spans()`.
* changed the way I expand an empty coverage span to improve performance
* fixed a typo that I had accidently left in, in visit.rs
Commit #12:
Optimized use of SourceMap and SourceFile
Commit #13:
Fixed a regression, and synched with upstream
Some generated test file names changed due to some new change upstream.
Commit #14:
Stripping out crate disambiguators from demangled names
These can vary depending on the test platform.
Commit #15:
Ignore llvm-cov show diff on test with generics, expand IO error message
Tests with generics produce llvm-cov show results with demangled names
that can include an unstable "crate disambiguator" (hex value). The
value changes when run in the Rust CI Windows environment. I added a sed
filter to strip them out (in a prior commit), but sed also appears to
fail in the same environment. Until I can figure out a workaround, I'm
just going to ignore this specific test result. I added a FIXME to
follow up later, but it's not that critical.
I also saw an error with Windows GNU, but the IO error did not
specify a path for the directory or file that triggered the error. I
updated the error messages to provide more info for next, time but also
noticed some other tests with similar steps did not fail. Looks
spurious.
Commit #16:
Modify rust-demangler to strip disambiguators by default
Commit #17:
Remove std::process::exit from coverage tests
Due to Issue #77553, programs that call std::process::exit() do not
generate coverage results on Windows MSVC.
Commit #18:
fix: test file paths exceeding Windows max path len
2020-09-01 16:15:17 -07:00
|
|
|
/// Attempts to open the MIR dump file with the given name and extension.
|
|
|
|
fn create_dump_file_with_basename(
|
|
|
|
tcx: TyCtxt<'_>,
|
|
|
|
file_basename: &str,
|
|
|
|
extension: &str,
|
|
|
|
) -> io::Result<io::BufWriter<fs::File>> {
|
|
|
|
let file_path = dump_path(tcx, file_basename, extension);
|
|
|
|
if let Some(parent) = file_path.parent() {
|
|
|
|
fs::create_dir_all(parent).map_err(|e| {
|
|
|
|
io::Error::new(
|
|
|
|
e.kind(),
|
2023-07-25 22:00:13 +02:00
|
|
|
format!("IO error creating MIR dump directory: {parent:?}; {e}"),
|
Updates to experimental coverage counter injection
This is a combination of 18 commits.
Commit #2:
Additional examples and some small improvements.
Commit #3:
fixed mir-opt non-mir extensions and spanview title elements
Corrected a fairly recent assumption in runtest.rs that all MIR dump
files end in .mir. (It was appending .mir to the graphviz .dot and
spanview .html file names when generating blessed output files. That
also left outdated files in the baseline alongside the files with the
incorrect names, which I've now removed.)
Updated spanview HTML title elements to match their content, replacing a
hardcoded and incorrect name that was left in accidentally when
originally submitted.
Commit #4:
added more test examples
also improved Makefiles with support for non-zero exit status and to
force validation of tests unless a specific test overrides it with a
specific comment.
Commit #5:
Fixed rare issues after testing on real-world crate
Commit #6:
Addressed PR feedback, and removed temporary -Zexperimental-coverage
-Zinstrument-coverage once again supports the latest capabilities of
LLVM instrprof coverage instrumentation.
Also fixed a bug in spanview.
Commit #7:
Fix closure handling, add tests for closures and inner items
And cleaned up other tests for consistency, and to make it more clear
where spans start/end by breaking up lines.
Commit #8:
renamed "typical" test results "expected"
Now that the `llvm-cov show` tests are improved to normally expect
matching actuals, and to allow individual tests to override that
expectation.
Commit #9:
test coverage of inline generic struct function
Commit #10:
Addressed review feedback
* Removed unnecessary Unreachable filter.
* Replaced a match wildcard with remining variants.
* Added more comments to help clarify the role of successors() in the
CFG traversal
Commit #11:
refactoring based on feedback
* refactored `fn coverage_spans()`.
* changed the way I expand an empty coverage span to improve performance
* fixed a typo that I had accidently left in, in visit.rs
Commit #12:
Optimized use of SourceMap and SourceFile
Commit #13:
Fixed a regression, and synched with upstream
Some generated test file names changed due to some new change upstream.
Commit #14:
Stripping out crate disambiguators from demangled names
These can vary depending on the test platform.
Commit #15:
Ignore llvm-cov show diff on test with generics, expand IO error message
Tests with generics produce llvm-cov show results with demangled names
that can include an unstable "crate disambiguator" (hex value). The
value changes when run in the Rust CI Windows environment. I added a sed
filter to strip them out (in a prior commit), but sed also appears to
fail in the same environment. Until I can figure out a workaround, I'm
just going to ignore this specific test result. I added a FIXME to
follow up later, but it's not that critical.
I also saw an error with Windows GNU, but the IO error did not
specify a path for the directory or file that triggered the error. I
updated the error messages to provide more info for next, time but also
noticed some other tests with similar steps did not fail. Looks
spurious.
Commit #16:
Modify rust-demangler to strip disambiguators by default
Commit #17:
Remove std::process::exit from coverage tests
Due to Issue #77553, programs that call std::process::exit() do not
generate coverage results on Windows MSVC.
Commit #18:
fix: test file paths exceeding Windows max path len
2020-09-01 16:15:17 -07:00
|
|
|
)
|
|
|
|
})?;
|
|
|
|
}
|
|
|
|
Ok(io::BufWriter::new(fs::File::create(&file_path).map_err(|e| {
|
2023-07-25 22:00:13 +02:00
|
|
|
io::Error::new(e.kind(), format!("IO error creating MIR dump file: {file_path:?}; {e}"))
|
Updates to experimental coverage counter injection
This is a combination of 18 commits.
Commit #2:
Additional examples and some small improvements.
Commit #3:
fixed mir-opt non-mir extensions and spanview title elements
Corrected a fairly recent assumption in runtest.rs that all MIR dump
files end in .mir. (It was appending .mir to the graphviz .dot and
spanview .html file names when generating blessed output files. That
also left outdated files in the baseline alongside the files with the
incorrect names, which I've now removed.)
Updated spanview HTML title elements to match their content, replacing a
hardcoded and incorrect name that was left in accidentally when
originally submitted.
Commit #4:
added more test examples
also improved Makefiles with support for non-zero exit status and to
force validation of tests unless a specific test overrides it with a
specific comment.
Commit #5:
Fixed rare issues after testing on real-world crate
Commit #6:
Addressed PR feedback, and removed temporary -Zexperimental-coverage
-Zinstrument-coverage once again supports the latest capabilities of
LLVM instrprof coverage instrumentation.
Also fixed a bug in spanview.
Commit #7:
Fix closure handling, add tests for closures and inner items
And cleaned up other tests for consistency, and to make it more clear
where spans start/end by breaking up lines.
Commit #8:
renamed "typical" test results "expected"
Now that the `llvm-cov show` tests are improved to normally expect
matching actuals, and to allow individual tests to override that
expectation.
Commit #9:
test coverage of inline generic struct function
Commit #10:
Addressed review feedback
* Removed unnecessary Unreachable filter.
* Replaced a match wildcard with remining variants.
* Added more comments to help clarify the role of successors() in the
CFG traversal
Commit #11:
refactoring based on feedback
* refactored `fn coverage_spans()`.
* changed the way I expand an empty coverage span to improve performance
* fixed a typo that I had accidently left in, in visit.rs
Commit #12:
Optimized use of SourceMap and SourceFile
Commit #13:
Fixed a regression, and synched with upstream
Some generated test file names changed due to some new change upstream.
Commit #14:
Stripping out crate disambiguators from demangled names
These can vary depending on the test platform.
Commit #15:
Ignore llvm-cov show diff on test with generics, expand IO error message
Tests with generics produce llvm-cov show results with demangled names
that can include an unstable "crate disambiguator" (hex value). The
value changes when run in the Rust CI Windows environment. I added a sed
filter to strip them out (in a prior commit), but sed also appears to
fail in the same environment. Until I can figure out a workaround, I'm
just going to ignore this specific test result. I added a FIXME to
follow up later, but it's not that critical.
I also saw an error with Windows GNU, but the IO error did not
specify a path for the directory or file that triggered the error. I
updated the error messages to provide more info for next, time but also
noticed some other tests with similar steps did not fail. Looks
spurious.
Commit #16:
Modify rust-demangler to strip disambiguators by default
Commit #17:
Remove std::process::exit from coverage tests
Due to Issue #77553, programs that call std::process::exit() do not
generate coverage results on Windows MSVC.
Commit #18:
fix: test file paths exceeding Windows max path len
2020-09-01 16:15:17 -07:00
|
|
|
})?))
|
|
|
|
}
|
|
|
|
|
2017-11-20 16:45:12 -05:00
|
|
|
/// Attempts to open a file where we should dump a given MIR or other
|
|
|
|
/// bit of MIR-related data. Used by `mir-dump`, but also by other
|
|
|
|
/// bits of code (e.g., NLL inference) that dump graphviz data or
|
|
|
|
/// other things, and hence takes the extension as an argument.
|
2021-12-15 19:32:30 -05:00
|
|
|
pub fn create_dump_file<'tcx>(
|
|
|
|
tcx: TyCtxt<'tcx>,
|
2017-11-20 16:45:12 -05:00
|
|
|
extension: &str,
|
2022-12-01 08:49:51 +00:00
|
|
|
pass_num: bool,
|
2017-11-20 16:45:12 -05:00
|
|
|
pass_name: &str,
|
2018-02-23 10:15:26 -08:00
|
|
|
disambiguator: &dyn Display,
|
2022-12-01 08:49:51 +00:00
|
|
|
body: &Body<'tcx>,
|
2019-09-10 13:43:54 +03:00
|
|
|
) -> io::Result<io::BufWriter<fs::File>> {
|
Updates to experimental coverage counter injection
This is a combination of 18 commits.
Commit #2:
Additional examples and some small improvements.
Commit #3:
fixed mir-opt non-mir extensions and spanview title elements
Corrected a fairly recent assumption in runtest.rs that all MIR dump
files end in .mir. (It was appending .mir to the graphviz .dot and
spanview .html file names when generating blessed output files. That
also left outdated files in the baseline alongside the files with the
incorrect names, which I've now removed.)
Updated spanview HTML title elements to match their content, replacing a
hardcoded and incorrect name that was left in accidentally when
originally submitted.
Commit #4:
added more test examples
also improved Makefiles with support for non-zero exit status and to
force validation of tests unless a specific test overrides it with a
specific comment.
Commit #5:
Fixed rare issues after testing on real-world crate
Commit #6:
Addressed PR feedback, and removed temporary -Zexperimental-coverage
-Zinstrument-coverage once again supports the latest capabilities of
LLVM instrprof coverage instrumentation.
Also fixed a bug in spanview.
Commit #7:
Fix closure handling, add tests for closures and inner items
And cleaned up other tests for consistency, and to make it more clear
where spans start/end by breaking up lines.
Commit #8:
renamed "typical" test results "expected"
Now that the `llvm-cov show` tests are improved to normally expect
matching actuals, and to allow individual tests to override that
expectation.
Commit #9:
test coverage of inline generic struct function
Commit #10:
Addressed review feedback
* Removed unnecessary Unreachable filter.
* Replaced a match wildcard with remining variants.
* Added more comments to help clarify the role of successors() in the
CFG traversal
Commit #11:
refactoring based on feedback
* refactored `fn coverage_spans()`.
* changed the way I expand an empty coverage span to improve performance
* fixed a typo that I had accidently left in, in visit.rs
Commit #12:
Optimized use of SourceMap and SourceFile
Commit #13:
Fixed a regression, and synched with upstream
Some generated test file names changed due to some new change upstream.
Commit #14:
Stripping out crate disambiguators from demangled names
These can vary depending on the test platform.
Commit #15:
Ignore llvm-cov show diff on test with generics, expand IO error message
Tests with generics produce llvm-cov show results with demangled names
that can include an unstable "crate disambiguator" (hex value). The
value changes when run in the Rust CI Windows environment. I added a sed
filter to strip them out (in a prior commit), but sed also appears to
fail in the same environment. Until I can figure out a workaround, I'm
just going to ignore this specific test result. I added a FIXME to
follow up later, but it's not that critical.
I also saw an error with Windows GNU, but the IO error did not
specify a path for the directory or file that triggered the error. I
updated the error messages to provide more info for next, time but also
noticed some other tests with similar steps did not fail. Looks
spurious.
Commit #16:
Modify rust-demangler to strip disambiguators by default
Commit #17:
Remove std::process::exit from coverage tests
Due to Issue #77553, programs that call std::process::exit() do not
generate coverage results on Windows MSVC.
Commit #18:
fix: test file paths exceeding Windows max path len
2020-09-01 16:15:17 -07:00
|
|
|
create_dump_file_with_basename(
|
|
|
|
tcx,
|
2022-12-01 08:49:51 +00:00
|
|
|
&dump_file_basename(tcx, pass_num, pass_name, disambiguator, body),
|
Updates to experimental coverage counter injection
This is a combination of 18 commits.
Commit #2:
Additional examples and some small improvements.
Commit #3:
fixed mir-opt non-mir extensions and spanview title elements
Corrected a fairly recent assumption in runtest.rs that all MIR dump
files end in .mir. (It was appending .mir to the graphviz .dot and
spanview .html file names when generating blessed output files. That
also left outdated files in the baseline alongside the files with the
incorrect names, which I've now removed.)
Updated spanview HTML title elements to match their content, replacing a
hardcoded and incorrect name that was left in accidentally when
originally submitted.
Commit #4:
added more test examples
also improved Makefiles with support for non-zero exit status and to
force validation of tests unless a specific test overrides it with a
specific comment.
Commit #5:
Fixed rare issues after testing on real-world crate
Commit #6:
Addressed PR feedback, and removed temporary -Zexperimental-coverage
-Zinstrument-coverage once again supports the latest capabilities of
LLVM instrprof coverage instrumentation.
Also fixed a bug in spanview.
Commit #7:
Fix closure handling, add tests for closures and inner items
And cleaned up other tests for consistency, and to make it more clear
where spans start/end by breaking up lines.
Commit #8:
renamed "typical" test results "expected"
Now that the `llvm-cov show` tests are improved to normally expect
matching actuals, and to allow individual tests to override that
expectation.
Commit #9:
test coverage of inline generic struct function
Commit #10:
Addressed review feedback
* Removed unnecessary Unreachable filter.
* Replaced a match wildcard with remining variants.
* Added more comments to help clarify the role of successors() in the
CFG traversal
Commit #11:
refactoring based on feedback
* refactored `fn coverage_spans()`.
* changed the way I expand an empty coverage span to improve performance
* fixed a typo that I had accidently left in, in visit.rs
Commit #12:
Optimized use of SourceMap and SourceFile
Commit #13:
Fixed a regression, and synched with upstream
Some generated test file names changed due to some new change upstream.
Commit #14:
Stripping out crate disambiguators from demangled names
These can vary depending on the test platform.
Commit #15:
Ignore llvm-cov show diff on test with generics, expand IO error message
Tests with generics produce llvm-cov show results with demangled names
that can include an unstable "crate disambiguator" (hex value). The
value changes when run in the Rust CI Windows environment. I added a sed
filter to strip them out (in a prior commit), but sed also appears to
fail in the same environment. Until I can figure out a workaround, I'm
just going to ignore this specific test result. I added a FIXME to
follow up later, but it's not that critical.
I also saw an error with Windows GNU, but the IO error did not
specify a path for the directory or file that triggered the error. I
updated the error messages to provide more info for next, time but also
noticed some other tests with similar steps did not fail. Looks
spurious.
Commit #16:
Modify rust-demangler to strip disambiguators by default
Commit #17:
Remove std::process::exit from coverage tests
Due to Issue #77553, programs that call std::process::exit() do not
generate coverage results on Windows MSVC.
Commit #18:
fix: test file paths exceeding Windows max path len
2020-09-01 16:15:17 -07:00
|
|
|
extension,
|
|
|
|
)
|
2016-03-22 16:05:28 -04:00
|
|
|
}
|
|
|
|
|
2023-09-15 08:49:37 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// Whole MIR bodies
|
|
|
|
|
2015-12-29 20:06:19 -06:00
|
|
|
/// Write out a human-readable textual representation for the given MIR.
|
2019-06-14 00:48:52 +03:00
|
|
|
pub fn write_mir_pretty<'tcx>(
|
|
|
|
tcx: TyCtxt<'tcx>,
|
2017-11-20 16:45:12 -05:00
|
|
|
single: Option<DefId>,
|
2023-09-15 08:49:37 +02:00
|
|
|
w: &mut dyn io::Write,
|
2017-11-20 16:45:12 -05:00
|
|
|
) -> io::Result<()> {
|
|
|
|
writeln!(w, "// WARNING: This output format is intended for human consumers only")?;
|
|
|
|
writeln!(w, "// and is subject to change without notice. Knock yourself out.")?;
|
2017-03-03 22:42:49 -05:00
|
|
|
|
2016-05-13 00:20:59 +02:00
|
|
|
let mut first = true;
|
2017-04-25 15:56:02 -04:00
|
|
|
for def_id in dump_mir_def_ids(tcx, single) {
|
2016-05-13 00:20:59 +02:00
|
|
|
if first {
|
|
|
|
first = false;
|
|
|
|
} else {
|
|
|
|
// Put empty lines between all items
|
2020-03-07 00:37:51 +01:00
|
|
|
writeln!(w)?;
|
2016-05-13 00:20:59 +02:00
|
|
|
}
|
|
|
|
|
2023-09-15 08:49:37 +02:00
|
|
|
let render_body = |w: &mut dyn io::Write, body| -> io::Result<()> {
|
2020-10-04 11:01:38 -07:00
|
|
|
write_mir_fn(tcx, body, &mut |_, _| Ok(()), w)?;
|
2021-01-11 17:24:41 +00:00
|
|
|
|
|
|
|
for body in tcx.promoted_mir(def_id) {
|
|
|
|
writeln!(w)?;
|
|
|
|
write_mir_fn(tcx, body, &mut |_, _| Ok(()), w)?;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
};
|
2021-02-10 17:32:45 +03:00
|
|
|
|
|
|
|
// For `const fn` we want to render both the optimized MIR and the MIR for ctfe.
|
|
|
|
if tcx.is_const_fn_raw(def_id) {
|
|
|
|
render_body(w, tcx.optimized_mir(def_id))?;
|
|
|
|
writeln!(w)?;
|
|
|
|
writeln!(w, "// MIR FOR CTFE")?;
|
|
|
|
// Do not use `render_body`, as that would render the promoteds again, but these
|
|
|
|
// are shared between mir_for_ctfe and optimized_mir
|
|
|
|
write_mir_fn(tcx, tcx.mir_for_ctfe(def_id), &mut |_, _| Ok(()), w)?;
|
|
|
|
} else {
|
2022-05-08 15:53:19 +02:00
|
|
|
let instance_mir = tcx.instance_mir(ty::InstanceDef::Item(def_id));
|
2021-02-10 17:32:45 +03:00
|
|
|
render_body(w, instance_mir)?;
|
2016-05-03 00:26:41 +03:00
|
|
|
}
|
2015-12-29 20:06:19 -06:00
|
|
|
}
|
2016-03-12 19:07:00 +02:00
|
|
|
Ok(())
|
2015-12-29 20:06:19 -06:00
|
|
|
}
|
|
|
|
|
2020-04-17 13:24:33 -04:00
|
|
|
/// Write out a human-readable textual representation for the given function.
|
2019-06-14 00:48:52 +03:00
|
|
|
pub fn write_mir_fn<'tcx, F>(
|
|
|
|
tcx: TyCtxt<'tcx>,
|
2019-06-03 18:26:48 -04:00
|
|
|
body: &Body<'tcx>,
|
2017-11-20 16:45:12 -05:00
|
|
|
extra_data: &mut F,
|
2023-09-15 08:49:37 +02:00
|
|
|
w: &mut dyn io::Write,
|
2017-11-20 16:45:12 -05:00
|
|
|
) -> io::Result<()>
|
2017-09-28 00:14:34 -04:00
|
|
|
where
|
2023-09-15 08:49:37 +02:00
|
|
|
F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
|
2017-09-28 00:14:34 -04:00
|
|
|
{
|
2020-10-04 11:01:38 -07:00
|
|
|
write_mir_intro(tcx, body, w)?;
|
2022-07-05 00:00:00 +00:00
|
|
|
for block in body.basic_blocks.indices() {
|
2017-09-28 00:14:34 -04:00
|
|
|
extra_data(PassWhere::BeforeBlock(block), w)?;
|
2019-06-03 18:26:48 -04:00
|
|
|
write_basic_block(tcx, block, body, extra_data, w)?;
|
2022-07-05 00:00:00 +00:00
|
|
|
if block.index() + 1 != body.basic_blocks.len() {
|
2020-03-07 00:37:51 +01:00
|
|
|
writeln!(w)?;
|
2016-05-31 20:27:36 +03:00
|
|
|
}
|
2016-03-22 10:08:44 -04:00
|
|
|
}
|
|
|
|
|
2016-03-19 07:13:54 -04:00
|
|
|
writeln!(w, "}}")?;
|
2020-03-11 11:49:00 +01:00
|
|
|
|
|
|
|
write_allocations(tcx, body, w)?;
|
|
|
|
|
2016-03-19 07:13:54 -04:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-09-15 08:49:37 +02:00
|
|
|
/// Prints local variables in a scope tree.
|
|
|
|
fn write_scope_tree(
|
|
|
|
tcx: TyCtxt<'_>,
|
|
|
|
body: &Body<'_>,
|
|
|
|
scope_tree: &FxHashMap<SourceScope, Vec<SourceScope>>,
|
|
|
|
w: &mut dyn io::Write,
|
|
|
|
parent: SourceScope,
|
|
|
|
depth: usize,
|
|
|
|
) -> io::Result<()> {
|
|
|
|
let indent = depth * INDENT.len();
|
|
|
|
|
|
|
|
// Local variable debuginfo.
|
|
|
|
for var_debug_info in &body.var_debug_info {
|
|
|
|
if var_debug_info.source_info.scope != parent {
|
|
|
|
// Not declared in this scope.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let indented_debug_info = format!("{0:1$}debug {2:?};", INDENT, indent, var_debug_info);
|
|
|
|
|
|
|
|
if tcx.sess.opts.unstable_opts.mir_include_spans {
|
|
|
|
writeln!(
|
|
|
|
w,
|
|
|
|
"{0:1$} // in {2}",
|
|
|
|
indented_debug_info,
|
|
|
|
ALIGN,
|
|
|
|
comment(tcx, var_debug_info.source_info),
|
|
|
|
)?;
|
|
|
|
} else {
|
|
|
|
writeln!(w, "{indented_debug_info}")?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Local variable types.
|
|
|
|
for (local, local_decl) in body.local_decls.iter_enumerated() {
|
|
|
|
if (1..body.arg_count + 1).contains(&local.index()) {
|
|
|
|
// Skip over argument locals, they're printed in the signature.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if local_decl.source_info.scope != parent {
|
|
|
|
// Not declared in this scope.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut_str = local_decl.mutability.prefix_str();
|
|
|
|
|
|
|
|
let mut indented_decl = ty::print::with_no_trimmed_paths!(format!(
|
|
|
|
"{0:1$}let {2}{3:?}: {4}",
|
|
|
|
INDENT, indent, mut_str, local, local_decl.ty
|
|
|
|
));
|
|
|
|
if let Some(user_ty) = &local_decl.user_ty {
|
|
|
|
for user_ty in user_ty.projections() {
|
|
|
|
write!(indented_decl, " as {user_ty:?}").unwrap();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
indented_decl.push(';');
|
|
|
|
|
|
|
|
let local_name = if local == RETURN_PLACE { " return place" } else { "" };
|
|
|
|
|
|
|
|
if tcx.sess.opts.unstable_opts.mir_include_spans {
|
|
|
|
writeln!(
|
|
|
|
w,
|
|
|
|
"{0:1$} //{2} in {3}",
|
|
|
|
indented_decl,
|
|
|
|
ALIGN,
|
|
|
|
local_name,
|
|
|
|
comment(tcx, local_decl.source_info),
|
|
|
|
)?;
|
|
|
|
} else {
|
|
|
|
writeln!(w, "{indented_decl}",)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let Some(children) = scope_tree.get(&parent) else {
|
|
|
|
return Ok(());
|
|
|
|
};
|
|
|
|
|
|
|
|
for &child in children {
|
|
|
|
let child_data = &body.source_scopes[child];
|
|
|
|
assert_eq!(child_data.parent_scope, Some(parent));
|
|
|
|
|
|
|
|
let (special, span) = if let Some((callee, callsite_span)) = child_data.inlined {
|
|
|
|
(
|
|
|
|
format!(
|
|
|
|
" (inlined {}{})",
|
|
|
|
if callee.def.requires_caller_location(tcx) { "#[track_caller] " } else { "" },
|
|
|
|
callee
|
|
|
|
),
|
|
|
|
Some(callsite_span),
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
(String::new(), None)
|
|
|
|
};
|
|
|
|
|
|
|
|
let indented_header = format!("{0:1$}scope {2}{3} {{", "", indent, child.index(), special);
|
|
|
|
|
|
|
|
if tcx.sess.opts.unstable_opts.mir_include_spans {
|
|
|
|
if let Some(span) = span {
|
|
|
|
writeln!(
|
|
|
|
w,
|
|
|
|
"{0:1$} // at {2}",
|
|
|
|
indented_header,
|
|
|
|
ALIGN,
|
|
|
|
tcx.sess.source_map().span_to_embeddable_string(span),
|
|
|
|
)?;
|
|
|
|
} else {
|
|
|
|
writeln!(w, "{indented_header}")?;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
writeln!(w, "{indented_header}")?;
|
|
|
|
}
|
|
|
|
|
|
|
|
write_scope_tree(tcx, body, scope_tree, w, child, depth + 1)?;
|
|
|
|
writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Debug for VarDebugInfo<'_> {
|
|
|
|
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
|
|
|
if let Some(box VarDebugInfoFragment { ty, ref projection }) = self.composite {
|
|
|
|
pre_fmt_projection(&projection[..], fmt)?;
|
|
|
|
write!(fmt, "({}: {})", self.name, ty)?;
|
|
|
|
post_fmt_projection(&projection[..], fmt)?;
|
|
|
|
} else {
|
|
|
|
write!(fmt, "{}", self.name)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
write!(fmt, " => {:?}", self.value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
|
|
|
|
/// local variables (both user-defined bindings and compiler temporaries).
|
|
|
|
pub fn write_mir_intro<'tcx>(
|
|
|
|
tcx: TyCtxt<'tcx>,
|
|
|
|
body: &Body<'_>,
|
|
|
|
w: &mut dyn io::Write,
|
|
|
|
) -> io::Result<()> {
|
|
|
|
write_mir_sig(tcx, body, w)?;
|
|
|
|
writeln!(w, "{{")?;
|
|
|
|
|
|
|
|
// construct a scope tree and write it out
|
|
|
|
let mut scope_tree: FxHashMap<SourceScope, Vec<SourceScope>> = Default::default();
|
|
|
|
for (index, scope_data) in body.source_scopes.iter().enumerate() {
|
|
|
|
if let Some(parent) = scope_data.parent_scope {
|
|
|
|
scope_tree.entry(parent).or_default().push(SourceScope::new(index));
|
|
|
|
} else {
|
|
|
|
// Only the argument scope has no parent, because it's the root.
|
|
|
|
assert_eq!(index, OUTERMOST_SOURCE_SCOPE.index());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
write_scope_tree(tcx, body, &scope_tree, w, OUTERMOST_SOURCE_SCOPE, 1)?;
|
|
|
|
|
|
|
|
// Add an empty line before the first block is printed.
|
|
|
|
writeln!(w)?;
|
|
|
|
|
2023-09-13 12:51:43 +10:00
|
|
|
if let Some(function_coverage_info) = &body.function_coverage_info {
|
|
|
|
write_function_coverage_info(function_coverage_info, w)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write_function_coverage_info(
|
|
|
|
function_coverage_info: &coverage::FunctionCoverageInfo,
|
|
|
|
w: &mut dyn io::Write,
|
|
|
|
) -> io::Result<()> {
|
|
|
|
let coverage::FunctionCoverageInfo { mappings, .. } = function_coverage_info;
|
|
|
|
|
|
|
|
for coverage::Mapping { term, code_region } in mappings {
|
|
|
|
writeln!(w, "{INDENT}coverage {term:?} => {code_region:?};")?;
|
|
|
|
}
|
|
|
|
writeln!(w)?;
|
|
|
|
|
2023-09-15 08:49:37 +02:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write_mir_sig(tcx: TyCtxt<'_>, body: &Body<'_>, w: &mut dyn io::Write) -> io::Result<()> {
|
|
|
|
use rustc_hir::def::DefKind;
|
|
|
|
|
|
|
|
trace!("write_mir_sig: {:?}", body.source.instance);
|
|
|
|
let def_id = body.source.def_id();
|
|
|
|
let kind = tcx.def_kind(def_id);
|
|
|
|
let is_function = match kind {
|
|
|
|
DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) => true,
|
|
|
|
_ => tcx.is_closure(def_id),
|
|
|
|
};
|
|
|
|
match (kind, body.source.promoted) {
|
|
|
|
(_, Some(i)) => write!(w, "{i:?} in ")?,
|
|
|
|
(DefKind::Const | DefKind::AssocConst, _) => write!(w, "const ")?,
|
|
|
|
(DefKind::Static(hir::Mutability::Not), _) => write!(w, "static ")?,
|
|
|
|
(DefKind::Static(hir::Mutability::Mut), _) => write!(w, "static mut ")?,
|
|
|
|
(_, _) if is_function => write!(w, "fn ")?,
|
|
|
|
(DefKind::AnonConst | DefKind::InlineConst, _) => {} // things like anon const, not an item
|
|
|
|
_ => bug!("Unexpected def kind {:?}", kind),
|
|
|
|
}
|
|
|
|
|
|
|
|
ty::print::with_forced_impl_filename_line! {
|
|
|
|
// see notes on #41697 elsewhere
|
|
|
|
write!(w, "{}", tcx.def_path_str(def_id))?
|
|
|
|
}
|
|
|
|
|
|
|
|
if body.source.promoted.is_none() && is_function {
|
|
|
|
write!(w, "(")?;
|
|
|
|
|
|
|
|
// fn argument types.
|
|
|
|
for (i, arg) in body.args_iter().enumerate() {
|
|
|
|
if i != 0 {
|
|
|
|
write!(w, ", ")?;
|
|
|
|
}
|
|
|
|
write!(w, "{:?}: {}", Place::from(arg), body.local_decls[arg].ty)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
write!(w, ") -> {}", body.return_ty())?;
|
|
|
|
} else {
|
|
|
|
assert_eq!(body.arg_count, 0);
|
|
|
|
write!(w, ": {} =", body.return_ty())?;
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(yield_ty) = body.yield_ty() {
|
|
|
|
writeln!(w)?;
|
|
|
|
writeln!(w, "yields {yield_ty}")?;
|
|
|
|
}
|
|
|
|
|
|
|
|
write!(w, " ")?;
|
|
|
|
// Next thing that gets printed is the opening {
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write_user_type_annotations(
|
|
|
|
tcx: TyCtxt<'_>,
|
|
|
|
body: &Body<'_>,
|
|
|
|
w: &mut dyn io::Write,
|
|
|
|
) -> io::Result<()> {
|
|
|
|
if !body.user_type_annotations.is_empty() {
|
|
|
|
writeln!(w, "| User Type Annotations")?;
|
|
|
|
}
|
|
|
|
for (index, annotation) in body.user_type_annotations.iter_enumerated() {
|
|
|
|
writeln!(
|
|
|
|
w,
|
|
|
|
"| {:?}: user_ty: {}, span: {}, inferred_ty: {}",
|
|
|
|
index.index(),
|
|
|
|
annotation.user_ty,
|
|
|
|
tcx.sess.source_map().span_to_embeddable_string(annotation.span),
|
|
|
|
with_no_trimmed_paths!(format!("{}", annotation.inferred_ty)),
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
if !body.user_type_annotations.is_empty() {
|
|
|
|
writeln!(w, "|")?;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option<DefId>) -> Vec<DefId> {
|
|
|
|
if let Some(i) = single {
|
|
|
|
vec![i]
|
|
|
|
} else {
|
|
|
|
tcx.mir_keys(()).iter().map(|def_id| def_id.to_def_id()).collect()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// Basic blocks and their parts (statements, terminators, ...)
|
|
|
|
|
2015-12-29 20:06:19 -06:00
|
|
|
/// Write out a human-readable textual representation for the given basic block.
|
2019-06-14 00:48:52 +03:00
|
|
|
pub fn write_basic_block<'tcx, F>(
|
|
|
|
tcx: TyCtxt<'tcx>,
|
2017-11-20 16:45:12 -05:00
|
|
|
block: BasicBlock,
|
2019-06-03 18:26:48 -04:00
|
|
|
body: &Body<'tcx>,
|
2017-11-20 16:45:12 -05:00
|
|
|
extra_data: &mut F,
|
2023-09-15 08:49:37 +02:00
|
|
|
w: &mut dyn io::Write,
|
2017-11-20 16:45:12 -05:00
|
|
|
) -> io::Result<()>
|
2017-09-28 00:14:34 -04:00
|
|
|
where
|
2023-09-15 08:49:37 +02:00
|
|
|
F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
|
2017-09-28 00:14:34 -04:00
|
|
|
{
|
2019-06-03 18:26:48 -04:00
|
|
|
let data = &body[block];
|
2015-12-29 20:06:19 -06:00
|
|
|
|
|
|
|
// Basic block label at the top.
|
2019-03-03 19:04:26 +00:00
|
|
|
let cleanup_text = if data.is_cleanup { " (cleanup)" } else { "" };
|
2023-07-25 22:00:13 +02:00
|
|
|
writeln!(w, "{INDENT}{block:?}{cleanup_text}: {{")?;
|
2015-12-29 20:06:19 -06:00
|
|
|
|
|
|
|
// List of statements in the middle.
|
2020-03-06 19:28:44 +01:00
|
|
|
let mut current_location = Location { block, statement_index: 0 };
|
2015-12-29 20:06:19 -06:00
|
|
|
for statement in &data.statements {
|
2017-11-20 16:45:12 -05:00
|
|
|
extra_data(PassWhere::BeforeLocation(current_location), w)?;
|
2023-07-25 22:00:13 +02:00
|
|
|
let indented_body = format!("{INDENT}{INDENT}{statement:?};");
|
2023-06-06 09:47:00 -04:00
|
|
|
if tcx.sess.opts.unstable_opts.mir_include_spans {
|
|
|
|
writeln!(
|
|
|
|
w,
|
|
|
|
"{:A$} // {}{}",
|
|
|
|
indented_body,
|
2023-07-25 22:00:13 +02:00
|
|
|
if tcx.sess.verbose() { format!("{current_location:?}: ") } else { String::new() },
|
2023-06-06 09:47:00 -04:00
|
|
|
comment(tcx, statement.source_info),
|
|
|
|
A = ALIGN,
|
|
|
|
)?;
|
|
|
|
} else {
|
2023-07-25 22:00:13 +02:00
|
|
|
writeln!(w, "{indented_body}")?;
|
2023-06-06 09:47:00 -04:00
|
|
|
}
|
2017-11-22 17:33:26 -05:00
|
|
|
|
|
|
|
write_extra(tcx, w, |visitor| {
|
2019-04-22 21:07:14 +01:00
|
|
|
visitor.visit_statement(statement, current_location);
|
2017-11-22 17:33:26 -05:00
|
|
|
})?;
|
|
|
|
|
2017-11-20 16:45:12 -05:00
|
|
|
extra_data(PassWhere::AfterLocation(current_location), w)?;
|
2016-03-22 10:08:44 -04:00
|
|
|
|
|
|
|
current_location.statement_index += 1;
|
2015-12-29 20:06:19 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Terminator at the bottom.
|
2017-11-20 16:45:12 -05:00
|
|
|
extra_data(PassWhere::BeforeLocation(current_location), w)?;
|
2016-05-13 00:20:59 +02:00
|
|
|
let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);
|
2023-06-06 09:47:00 -04:00
|
|
|
if tcx.sess.opts.unstable_opts.mir_include_spans {
|
|
|
|
writeln!(
|
|
|
|
w,
|
|
|
|
"{:A$} // {}{}",
|
|
|
|
indented_terminator,
|
2023-07-25 22:00:13 +02:00
|
|
|
if tcx.sess.verbose() { format!("{current_location:?}: ") } else { String::new() },
|
2023-06-06 09:47:00 -04:00
|
|
|
comment(tcx, data.terminator().source_info),
|
|
|
|
A = ALIGN,
|
|
|
|
)?;
|
|
|
|
} else {
|
2023-07-25 22:00:13 +02:00
|
|
|
writeln!(w, "{indented_terminator}")?;
|
2023-06-06 09:47:00 -04:00
|
|
|
}
|
2017-11-22 17:33:26 -05:00
|
|
|
|
2023-09-15 08:49:37 +02:00
|
|
|
write_extra(tcx, w, |visitor| {
|
|
|
|
visitor.visit_terminator(data.terminator(), current_location);
|
|
|
|
})?;
|
|
|
|
|
|
|
|
extra_data(PassWhere::AfterLocation(current_location), w)?;
|
|
|
|
extra_data(PassWhere::AfterTerminator(block), w)?;
|
|
|
|
|
|
|
|
writeln!(w, "{INDENT}}}")
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Debug for Statement<'_> {
|
|
|
|
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
|
|
|
use self::StatementKind::*;
|
|
|
|
match self.kind {
|
|
|
|
Assign(box (ref place, ref rv)) => write!(fmt, "{place:?} = {rv:?}"),
|
|
|
|
FakeRead(box (ref cause, ref place)) => {
|
|
|
|
write!(fmt, "FakeRead({cause:?}, {place:?})")
|
|
|
|
}
|
|
|
|
Retag(ref kind, ref place) => write!(
|
|
|
|
fmt,
|
|
|
|
"Retag({}{:?})",
|
|
|
|
match kind {
|
|
|
|
RetagKind::FnEntry => "[fn entry] ",
|
|
|
|
RetagKind::TwoPhase => "[2phase] ",
|
|
|
|
RetagKind::Raw => "[raw] ",
|
|
|
|
RetagKind::Default => "",
|
|
|
|
},
|
|
|
|
place,
|
|
|
|
),
|
|
|
|
StorageLive(ref place) => write!(fmt, "StorageLive({place:?})"),
|
|
|
|
StorageDead(ref place) => write!(fmt, "StorageDead({place:?})"),
|
|
|
|
SetDiscriminant { ref place, variant_index } => {
|
|
|
|
write!(fmt, "discriminant({place:?}) = {variant_index:?}")
|
|
|
|
}
|
|
|
|
Deinit(ref place) => write!(fmt, "Deinit({place:?})"),
|
|
|
|
PlaceMention(ref place) => {
|
|
|
|
write!(fmt, "PlaceMention({place:?})")
|
|
|
|
}
|
|
|
|
AscribeUserType(box (ref place, ref c_ty), ref variance) => {
|
|
|
|
write!(fmt, "AscribeUserType({place:?}, {variance:?}, {c_ty:?})")
|
|
|
|
}
|
2023-09-13 12:51:43 +10:00
|
|
|
Coverage(box mir::Coverage { ref kind }) => write!(fmt, "Coverage::{kind:?}"),
|
2023-09-15 08:49:37 +02:00
|
|
|
Intrinsic(box ref intrinsic) => write!(fmt, "{intrinsic}"),
|
|
|
|
ConstEvalCounter => write!(fmt, "ConstEvalCounter"),
|
|
|
|
Nop => write!(fmt, "nop"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-20 20:51:14 +02:00
|
|
|
impl Display for NonDivergingIntrinsic<'_> {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
match self {
|
|
|
|
Self::Assume(op) => write!(f, "assume({op:?})"),
|
|
|
|
Self::CopyNonOverlapping(CopyNonOverlapping { src, dst, count }) => {
|
|
|
|
write!(f, "copy_nonoverlapping(dst = {dst:?}, src = {src:?}, count = {count:?})")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-15 08:49:37 +02:00
|
|
|
impl<'tcx> Debug for TerminatorKind<'tcx> {
|
|
|
|
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
|
|
|
self.fmt_head(fmt)?;
|
|
|
|
let successor_count = self.successors().count();
|
|
|
|
let labels = self.fmt_successor_labels();
|
|
|
|
assert_eq!(successor_count, labels.len());
|
|
|
|
|
|
|
|
// `Cleanup` is already included in successors
|
|
|
|
let show_unwind = !matches!(self.unwind(), None | Some(UnwindAction::Cleanup(_)));
|
|
|
|
let fmt_unwind = |fmt: &mut Formatter<'_>| -> fmt::Result {
|
|
|
|
write!(fmt, "unwind ")?;
|
|
|
|
match self.unwind() {
|
|
|
|
// Not needed or included in successors
|
|
|
|
None | Some(UnwindAction::Cleanup(_)) => unreachable!(),
|
|
|
|
Some(UnwindAction::Continue) => write!(fmt, "continue"),
|
|
|
|
Some(UnwindAction::Unreachable) => write!(fmt, "unreachable"),
|
|
|
|
Some(UnwindAction::Terminate(reason)) => {
|
|
|
|
write!(fmt, "terminate({})", reason.as_short_str())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
match (successor_count, show_unwind) {
|
|
|
|
(0, false) => Ok(()),
|
|
|
|
(0, true) => {
|
|
|
|
write!(fmt, " -> ")?;
|
|
|
|
fmt_unwind(fmt)
|
|
|
|
}
|
|
|
|
(1, false) => write!(fmt, " -> {:?}", self.successors().next().unwrap()),
|
|
|
|
_ => {
|
|
|
|
write!(fmt, " -> [")?;
|
|
|
|
for (i, target) in self.successors().enumerate() {
|
|
|
|
if i > 0 {
|
|
|
|
write!(fmt, ", ")?;
|
|
|
|
}
|
|
|
|
write!(fmt, "{}: {:?}", labels[i], target)?;
|
|
|
|
}
|
|
|
|
if show_unwind {
|
|
|
|
write!(fmt, ", ")?;
|
|
|
|
fmt_unwind(fmt)?;
|
|
|
|
}
|
|
|
|
write!(fmt, "]")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'tcx> TerminatorKind<'tcx> {
|
|
|
|
/// Writes the "head" part of the terminator; that is, its name and the data it uses to pick the
|
|
|
|
/// successor basic block, if any. The only information not included is the list of possible
|
|
|
|
/// successors, which may be rendered differently between the text and the graphviz format.
|
|
|
|
pub fn fmt_head<W: fmt::Write>(&self, fmt: &mut W) -> fmt::Result {
|
|
|
|
use self::TerminatorKind::*;
|
|
|
|
match self {
|
|
|
|
Goto { .. } => write!(fmt, "goto"),
|
|
|
|
SwitchInt { discr, .. } => write!(fmt, "switchInt({discr:?})"),
|
|
|
|
Return => write!(fmt, "return"),
|
|
|
|
GeneratorDrop => write!(fmt, "generator_drop"),
|
|
|
|
UnwindResume => write!(fmt, "resume"),
|
|
|
|
UnwindTerminate(reason) => {
|
|
|
|
write!(fmt, "abort({})", reason.as_short_str())
|
|
|
|
}
|
|
|
|
Yield { value, resume_arg, .. } => write!(fmt, "{resume_arg:?} = yield({value:?})"),
|
|
|
|
Unreachable => write!(fmt, "unreachable"),
|
|
|
|
Drop { place, .. } => write!(fmt, "drop({place:?})"),
|
|
|
|
Call { func, args, destination, .. } => {
|
|
|
|
write!(fmt, "{destination:?} = ")?;
|
|
|
|
write!(fmt, "{func:?}(")?;
|
|
|
|
for (index, arg) in args.iter().enumerate() {
|
|
|
|
if index > 0 {
|
|
|
|
write!(fmt, ", ")?;
|
|
|
|
}
|
|
|
|
write!(fmt, "{arg:?}")?;
|
|
|
|
}
|
|
|
|
write!(fmt, ")")
|
|
|
|
}
|
|
|
|
Assert { cond, expected, msg, .. } => {
|
|
|
|
write!(fmt, "assert(")?;
|
|
|
|
if !expected {
|
|
|
|
write!(fmt, "!")?;
|
|
|
|
}
|
|
|
|
write!(fmt, "{cond:?}, ")?;
|
|
|
|
msg.fmt_assert_args(fmt)?;
|
|
|
|
write!(fmt, ")")
|
|
|
|
}
|
|
|
|
FalseEdge { .. } => write!(fmt, "falseEdge"),
|
|
|
|
FalseUnwind { .. } => write!(fmt, "falseUnwind"),
|
|
|
|
InlineAsm { template, ref operands, options, .. } => {
|
|
|
|
write!(fmt, "asm!(\"{}\"", InlineAsmTemplatePiece::to_string(template))?;
|
|
|
|
for op in operands {
|
|
|
|
write!(fmt, ", ")?;
|
|
|
|
let print_late = |&late| if late { "late" } else { "" };
|
|
|
|
match op {
|
|
|
|
InlineAsmOperand::In { reg, value } => {
|
|
|
|
write!(fmt, "in({reg}) {value:?}")?;
|
|
|
|
}
|
|
|
|
InlineAsmOperand::Out { reg, late, place: Some(place) } => {
|
|
|
|
write!(fmt, "{}out({}) {:?}", print_late(late), reg, place)?;
|
|
|
|
}
|
|
|
|
InlineAsmOperand::Out { reg, late, place: None } => {
|
|
|
|
write!(fmt, "{}out({}) _", print_late(late), reg)?;
|
|
|
|
}
|
|
|
|
InlineAsmOperand::InOut {
|
|
|
|
reg,
|
|
|
|
late,
|
|
|
|
in_value,
|
|
|
|
out_place: Some(out_place),
|
|
|
|
} => {
|
|
|
|
write!(
|
|
|
|
fmt,
|
|
|
|
"in{}out({}) {:?} => {:?}",
|
|
|
|
print_late(late),
|
|
|
|
reg,
|
|
|
|
in_value,
|
|
|
|
out_place
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
InlineAsmOperand::InOut { reg, late, in_value, out_place: None } => {
|
|
|
|
write!(fmt, "in{}out({}) {:?} => _", print_late(late), reg, in_value)?;
|
|
|
|
}
|
|
|
|
InlineAsmOperand::Const { value } => {
|
|
|
|
write!(fmt, "const {value:?}")?;
|
|
|
|
}
|
|
|
|
InlineAsmOperand::SymFn { value } => {
|
|
|
|
write!(fmt, "sym_fn {value:?}")?;
|
|
|
|
}
|
|
|
|
InlineAsmOperand::SymStatic { def_id } => {
|
|
|
|
write!(fmt, "sym_static {def_id:?}")?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
write!(fmt, ", options({options:?}))")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the list of labels for the edges to the successor basic blocks.
|
|
|
|
pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
|
|
|
|
use self::TerminatorKind::*;
|
|
|
|
match *self {
|
|
|
|
Return | UnwindResume | UnwindTerminate(_) | Unreachable | GeneratorDrop => vec![],
|
|
|
|
Goto { .. } => vec!["".into()],
|
|
|
|
SwitchInt { ref targets, .. } => targets
|
|
|
|
.values
|
|
|
|
.iter()
|
|
|
|
.map(|&u| Cow::Owned(u.to_string()))
|
|
|
|
.chain(iter::once("otherwise".into()))
|
|
|
|
.collect(),
|
|
|
|
Call { target: Some(_), unwind: UnwindAction::Cleanup(_), .. } => {
|
|
|
|
vec!["return".into(), "unwind".into()]
|
|
|
|
}
|
|
|
|
Call { target: Some(_), unwind: _, .. } => vec!["return".into()],
|
|
|
|
Call { target: None, unwind: UnwindAction::Cleanup(_), .. } => vec!["unwind".into()],
|
|
|
|
Call { target: None, unwind: _, .. } => vec![],
|
|
|
|
Yield { drop: Some(_), .. } => vec!["resume".into(), "drop".into()],
|
|
|
|
Yield { drop: None, .. } => vec!["resume".into()],
|
|
|
|
Drop { unwind: UnwindAction::Cleanup(_), .. } => vec!["return".into(), "unwind".into()],
|
|
|
|
Drop { unwind: _, .. } => vec!["return".into()],
|
|
|
|
Assert { unwind: UnwindAction::Cleanup(_), .. } => {
|
|
|
|
vec!["success".into(), "unwind".into()]
|
|
|
|
}
|
|
|
|
Assert { unwind: _, .. } => vec!["success".into()],
|
|
|
|
FalseEdge { .. } => vec!["real".into(), "imaginary".into()],
|
|
|
|
FalseUnwind { unwind: UnwindAction::Cleanup(_), .. } => {
|
|
|
|
vec!["real".into(), "unwind".into()]
|
|
|
|
}
|
|
|
|
FalseUnwind { unwind: _, .. } => vec!["real".into()],
|
|
|
|
InlineAsm { destination: Some(_), unwind: UnwindAction::Cleanup(_), .. } => {
|
|
|
|
vec!["return".into(), "unwind".into()]
|
|
|
|
}
|
|
|
|
InlineAsm { destination: Some(_), unwind: _, .. } => {
|
|
|
|
vec!["return".into()]
|
|
|
|
}
|
|
|
|
InlineAsm { destination: None, unwind: UnwindAction::Cleanup(_), .. } => {
|
|
|
|
vec!["unwind".into()]
|
|
|
|
}
|
|
|
|
InlineAsm { destination: None, unwind: _, .. } => vec![],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'tcx> Debug for Rvalue<'tcx> {
|
|
|
|
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
|
|
|
use self::Rvalue::*;
|
|
|
|
|
|
|
|
match *self {
|
|
|
|
Use(ref place) => write!(fmt, "{place:?}"),
|
|
|
|
Repeat(ref a, b) => {
|
|
|
|
write!(fmt, "[{a:?}; ")?;
|
|
|
|
pretty_print_const(b, fmt, false)?;
|
|
|
|
write!(fmt, "]")
|
|
|
|
}
|
|
|
|
Len(ref a) => write!(fmt, "Len({a:?})"),
|
|
|
|
Cast(ref kind, ref place, ref ty) => {
|
|
|
|
with_no_trimmed_paths!(write!(fmt, "{place:?} as {ty} ({kind:?})"))
|
|
|
|
}
|
|
|
|
BinaryOp(ref op, box (ref a, ref b)) => write!(fmt, "{op:?}({a:?}, {b:?})"),
|
|
|
|
CheckedBinaryOp(ref op, box (ref a, ref b)) => {
|
|
|
|
write!(fmt, "Checked{op:?}({a:?}, {b:?})")
|
|
|
|
}
|
|
|
|
UnaryOp(ref op, ref a) => write!(fmt, "{op:?}({a:?})"),
|
|
|
|
Discriminant(ref place) => write!(fmt, "discriminant({place:?})"),
|
|
|
|
NullaryOp(ref op, ref t) => {
|
|
|
|
let t = with_no_trimmed_paths!(format!("{}", t));
|
|
|
|
match op {
|
|
|
|
NullOp::SizeOf => write!(fmt, "SizeOf({t})"),
|
|
|
|
NullOp::AlignOf => write!(fmt, "AlignOf({t})"),
|
|
|
|
NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({t}, {fields:?})"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ThreadLocalRef(did) => ty::tls::with(|tcx| {
|
|
|
|
let muta = tcx.static_mutability(did).unwrap().prefix_str();
|
|
|
|
write!(fmt, "&/*tls*/ {}{}", muta, tcx.def_path_str(did))
|
|
|
|
}),
|
|
|
|
Ref(region, borrow_kind, ref place) => {
|
|
|
|
let kind_str = match borrow_kind {
|
|
|
|
BorrowKind::Shared => "",
|
|
|
|
BorrowKind::Shallow => "shallow ",
|
|
|
|
BorrowKind::Mut { .. } => "mut ",
|
|
|
|
};
|
|
|
|
|
|
|
|
// When printing regions, add trailing space if necessary.
|
|
|
|
let print_region = ty::tls::with(|tcx| {
|
|
|
|
tcx.sess.verbose() || tcx.sess.opts.unstable_opts.identify_regions
|
|
|
|
});
|
|
|
|
let region = if print_region {
|
|
|
|
let mut region = region.to_string();
|
|
|
|
if !region.is_empty() {
|
|
|
|
region.push(' ');
|
|
|
|
}
|
|
|
|
region
|
|
|
|
} else {
|
|
|
|
// Do not even print 'static
|
|
|
|
String::new()
|
|
|
|
};
|
|
|
|
write!(fmt, "&{region}{kind_str}{place:?}")
|
|
|
|
}
|
|
|
|
|
|
|
|
CopyForDeref(ref place) => write!(fmt, "deref_copy {place:#?}"),
|
|
|
|
|
|
|
|
AddressOf(mutability, ref place) => {
|
|
|
|
let kind_str = match mutability {
|
|
|
|
Mutability::Mut => "mut",
|
|
|
|
Mutability::Not => "const",
|
|
|
|
};
|
|
|
|
|
|
|
|
write!(fmt, "&raw {kind_str} {place:?}")
|
|
|
|
}
|
|
|
|
|
|
|
|
Aggregate(ref kind, ref places) => {
|
|
|
|
let fmt_tuple = |fmt: &mut Formatter<'_>, name: &str| {
|
|
|
|
let mut tuple_fmt = fmt.debug_tuple(name);
|
|
|
|
for place in places {
|
|
|
|
tuple_fmt.field(place);
|
|
|
|
}
|
|
|
|
tuple_fmt.finish()
|
|
|
|
};
|
|
|
|
|
|
|
|
match **kind {
|
|
|
|
AggregateKind::Array(_) => write!(fmt, "{places:?}"),
|
|
|
|
|
|
|
|
AggregateKind::Tuple => {
|
|
|
|
if places.is_empty() {
|
|
|
|
write!(fmt, "()")
|
|
|
|
} else {
|
|
|
|
fmt_tuple(fmt, "")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
AggregateKind::Adt(adt_did, variant, args, _user_ty, _) => {
|
|
|
|
ty::tls::with(|tcx| {
|
|
|
|
let variant_def = &tcx.adt_def(adt_did).variant(variant);
|
|
|
|
let args = tcx.lift(args).expect("could not lift for printing");
|
|
|
|
let name = FmtPrinter::new(tcx, Namespace::ValueNS)
|
|
|
|
.print_def_path(variant_def.def_id, args)?
|
|
|
|
.into_buffer();
|
|
|
|
|
|
|
|
match variant_def.ctor_kind() {
|
|
|
|
Some(CtorKind::Const) => fmt.write_str(&name),
|
|
|
|
Some(CtorKind::Fn) => fmt_tuple(fmt, &name),
|
|
|
|
None => {
|
|
|
|
let mut struct_fmt = fmt.debug_struct(&name);
|
|
|
|
for (field, place) in iter::zip(&variant_def.fields, places) {
|
|
|
|
struct_fmt.field(field.name.as_str(), place);
|
|
|
|
}
|
|
|
|
struct_fmt.finish()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
AggregateKind::Closure(def_id, args) => ty::tls::with(|tcx| {
|
|
|
|
let name = if tcx.sess.opts.unstable_opts.span_free_formats {
|
|
|
|
let args = tcx.lift(args).unwrap();
|
2023-09-09 08:36:50 +02:00
|
|
|
format!("{{closure@{}}}", tcx.def_path_str_with_args(def_id, args),)
|
2023-09-15 08:49:37 +02:00
|
|
|
} else {
|
|
|
|
let span = tcx.def_span(def_id);
|
|
|
|
format!(
|
2023-09-09 08:36:50 +02:00
|
|
|
"{{closure@{}}}",
|
2023-09-15 08:49:37 +02:00
|
|
|
tcx.sess.source_map().span_to_diagnostic_string(span)
|
|
|
|
)
|
|
|
|
};
|
|
|
|
let mut struct_fmt = fmt.debug_struct(&name);
|
|
|
|
|
|
|
|
// FIXME(project-rfc-2229#48): This should be a list of capture names/places
|
|
|
|
if let Some(def_id) = def_id.as_local()
|
|
|
|
&& let Some(upvars) = tcx.upvars_mentioned(def_id)
|
|
|
|
{
|
|
|
|
for (&var_id, place) in iter::zip(upvars.keys(), places) {
|
|
|
|
let var_name = tcx.hir().name(var_id);
|
|
|
|
struct_fmt.field(var_name.as_str(), place);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (index, place) in places.iter().enumerate() {
|
|
|
|
struct_fmt.field(&format!("{index}"), place);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct_fmt.finish()
|
|
|
|
}),
|
|
|
|
|
|
|
|
AggregateKind::Generator(def_id, _, _) => ty::tls::with(|tcx| {
|
2023-09-09 08:36:50 +02:00
|
|
|
let name = format!("{{generator@{:?}}}", tcx.def_span(def_id));
|
2023-09-15 08:49:37 +02:00
|
|
|
let mut struct_fmt = fmt.debug_struct(&name);
|
|
|
|
|
|
|
|
// FIXME(project-rfc-2229#48): This should be a list of capture names/places
|
|
|
|
if let Some(def_id) = def_id.as_local()
|
|
|
|
&& let Some(upvars) = tcx.upvars_mentioned(def_id)
|
|
|
|
{
|
|
|
|
for (&var_id, place) in iter::zip(upvars.keys(), places) {
|
|
|
|
let var_name = tcx.hir().name(var_id);
|
|
|
|
struct_fmt.field(var_name.as_str(), place);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (index, place) in places.iter().enumerate() {
|
|
|
|
struct_fmt.field(&format!("{index}"), place);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct_fmt.finish()
|
|
|
|
}),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ShallowInitBox(ref place, ref ty) => {
|
|
|
|
with_no_trimmed_paths!(write!(fmt, "ShallowInitBox({place:?}, {ty})"))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'tcx> Debug for Operand<'tcx> {
|
|
|
|
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
|
|
|
use self::Operand::*;
|
|
|
|
match *self {
|
|
|
|
Constant(ref a) => write!(fmt, "{a:?}"),
|
|
|
|
Copy(ref place) => write!(fmt, "{place:?}"),
|
|
|
|
Move(ref place) => write!(fmt, "move {place:?}"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-20 20:51:14 +02:00
|
|
|
impl<'tcx> Debug for ConstOperand<'tcx> {
|
|
|
|
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
|
|
|
write!(fmt, "{self}")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'tcx> Display for ConstOperand<'tcx> {
|
|
|
|
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
|
|
|
match self.ty().kind() {
|
|
|
|
ty::FnDef(..) => {}
|
|
|
|
_ => write!(fmt, "const ")?,
|
|
|
|
}
|
|
|
|
Display::fmt(&self.const_, fmt)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-15 08:49:37 +02:00
|
|
|
impl Debug for Place<'_> {
|
|
|
|
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
|
|
|
self.as_ref().fmt(fmt)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Debug for PlaceRef<'_> {
|
|
|
|
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
|
|
|
pre_fmt_projection(self.projection, fmt)?;
|
|
|
|
write!(fmt, "{:?}", self.local)?;
|
|
|
|
post_fmt_projection(self.projection, fmt)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn pre_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> fmt::Result {
|
|
|
|
for &elem in projection.iter().rev() {
|
|
|
|
match elem {
|
|
|
|
ProjectionElem::OpaqueCast(_)
|
2023-08-28 11:19:19 +03:00
|
|
|
| ProjectionElem::Subtype(_)
|
2023-09-15 08:49:37 +02:00
|
|
|
| ProjectionElem::Downcast(_, _)
|
|
|
|
| ProjectionElem::Field(_, _) => {
|
|
|
|
write!(fmt, "(").unwrap();
|
|
|
|
}
|
|
|
|
ProjectionElem::Deref => {
|
|
|
|
write!(fmt, "(*").unwrap();
|
|
|
|
}
|
|
|
|
ProjectionElem::Index(_)
|
|
|
|
| ProjectionElem::ConstantIndex { .. }
|
|
|
|
| ProjectionElem::Subslice { .. } => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2017-11-22 17:33:26 -05:00
|
|
|
|
2023-09-15 08:49:37 +02:00
|
|
|
fn post_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> fmt::Result {
|
|
|
|
for &elem in projection.iter() {
|
|
|
|
match elem {
|
|
|
|
ProjectionElem::OpaqueCast(ty) => {
|
|
|
|
write!(fmt, " as {ty})")?;
|
|
|
|
}
|
2023-08-28 11:19:19 +03:00
|
|
|
ProjectionElem::Subtype(ty) => {
|
2023-10-02 11:22:48 +03:00
|
|
|
write!(fmt, " as subtype {ty})")?;
|
2023-08-28 11:19:19 +03:00
|
|
|
}
|
2023-09-15 08:49:37 +02:00
|
|
|
ProjectionElem::Downcast(Some(name), _index) => {
|
|
|
|
write!(fmt, " as {name})")?;
|
|
|
|
}
|
|
|
|
ProjectionElem::Downcast(None, index) => {
|
|
|
|
write!(fmt, " as variant#{index:?})")?;
|
|
|
|
}
|
|
|
|
ProjectionElem::Deref => {
|
|
|
|
write!(fmt, ")")?;
|
|
|
|
}
|
|
|
|
ProjectionElem::Field(field, ty) => {
|
|
|
|
with_no_trimmed_paths!(write!(fmt, ".{:?}: {})", field.index(), ty)?);
|
|
|
|
}
|
|
|
|
ProjectionElem::Index(ref index) => {
|
|
|
|
write!(fmt, "[{index:?}]")?;
|
|
|
|
}
|
|
|
|
ProjectionElem::ConstantIndex { offset, min_length, from_end: false } => {
|
|
|
|
write!(fmt, "[{offset:?} of {min_length:?}]")?;
|
|
|
|
}
|
|
|
|
ProjectionElem::ConstantIndex { offset, min_length, from_end: true } => {
|
|
|
|
write!(fmt, "[-{offset:?} of {min_length:?}]")?;
|
|
|
|
}
|
2023-09-27 23:48:47 +02:00
|
|
|
ProjectionElem::Subslice { from, to: 0, from_end: true } => {
|
2023-09-15 08:49:37 +02:00
|
|
|
write!(fmt, "[{from:?}:]")?;
|
|
|
|
}
|
2023-09-27 23:48:47 +02:00
|
|
|
ProjectionElem::Subslice { from: 0, to, from_end: true } => {
|
2023-09-15 08:49:37 +02:00
|
|
|
write!(fmt, "[:-{to:?}]")?;
|
|
|
|
}
|
|
|
|
ProjectionElem::Subslice { from, to, from_end: true } => {
|
|
|
|
write!(fmt, "[{from:?}:-{to:?}]")?;
|
|
|
|
}
|
|
|
|
ProjectionElem::Subslice { from, to, from_end: false } => {
|
|
|
|
write!(fmt, "[{from:?}..{to:?}]")?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-12-29 20:06:19 -06:00
|
|
|
|
2023-09-15 08:49:37 +02:00
|
|
|
Ok(())
|
2015-12-29 20:06:19 -06:00
|
|
|
}
|
|
|
|
|
2017-11-22 17:33:26 -05:00
|
|
|
/// After we print the main statement, we sometimes dump extra
|
|
|
|
/// information. There's often a lot of little things "nuzzled up" in
|
|
|
|
/// a statement.
|
2023-09-15 08:49:37 +02:00
|
|
|
fn write_extra<'tcx, F>(
|
|
|
|
tcx: TyCtxt<'tcx>,
|
|
|
|
write: &mut dyn io::Write,
|
|
|
|
mut visit_op: F,
|
|
|
|
) -> io::Result<()>
|
2017-11-17 14:08:47 -05:00
|
|
|
where
|
2019-06-14 00:48:52 +03:00
|
|
|
F: FnMut(&mut ExtraComments<'tcx>),
|
2017-11-22 17:33:26 -05:00
|
|
|
{
|
2023-06-06 09:47:00 -04:00
|
|
|
if tcx.sess.opts.unstable_opts.mir_include_spans {
|
|
|
|
let mut extra_comments = ExtraComments { tcx, comments: vec![] };
|
|
|
|
visit_op(&mut extra_comments);
|
|
|
|
for comment in extra_comments.comments {
|
|
|
|
writeln!(write, "{:A$} // {}", "", comment, A = ALIGN)?;
|
|
|
|
}
|
2017-11-22 17:33:26 -05:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-06-14 00:48:52 +03:00
|
|
|
struct ExtraComments<'tcx> {
|
2020-06-04 23:05:28 -04:00
|
|
|
tcx: TyCtxt<'tcx>,
|
2017-11-22 17:33:26 -05:00
|
|
|
comments: Vec<String>,
|
|
|
|
}
|
|
|
|
|
2021-12-15 19:32:30 -05:00
|
|
|
impl<'tcx> ExtraComments<'tcx> {
|
2017-11-22 17:33:26 -05:00
|
|
|
fn push(&mut self, lines: &str) {
|
2018-07-23 15:32:57 +02:00
|
|
|
for line in lines.split('\n') {
|
2017-11-22 17:33:26 -05:00
|
|
|
self.comments.push(line.to_string());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-20 22:10:40 +01:00
|
|
|
fn use_verbose(ty: Ty<'_>, fn_def: bool) -> bool {
|
Overhaul `TyS` and `Ty`.
Specifically, change `Ty` from this:
```
pub type Ty<'tcx> = &'tcx TyS<'tcx>;
```
to this
```
pub struct Ty<'tcx>(Interned<'tcx, TyS<'tcx>>);
```
There are two benefits to this.
- It's now a first class type, so we can define methods on it. This
means we can move a lot of methods away from `TyS`, leaving `TyS` as a
barely-used type, which is appropriate given that it's not meant to
be used directly.
- The uniqueness requirement is now explicit, via the `Interned` type.
E.g. the pointer-based `Eq` and `Hash` comes from `Interned`, rather
than via `TyS`, which wasn't obvious at all.
Much of this commit is boring churn. The interesting changes are in
these files:
- compiler/rustc_middle/src/arena.rs
- compiler/rustc_middle/src/mir/visit.rs
- compiler/rustc_middle/src/ty/context.rs
- compiler/rustc_middle/src/ty/mod.rs
Specifically:
- Most mentions of `TyS` are removed. It's very much a dumb struct now;
`Ty` has all the smarts.
- `TyS` now has `crate` visibility instead of `pub`.
- `TyS::make_for_test` is removed in favour of the static `BOOL_TY`,
which just works better with the new structure.
- The `Eq`/`Ord`/`Hash` impls are removed from `TyS`. `Interned`s impls
of `Eq`/`Hash` now suffice. `Ord` is now partly on `Interned`
(pointer-based, for the `Equal` case) and partly on `TyS`
(contents-based, for the other cases).
- There are many tedious sigil adjustments, i.e. adding or removing `*`
or `&`. They seem to be unavoidable.
2022-01-25 14:13:38 +11:00
|
|
|
match *ty.kind() {
|
2020-12-13 04:14:08 +00:00
|
|
|
ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char | ty::Float(_) => false,
|
|
|
|
// Unit type
|
|
|
|
ty::Tuple(g_args) if g_args.is_empty() => false,
|
2022-02-07 16:06:31 +01:00
|
|
|
ty::Tuple(g_args) => g_args.iter().any(|g_arg| use_verbose(g_arg, fn_def)),
|
2021-06-23 10:39:23 +08:00
|
|
|
ty::Array(ty, _) => use_verbose(ty, fn_def),
|
|
|
|
ty::FnDef(..) => fn_def,
|
2020-12-13 04:14:08 +00:00
|
|
|
_ => true,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-15 19:32:30 -05:00
|
|
|
impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
|
2023-09-20 20:51:14 +02:00
|
|
|
fn visit_constant(&mut self, constant: &ConstOperand<'tcx>, _location: Location) {
|
|
|
|
let ConstOperand { span, user_ty, const_ } = constant;
|
|
|
|
if use_verbose(const_.ty(), true) {
|
|
|
|
self.push("mir::ConstOperand");
|
2021-06-23 10:39:23 +08:00
|
|
|
self.push(&format!(
|
|
|
|
"+ span: {}",
|
|
|
|
self.tcx.sess.source_map().span_to_embeddable_string(*span)
|
|
|
|
));
|
|
|
|
if let Some(user_ty) = user_ty {
|
2023-07-25 22:00:13 +02:00
|
|
|
self.push(&format!("+ user_ty: {user_ty:?}"));
|
2021-06-23 10:39:23 +08:00
|
|
|
}
|
2017-11-22 17:33:26 -05:00
|
|
|
|
2023-09-15 08:21:40 +02:00
|
|
|
let fmt_val = |val: ConstValue<'tcx>, ty: Ty<'tcx>| {
|
|
|
|
let tcx = self.tcx;
|
|
|
|
rustc_data_structures::make_display(move |fmt| {
|
|
|
|
pretty_print_const_value_tcx(tcx, val, ty, fmt)
|
|
|
|
})
|
2022-05-06 10:30:29 +02:00
|
|
|
};
|
|
|
|
|
2023-09-15 08:21:40 +02:00
|
|
|
// FIXME: call pretty_print_const_valtree?
|
2022-02-16 10:56:01 +01:00
|
|
|
let fmt_valtree = |valtree: &ty::ValTree<'tcx>| match valtree {
|
2023-09-15 08:21:40 +02:00
|
|
|
ty::ValTree::Leaf(leaf) => format!("Leaf({leaf:?})"),
|
|
|
|
ty::ValTree::Branch(_) => "Branch(..)".to_string(),
|
2022-02-16 10:56:01 +01:00
|
|
|
};
|
|
|
|
|
2023-09-20 20:51:14 +02:00
|
|
|
let val = match const_ {
|
|
|
|
Const::Ty(ct) => match ct.kind() {
|
2023-09-15 08:21:40 +02:00
|
|
|
ty::ConstKind::Param(p) => format!("ty::Param({p})"),
|
2022-06-27 16:32:47 +02:00
|
|
|
ty::ConstKind::Unevaluated(uv) => {
|
2023-09-15 08:21:40 +02:00
|
|
|
format!("ty::Unevaluated({}, {:?})", self.tcx.def_path_str(uv.def), uv.args,)
|
2022-06-27 16:32:47 +02:00
|
|
|
}
|
2023-09-15 08:21:40 +02:00
|
|
|
ty::ConstKind::Value(val) => format!("ty::Valtree({})", fmt_valtree(&val)),
|
|
|
|
// No `ty::` prefix since we also use this to represent errors from `mir::Unevaluated`.
|
2022-02-21 08:10:07 +01:00
|
|
|
ty::ConstKind::Error(_) => "Error".to_string(),
|
|
|
|
// These variants shouldn't exist in the MIR.
|
|
|
|
ty::ConstKind::Placeholder(_)
|
|
|
|
| ty::ConstKind::Infer(_)
|
2022-07-27 07:27:52 +00:00
|
|
|
| ty::ConstKind::Expr(_)
|
2023-09-20 20:51:14 +02:00
|
|
|
| ty::ConstKind::Bound(..) => bug!("unexpected MIR constant: {:?}", const_),
|
2022-02-21 08:10:07 +01:00
|
|
|
},
|
2023-09-20 20:51:14 +02:00
|
|
|
Const::Unevaluated(uv, _) => {
|
2022-06-27 16:32:47 +02:00
|
|
|
format!(
|
|
|
|
"Unevaluated({}, {:?}, {:?})",
|
2022-05-08 15:53:19 +02:00
|
|
|
self.tcx.def_path_str(uv.def),
|
2023-07-11 22:35:29 +01:00
|
|
|
uv.args,
|
2022-06-27 16:32:47 +02:00
|
|
|
uv.promoted,
|
|
|
|
)
|
|
|
|
}
|
2023-09-20 20:51:14 +02:00
|
|
|
Const::Val(val, ty) => format!("Value({})", fmt_val(*val, *ty)),
|
2021-03-29 16:35:21 +00:00
|
|
|
};
|
2022-02-21 08:10:07 +01:00
|
|
|
|
2022-06-10 11:18:06 +10:00
|
|
|
// This reflects what `Const` looked liked before `val` was renamed
|
|
|
|
// as `kind`. We print it like this to avoid having to update
|
|
|
|
// expected output in a lot of tests.
|
2023-09-20 20:51:14 +02:00
|
|
|
self.push(&format!("+ const_: Const {{ ty: {}, val: {} }}", const_.ty(), val));
|
2020-08-15 13:04:18 -04:00
|
|
|
}
|
2017-11-22 17:33:26 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
|
|
|
|
self.super_rvalue(rvalue, location);
|
2020-03-22 13:36:56 +01:00
|
|
|
if let Rvalue::Aggregate(kind, _) = rvalue {
|
|
|
|
match **kind {
|
2023-07-11 22:35:29 +01:00
|
|
|
AggregateKind::Closure(def_id, args) => {
|
2018-07-28 14:40:32 +02:00
|
|
|
self.push("closure");
|
2023-07-25 22:00:13 +02:00
|
|
|
self.push(&format!("+ def_id: {def_id:?}"));
|
|
|
|
self.push(&format!("+ args: {args:#?}"));
|
2017-11-22 17:33:26 -05:00
|
|
|
}
|
|
|
|
|
2023-07-11 22:35:29 +01:00
|
|
|
AggregateKind::Generator(def_id, args, movability) => {
|
2018-07-28 14:40:32 +02:00
|
|
|
self.push("generator");
|
2023-07-25 22:00:13 +02:00
|
|
|
self.push(&format!("+ def_id: {def_id:?}"));
|
|
|
|
self.push(&format!("+ args: {args:#?}"));
|
|
|
|
self.push(&format!("+ movability: {movability:?}"));
|
2017-11-22 17:33:26 -05:00
|
|
|
}
|
|
|
|
|
2018-08-09 12:24:52 -04:00
|
|
|
AggregateKind::Adt(_, _, _, Some(user_ty), _) => {
|
|
|
|
self.push("adt");
|
2023-07-25 22:00:13 +02:00
|
|
|
self.push(&format!("+ user_ty: {user_ty:?}"));
|
2018-08-09 12:24:52 -04:00
|
|
|
}
|
|
|
|
|
2017-11-22 17:33:26 -05:00
|
|
|
_ => {}
|
2020-03-22 13:36:56 +01:00
|
|
|
}
|
2017-11-22 17:33:26 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-06 09:47:00 -04:00
|
|
|
fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo) -> String {
|
|
|
|
let location = tcx.sess.source_map().span_to_embeddable_string(span);
|
2022-07-27 20:42:07 +02:00
|
|
|
format!("scope {} at {}", scope.index(), location,)
|
2016-03-22 10:08:44 -04:00
|
|
|
}
|
|
|
|
|
2023-09-15 08:49:37 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// Allocations
|
2016-04-28 16:13:44 +02:00
|
|
|
|
2020-03-11 11:49:00 +01:00
|
|
|
/// Find all `AllocId`s mentioned (recursively) in the MIR body and print their corresponding
|
|
|
|
/// allocations.
|
|
|
|
pub fn write_allocations<'tcx>(
|
|
|
|
tcx: TyCtxt<'tcx>,
|
|
|
|
body: &Body<'_>,
|
2023-09-15 08:49:37 +02:00
|
|
|
w: &mut dyn io::Write,
|
2020-03-11 11:49:00 +01:00
|
|
|
) -> io::Result<()> {
|
Introduce `ConstAllocation`.
Currently some `Allocation`s are interned, some are not, and it's very
hard to tell at a use point which is which.
This commit introduces `ConstAllocation` for the known-interned ones,
which makes the division much clearer. `ConstAllocation::inner()` is
used to get the underlying `Allocation`.
In some places it's natural to use an `Allocation`, in some it's natural
to use a `ConstAllocation`, and in some places there's no clear choice.
I've tried to make things look as nice as possible, while generally
favouring `ConstAllocation`, which is the type that embodies more
information. This does require quite a few calls to `inner()`.
The commit also tweaks how `PartialOrd` works for `Interned`. The
previous code was too clever by half, building on `T: Ord` to make the
code shorter. That caused problems with deriving `PartialOrd` and `Ord`
for `ConstAllocation`, so I changed it to build on `T: PartialOrd`,
which is slightly more verbose but much more standard and avoided the
problems.
2022-03-02 07:15:04 +11:00
|
|
|
fn alloc_ids_from_alloc(
|
|
|
|
alloc: ConstAllocation<'_>,
|
|
|
|
) -> impl DoubleEndedIterator<Item = AllocId> + '_ {
|
2022-11-06 13:00:09 +01:00
|
|
|
alloc.inner().provenance().ptrs().values().map(|id| *id)
|
2020-03-11 11:49:00 +01:00
|
|
|
}
|
2022-02-16 10:56:01 +01:00
|
|
|
|
|
|
|
fn alloc_ids_from_const_val(val: ConstValue<'_>) -> impl Iterator<Item = AllocId> + '_ {
|
2020-03-11 11:49:00 +01:00
|
|
|
match val {
|
2022-03-07 02:18:36 +09:00
|
|
|
ConstValue::Scalar(interpret::Scalar::Ptr(ptr, _)) => {
|
2023-09-11 20:01:48 +02:00
|
|
|
Either::Left(std::iter::once(ptr.provenance))
|
2020-03-11 11:49:00 +01:00
|
|
|
}
|
2023-09-11 20:01:48 +02:00
|
|
|
ConstValue::Scalar(interpret::Scalar::Int { .. }) => Either::Right(std::iter::empty()),
|
|
|
|
ConstValue::ZeroSized => Either::Right(std::iter::empty()),
|
|
|
|
ConstValue::Slice { .. } => {
|
|
|
|
// `u8`/`str` slices, shouldn't contain pointers that we want to print.
|
|
|
|
Either::Right(std::iter::empty())
|
2020-03-11 11:49:00 +01:00
|
|
|
}
|
2023-09-12 07:49:25 +02:00
|
|
|
ConstValue::Indirect { alloc_id, .. } => {
|
|
|
|
// FIXME: we don't actually want to print all of these, since some are printed nicely directly as values inline in MIR.
|
|
|
|
// Really we'd want `pretty_print_const_value` to decide which allocations to print, instead of having a separate visitor.
|
|
|
|
Either::Left(std::iter::once(alloc_id))
|
|
|
|
}
|
2020-03-11 11:49:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
struct CollectAllocIds(BTreeSet<AllocId>);
|
2022-03-07 13:51:59 +01:00
|
|
|
|
|
|
|
impl<'tcx> Visitor<'tcx> for CollectAllocIds {
|
2023-09-20 20:51:14 +02:00
|
|
|
fn visit_constant(&mut self, c: &ConstOperand<'tcx>, _: Location) {
|
|
|
|
match c.const_ {
|
|
|
|
Const::Ty(_) | Const::Unevaluated(..) => {}
|
|
|
|
Const::Val(val, _) => {
|
2022-02-16 10:56:01 +01:00
|
|
|
self.0.extend(alloc_ids_from_const_val(val));
|
2022-03-07 13:51:59 +01:00
|
|
|
}
|
|
|
|
}
|
2020-03-11 11:49:00 +01:00
|
|
|
}
|
|
|
|
}
|
2022-03-07 13:51:59 +01:00
|
|
|
|
2020-03-11 11:49:00 +01:00
|
|
|
let mut visitor = CollectAllocIds(Default::default());
|
2022-03-07 13:51:59 +01:00
|
|
|
visitor.visit_body(body);
|
|
|
|
|
2020-04-04 13:21:25 +02:00
|
|
|
// `seen` contains all seen allocations, including the ones we have *not* printed yet.
|
|
|
|
// The protocol is to first `insert` into `seen`, and only if that returns `true`
|
|
|
|
// then push to `todo`.
|
2020-03-11 11:49:00 +01:00
|
|
|
let mut seen = visitor.0;
|
|
|
|
let mut todo: Vec<_> = seen.iter().copied().collect();
|
|
|
|
while let Some(id) = todo.pop() {
|
2020-04-04 13:21:25 +02:00
|
|
|
let mut write_allocation_track_relocs =
|
2023-09-15 08:49:37 +02:00
|
|
|
|w: &mut dyn io::Write, alloc: ConstAllocation<'tcx>| -> io::Result<()> {
|
2020-04-04 13:21:25 +02:00
|
|
|
// `.rev()` because we are popping them from the back of the `todo` vector.
|
|
|
|
for id in alloc_ids_from_alloc(alloc).rev() {
|
|
|
|
if seen.insert(id) {
|
|
|
|
todo.push(id);
|
2020-03-11 11:49:00 +01:00
|
|
|
}
|
|
|
|
}
|
Introduce `ConstAllocation`.
Currently some `Allocation`s are interned, some are not, and it's very
hard to tell at a use point which is which.
This commit introduces `ConstAllocation` for the known-interned ones,
which makes the division much clearer. `ConstAllocation::inner()` is
used to get the underlying `Allocation`.
In some places it's natural to use an `Allocation`, in some it's natural
to use a `ConstAllocation`, and in some places there's no clear choice.
I've tried to make things look as nice as possible, while generally
favouring `ConstAllocation`, which is the type that embodies more
information. This does require quite a few calls to `inner()`.
The commit also tweaks how `PartialOrd` works for `Interned`. The
previous code was too clever by half, building on `T: Ord` to make the
code shorter. That caused problems with deriving `PartialOrd` and `Ord`
for `ConstAllocation`, so I changed it to build on `T: PartialOrd`,
which is slightly more verbose but much more standard and avoided the
problems.
2022-03-02 07:15:04 +11:00
|
|
|
write!(w, "{}", display_allocation(tcx, alloc.inner()))
|
2020-03-11 11:49:00 +01:00
|
|
|
};
|
2022-07-02 10:53:34 -04:00
|
|
|
write!(w, "\n{id:?}")?;
|
2022-07-17 11:40:34 -04:00
|
|
|
match tcx.try_get_global_alloc(id) {
|
2020-03-11 11:49:00 +01:00
|
|
|
// This can't really happen unless there are bugs, but it doesn't cost us anything to
|
|
|
|
// gracefully handle it and allow buggy rustc to be debugged via allocation printing.
|
|
|
|
None => write!(w, " (deallocated)")?,
|
2022-07-02 10:53:34 -04:00
|
|
|
Some(GlobalAlloc::Function(inst)) => write!(w, " (fn: {inst})")?,
|
2022-07-19 19:57:44 -04:00
|
|
|
Some(GlobalAlloc::VTable(ty, Some(trait_ref))) => {
|
2022-07-17 11:40:34 -04:00
|
|
|
write!(w, " (vtable: impl {trait_ref} for {ty})")?
|
|
|
|
}
|
2022-07-19 19:57:44 -04:00
|
|
|
Some(GlobalAlloc::VTable(ty, None)) => {
|
|
|
|
write!(w, " (vtable: impl <auto trait> for {ty})")?
|
|
|
|
}
|
2020-03-11 11:49:00 +01:00
|
|
|
Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => {
|
2020-07-31 13:27:54 +02:00
|
|
|
match tcx.eval_static_initializer(did) {
|
|
|
|
Ok(alloc) => {
|
2020-03-11 11:49:00 +01:00
|
|
|
write!(w, " (static: {}, ", tcx.def_path_str(did))?;
|
2020-04-04 13:21:25 +02:00
|
|
|
write_allocation_track_relocs(w, alloc)?;
|
2020-03-11 11:49:00 +01:00
|
|
|
}
|
|
|
|
Err(_) => write!(
|
|
|
|
w,
|
|
|
|
" (static: {}, error during initializer evaluation)",
|
|
|
|
tcx.def_path_str(did)
|
|
|
|
)?,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Some(GlobalAlloc::Static(did)) => {
|
|
|
|
write!(w, " (extern static: {})", tcx.def_path_str(did))?
|
|
|
|
}
|
|
|
|
Some(GlobalAlloc::Memory(alloc)) => {
|
|
|
|
write!(w, " (")?;
|
2020-04-04 13:21:25 +02:00
|
|
|
write_allocation_track_relocs(w, alloc)?
|
2020-03-11 11:49:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
writeln!(w)?;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-04-04 13:21:25 +02:00
|
|
|
/// Dumps the size and metadata and content of an allocation to the given writer.
|
|
|
|
/// The expectation is that the caller first prints other relevant metadata, so the exact
|
|
|
|
/// format of this function is (*without* leading or trailing newline):
|
2020-10-04 19:08:40 -07:00
|
|
|
///
|
|
|
|
/// ```text
|
2020-04-04 13:21:25 +02:00
|
|
|
/// size: {}, align: {}) {
|
|
|
|
/// <bytes>
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// The byte format is similar to how hex editors print bytes. Each line starts with the address of
|
|
|
|
/// the start of the line, followed by all bytes in hex format (space separated).
|
|
|
|
/// If the allocation is small enough to fit into a single line, no start address is given.
|
|
|
|
/// After the hex dump, an ascii dump follows, replacing all unprintable characters (control
|
|
|
|
/// characters or characters whose value is larger than 127) with a `.`
|
2022-08-27 14:11:19 -04:00
|
|
|
/// This also prints provenance adequately.
|
2023-02-13 22:30:53 -05:00
|
|
|
pub fn display_allocation<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>(
|
2020-04-04 13:21:25 +02:00
|
|
|
tcx: TyCtxt<'tcx>,
|
2023-02-13 22:30:53 -05:00
|
|
|
alloc: &'a Allocation<Prov, Extra, Bytes>,
|
|
|
|
) -> RenderAllocation<'a, 'tcx, Prov, Extra, Bytes> {
|
2020-07-28 19:16:09 +02:00
|
|
|
RenderAllocation { tcx, alloc }
|
|
|
|
}
|
|
|
|
|
|
|
|
#[doc(hidden)]
|
2023-02-13 22:30:53 -05:00
|
|
|
pub struct RenderAllocation<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes> {
|
2020-07-28 19:16:09 +02:00
|
|
|
tcx: TyCtxt<'tcx>,
|
2023-02-13 22:30:53 -05:00
|
|
|
alloc: &'a Allocation<Prov, Extra, Bytes>,
|
2020-07-28 19:16:09 +02:00
|
|
|
}
|
|
|
|
|
2023-02-13 22:30:53 -05:00
|
|
|
impl<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes> std::fmt::Display
|
|
|
|
for RenderAllocation<'a, 'tcx, Prov, Extra, Bytes>
|
2021-12-15 19:48:59 -05:00
|
|
|
{
|
2020-07-28 19:16:09 +02:00
|
|
|
fn fmt(&self, w: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
let RenderAllocation { tcx, alloc } = *self;
|
2021-05-17 13:30:16 +02:00
|
|
|
write!(w, "size: {}, align: {})", alloc.size().bytes(), alloc.align.bytes())?;
|
|
|
|
if alloc.size() == Size::ZERO {
|
2020-07-28 19:16:09 +02:00
|
|
|
// We are done.
|
|
|
|
return write!(w, " {{}}");
|
|
|
|
}
|
|
|
|
// Write allocation bytes.
|
|
|
|
writeln!(w, " {{")?;
|
|
|
|
write_allocation_bytes(tcx, alloc, w, " ")?;
|
|
|
|
write!(w, "}}")?;
|
|
|
|
Ok(())
|
2020-04-04 13:21:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-28 19:16:09 +02:00
|
|
|
fn write_allocation_endline(w: &mut dyn std::fmt::Write, ascii: &str) -> std::fmt::Result {
|
2020-03-11 11:49:00 +01:00
|
|
|
for _ in 0..(BYTES_PER_LINE - ascii.chars().count()) {
|
|
|
|
write!(w, " ")?;
|
|
|
|
}
|
2023-07-25 22:00:13 +02:00
|
|
|
writeln!(w, " │ {ascii}")
|
2020-03-11 11:49:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Number of bytes to print per allocation hex dump line.
|
|
|
|
const BYTES_PER_LINE: usize = 16;
|
|
|
|
|
|
|
|
/// Prints the line start address and returns the new line start address.
|
|
|
|
fn write_allocation_newline(
|
2020-07-28 19:16:09 +02:00
|
|
|
w: &mut dyn std::fmt::Write,
|
2020-03-11 11:49:00 +01:00
|
|
|
mut line_start: Size,
|
|
|
|
ascii: &str,
|
|
|
|
pos_width: usize,
|
|
|
|
prefix: &str,
|
2020-07-28 19:16:09 +02:00
|
|
|
) -> Result<Size, std::fmt::Error> {
|
2020-03-11 11:49:00 +01:00
|
|
|
write_allocation_endline(w, ascii)?;
|
|
|
|
line_start += Size::from_bytes(BYTES_PER_LINE);
|
|
|
|
write!(w, "{}0x{:02$x} │ ", prefix, line_start.bytes(), pos_width)?;
|
|
|
|
Ok(line_start)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The `prefix` argument allows callers to add an arbitrary prefix before each line (even if there
|
|
|
|
/// is only one line). Note that your prefix should contain a trailing space as the lines are
|
|
|
|
/// printed directly after it.
|
2023-05-17 10:30:14 +00:00
|
|
|
pub fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>(
|
2020-03-11 11:49:00 +01:00
|
|
|
tcx: TyCtxt<'tcx>,
|
2023-02-13 22:30:53 -05:00
|
|
|
alloc: &Allocation<Prov, Extra, Bytes>,
|
2020-07-28 19:16:09 +02:00
|
|
|
w: &mut dyn std::fmt::Write,
|
2020-03-11 11:49:00 +01:00
|
|
|
prefix: &str,
|
2020-07-28 19:16:09 +02:00
|
|
|
) -> std::fmt::Result {
|
2021-05-17 13:30:16 +02:00
|
|
|
let num_lines = alloc.size().bytes_usize().saturating_sub(BYTES_PER_LINE);
|
2020-03-11 11:49:00 +01:00
|
|
|
// Number of chars needed to represent all line numbers.
|
2021-06-14 21:32:42 +03:00
|
|
|
let pos_width = hex_number_length(alloc.size().bytes());
|
2020-03-11 11:49:00 +01:00
|
|
|
|
|
|
|
if num_lines > 0 {
|
|
|
|
write!(w, "{}0x{:02$x} │ ", prefix, 0, pos_width)?;
|
|
|
|
} else {
|
2023-07-25 22:00:13 +02:00
|
|
|
write!(w, "{prefix}")?;
|
2020-03-11 11:49:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
let mut i = Size::ZERO;
|
|
|
|
let mut line_start = Size::ZERO;
|
|
|
|
|
|
|
|
let ptr_size = tcx.data_layout.pointer_size;
|
|
|
|
|
|
|
|
let mut ascii = String::new();
|
|
|
|
|
|
|
|
let oversized_ptr = |target: &mut String, width| {
|
|
|
|
if target.len() > width {
|
|
|
|
write!(target, " ({} ptr bytes)", ptr_size.bytes()).unwrap();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-05-17 13:30:16 +02:00
|
|
|
while i < alloc.size() {
|
2020-03-11 11:49:00 +01:00
|
|
|
// The line start already has a space. While we could remove that space from the line start
|
|
|
|
// printing and unconditionally print a space here, that would cause the single-line case
|
|
|
|
// to have a single space before it, which looks weird.
|
|
|
|
if i != line_start {
|
|
|
|
write!(w, " ")?;
|
|
|
|
}
|
2022-11-06 13:00:09 +01:00
|
|
|
if let Some(prov) = alloc.provenance().get_ptr(i) {
|
2022-08-27 14:11:19 -04:00
|
|
|
// Memory with provenance must be defined
|
2022-11-06 13:44:50 +01:00
|
|
|
assert!(alloc.init_mask().is_range_initialized(alloc_range(i, ptr_size)).is_ok());
|
2020-03-11 11:49:00 +01:00
|
|
|
let j = i.bytes_usize();
|
2020-08-08 07:53:47 -06:00
|
|
|
let offset = alloc
|
|
|
|
.inspect_with_uninit_and_ptr_outside_interpreter(j..j + ptr_size.bytes_usize());
|
2020-03-11 11:49:00 +01:00
|
|
|
let offset = read_target_uint(tcx.data_layout.endian, offset).unwrap();
|
2020-04-26 15:58:53 +02:00
|
|
|
let offset = Size::from_bytes(offset);
|
2022-08-27 14:11:19 -04:00
|
|
|
let provenance_width = |bytes| bytes * 3;
|
2022-07-18 18:47:31 -04:00
|
|
|
let ptr = Pointer::new(prov, offset);
|
2023-07-25 22:00:13 +02:00
|
|
|
let mut target = format!("{ptr:?}");
|
2022-08-27 14:11:19 -04:00
|
|
|
if target.len() > provenance_width(ptr_size.bytes_usize() - 1) {
|
2020-04-26 18:14:16 +02:00
|
|
|
// This is too long, try to save some space.
|
2023-07-25 22:00:13 +02:00
|
|
|
target = format!("{ptr:#?}");
|
2020-04-26 18:14:16 +02:00
|
|
|
}
|
2020-03-11 11:49:00 +01:00
|
|
|
if ((i - line_start) + ptr_size).bytes_usize() > BYTES_PER_LINE {
|
2022-08-27 14:11:19 -04:00
|
|
|
// This branch handles the situation where a provenance starts in the current line
|
2020-03-11 11:49:00 +01:00
|
|
|
// but ends in the next one.
|
|
|
|
let remainder = Size::from_bytes(BYTES_PER_LINE) - (i - line_start);
|
|
|
|
let overflow = ptr_size - remainder;
|
2022-08-27 14:11:19 -04:00
|
|
|
let remainder_width = provenance_width(remainder.bytes_usize()) - 2;
|
|
|
|
let overflow_width = provenance_width(overflow.bytes_usize() - 1) + 1;
|
2022-11-06 13:00:09 +01:00
|
|
|
ascii.push('╾'); // HEAVY LEFT AND LIGHT RIGHT
|
|
|
|
for _ in 1..remainder.bytes() {
|
|
|
|
ascii.push('─'); // LIGHT HORIZONTAL
|
2020-03-11 11:49:00 +01:00
|
|
|
}
|
|
|
|
if overflow_width > remainder_width && overflow_width >= target.len() {
|
2022-08-27 14:11:19 -04:00
|
|
|
// The case where the provenance fits into the part in the next line
|
2020-03-11 11:49:00 +01:00
|
|
|
write!(w, "╾{0:─^1$}", "", remainder_width)?;
|
|
|
|
line_start =
|
|
|
|
write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?;
|
|
|
|
ascii.clear();
|
2023-07-25 22:00:13 +02:00
|
|
|
write!(w, "{target:─^overflow_width$}╼")?;
|
2020-03-11 11:49:00 +01:00
|
|
|
} else {
|
|
|
|
oversized_ptr(&mut target, remainder_width);
|
2023-07-25 22:00:13 +02:00
|
|
|
write!(w, "╾{target:─^remainder_width$}")?;
|
2020-03-11 11:49:00 +01:00
|
|
|
line_start =
|
|
|
|
write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?;
|
|
|
|
write!(w, "{0:─^1$}╼", "", overflow_width)?;
|
|
|
|
ascii.clear();
|
|
|
|
}
|
|
|
|
for _ in 0..overflow.bytes() - 1 {
|
|
|
|
ascii.push('─');
|
|
|
|
}
|
2022-11-06 13:00:09 +01:00
|
|
|
ascii.push('╼'); // LIGHT LEFT AND HEAVY RIGHT
|
2020-03-11 11:49:00 +01:00
|
|
|
i += ptr_size;
|
|
|
|
continue;
|
|
|
|
} else {
|
2022-08-27 14:11:19 -04:00
|
|
|
// This branch handles a provenance that starts and ends in the current line.
|
|
|
|
let provenance_width = provenance_width(ptr_size.bytes_usize() - 1);
|
|
|
|
oversized_ptr(&mut target, provenance_width);
|
2020-03-11 11:49:00 +01:00
|
|
|
ascii.push('╾');
|
2023-07-25 22:00:13 +02:00
|
|
|
write!(w, "╾{target:─^provenance_width$}╼")?;
|
2020-03-11 11:49:00 +01:00
|
|
|
for _ in 0..ptr_size.bytes() - 2 {
|
|
|
|
ascii.push('─');
|
|
|
|
}
|
|
|
|
ascii.push('╼');
|
|
|
|
i += ptr_size;
|
|
|
|
}
|
2022-11-06 13:00:09 +01:00
|
|
|
} else if let Some(prov) = alloc.provenance().get(i, &tcx) {
|
|
|
|
// Memory with provenance must be defined
|
2022-11-06 13:44:50 +01:00
|
|
|
assert!(
|
|
|
|
alloc.init_mask().is_range_initialized(alloc_range(i, Size::from_bytes(1))).is_ok()
|
|
|
|
);
|
2022-11-06 13:00:09 +01:00
|
|
|
ascii.push('━'); // HEAVY HORIZONTAL
|
|
|
|
// We have two characters to display this, which is obviously not enough.
|
|
|
|
// Format is similar to "oversized" above.
|
|
|
|
let j = i.bytes_usize();
|
|
|
|
let c = alloc.inspect_with_uninit_and_ptr_outside_interpreter(j..j + 1)[0];
|
2023-07-25 22:00:13 +02:00
|
|
|
write!(w, "╾{c:02x}{prov:#?} (1 ptr byte)╼")?;
|
2022-11-06 13:00:09 +01:00
|
|
|
i += Size::from_bytes(1);
|
2022-11-06 13:44:50 +01:00
|
|
|
} else if alloc
|
|
|
|
.init_mask()
|
|
|
|
.is_range_initialized(alloc_range(i, Size::from_bytes(1)))
|
|
|
|
.is_ok()
|
|
|
|
{
|
2020-03-11 11:49:00 +01:00
|
|
|
let j = i.bytes_usize();
|
|
|
|
|
2022-08-27 14:11:19 -04:00
|
|
|
// Checked definedness (and thus range) and provenance. This access also doesn't
|
2020-03-11 11:49:00 +01:00
|
|
|
// influence interpreter execution but is only for debugging.
|
2020-08-08 07:53:47 -06:00
|
|
|
let c = alloc.inspect_with_uninit_and_ptr_outside_interpreter(j..j + 1)[0];
|
2023-07-25 22:00:13 +02:00
|
|
|
write!(w, "{c:02x}")?;
|
2020-03-11 11:49:00 +01:00
|
|
|
if c.is_ascii_control() || c >= 0x80 {
|
|
|
|
ascii.push('.');
|
|
|
|
} else {
|
|
|
|
ascii.push(char::from(c));
|
|
|
|
}
|
|
|
|
i += Size::from_bytes(1);
|
|
|
|
} else {
|
|
|
|
write!(w, "__")?;
|
|
|
|
ascii.push('░');
|
|
|
|
i += Size::from_bytes(1);
|
|
|
|
}
|
|
|
|
// Print a new line header if the next line still has some bytes to print.
|
2021-05-17 13:30:16 +02:00
|
|
|
if i == line_start + Size::from_bytes(BYTES_PER_LINE) && i != alloc.size() {
|
2020-03-11 11:49:00 +01:00
|
|
|
line_start = write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?;
|
|
|
|
ascii.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
write_allocation_endline(w, &ascii)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-09-15 08:49:37 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// Constants
|
2021-06-14 21:32:42 +03:00
|
|
|
|
2023-09-14 23:33:42 +02:00
|
|
|
fn pretty_print_byte_str(fmt: &mut Formatter<'_>, byte_str: &[u8]) -> fmt::Result {
|
|
|
|
write!(fmt, "b\"{}\"", byte_str.escape_ascii())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn comma_sep<'tcx>(
|
2023-09-15 08:21:40 +02:00
|
|
|
tcx: TyCtxt<'tcx>,
|
2023-09-14 23:33:42 +02:00
|
|
|
fmt: &mut Formatter<'_>,
|
|
|
|
elems: Vec<(ConstValue<'tcx>, Ty<'tcx>)>,
|
|
|
|
) -> fmt::Result {
|
|
|
|
let mut first = true;
|
|
|
|
for (ct, ty) in elems {
|
|
|
|
if !first {
|
|
|
|
fmt.write_str(", ")?;
|
|
|
|
}
|
2023-09-15 08:21:40 +02:00
|
|
|
pretty_print_const_value_tcx(tcx, ct, ty, fmt)?;
|
2023-09-14 23:33:42 +02:00
|
|
|
first = false;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-09-15 08:21:40 +02:00
|
|
|
fn pretty_print_const_value_tcx<'tcx>(
|
|
|
|
tcx: TyCtxt<'tcx>,
|
2023-09-14 23:33:42 +02:00
|
|
|
ct: ConstValue<'tcx>,
|
|
|
|
ty: Ty<'tcx>,
|
|
|
|
fmt: &mut Formatter<'_>,
|
|
|
|
) -> fmt::Result {
|
|
|
|
use crate::ty::print::PrettyPrinter;
|
|
|
|
|
2023-09-15 08:21:40 +02:00
|
|
|
if tcx.sess.verbose() {
|
|
|
|
fmt.write_str(&format!("ConstValue({ct:?}: {ty})"))?;
|
|
|
|
return Ok(());
|
|
|
|
}
|
2023-09-14 23:33:42 +02:00
|
|
|
|
2023-09-15 08:21:40 +02:00
|
|
|
let u8_type = tcx.types.u8;
|
|
|
|
match (ct, ty.kind()) {
|
|
|
|
// Byte/string slices, printed as (byte) string literals.
|
|
|
|
(_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Str) => {
|
|
|
|
if let Some(data) = ct.try_get_slice_bytes_for_diagnostics(tcx) {
|
|
|
|
fmt.write_str(&format!("{:?}", String::from_utf8_lossy(data)))?;
|
|
|
|
return Ok(());
|
2023-09-14 23:33:42 +02:00
|
|
|
}
|
2023-09-15 08:21:40 +02:00
|
|
|
}
|
|
|
|
(_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Slice(t) if *t == u8_type) => {
|
|
|
|
if let Some(data) = ct.try_get_slice_bytes_for_diagnostics(tcx) {
|
|
|
|
pretty_print_byte_str(fmt, data)?;
|
2023-09-14 23:33:42 +02:00
|
|
|
return Ok(());
|
|
|
|
}
|
2023-09-15 08:21:40 +02:00
|
|
|
}
|
|
|
|
(ConstValue::Indirect { alloc_id, offset }, ty::Array(t, n)) if *t == u8_type => {
|
|
|
|
let n = n.try_to_target_usize(tcx).unwrap();
|
|
|
|
let alloc = tcx.global_alloc(alloc_id).unwrap_memory();
|
|
|
|
// cast is ok because we already checked for pointer size (32 or 64 bit) above
|
|
|
|
let range = AllocRange { start: offset, size: Size::from_bytes(n) };
|
|
|
|
let byte_str = alloc.inner().get_bytes_strip_provenance(&tcx, range).unwrap();
|
|
|
|
fmt.write_str("*")?;
|
|
|
|
pretty_print_byte_str(fmt, byte_str)?;
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
// Aggregates, printed as array/tuple/struct/variant construction syntax.
|
|
|
|
//
|
|
|
|
// NB: the `has_non_region_param` check ensures that we can use
|
|
|
|
// the `destructure_const` query with an empty `ty::ParamEnv` without
|
|
|
|
// introducing ICEs (e.g. via `layout_of`) from missing bounds.
|
|
|
|
// E.g. `transmute([0usize; 2]): (u8, *mut T)` needs to know `T: Sized`
|
|
|
|
// to be able to destructure the tuple into `(0u8, *mut T)`
|
|
|
|
(_, ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) if !ty.has_non_region_param() => {
|
|
|
|
let ct = tcx.lift(ct).unwrap();
|
|
|
|
let ty = tcx.lift(ty).unwrap();
|
2023-09-22 09:14:39 +00:00
|
|
|
if let Some(contents) = tcx.try_destructure_mir_constant_for_diagnostics(ct, ty) {
|
2023-09-15 08:21:40 +02:00
|
|
|
let fields: Vec<(ConstValue<'_>, Ty<'_>)> = contents.fields.to_vec();
|
|
|
|
match *ty.kind() {
|
|
|
|
ty::Array(..) => {
|
|
|
|
fmt.write_str("[")?;
|
|
|
|
comma_sep(tcx, fmt, fields)?;
|
|
|
|
fmt.write_str("]")?;
|
|
|
|
}
|
|
|
|
ty::Tuple(..) => {
|
|
|
|
fmt.write_str("(")?;
|
|
|
|
comma_sep(tcx, fmt, fields)?;
|
|
|
|
if contents.fields.len() == 1 {
|
|
|
|
fmt.write_str(",")?;
|
2023-09-14 23:33:42 +02:00
|
|
|
}
|
2023-09-15 08:21:40 +02:00
|
|
|
fmt.write_str(")")?;
|
|
|
|
}
|
|
|
|
ty::Adt(def, _) if def.variants().is_empty() => {
|
|
|
|
fmt.write_str(&format!("{{unreachable(): {ty}}}"))?;
|
|
|
|
}
|
|
|
|
ty::Adt(def, args) => {
|
|
|
|
let variant_idx = contents
|
|
|
|
.variant
|
|
|
|
.expect("destructed mir constant of adt without variant idx");
|
|
|
|
let variant_def = &def.variant(variant_idx);
|
|
|
|
let args = tcx.lift(args).unwrap();
|
|
|
|
let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
|
|
|
|
cx.print_alloc_ids = true;
|
|
|
|
let cx = cx.print_value_path(variant_def.def_id, args)?;
|
|
|
|
fmt.write_str(&cx.into_buffer())?;
|
|
|
|
|
|
|
|
match variant_def.ctor_kind() {
|
|
|
|
Some(CtorKind::Const) => {}
|
|
|
|
Some(CtorKind::Fn) => {
|
|
|
|
fmt.write_str("(")?;
|
|
|
|
comma_sep(tcx, fmt, fields)?;
|
|
|
|
fmt.write_str(")")?;
|
2023-09-14 23:33:42 +02:00
|
|
|
}
|
2023-09-15 08:21:40 +02:00
|
|
|
None => {
|
|
|
|
fmt.write_str(" {{ ")?;
|
|
|
|
let mut first = true;
|
|
|
|
for (field_def, (ct, ty)) in iter::zip(&variant_def.fields, fields)
|
|
|
|
{
|
|
|
|
if !first {
|
|
|
|
fmt.write_str(", ")?;
|
2023-09-14 23:33:42 +02:00
|
|
|
}
|
2023-09-15 08:21:40 +02:00
|
|
|
write!(fmt, "{}: ", field_def.name)?;
|
|
|
|
pretty_print_const_value_tcx(tcx, ct, ty, fmt)?;
|
|
|
|
first = false;
|
2023-09-14 23:33:42 +02:00
|
|
|
}
|
2023-09-15 08:21:40 +02:00
|
|
|
fmt.write_str(" }}")?;
|
2023-09-14 23:33:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-09-15 08:21:40 +02:00
|
|
|
_ => unreachable!(),
|
2023-09-14 23:33:42 +02:00
|
|
|
}
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
}
|
2023-09-15 08:21:40 +02:00
|
|
|
(ConstValue::Scalar(scalar), _) => {
|
|
|
|
let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
|
|
|
|
cx.print_alloc_ids = true;
|
|
|
|
let ty = tcx.lift(ty).unwrap();
|
|
|
|
cx = cx.pretty_print_const_scalar(scalar, ty)?;
|
|
|
|
fmt.write_str(&cx.into_buffer())?;
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
(ConstValue::ZeroSized, ty::FnDef(d, s)) => {
|
|
|
|
let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
|
|
|
|
cx.print_alloc_ids = true;
|
|
|
|
let cx = cx.print_value_path(*d, s)?;
|
|
|
|
fmt.write_str(&cx.into_buffer())?;
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
// FIXME(oli-obk): also pretty print arrays and other aggregate constants by reading
|
|
|
|
// their fields instead of just dumping the memory.
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
// Fall back to debug pretty printing for invalid constants.
|
|
|
|
write!(fmt, "{ct:?}: {ty}")
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn pretty_print_const_value<'tcx>(
|
|
|
|
ct: ConstValue<'tcx>,
|
|
|
|
ty: Ty<'tcx>,
|
|
|
|
fmt: &mut Formatter<'_>,
|
|
|
|
) -> fmt::Result {
|
|
|
|
ty::tls::with(|tcx| {
|
|
|
|
let ct = tcx.lift(ct).unwrap();
|
|
|
|
let ty = tcx.lift(ty).unwrap();
|
|
|
|
pretty_print_const_value_tcx(tcx, ct, ty, fmt)
|
2023-09-14 23:33:42 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-09-15 08:49:37 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// Miscellaneous
|
|
|
|
|
2021-06-14 21:32:42 +03:00
|
|
|
/// Calc converted u64 decimal into hex and return it's length in chars
|
|
|
|
///
|
|
|
|
/// ```ignore (cannot-test-private-function)
|
|
|
|
/// assert_eq!(1, hex_number_length(0));
|
|
|
|
/// assert_eq!(1, hex_number_length(1));
|
|
|
|
/// assert_eq!(2, hex_number_length(16));
|
|
|
|
/// ```
|
|
|
|
fn hex_number_length(x: u64) -> usize {
|
|
|
|
if x == 0 {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
let mut length = 0;
|
|
|
|
let mut x_left = x;
|
|
|
|
while x_left > 0 {
|
|
|
|
x_left /= 16;
|
|
|
|
length += 1;
|
|
|
|
}
|
|
|
|
length
|
|
|
|
}
|