]>
Commit | Line | Data |
---|---|---|
7cac9316 | 1 | use rustc::hir::def_id::{DefId, LOCAL_CRATE}; |
c30ab7b3 | 2 | use rustc::mir::*; |
ff7c6d11 XL |
3 | use rustc::mir::visit::Visitor; |
4 | use rustc::ty::{self, TyCtxt}; | |
476ff2be | 5 | use rustc_data_structures::fx::FxHashMap; |
e74abb32 | 6 | use rustc_index::vec::Idx; |
54a0048b | 7 | use std::fmt::Display; |
b7449926 | 8 | use std::fmt::Write as _; |
54a0048b | 9 | use std::fs; |
9cc50fc6 | 10 | use std::io::{self, Write}; |
ff7c6d11 | 11 | use std::path::{Path, PathBuf}; |
abe05a73 | 12 | use super::graphviz::write_mir_fn_graphviz; |
9fa01778 | 13 | use crate::transform::MirSource; |
9cc50fc6 | 14 | |
0731742a | 15 | const INDENT: &str = " "; |
a7813a04 | 16 | /// Alignment for lining up comments following MIR statements |
ff7c6d11 | 17 | pub(crate) const ALIGN: usize = 40; |
9cc50fc6 | 18 | |
abe05a73 XL |
19 | /// An indication of where we are in the control flow graph. Used for printing |
20 | /// extra information in `dump_mir` | |
21 | pub enum PassWhere { | |
22 | /// We have not started dumping the control flow graph, but we are about to. | |
23 | BeforeCFG, | |
24 | ||
25 | /// We just finished dumping the control flow graph. This is right before EOF | |
26 | AfterCFG, | |
27 | ||
28 | /// We are about to start dumping the given basic block. | |
29 | BeforeBlock(BasicBlock), | |
30 | ||
ff7c6d11 XL |
31 | /// We are just about to dump the given statement or terminator. |
32 | BeforeLocation(Location), | |
33 | ||
34 | /// We just dumped the given statement or terminator. | |
35 | AfterLocation(Location), | |
8faf50e0 XL |
36 | |
37 | /// We just dumped the terminator for a block but not the closing `}`. | |
38 | AfterTerminator(BasicBlock), | |
abe05a73 XL |
39 | } |
40 | ||
54a0048b SL |
41 | /// If the session is properly configured, dumps a human-readable |
42 | /// representation of the mir into: | |
43 | /// | |
a7813a04 | 44 | /// ```text |
7cac9316 | 45 | /// rustc.node<node_id>.<pass_num>.<pass_name>.<disambiguator> |
54a0048b SL |
46 | /// ``` |
47 | /// | |
48 | /// Output from this function is controlled by passing `-Z dump-mir=<filter>`, | |
49 | /// where `<filter>` takes the following forms: | |
50 | /// | |
51 | /// - `all` -- dump MIR for all fns, all passes, all everything | |
ff7c6d11 XL |
52 | /// - a filter defined by a set of substrings combined with `&` and `|` |
53 | /// (`&` has higher precedence). At least one of the `|`-separated groups | |
54 | /// must match; an `|`-separated group matches if all of its `&`-separated | |
55 | /// substrings are matched. | |
56 | /// | |
57 | /// Example: | |
58 | /// | |
59 | /// - `nll` == match if `nll` appears in the name | |
60 | /// - `foo & nll` == match if `foo` and `nll` both appear in the name | |
61 | /// - `foo & nll | typeck` == match if `foo` and `nll` both appear in the name | |
62 | /// or `typeck` appears in the name. | |
63 | /// - `foo & nll | bar & typeck` == match if `foo` and `nll` both appear in the name | |
64 | /// or `typeck` and `bar` both appear in the name. | |
dc9dc135 XL |
65 | pub fn dump_mir<'tcx, F>( |
66 | tcx: TyCtxt<'tcx>, | |
0531ce1d | 67 | pass_num: Option<&dyn Display>, |
ff7c6d11 | 68 | pass_name: &str, |
0531ce1d | 69 | disambiguator: &dyn Display, |
9fa01778 | 70 | source: MirSource<'tcx>, |
dc9dc135 | 71 | body: &Body<'tcx>, |
ff7c6d11 XL |
72 | extra_data: F, |
73 | ) where | |
0531ce1d | 74 | F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>, |
abe05a73 | 75 | { |
7cac9316 XL |
76 | if !dump_enabled(tcx, pass_name, source) { |
77 | return; | |
78 | } | |
79 | ||
532ac7d7 | 80 | let node_path = ty::print::with_forced_impl_filename_line(|| { |
ff7c6d11 | 81 | // see notes on #41697 below |
532ac7d7 | 82 | tcx.def_path_str(source.def_id()) |
7cac9316 | 83 | }); |
ff7c6d11 XL |
84 | dump_matched_mir_node( |
85 | tcx, | |
86 | pass_num, | |
87 | pass_name, | |
88 | &node_path, | |
89 | disambiguator, | |
90 | source, | |
dc9dc135 | 91 | body, |
ff7c6d11 XL |
92 | extra_data, |
93 | ); | |
7cac9316 XL |
94 | } |
95 | ||
dc9dc135 | 96 | pub fn dump_enabled<'tcx>(tcx: TyCtxt<'tcx>, pass_name: &str, source: MirSource<'tcx>) -> bool { |
54a0048b | 97 | let filters = match tcx.sess.opts.debugging_opts.dump_mir { |
7cac9316 | 98 | None => return false, |
54a0048b SL |
99 | Some(ref filters) => filters, |
100 | }; | |
532ac7d7 | 101 | let node_path = ty::print::with_forced_impl_filename_line(|| { |
ff7c6d11 | 102 | // see notes on #41697 below |
532ac7d7 | 103 | tcx.def_path_str(source.def_id()) |
7cac9316 | 104 | }); |
8faf50e0 XL |
105 | filters.split('|').any(|or_filter| { |
106 | or_filter.split('&').all(|and_filter| { | |
ff7c6d11 XL |
107 | and_filter == "all" || pass_name.contains(and_filter) || node_path.contains(and_filter) |
108 | }) | |
109 | }) | |
7cac9316 | 110 | } |
54a0048b | 111 | |
7cac9316 | 112 | // #41697 -- we use `with_forced_impl_filename_line()` because |
532ac7d7 | 113 | // `def_path_str()` would otherwise trigger `type_of`, and this can |
7cac9316 XL |
114 | // run while we are already attempting to evaluate `type_of`. |
115 | ||
dc9dc135 XL |
116 | fn dump_matched_mir_node<'tcx, F>( |
117 | tcx: TyCtxt<'tcx>, | |
0531ce1d | 118 | pass_num: Option<&dyn Display>, |
ff7c6d11 XL |
119 | pass_name: &str, |
120 | node_path: &str, | |
0531ce1d | 121 | disambiguator: &dyn Display, |
9fa01778 | 122 | source: MirSource<'tcx>, |
dc9dc135 | 123 | body: &Body<'tcx>, |
ff7c6d11 XL |
124 | mut extra_data: F, |
125 | ) where | |
0531ce1d | 126 | F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>, |
abe05a73 | 127 | { |
9fa01778 | 128 | let _: io::Result<()> = try { |
ff7c6d11 XL |
129 | let mut file = create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, source)?; |
130 | writeln!(file, "// MIR for `{}`", node_path)?; | |
131 | writeln!(file, "// source = {:?}", source)?; | |
132 | writeln!(file, "// pass_name = {}", pass_name)?; | |
133 | writeln!(file, "// disambiguator = {}", disambiguator)?; | |
dc9dc135 | 134 | if let Some(ref layout) = body.generator_layout { |
ff7c6d11 XL |
135 | writeln!(file, "// generator_layout = {:?}", layout)?; |
136 | } | |
137 | writeln!(file, "")?; | |
138 | extra_data(PassWhere::BeforeCFG, &mut file)?; | |
dc9dc135 XL |
139 | write_user_type_annotations(body, &mut file)?; |
140 | write_mir_fn(tcx, source, body, &mut extra_data, &mut file)?; | |
ff7c6d11 | 141 | extra_data(PassWhere::AfterCFG, &mut file)?; |
94b46f34 | 142 | }; |
ff7c6d11 XL |
143 | |
144 | if tcx.sess.opts.debugging_opts.dump_mir_graphviz { | |
9fa01778 | 145 | let _: io::Result<()> = try { |
ff7c6d11 XL |
146 | let mut file = |
147 | create_dump_file(tcx, "dot", pass_num, pass_name, disambiguator, source)?; | |
e74abb32 | 148 | write_mir_fn_graphviz(tcx, source.def_id(), body, false, &mut file)?; |
94b46f34 | 149 | }; |
ff7c6d11 XL |
150 | } |
151 | } | |
152 | ||
153 | /// Returns the path to the filename where we should dump a given MIR. | |
154 | /// Also used by other bits of code (e.g., NLL inference) that dump | |
155 | /// graphviz data or other things. | |
156 | fn dump_path( | |
dc9dc135 | 157 | tcx: TyCtxt<'_>, |
ff7c6d11 | 158 | extension: &str, |
0531ce1d | 159 | pass_num: Option<&dyn Display>, |
ff7c6d11 | 160 | pass_name: &str, |
0531ce1d | 161 | disambiguator: &dyn Display, |
9fa01778 | 162 | source: MirSource<'tcx>, |
ff7c6d11 | 163 | ) -> PathBuf { |
abe05a73 XL |
164 | let promotion_id = match source.promoted { |
165 | Some(id) => format!("-{:?}", id), | |
ff7c6d11 | 166 | None => String::new(), |
3157f602 XL |
167 | }; |
168 | ||
7cac9316 | 169 | let pass_num = if tcx.sess.opts.debugging_opts.dump_mir_exclude_pass_number { |
8faf50e0 | 170 | String::new() |
7cac9316 XL |
171 | } else { |
172 | match pass_num { | |
8faf50e0 | 173 | None => ".-------".to_string(), |
abe05a73 | 174 | Some(pass_num) => format!(".{}", pass_num), |
7cac9316 XL |
175 | } |
176 | }; | |
177 | ||
5bcae85e | 178 | let mut file_path = PathBuf::new(); |
2c00a5a8 | 179 | file_path.push(Path::new(&tcx.sess.opts.debugging_opts.dump_mir_dir)); |
abe05a73 | 180 | |
9fa01778 XL |
181 | let item_name = tcx |
182 | .def_path(source.def_id()) | |
ff7c6d11 | 183 | .to_filename_friendly_no_crate(); |
9fa01778 XL |
184 | // All drop shims have the same DefId, so we have to add the type |
185 | // to get unique file names. | |
186 | let shim_disambiguator = match source.instance { | |
187 | ty::InstanceDef::DropGlue(_, Some(ty)) => { | |
188 | // Unfortunately, pretty-printed typed are not very filename-friendly. | |
189 | // We dome some filtering. | |
190 | let mut s = ".".to_owned(); | |
191 | s.extend(ty.to_string() | |
192 | .chars() | |
193 | .filter_map(|c| match c { | |
194 | ' ' => None, | |
195 | ':' | '<' | '>' => Some('_'), | |
196 | c => Some(c) | |
197 | })); | |
198 | s | |
199 | } | |
200 | _ => String::new(), | |
201 | }; | |
ff7c6d11 XL |
202 | |
203 | let file_name = format!( | |
9fa01778 | 204 | "rustc.{}{}{}{}.{}.{}.{}", |
ff7c6d11 | 205 | item_name, |
9fa01778 | 206 | shim_disambiguator, |
ff7c6d11 XL |
207 | promotion_id, |
208 | pass_num, | |
209 | pass_name, | |
210 | disambiguator, | |
211 | extension, | |
212 | ); | |
213 | ||
5bcae85e | 214 | file_path.push(&file_name); |
abe05a73 | 215 | |
ff7c6d11 XL |
216 | file_path |
217 | } | |
218 | ||
219 | /// Attempts to open a file where we should dump a given MIR or other | |
220 | /// bit of MIR-related data. Used by `mir-dump`, but also by other | |
221 | /// bits of code (e.g., NLL inference) that dump graphviz data or | |
222 | /// other things, and hence takes the extension as an argument. | |
223 | pub(crate) fn create_dump_file( | |
dc9dc135 | 224 | tcx: TyCtxt<'_>, |
ff7c6d11 | 225 | extension: &str, |
0531ce1d | 226 | pass_num: Option<&dyn Display>, |
ff7c6d11 | 227 | pass_name: &str, |
0531ce1d | 228 | disambiguator: &dyn Display, |
9fa01778 | 229 | source: MirSource<'tcx>, |
e1599b0c | 230 | ) -> io::Result<io::BufWriter<fs::File>> { |
ff7c6d11 XL |
231 | let file_path = dump_path(tcx, extension, pass_num, pass_name, disambiguator, source); |
232 | if let Some(parent) = file_path.parent() { | |
233 | fs::create_dir_all(parent)?; | |
abe05a73 | 234 | } |
e1599b0c | 235 | Ok(io::BufWriter::new(fs::File::create(&file_path)?)) |
54a0048b SL |
236 | } |
237 | ||
9cc50fc6 | 238 | /// Write out a human-readable textual representation for the given MIR. |
dc9dc135 XL |
239 | pub fn write_mir_pretty<'tcx>( |
240 | tcx: TyCtxt<'tcx>, | |
ff7c6d11 | 241 | single: Option<DefId>, |
0531ce1d | 242 | w: &mut dyn Write, |
ff7c6d11 XL |
243 | ) -> io::Result<()> { |
244 | writeln!( | |
245 | w, | |
246 | "// WARNING: This output format is intended for human consumers only" | |
247 | )?; | |
248 | writeln!( | |
249 | w, | |
250 | "// and is subject to change without notice. Knock yourself out." | |
251 | )?; | |
cc61c64b | 252 | |
a7813a04 | 253 | let mut first = true; |
7cac9316 | 254 | for def_id in dump_mir_def_ids(tcx, single) { |
dc9dc135 | 255 | let body = &tcx.optimized_mir(def_id); |
5bcae85e | 256 | |
a7813a04 XL |
257 | if first { |
258 | first = false; | |
259 | } else { | |
260 | // Put empty lines between all items | |
261 | writeln!(w, "")?; | |
262 | } | |
263 | ||
dc9dc135 | 264 | write_mir_fn(tcx, MirSource::item(def_id), body, &mut |_, _| Ok(()), w)?; |
a7813a04 | 265 | |
416331ca | 266 | for (i, body) in tcx.promoted_mir(def_id).iter_enumerated() { |
a7813a04 | 267 | writeln!(w, "")?; |
abe05a73 | 268 | let src = MirSource { |
9fa01778 | 269 | instance: ty::InstanceDef::Item(def_id), |
ff7c6d11 | 270 | promoted: Some(i), |
abe05a73 | 271 | }; |
dc9dc135 | 272 | write_mir_fn(tcx, src, body, &mut |_, _| Ok(()), w)?; |
a7813a04 | 273 | } |
54a0048b SL |
274 | } |
275 | Ok(()) | |
276 | } | |
9cc50fc6 | 277 | |
dc9dc135 XL |
278 | pub fn write_mir_fn<'tcx, F>( |
279 | tcx: TyCtxt<'tcx>, | |
9fa01778 | 280 | src: MirSource<'tcx>, |
dc9dc135 | 281 | body: &Body<'tcx>, |
ff7c6d11 | 282 | extra_data: &mut F, |
0531ce1d | 283 | w: &mut dyn Write, |
ff7c6d11 | 284 | ) -> io::Result<()> |
abe05a73 | 285 | where |
0531ce1d | 286 | F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>, |
abe05a73 | 287 | { |
dc9dc135 XL |
288 | write_mir_intro(tcx, src, body, w)?; |
289 | for block in body.basic_blocks().indices() { | |
abe05a73 | 290 | extra_data(PassWhere::BeforeBlock(block), w)?; |
dc9dc135 XL |
291 | write_basic_block(tcx, block, body, extra_data, w)?; |
292 | if block.index() + 1 != body.basic_blocks().len() { | |
3157f602 XL |
293 | writeln!(w, "")?; |
294 | } | |
54a0048b SL |
295 | } |
296 | ||
54a0048b SL |
297 | writeln!(w, "}}")?; |
298 | Ok(()) | |
9cc50fc6 SL |
299 | } |
300 | ||
301 | /// Write out a human-readable textual representation for the given basic block. | |
dc9dc135 XL |
302 | pub fn write_basic_block<'tcx, F>( |
303 | tcx: TyCtxt<'tcx>, | |
ff7c6d11 | 304 | block: BasicBlock, |
dc9dc135 | 305 | body: &Body<'tcx>, |
ff7c6d11 | 306 | extra_data: &mut F, |
0531ce1d | 307 | w: &mut dyn Write, |
ff7c6d11 | 308 | ) -> io::Result<()> |
abe05a73 | 309 | where |
0531ce1d | 310 | F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>, |
abe05a73 | 311 | { |
dc9dc135 | 312 | let data = &body[block]; |
9cc50fc6 SL |
313 | |
314 | // Basic block label at the top. | |
532ac7d7 XL |
315 | let cleanup_text = if data.is_cleanup { " (cleanup)" } else { "" }; |
316 | writeln!(w, "{}{:?}{}: {{", INDENT, block, cleanup_text)?; | |
9cc50fc6 SL |
317 | |
318 | // List of statements in the middle. | |
ff7c6d11 XL |
319 | let mut current_location = Location { |
320 | block: block, | |
321 | statement_index: 0, | |
322 | }; | |
9cc50fc6 | 323 | for statement in &data.statements { |
ff7c6d11 | 324 | extra_data(PassWhere::BeforeLocation(current_location), w)?; |
dc9dc135 | 325 | let indented_body = format!("{0}{0}{1:?};", INDENT, statement); |
ff7c6d11 XL |
326 | writeln!( |
327 | w, | |
328 | "{:A$} // {:?}: {}", | |
dc9dc135 | 329 | indented_body, |
ff7c6d11 XL |
330 | current_location, |
331 | comment(tcx, statement.source_info), | |
332 | A = ALIGN, | |
333 | )?; | |
334 | ||
335 | write_extra(tcx, w, |visitor| { | |
48663c56 | 336 | visitor.visit_statement(statement, current_location); |
ff7c6d11 XL |
337 | })?; |
338 | ||
339 | extra_data(PassWhere::AfterLocation(current_location), w)?; | |
54a0048b SL |
340 | |
341 | current_location.statement_index += 1; | |
9cc50fc6 SL |
342 | } |
343 | ||
344 | // Terminator at the bottom. | |
ff7c6d11 | 345 | extra_data(PassWhere::BeforeLocation(current_location), w)?; |
a7813a04 | 346 | let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind); |
ff7c6d11 XL |
347 | writeln!( |
348 | w, | |
349 | "{:A$} // {:?}: {}", | |
350 | indented_terminator, | |
351 | current_location, | |
352 | comment(tcx, data.terminator().source_info), | |
353 | A = ALIGN, | |
354 | )?; | |
355 | ||
356 | write_extra(tcx, w, |visitor| { | |
48663c56 | 357 | visitor.visit_terminator(data.terminator(), current_location); |
ff7c6d11 XL |
358 | })?; |
359 | ||
360 | extra_data(PassWhere::AfterLocation(current_location), w)?; | |
8faf50e0 | 361 | extra_data(PassWhere::AfterTerminator(block), w)?; |
9cc50fc6 | 362 | |
5bcae85e | 363 | writeln!(w, "{}}}", INDENT) |
9cc50fc6 SL |
364 | } |
365 | ||
ff7c6d11 XL |
366 | /// After we print the main statement, we sometimes dump extra |
367 | /// information. There's often a lot of little things "nuzzled up" in | |
368 | /// a statement. | |
dc9dc135 | 369 | fn write_extra<'tcx, F>(tcx: TyCtxt<'tcx>, write: &mut dyn Write, mut visit_op: F) -> io::Result<()> |
ff7c6d11 | 370 | where |
dc9dc135 | 371 | F: FnMut(&mut ExtraComments<'tcx>), |
ff7c6d11 XL |
372 | { |
373 | let mut extra_comments = ExtraComments { | |
374 | _tcx: tcx, | |
375 | comments: vec![], | |
376 | }; | |
377 | visit_op(&mut extra_comments); | |
378 | for comment in extra_comments.comments { | |
379 | writeln!(write, "{:A$} // {}", "", comment, A = ALIGN)?; | |
380 | } | |
381 | Ok(()) | |
382 | } | |
383 | ||
dc9dc135 XL |
384 | struct ExtraComments<'tcx> { |
385 | _tcx: TyCtxt<'tcx>, // don't need it now, but bet we will soon | |
ff7c6d11 XL |
386 | comments: Vec<String>, |
387 | } | |
388 | ||
dc9dc135 | 389 | impl ExtraComments<'tcx> { |
ff7c6d11 | 390 | fn push(&mut self, lines: &str) { |
8faf50e0 | 391 | for line in lines.split('\n') { |
ff7c6d11 XL |
392 | self.comments.push(line.to_string()); |
393 | } | |
394 | } | |
395 | } | |
396 | ||
dc9dc135 | 397 | impl Visitor<'tcx> for ExtraComments<'tcx> { |
ff7c6d11 XL |
398 | fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) { |
399 | self.super_constant(constant, location); | |
e1599b0c | 400 | let Constant { span, user_ty, literal } = constant; |
8faf50e0 | 401 | self.push("mir::Constant"); |
2c00a5a8 | 402 | self.push(&format!("+ span: {:?}", span)); |
b7449926 XL |
403 | if let Some(user_ty) = user_ty { |
404 | self.push(&format!("+ user_ty: {:?}", user_ty)); | |
405 | } | |
2c00a5a8 | 406 | self.push(&format!("+ literal: {:?}", literal)); |
ff7c6d11 XL |
407 | } |
408 | ||
532ac7d7 | 409 | fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, _: Location) { |
ff7c6d11 | 410 | self.super_const(constant); |
532ac7d7 XL |
411 | let ty::Const { ty, val, .. } = constant; |
412 | self.push("ty::Const"); | |
413 | self.push(&format!("+ ty: {:?}", ty)); | |
414 | self.push(&format!("+ val: {:?}", val)); | |
ff7c6d11 XL |
415 | } |
416 | ||
417 | fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { | |
418 | self.super_rvalue(rvalue, location); | |
419 | match rvalue { | |
420 | Rvalue::Aggregate(kind, _) => match **kind { | |
421 | AggregateKind::Closure(def_id, substs) => { | |
8faf50e0 | 422 | self.push("closure"); |
2c00a5a8 XL |
423 | self.push(&format!("+ def_id: {:?}", def_id)); |
424 | self.push(&format!("+ substs: {:#?}", substs)); | |
ff7c6d11 XL |
425 | } |
426 | ||
94b46f34 | 427 | AggregateKind::Generator(def_id, substs, movability) => { |
8faf50e0 | 428 | self.push("generator"); |
2c00a5a8 XL |
429 | self.push(&format!("+ def_id: {:?}", def_id)); |
430 | self.push(&format!("+ substs: {:#?}", substs)); | |
94b46f34 | 431 | self.push(&format!("+ movability: {:?}", movability)); |
ff7c6d11 XL |
432 | } |
433 | ||
b7449926 XL |
434 | AggregateKind::Adt(_, _, _, Some(user_ty), _) => { |
435 | self.push("adt"); | |
436 | self.push(&format!("+ user_ty: {:?}", user_ty)); | |
437 | } | |
438 | ||
ff7c6d11 XL |
439 | _ => {} |
440 | }, | |
441 | ||
442 | _ => {} | |
443 | } | |
444 | } | |
445 | } | |
446 | ||
dc9dc135 | 447 | fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo) -> String { |
ff7c6d11 XL |
448 | format!( |
449 | "scope {} at {}", | |
450 | scope.index(), | |
b7449926 | 451 | tcx.sess.source_map().span_to_string(span) |
ff7c6d11 | 452 | ) |
54a0048b SL |
453 | } |
454 | ||
48663c56 | 455 | /// Prints local variables in a scope tree. |
ff7c6d11 | 456 | fn write_scope_tree( |
dc9dc135 XL |
457 | tcx: TyCtxt<'_>, |
458 | body: &Body<'_>, | |
94b46f34 | 459 | scope_tree: &FxHashMap<SourceScope, Vec<SourceScope>>, |
0531ce1d | 460 | w: &mut dyn Write, |
94b46f34 | 461 | parent: SourceScope, |
ff7c6d11 XL |
462 | depth: usize, |
463 | ) -> io::Result<()> { | |
3157f602 | 464 | let indent = depth * INDENT.len(); |
a7813a04 | 465 | |
60c5eb7d XL |
466 | // Local variable debuginfo. |
467 | for var_debug_info in &body.var_debug_info { | |
468 | if var_debug_info.source_info.scope != parent { | |
469 | // Not declared in this scope. | |
470 | continue; | |
471 | } | |
472 | ||
473 | let indented_debug_info = format!( | |
474 | "{0:1$}debug {2} => {3:?};", | |
475 | INDENT, | |
476 | indent, | |
477 | var_debug_info.name, | |
478 | var_debug_info.place, | |
479 | ); | |
480 | ||
481 | writeln!( | |
482 | w, | |
483 | "{0:1$} // in {2}", | |
484 | indented_debug_info, | |
485 | ALIGN, | |
486 | comment(tcx, var_debug_info.source_info), | |
487 | )?; | |
488 | } | |
489 | ||
48663c56 | 490 | // Local variable types (including the user's name in a comment). |
dc9dc135 XL |
491 | for (local, local_decl) in body.local_decls.iter_enumerated() { |
492 | if (1..body.arg_count+1).contains(&local.index()) { | |
48663c56 XL |
493 | // Skip over argument locals, they're printed in the signature. |
494 | continue; | |
495 | } | |
496 | ||
497 | if local_decl.source_info.scope != parent { | |
498 | // Not declared in this scope. | |
499 | continue; | |
500 | } | |
501 | ||
502 | let mut_str = if local_decl.mutability == Mutability::Mut { | |
503 | "mut " | |
504 | } else { | |
505 | "" | |
506 | }; | |
507 | ||
508 | let mut indented_decl = format!( | |
509 | "{0:1$}let {2}{3:?}: {4:?}", | |
510 | INDENT, | |
511 | indent, | |
512 | mut_str, | |
513 | local, | |
514 | local_decl.ty | |
515 | ); | |
516 | for user_ty in local_decl.user_ty.projections() { | |
517 | write!(indented_decl, " as {:?}", user_ty).unwrap(); | |
518 | } | |
519 | indented_decl.push_str(";"); | |
520 | ||
521 | let local_name = if local == RETURN_PLACE { | |
522 | format!(" return place") | |
48663c56 XL |
523 | } else { |
524 | String::new() | |
525 | }; | |
526 | ||
527 | writeln!( | |
528 | w, | |
529 | "{0:1$} //{2} in {3}", | |
530 | indented_decl, | |
531 | ALIGN, | |
532 | local_name, | |
533 | comment(tcx, local_decl.source_info), | |
534 | )?; | |
535 | } | |
536 | ||
a7813a04 | 537 | let children = match scope_tree.get(&parent) { |
48663c56 | 538 | Some(childs) => childs, |
a7813a04 XL |
539 | None => return Ok(()), |
540 | }; | |
541 | ||
3157f602 | 542 | for &child in children { |
dc9dc135 | 543 | assert_eq!(body.source_scopes[child].parent_scope, Some(parent)); |
3157f602 | 544 | writeln!(w, "{0:1$}scope {2} {{", "", indent, child.index())?; |
dc9dc135 | 545 | write_scope_tree(tcx, body, scope_tree, w, child, depth + 1)?; |
3157f602 | 546 | writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?; |
54a0048b | 547 | } |
a7813a04 | 548 | |
54a0048b SL |
549 | Ok(()) |
550 | } | |
551 | ||
9cc50fc6 SL |
552 | /// Write out a human-readable textual representation of the MIR's `fn` type and the types of its |
553 | /// local variables (both user-defined bindings and compiler temporaries). | |
dc9dc135 XL |
554 | pub fn write_mir_intro<'tcx>( |
555 | tcx: TyCtxt<'tcx>, | |
9fa01778 | 556 | src: MirSource<'tcx>, |
dc9dc135 | 557 | body: &Body<'_>, |
0531ce1d | 558 | w: &mut dyn Write, |
ff7c6d11 | 559 | ) -> io::Result<()> { |
dc9dc135 | 560 | write_mir_sig(tcx, src, body, w)?; |
2c00a5a8 | 561 | writeln!(w, "{{")?; |
3157f602 XL |
562 | |
563 | // construct a scope tree and write it out | |
0bf4aa26 | 564 | let mut scope_tree: FxHashMap<SourceScope, Vec<SourceScope>> = Default::default(); |
dc9dc135 | 565 | for (index, scope_data) in body.source_scopes.iter().enumerate() { |
3157f602 | 566 | if let Some(parent) = scope_data.parent_scope { |
ff7c6d11 XL |
567 | scope_tree |
568 | .entry(parent) | |
b7449926 | 569 | .or_default() |
94b46f34 | 570 | .push(SourceScope::new(index)); |
3157f602 XL |
571 | } else { |
572 | // Only the argument scope has no parent, because it's the root. | |
94b46f34 | 573 | assert_eq!(index, OUTERMOST_SOURCE_SCOPE.index()); |
3157f602 XL |
574 | } |
575 | } | |
576 | ||
dc9dc135 | 577 | write_scope_tree(tcx, body, &scope_tree, w, OUTERMOST_SOURCE_SCOPE, 1)?; |
3157f602 | 578 | |
c30ab7b3 SL |
579 | // Add an empty line before the first block is printed. |
580 | writeln!(w, "")?; | |
581 | ||
582 | Ok(()) | |
3157f602 XL |
583 | } |
584 | ||
9fa01778 | 585 | fn write_mir_sig( |
dc9dc135 | 586 | tcx: TyCtxt<'_>, |
9fa01778 | 587 | src: MirSource<'tcx>, |
dc9dc135 | 588 | body: &Body<'_>, |
9fa01778 XL |
589 | w: &mut dyn Write, |
590 | ) -> io::Result<()> { | |
48663c56 | 591 | use rustc::hir::def::DefKind; |
9fa01778 XL |
592 | |
593 | trace!("write_mir_sig: {:?}", src.instance); | |
48663c56 XL |
594 | let kind = tcx.def_kind(src.def_id()); |
595 | let is_function = match kind { | |
596 | Some(DefKind::Fn) | |
597 | | Some(DefKind::Method) | |
598 | | Some(DefKind::Ctor(..)) => true, | |
9fa01778 XL |
599 | _ => tcx.is_closure(src.def_id()), |
600 | }; | |
48663c56 | 601 | match (kind, src.promoted) { |
9fa01778 | 602 | (_, Some(i)) => write!(w, "{:?} in ", i)?, |
48663c56 | 603 | (Some(DefKind::Const), _) |
dc9dc135 | 604 | | (Some(DefKind::AssocConst), _) => write!(w, "const ")?, |
48663c56 XL |
605 | (Some(DefKind::Static), _) => |
606 | write!(w, "static {}", if tcx.is_mutable_static(src.def_id()) { "mut " } else { "" })?, | |
9fa01778 XL |
607 | (_, _) if is_function => write!(w, "fn ")?, |
608 | (None, _) => {}, // things like anon const, not an item | |
48663c56 | 609 | _ => bug!("Unexpected def kind {:?}", kind), |
9cc50fc6 SL |
610 | } |
611 | ||
532ac7d7 | 612 | ty::print::with_forced_impl_filename_line(|| { |
ff7c6d11 | 613 | // see notes on #41697 elsewhere |
532ac7d7 | 614 | write!(w, " {}", tcx.def_path_str(src.def_id())) |
7cac9316 | 615 | })?; |
a7813a04 | 616 | |
9fa01778 XL |
617 | if src.promoted.is_none() && is_function { |
618 | write!(w, "(")?; | |
ea8adc8c | 619 | |
9fa01778 | 620 | // fn argument types. |
dc9dc135 | 621 | for (i, arg) in body.args_iter().enumerate() { |
9fa01778 XL |
622 | if i != 0 { |
623 | write!(w, ", ")?; | |
a7813a04 | 624 | } |
dc9dc135 | 625 | write!(w, "{:?}: {}", Place::from(arg), body.local_decls[arg].ty)?; |
ea8adc8c | 626 | } |
9fa01778 | 627 | |
dc9dc135 | 628 | write!(w, ") -> {}", body.return_ty())?; |
9fa01778 | 629 | } else { |
dc9dc135 XL |
630 | assert_eq!(body.arg_count, 0); |
631 | write!(w, ": {} =", body.return_ty())?; | |
9cc50fc6 | 632 | } |
2c00a5a8 | 633 | |
dc9dc135 | 634 | if let Some(yield_ty) = body.yield_ty { |
2c00a5a8 XL |
635 | writeln!(w)?; |
636 | writeln!(w, "yields {}", yield_ty)?; | |
637 | } | |
638 | ||
9fa01778 XL |
639 | write!(w, " ")?; |
640 | // Next thing that gets printed is the opening { | |
641 | ||
2c00a5a8 | 642 | Ok(()) |
3157f602 | 643 | } |
9cc50fc6 | 644 | |
dc9dc135 XL |
645 | fn write_user_type_annotations(body: &Body<'_>, w: &mut dyn Write) -> io::Result<()> { |
646 | if !body.user_type_annotations.is_empty() { | |
0731742a XL |
647 | writeln!(w, "| User Type Annotations")?; |
648 | } | |
dc9dc135 | 649 | for (index, annotation) in body.user_type_annotations.iter_enumerated() { |
9fa01778 | 650 | writeln!(w, "| {:?}: {:?} at {:?}", index.index(), annotation.user_ty, annotation.span)?; |
0731742a | 651 | } |
dc9dc135 | 652 | if !body.user_type_annotations.is_empty() { |
0731742a XL |
653 | writeln!(w, "|")?; |
654 | } | |
655 | Ok(()) | |
656 | } | |
657 | ||
dc9dc135 | 658 | pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option<DefId>) -> Vec<DefId> { |
7cac9316 XL |
659 | if let Some(i) = single { |
660 | vec![i] | |
661 | } else { | |
662 | tcx.mir_keys(LOCAL_CRATE).iter().cloned().collect() | |
663 | } | |
664 | } |