From d62bcdb866149bc7c4a83f3da56ec1b43c1234a0 Mon Sep 17 00:00:00 2001 From: Stevengre Date: Sat, 28 Feb 2026 10:19:09 +0800 Subject: [PATCH 01/10] fix: record closure Ty in type table after instance traversal After resolving a closure instance, the visitor early-returned without inserting the closure's Ty into the types collection. This caused downstream consumers (kmir) to miss closure type metadata, leading to thunked zero-sized constants that blocked execution. Now the closure's Ty, kind, and layout shape are recorded in the type table after successful instance traversal. --- src/printer.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/printer.rs b/src/printer.rs index 3b86116e..03bb8db6 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -698,7 +698,12 @@ impl Visitor for TyCollector<'_> { self.resolved.insert(*ty); let instance = Instance::resolve_closure(def, args, stable_mir::ty::ClosureKind::Fn).unwrap(); - self.visit_instance(instance) + let control = self.visit_instance(instance); + if matches!(control, ControlFlow::Continue(_)) { + let maybe_layout_shape = ty.layout().ok().map(|layout| layout.shape()); + self.types.insert(*ty, (ty.kind(), maybe_layout_shape)); + } + control } // Break on CoroutineWitnesses, because they aren't expected when getting the layout TyKind::RigidTy(RigidTy::CoroutineWitness(..)) => ControlFlow::Break(()), From 161ef30c8898fd95680c5dccd5dfd30e51937d9c Mon Sep 17 00:00:00 2001 From: Stevengre Date: Sat, 28 Feb 2026 11:46:00 +0800 Subject: [PATCH 02/10] test(integration): expose closure Ty metadata in program expectations --- Cargo.toml | 2 ++ .../integration/programs/assert_eq.smir.json.expected | 5 +++++ tests/integration/programs/binop.smir.json.expected | 5 +++++ .../programs/char-trivial.smir.json.expected | 5 +++++ .../programs/closure-args.smir.json.expected | 10 ++++++++++ .../programs/closure-no-args.smir.json.expected | 10 ++++++++++ .../programs/const-arithm-simple.smir.json.expected | 5 +++++ tests/integration/programs/div.smir.json.expected | 5 +++++ .../programs/double-ref-deref.smir.json.expected | 5 +++++ tests/integration/programs/enum.smir.json.expected | 5 +++++ .../integration/programs/fibonacci.smir.json.expected | 5 +++++ tests/integration/programs/float.smir.json.expected | 5 +++++ .../programs/fn-ptr-in-arg.smir.json.expected | 5 +++++ tests/integration/programs/modulo.smir.json.expected | 5 +++++ .../programs/mutual_recursion.smir.json.expected | 5 +++++ .../programs/option-construction.smir.json.expected | 5 +++++ .../programs/param_types.smir.json.expected | 5 +++++ .../programs/primitive-type-bounds.smir.json.expected | 5 +++++ .../programs/recursion-simple-match.smir.json.expected | 5 +++++ .../programs/recursion-simple.smir.json.expected | 5 +++++ .../integration/programs/ref-deref.smir.json.expected | 5 +++++ tests/integration/programs/shl_min.smir.json.expected | 5 +++++ tests/integration/programs/slice.smir.json.expected | 5 +++++ .../programs/strange-ref-deref.smir.json.expected | 5 +++++ tests/integration/programs/struct.smir.json.expected | 5 +++++ tests/integration/programs/sum-to-n.smir.json.expected | 5 +++++ tests/integration/programs/tuple-eq.smir.json.expected | 5 +++++ .../programs/tuples-simple.smir.json.expected | 5 +++++ .../integration/programs/weirdRefs.smir.json.expected | 5 +++++ 29 files changed, 152 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 1ca983eb..c514c2f0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,3 +21,5 @@ debug_log = [] [package.metadata.rust-analyzer] # This package uses rustc crates. rustc_private=true + +[workspace] diff --git a/tests/integration/programs/assert_eq.smir.json.expected b/tests/integration/programs/assert_eq.smir.json.expected index ca3c4816..2c8a94e4 100644 --- a/tests/integration/programs/assert_eq.smir.json.expected +++ b/tests/integration/programs/assert_eq.smir.json.expected @@ -5725,6 +5725,11 @@ "pointee_type": "elided" } } + ], + [ + { + "FunType": "{closure@std::rt::lang_start<()>::{closure#0}}" + } ] ] } diff --git a/tests/integration/programs/binop.smir.json.expected b/tests/integration/programs/binop.smir.json.expected index 66f531a6..006fea64 100644 --- a/tests/integration/programs/binop.smir.json.expected +++ b/tests/integration/programs/binop.smir.json.expected @@ -10104,6 +10104,11 @@ "pointee_type": "elided" } } + ], + [ + { + "FunType": "{closure@std::rt::lang_start<()>::{closure#0}}" + } ] ] } diff --git a/tests/integration/programs/char-trivial.smir.json.expected b/tests/integration/programs/char-trivial.smir.json.expected index 0fa7ebb6..91c17bc4 100644 --- a/tests/integration/programs/char-trivial.smir.json.expected +++ b/tests/integration/programs/char-trivial.smir.json.expected @@ -2165,6 +2165,11 @@ "pointee_type": "elided" } } + ], + [ + { + "FunType": "{closure@std::rt::lang_start<()>::{closure#0}}" + } ] ] } diff --git a/tests/integration/programs/closure-args.smir.json.expected b/tests/integration/programs/closure-args.smir.json.expected index 2d683466..1e37088e 100644 --- a/tests/integration/programs/closure-args.smir.json.expected +++ b/tests/integration/programs/closure-args.smir.json.expected @@ -2602,6 +2602,16 @@ "pointee_type": "elided" } } + ], + [ + { + "FunType": "{closure@std::rt::lang_start<()>::{closure#0}}" + } + ], + [ + { + "FunType": "{closure@tests/integration/programs/closure-args.rs:2:15: 2:28}" + } ] ] } diff --git a/tests/integration/programs/closure-no-args.smir.json.expected b/tests/integration/programs/closure-no-args.smir.json.expected index 466a144c..2cc1a0b3 100644 --- a/tests/integration/programs/closure-no-args.smir.json.expected +++ b/tests/integration/programs/closure-no-args.smir.json.expected @@ -2297,6 +2297,16 @@ "pointee_type": "elided" } } + ], + [ + { + "FunType": "{closure@std::rt::lang_start<()>::{closure#0}}" + } + ], + [ + { + "FunType": "{closure@tests/integration/programs/closure-no-args.rs:2:15: 2:24}" + } ] ] } diff --git a/tests/integration/programs/const-arithm-simple.smir.json.expected b/tests/integration/programs/const-arithm-simple.smir.json.expected index e5462020..657d87ec 100644 --- a/tests/integration/programs/const-arithm-simple.smir.json.expected +++ b/tests/integration/programs/const-arithm-simple.smir.json.expected @@ -2418,6 +2418,11 @@ "pointee_type": "elided" } } + ], + [ + { + "FunType": "{closure@std::rt::lang_start<()>::{closure#0}}" + } ] ] } diff --git a/tests/integration/programs/div.smir.json.expected b/tests/integration/programs/div.smir.json.expected index 3f2de941..57304dfc 100644 --- a/tests/integration/programs/div.smir.json.expected +++ b/tests/integration/programs/div.smir.json.expected @@ -2519,6 +2519,11 @@ "pointee_type": "elided" } } + ], + [ + { + "FunType": "{closure@std::rt::lang_start<()>::{closure#0}}" + } ] ] } diff --git a/tests/integration/programs/double-ref-deref.smir.json.expected b/tests/integration/programs/double-ref-deref.smir.json.expected index bf8812eb..a8ef03f2 100644 --- a/tests/integration/programs/double-ref-deref.smir.json.expected +++ b/tests/integration/programs/double-ref-deref.smir.json.expected @@ -2339,6 +2339,11 @@ "pointee_type": "elided" } } + ], + [ + { + "FunType": "{closure@std::rt::lang_start<()>::{closure#0}}" + } ] ] } diff --git a/tests/integration/programs/enum.smir.json.expected b/tests/integration/programs/enum.smir.json.expected index 623e7c54..e0ee00fc 100644 --- a/tests/integration/programs/enum.smir.json.expected +++ b/tests/integration/programs/enum.smir.json.expected @@ -1952,6 +1952,11 @@ "pointee_type": "elided" } } + ], + [ + { + "FunType": "{closure@std::rt::lang_start<()>::{closure#0}}" + } ] ] } diff --git a/tests/integration/programs/fibonacci.smir.json.expected b/tests/integration/programs/fibonacci.smir.json.expected index 588ca660..ab5f8995 100644 --- a/tests/integration/programs/fibonacci.smir.json.expected +++ b/tests/integration/programs/fibonacci.smir.json.expected @@ -2882,6 +2882,11 @@ "pointee_type": "elided" } } + ], + [ + { + "FunType": "{closure@std::rt::lang_start<()>::{closure#0}}" + } ] ] } diff --git a/tests/integration/programs/float.smir.json.expected b/tests/integration/programs/float.smir.json.expected index fa1a755a..789b3f91 100644 --- a/tests/integration/programs/float.smir.json.expected +++ b/tests/integration/programs/float.smir.json.expected @@ -2731,6 +2731,11 @@ "pointee_type": "elided" } } + ], + [ + { + "FunType": "{closure@std::rt::lang_start<()>::{closure#0}}" + } ] ] } diff --git a/tests/integration/programs/fn-ptr-in-arg.smir.json.expected b/tests/integration/programs/fn-ptr-in-arg.smir.json.expected index bcae8759..ff66d9d9 100644 --- a/tests/integration/programs/fn-ptr-in-arg.smir.json.expected +++ b/tests/integration/programs/fn-ptr-in-arg.smir.json.expected @@ -7484,6 +7484,11 @@ "pointee_type": "elided" } } + ], + [ + { + "FunType": "{closure@std::rt::lang_start<()>::{closure#0}}" + } ] ] } diff --git a/tests/integration/programs/modulo.smir.json.expected b/tests/integration/programs/modulo.smir.json.expected index de4f0bff..baac1651 100644 --- a/tests/integration/programs/modulo.smir.json.expected +++ b/tests/integration/programs/modulo.smir.json.expected @@ -2517,6 +2517,11 @@ "pointee_type": "elided" } } + ], + [ + { + "FunType": "{closure@std::rt::lang_start<()>::{closure#0}}" + } ] ] } diff --git a/tests/integration/programs/mutual_recursion.smir.json.expected b/tests/integration/programs/mutual_recursion.smir.json.expected index 7e16f2b5..ffbbbd24 100644 --- a/tests/integration/programs/mutual_recursion.smir.json.expected +++ b/tests/integration/programs/mutual_recursion.smir.json.expected @@ -2834,6 +2834,11 @@ "pointee_type": "elided" } } + ], + [ + { + "FunType": "{closure@std::rt::lang_start<()>::{closure#0}}" + } ] ] } diff --git a/tests/integration/programs/option-construction.smir.json.expected b/tests/integration/programs/option-construction.smir.json.expected index a8518321..a3bdf756 100644 --- a/tests/integration/programs/option-construction.smir.json.expected +++ b/tests/integration/programs/option-construction.smir.json.expected @@ -2458,6 +2458,11 @@ "pointee_type": "elided" } } + ], + [ + { + "FunType": "{closure@std::rt::lang_start<()>::{closure#0}}" + } ] ] } diff --git a/tests/integration/programs/param_types.smir.json.expected b/tests/integration/programs/param_types.smir.json.expected index ebb0e199..0cc5cb79 100644 --- a/tests/integration/programs/param_types.smir.json.expected +++ b/tests/integration/programs/param_types.smir.json.expected @@ -5271,6 +5271,11 @@ "pointee_type": "elided" } } + ], + [ + { + "FunType": "{closure@std::rt::lang_start<()>::{closure#0}}" + } ] ] } diff --git a/tests/integration/programs/primitive-type-bounds.smir.json.expected b/tests/integration/programs/primitive-type-bounds.smir.json.expected index 7f09f5c6..aa124337 100644 --- a/tests/integration/programs/primitive-type-bounds.smir.json.expected +++ b/tests/integration/programs/primitive-type-bounds.smir.json.expected @@ -2450,6 +2450,11 @@ "pointee_type": "elided" } } + ], + [ + { + "FunType": "{closure@std::rt::lang_start<()>::{closure#0}}" + } ] ] } diff --git a/tests/integration/programs/recursion-simple-match.smir.json.expected b/tests/integration/programs/recursion-simple-match.smir.json.expected index dc4b4495..e12f1943 100644 --- a/tests/integration/programs/recursion-simple-match.smir.json.expected +++ b/tests/integration/programs/recursion-simple-match.smir.json.expected @@ -2649,6 +2649,11 @@ "pointee_type": "elided" } } + ], + [ + { + "FunType": "{closure@std::rt::lang_start<()>::{closure#0}}" + } ] ] } diff --git a/tests/integration/programs/recursion-simple.smir.json.expected b/tests/integration/programs/recursion-simple.smir.json.expected index 0078605b..d5912891 100644 --- a/tests/integration/programs/recursion-simple.smir.json.expected +++ b/tests/integration/programs/recursion-simple.smir.json.expected @@ -2649,6 +2649,11 @@ "pointee_type": "elided" } } + ], + [ + { + "FunType": "{closure@std::rt::lang_start<()>::{closure#0}}" + } ] ] } diff --git a/tests/integration/programs/ref-deref.smir.json.expected b/tests/integration/programs/ref-deref.smir.json.expected index 5a3f2820..1a32f296 100644 --- a/tests/integration/programs/ref-deref.smir.json.expected +++ b/tests/integration/programs/ref-deref.smir.json.expected @@ -2255,6 +2255,11 @@ "pointee_type": "elided" } } + ], + [ + { + "FunType": "{closure@std::rt::lang_start<()>::{closure#0}}" + } ] ] } diff --git a/tests/integration/programs/shl_min.smir.json.expected b/tests/integration/programs/shl_min.smir.json.expected index 28e5af97..f2cc9a2a 100644 --- a/tests/integration/programs/shl_min.smir.json.expected +++ b/tests/integration/programs/shl_min.smir.json.expected @@ -3983,6 +3983,11 @@ "pointee_type": "elided" } } + ], + [ + { + "FunType": "{closure@std::rt::lang_start<()>::{closure#0}}" + } ] ] } diff --git a/tests/integration/programs/slice.smir.json.expected b/tests/integration/programs/slice.smir.json.expected index 93d97a2a..9d046429 100644 --- a/tests/integration/programs/slice.smir.json.expected +++ b/tests/integration/programs/slice.smir.json.expected @@ -5853,6 +5853,11 @@ "pointee_type": "elided" } } + ], + [ + { + "FunType": "{closure@std::rt::lang_start<()>::{closure#0}}" + } ] ] } diff --git a/tests/integration/programs/strange-ref-deref.smir.json.expected b/tests/integration/programs/strange-ref-deref.smir.json.expected index afd06bf6..9d288e48 100644 --- a/tests/integration/programs/strange-ref-deref.smir.json.expected +++ b/tests/integration/programs/strange-ref-deref.smir.json.expected @@ -2342,6 +2342,11 @@ "pointee_type": "elided" } } + ], + [ + { + "FunType": "{closure@std::rt::lang_start<()>::{closure#0}}" + } ] ] } diff --git a/tests/integration/programs/struct.smir.json.expected b/tests/integration/programs/struct.smir.json.expected index 2556bdd6..12205a03 100644 --- a/tests/integration/programs/struct.smir.json.expected +++ b/tests/integration/programs/struct.smir.json.expected @@ -2549,6 +2549,11 @@ "pointee_type": "elided" } } + ], + [ + { + "FunType": "{closure@std::rt::lang_start<()>::{closure#0}}" + } ] ] } diff --git a/tests/integration/programs/sum-to-n.smir.json.expected b/tests/integration/programs/sum-to-n.smir.json.expected index e4bef965..8bfec1da 100644 --- a/tests/integration/programs/sum-to-n.smir.json.expected +++ b/tests/integration/programs/sum-to-n.smir.json.expected @@ -3070,6 +3070,11 @@ "pointee_type": "elided" } } + ], + [ + { + "FunType": "{closure@std::rt::lang_start<()>::{closure#0}}" + } ] ] } diff --git a/tests/integration/programs/tuple-eq.smir.json.expected b/tests/integration/programs/tuple-eq.smir.json.expected index 584c6fdf..5bf95a05 100644 --- a/tests/integration/programs/tuple-eq.smir.json.expected +++ b/tests/integration/programs/tuple-eq.smir.json.expected @@ -3086,6 +3086,11 @@ "pointee_type": "elided" } } + ], + [ + { + "FunType": "{closure@std::rt::lang_start<()>::{closure#0}}" + } ] ] } diff --git a/tests/integration/programs/tuples-simple.smir.json.expected b/tests/integration/programs/tuples-simple.smir.json.expected index ecfd043f..fee261a9 100644 --- a/tests/integration/programs/tuples-simple.smir.json.expected +++ b/tests/integration/programs/tuples-simple.smir.json.expected @@ -2336,6 +2336,11 @@ "pointee_type": "elided" } } + ], + [ + { + "FunType": "{closure@std::rt::lang_start<()>::{closure#0}}" + } ] ] } diff --git a/tests/integration/programs/weirdRefs.smir.json.expected b/tests/integration/programs/weirdRefs.smir.json.expected index dabdce54..387bf3b7 100644 --- a/tests/integration/programs/weirdRefs.smir.json.expected +++ b/tests/integration/programs/weirdRefs.smir.json.expected @@ -3849,6 +3849,11 @@ "pointee_type": "elided" } } + ], + [ + { + "FunType": "{closure@std::rt::lang_start<()>::{closure#0}}" + } ] ] } From 07d64aa712e3c1cef304c047c99627e97a3c6a26 Mon Sep 17 00:00:00 2001 From: Stevengre Date: Sat, 28 Feb 2026 11:46:22 +0800 Subject: [PATCH 03/10] style(rustfmt): normalize source formatting for nightly-2024-11-29 --- src/bin/cargo_stable_mir_json.rs | 16 +++-- src/driver.rs | 12 ++-- src/lib.rs | 3 +- src/main.rs | 8 ++- src/mk_graph/context.rs | 25 ++++--- src/mk_graph/index.rs | 18 ++++-- src/mk_graph/mod.rs | 21 +++--- src/mk_graph/output/d2.rs | 18 +++--- src/mk_graph/output/dot.rs | 48 ++++++++------ src/mk_graph/util.rs | 15 +++-- src/printer.rs | 108 +++++++++++++++++-------------- 11 files changed, 167 insertions(+), 125 deletions(-) diff --git a/src/bin/cargo_stable_mir_json.rs b/src/bin/cargo_stable_mir_json.rs index 2ab3c9cd..edf5cad7 100644 --- a/src/bin/cargo_stable_mir_json.rs +++ b/src/bin/cargo_stable_mir_json.rs @@ -1,10 +1,12 @@ -use std::env; - -use std::io::Write; -use std::os::unix::fs::PermissionsExt; -use std::path::{Path, PathBuf}; - -use anyhow::{bail, Result}; +use { + anyhow::{bail, Result}, + std::{ + env, + io::Write, + os::unix::fs::PermissionsExt, + path::{Path, PathBuf}, + }, +}; fn main() -> Result<()> { let args: Vec<_> = env::args().collect(); diff --git a/src/driver.rs b/src/driver.rs index 7522ebd8..4afa649f 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -1,7 +1,7 @@ //! This module provides a compiler driver such that: //! -//! 1. the rustc compiler context is available -//! 2. the rustc `stable_mir` APIs are available +//! 1. the rustc compiler context is available +//! 2. the rustc `stable_mir` APIs are available //! //! It exports a single function: //! @@ -22,10 +22,10 @@ extern crate rustc_interface; extern crate rustc_middle; extern crate rustc_session; extern crate rustc_smir; -use rustc_driver::Compilation; -use rustc_interface::interface::Compiler; -use rustc_middle::ty::TyCtxt; -use rustc_smir::rustc_internal; +use { + rustc_driver::Compilation, rustc_interface::interface::Compiler, rustc_middle::ty::TyCtxt, + rustc_smir::rustc_internal, +}; struct StableMirCallbacks { callback_fn: fn(TyCtxt) -> (), diff --git a/src/lib.rs b/src/lib.rs index 86ca158f..a0c954af 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,5 +2,4 @@ pub mod driver; pub mod mk_graph; pub mod printer; -pub use driver::stable_mir_driver; -pub use printer::*; +pub use {driver::stable_mir_driver, printer::*}; diff --git a/src/main.rs b/src/main.rs index deaaebb3..852d6fe6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,9 +2,11 @@ use std::env; pub mod driver; pub mod printer; -use driver::stable_mir_driver; -use printer::emit_smir; -use stable_mir_json::mk_graph::{emit_d2file, emit_dotfile}; +use { + driver::stable_mir_driver, + printer::emit_smir, + stable_mir_json::mk_graph::{emit_d2file, emit_dotfile}, +}; fn main() { let mut args: Vec = env::args().collect(); diff --git a/src/mk_graph/context.rs b/src/mk_graph/context.rs index e5507981..38e956f2 100644 --- a/src/mk_graph/context.rs +++ b/src/mk_graph/context.rs @@ -3,16 +3,20 @@ use std::collections::HashMap; extern crate stable_mir; -use stable_mir::mir::{ - BorrowKind, ConstOperand, Mutability, NonDivergingIntrinsic, Operand, Rvalue, Statement, - StatementKind, Terminator, TerminatorKind, +use { + super::{ + index::{AllocIndex, LayoutInfo, TypeEntry, TypeIndex, TypeKind}, + util::{function_string, short_fn_name, GraphLabelString}, + }, + crate::printer::SmirJson, + stable_mir::{ + mir::{ + BorrowKind, ConstOperand, Mutability, NonDivergingIntrinsic, Operand, Rvalue, + Statement, StatementKind, Terminator, TerminatorKind, + }, + ty::{ConstantKind, IndexedVal, MirConst, Ty}, + }, }; -use stable_mir::ty::{ConstantKind, IndexedVal, MirConst, Ty}; - -use crate::printer::SmirJson; - -use super::index::{AllocIndex, LayoutInfo, TypeEntry, TypeIndex, TypeKind}; -use super::util::{function_string, short_fn_name, GraphLabelString}; // ============================================================================= // GraphContext @@ -112,7 +116,8 @@ impl GraphContext { lines } - /// Resolve a call target to a function name if it's a constant function pointer + /// Resolve a call target to a function name if it's a constant function + /// pointer pub fn resolve_call_target(&self, func: &Operand) -> Option { match func { Operand::Constant(ConstOperand { const_, .. }) => { diff --git a/src/mk_graph/index.rs b/src/mk_graph/index.rs index 58400c45..78d9327d 100644 --- a/src/mk_graph/index.rs +++ b/src/mk_graph/index.rs @@ -3,12 +3,15 @@ use std::collections::HashMap; extern crate stable_mir; -use stable_mir::abi::{FieldsShape, LayoutShape}; -use stable_mir::mir::alloc::GlobalAlloc; -use stable_mir::ty::{IndexedVal, Ty}; -use stable_mir::CrateDef; - -use crate::printer::{AllocInfo, TypeMetadata}; +use { + crate::printer::{AllocInfo, TypeMetadata}, + stable_mir::{ + abi::{FieldsShape, LayoutShape}, + mir::alloc::GlobalAlloc, + ty::{IndexedVal, Ty}, + CrateDef, + }, +}; // ============================================================================= // Index Structures @@ -306,7 +309,8 @@ impl TypeEntry { .iter() .map(|&t| FieldInfo { ty: t, - offset: None, // Enum variant offsets require variant-specific layout + offset: None, /* Enum variant offsets require variant-specific + * layout */ }) .collect(), }) diff --git a/src/mk_graph/mod.rs b/src/mk_graph/mod.rs index cc67fab1..a3b22ba0 100644 --- a/src/mk_graph/mod.rs +++ b/src/mk_graph/mod.rs @@ -3,16 +3,19 @@ //! This module provides functionality to generate graph visualizations //! of Rust's MIR in various formats (DOT, D2). -use std::fs::File; -use std::io::{self, Write}; +use std::{ + fs::File, + io::{self, Write}, +}; extern crate rustc_middle; use rustc_middle::ty::TyCtxt; extern crate rustc_session; -use rustc_session::config::{OutFileName, OutputType}; - -use crate::printer::collect_smir; +use { + crate::printer::collect_smir, + rustc_session::config::{OutFileName, OutputType}, +}; // Sub-modules pub mod context; @@ -21,9 +24,11 @@ pub mod output; pub mod util; // Re-exports for convenience -pub use context::GraphContext; -pub use index::{AllocEntry, AllocIndex, AllocKind, TypeIndex}; -pub use util::GraphLabelString; +pub use { + context::GraphContext, + index::{AllocEntry, AllocIndex, AllocKind, TypeIndex}, + util::GraphLabelString, +}; // ============================================================================= // Entry Points diff --git a/src/mk_graph/output/d2.rs b/src/mk_graph/output/d2.rs index e33697f6..0563f53a 100644 --- a/src/mk_graph/output/d2.rs +++ b/src/mk_graph/output/d2.rs @@ -1,14 +1,16 @@ //! D2 diagram format output for MIR graphs. extern crate stable_mir; -use stable_mir::mir::TerminatorKind; - -use crate::printer::SmirJson; -use crate::MonoItemKind; - -use crate::mk_graph::context::GraphContext; -use crate::mk_graph::util::{ - escape_d2, is_unqualified, name_lines, short_name, terminator_targets, +use { + crate::{ + mk_graph::{ + context::GraphContext, + util::{escape_d2, is_unqualified, name_lines, short_name, terminator_targets}, + }, + printer::SmirJson, + MonoItemKind, + }, + stable_mir::mir::TerminatorKind, }; impl SmirJson<'_> { diff --git a/src/mk_graph/output/dot.rs b/src/mk_graph/output/dot.rs index 69b7d629..696c6440 100644 --- a/src/mk_graph/output/dot.rs +++ b/src/mk_graph/output/dot.rs @@ -1,17 +1,22 @@ //! DOT (Graphviz) format output for MIR graphs. -use std::collections::HashSet; - -use dot_writer::{Attributes, Color, DotWriter, Scope, Shape, Style}; +use { + dot_writer::{Attributes, Color, DotWriter, Scope, Shape, Style}, + std::collections::HashSet, +}; extern crate stable_mir; -use stable_mir::mir::{BasicBlock, ConstOperand, Operand, TerminatorKind, UnwindAction}; - -use crate::printer::SmirJson; -use crate::MonoItemKind; - -use crate::mk_graph::context::GraphContext; -use crate::mk_graph::util::{block_name, is_unqualified, name_lines, short_name, GraphLabelString}; +use { + crate::{ + mk_graph::{ + context::GraphContext, + util::{block_name, is_unqualified, name_lines, short_name, GraphLabelString}, + }, + printer::SmirJson, + MonoItemKind, + }, + stable_mir::mir::{BasicBlock, ConstOperand, Operand, TerminatorKind, UnwindAction}, +}; impl SmirJson<'_> { /// Convert the MIR to DOT (Graphviz) format @@ -91,7 +96,8 @@ impl SmirJson<'_> { local_node.set("color", "palegreen3", false); drop(local_node); - // Cannot define local functions that capture env. variables. Instead we define _closures_. + // Cannot define local functions that capture env. variables. Instead we + // define _closures_. let process_block = |cluster: &mut Scope<'_, '_>, node_id: usize, b: &BasicBlock| { let name = &item.symbol_name; @@ -173,8 +179,11 @@ impl SmirJson<'_> { .set_label(&dest); } - // The call edge has to be drawn outside the cluster, outside this function (cluster borrows &mut graph)! - // Code for that is therefore separated into its own second function below. + // The call edge has to be drawn outside + // the cluster, outside this function + // (cluster borrows &mut graph)! + // Code for that is therefore separated + // into its own second function below. } Assert { cond, @@ -235,9 +244,10 @@ impl SmirJson<'_> { drop(c); // so we can borrow graph again - // call edges have to be added _outside_ the cluster of blocks for one function - // because they go between different clusters. Due to a scope/borrow issue, we have - // to make a 2nd pass over the bodies of the item. + // call edges have to be added _outside_ the cluster of blocks for one + // function because they go between different + // clusters. Due to a scope/borrow issue, we have to + // make a 2nd pass over the bodies of the item. let add_call_edges = |graph: &mut Scope<'_, '_>, offset: usize, bs: &Vec| { for (i, b) in bs.iter().enumerate() { @@ -252,7 +262,8 @@ impl SmirJson<'_> { if let Some(callee) = ctx.functions.get(&const_.ty()) { - // callee node/body will be added when its body is added, missing ones added before + // callee node/body will be added when its + // body is added, missing ones added before graph.edge( &this_block, block_name(callee, 0), @@ -260,7 +271,8 @@ impl SmirJson<'_> { } else { let unknown = format!("{}", const_.ty()); // pathological case, could panic! instead. - // all unknown callees will be collapsed into one `unknown` node + // all unknown callees will be collapsed + // into one `unknown` node graph.edge(&this_block, unknown) } } diff --git a/src/mk_graph/util.rs b/src/mk_graph/util.rs index 41d1fbec..d3f4c528 100644 --- a/src/mk_graph/util.rs +++ b/src/mk_graph/util.rs @@ -3,13 +3,16 @@ use std::hash::{DefaultHasher, Hash, Hasher}; extern crate stable_mir; -use stable_mir::mir::{ - AggregateKind, BorrowKind, ConstOperand, Mutability, NonDivergingIntrinsic, NullOp, Operand, - Place, ProjectionElem, Rvalue, Terminator, TerminatorKind, UnwindAction, +use { + crate::printer::FnSymType, + stable_mir::{ + mir::{ + AggregateKind, BorrowKind, ConstOperand, Mutability, NonDivergingIntrinsic, NullOp, + Operand, Place, ProjectionElem, Rvalue, Terminator, TerminatorKind, UnwindAction, + }, + ty::{IndexedVal, RigidTy}, + }, }; -use stable_mir::ty::{IndexedVal, RigidTy}; - -use crate::printer::FnSymType; // ============================================================================= // GraphLabelString Trait diff --git a/src/printer.rs b/src/printer.rs index 03bb8db6..d29cf924 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -1,11 +1,10 @@ -use std::hash::Hash; -use std::io::Write; -use std::ops::ControlFlow; use std::{ collections::{HashMap, HashSet}, fs::File, - io, + hash::Hash, + io::{self, Write}, iter::Iterator, + ops::ControlFlow, str, vec::Vec, }; @@ -15,32 +14,36 @@ extern crate rustc_session; extern crate rustc_smir; extern crate rustc_span; extern crate stable_mir; -// HACK: typically, we would source serde/serde_json separately from the compiler -// However, due to issues matching crate versions when we have our own serde -// in addition to the rustc serde, we force ourselves to use rustc serde +// HACK: typically, we would source serde/serde_json separately from the +// compiler However, due to issues matching crate versions when we have +// our own serde in addition to the rustc serde, we force ourselves to use +// rustc serde extern crate serde; extern crate serde_json; -use rustc_middle as middle; -use rustc_middle::ty::{ - EarlyBinder, FnSig, GenericArgs, List, Ty, TyCtxt, TypeFoldable, TypingEnv, -}; -use rustc_session::config::{OutFileName, OutputType}; -use rustc_smir::rustc_internal::{self, internal}; -use rustc_span::{ - def_id::{DefId, LOCAL_CRATE}, - symbol, -}; -use serde::{Serialize, Serializer}; -use stable_mir::{ - abi::{FieldsShape, LayoutShape}, - mir::mono::{Instance, InstanceKind, MonoItem}, - mir::{ - alloc::AllocId, alloc::GlobalAlloc, visit::MirVisitor, Body, LocalDecl, Rvalue, Terminator, - TerminatorKind, +use { + rustc_middle::{ + self as middle, + ty::{EarlyBinder, FnSig, GenericArgs, List, Ty, TyCtxt, TypeFoldable, TypingEnv}, + }, + rustc_session::config::{OutFileName, OutputType}, + rustc_smir::rustc_internal::{self, internal}, + rustc_span::{ + def_id::{DefId, LOCAL_CRATE}, + symbol, + }, + serde::{Serialize, Serializer}, + stable_mir::{ + abi::{FieldsShape, LayoutShape}, + mir::{ + alloc::{AllocId, GlobalAlloc}, + mono::{Instance, InstanceKind, MonoItem}, + visit::MirVisitor, + Body, LocalDecl, Rvalue, Terminator, TerminatorKind, + }, + ty::{AdtDef, Allocation, ConstDef, ForeignItemKind, IndexedVal, RigidTy, TyKind}, + visitor::{Visitable, Visitor}, + CrateDef, CrateItem, ItemKind, }, - ty::{AdtDef, Allocation, ConstDef, ForeignItemKind, IndexedVal, RigidTy, TyKind}, - visitor::{Visitable, Visitor}, - CrateDef, CrateItem, ItemKind, }; // Structs for serializing extra details about mono items @@ -307,8 +310,9 @@ pub struct Item { } impl Item { - /// Returns the pre-collected body and appropriate locals slice, if available. - /// For functions, locals come from the body; for statics, locals are empty. + /// Returns the pre-collected body and appropriate locals slice, if + /// available. For functions, locals come from the body; for statics, + /// locals are empty. fn body_and_locals(&self) -> Option<(&Body, &[LocalDecl])> { match &self.mono_item_kind { MonoItemKind::MonoItemFn { @@ -524,13 +528,13 @@ type LinkMap<'tcx> = HashMap, (ItemSource, FnSymType)>; /// behavior in debug builds. This serves two purposes: /// /// 1. Duplicate detection: if the same AllocId is inserted twice, that -/// indicates a body was walked more than once (a regression the -/// declarative pipeline is designed to prevent). +/// indicates a body was walked more than once (a regression the declarative +/// pipeline is designed to prevent). /// -/// 2. Coherence verification (via `verify_coherence`): after collection, -/// checks that every AllocId in the stored Item bodies actually -/// exists in this map. A mismatch means the analysis walked a -/// different body than what's stored (the original alloc-id bug). +/// 2. Coherence verification (via `verify_coherence`): after collection, checks +/// that every AllocId in the stored Item bodies actually exists in this map. +/// A mismatch means the analysis walked a different body than what's stored +/// (the original alloc-id bug). /// /// In release builds the tracking fields are compiled out, making this /// a zero-cost wrapper. @@ -761,9 +765,9 @@ impl Visitor for TyCollector<'_> { } } -/// Single-pass body visitor that collects all derived information from a MIR body: -/// link map entries (calls, drops, fn pointers), allocations, types, spans, -/// and unevaluated constant references (for transitive item discovery). +/// Single-pass body visitor that collects all derived information from a MIR +/// body: link map entries (calls, drops, fn pointers), allocations, types, +/// spans, and unevaluated constant references (for transitive item discovery). /// /// By combining what was previously two separate visitors (BodyAnalyzer /// and UnevaluatedConstCollector), each body is walked exactly once. @@ -903,7 +907,8 @@ fn get_prov_ty(ty: stable_mir::ty::Ty, offset: &usize) -> Option - // FIXME we'd have to figure out which variant we are dealing with (requires the data) + // FIXME we'd have to figure out which variant we are dealing with (requires the + // data) { None } @@ -1049,8 +1054,10 @@ impl MirVisitor for BodyAnalyzer<'_, '_> { } fn visit_terminator(&mut self, term: &Terminator, loc: stable_mir::mir::visit::Location) { - use stable_mir::mir::{ConstOperand, Operand::Constant}; - use TerminatorKind::*; + use { + stable_mir::mir::{ConstOperand, Operand::Constant}, + TerminatorKind::*, + }; let fn_sym = match &term.kind { Call { func: Constant(ConstOperand { const_: cnst, .. }), @@ -1168,8 +1175,9 @@ impl MirVisitor for BodyAnalyzer<'_, '_> { } } -/// Result of collecting all mono items and analyzing their bodies in a single pass. -/// After this phase completes, no more inst.body() calls should be needed. +/// Result of collecting all mono items and analyzing their bodies in a single +/// pass. After this phase completes, no more inst.body() calls should be +/// needed. struct CollectedCrate { items: Vec, unevaluated_consts: HashMap, @@ -1182,8 +1190,8 @@ struct DerivedInfo<'tcx> { spans: SpanMap, } -/// Enqueue newly discovered unevaluated-const items into the fixpoint work queue. -/// Each new item calls mk_item (which calls inst.body() exactly once). +/// Enqueue newly discovered unevaluated-const items into the fixpoint work +/// queue. Each new item calls mk_item (which calls inst.body() exactly once). fn enqueue_unevaluated_consts( tcx: TyCtxt<'_>, discovered: Vec, @@ -1362,14 +1370,13 @@ fn mk_type_metadata( t: TyKind, layout: Option, ) -> Option<(stable_mir::ty::Ty, TypeMetadata)> { - use stable_mir::ty::RigidTy::*; - use TyKind::RigidTy as T; - use TypeMetadata::*; + use {stable_mir::ty::RigidTy::*, TyKind::RigidTy as T, TypeMetadata::*}; let name = format!("{k}"); // prints name with type parameters match t { T(prim_type) if t.is_primitive() => Some((k, PrimitiveType(prim_type))), // for enums, we need a mapping of variantIdx to discriminant - // this requires access to the internals and is not provided as an interface function at the moment + // this requires access to the internals and is not provided as an interface function at the + // moment T(Adt(adt_def, args)) if t.is_enum() => { let adt_internal = rustc_internal::internal(tcx, adt_def); let discriminants = adt_internal @@ -1480,7 +1487,8 @@ fn mk_type_metadata( )), // for tuples the element types are provided T(Tuple(types)) => Some((k, TupleType { types, layout })), - // opaque function types (fun ptrs, closures, FnDef) are only provided to avoid dangling ty references + // opaque function types (fun ptrs, closures, FnDef) are only provided to avoid dangling ty + // references T(FnDef(_, _)) | T(FnPtr(_)) | T(Closure(_, _)) => Some((k, FunType(name))), // other types are not provided either T(Foreign(_)) From 051c44bc9577c57ddba90b72b2e94279faddefe8 Mon Sep 17 00:00:00 2001 From: Stevengre Date: Sat, 28 Feb 2026 15:39:30 +0800 Subject: [PATCH 04/10] fix(printer): avoid panic on non-builtin static/vtable alloc pointers --- src/printer.rs | 64 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 16 deletions(-) diff --git a/src/printer.rs b/src/printer.rs index d29cf924..287bf084 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -982,24 +982,56 @@ fn collect_alloc( } } GlobalAlloc::Static(_) => { - assert!( - kind.clone().builtin_deref(true).is_some(), - "Allocated pointer is not a built-in pointer type: {:?}", - kind - ); - val_collector - .visited_allocs - .insert(val, (ty, global_alloc.clone())); + if !kind.clone().builtin_deref(true).is_some() { + let prov_ty = get_prov_ty(ty, offset); + debug_log_println!( + "DEBUG: GlobalAlloc::Static with non-builtin-deref type; alloc_id={:?}, ty={:?}, offset={}, kind={:?}, recovered_prov_ty={:?}", + val, + ty, + offset, + kind, + prov_ty + ); + if let Some(p_ty) = prov_ty { + val_collector + .visited_allocs + .insert(val, (p_ty, global_alloc.clone())); + } else { + val_collector + .visited_allocs + .insert(val, (stable_mir::ty::Ty::to_val(0), global_alloc.clone())); + } + } else { + val_collector + .visited_allocs + .insert(val, (ty, global_alloc.clone())); + } } GlobalAlloc::VTable(_, _) => { - assert!( - kind.clone().builtin_deref(true).is_some(), - "Allocated pointer is not a built-in pointer type: {:?}", - kind - ); - val_collector - .visited_allocs - .insert(val, (ty, global_alloc.clone())); + if !kind.clone().builtin_deref(true).is_some() { + let prov_ty = get_prov_ty(ty, offset); + debug_log_println!( + "DEBUG: GlobalAlloc::VTable with non-builtin-deref type; alloc_id={:?}, ty={:?}, offset={}, kind={:?}, recovered_prov_ty={:?}", + val, + ty, + offset, + kind, + prov_ty + ); + if let Some(p_ty) = prov_ty { + val_collector + .visited_allocs + .insert(val, (p_ty, global_alloc.clone())); + } else { + val_collector + .visited_allocs + .insert(val, (stable_mir::ty::Ty::to_val(0), global_alloc.clone())); + } + } else { + val_collector + .visited_allocs + .insert(val, (ty, global_alloc.clone())); + } } GlobalAlloc::Function(_) => { if !kind.is_fn_ptr() { From 5660fba79a31781f6443cb1e828e015b9495129e Mon Sep 17 00:00:00 2001 From: Stevengre Date: Sat, 28 Feb 2026 19:49:47 +0800 Subject: [PATCH 05/10] fix(ci): satisfy clippy gate and stabilize integration-test paths --- Makefile | 2 +- src/printer.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 3d41d14e..423af765 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ rustup-clear-toolchain: rustup override unset --nonexistent rustup toolchain uninstall "${TOOLCHAIN_NAME}" -TESTDIR=$(CURDIR)/tests/integration/programs +TESTDIR=tests/integration/programs .PHONY: integration-test integration-test: TESTS ?= $(shell find $(TESTDIR) -type f -name "*.rs") diff --git a/src/printer.rs b/src/printer.rs index 287bf084..3ad9e95c 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -982,7 +982,7 @@ fn collect_alloc( } } GlobalAlloc::Static(_) => { - if !kind.clone().builtin_deref(true).is_some() { + if kind.clone().builtin_deref(true).is_none() { let prov_ty = get_prov_ty(ty, offset); debug_log_println!( "DEBUG: GlobalAlloc::Static with non-builtin-deref type; alloc_id={:?}, ty={:?}, offset={}, kind={:?}, recovered_prov_ty={:?}", @@ -1008,7 +1008,7 @@ fn collect_alloc( } } GlobalAlloc::VTable(_, _) => { - if !kind.clone().builtin_deref(true).is_some() { + if kind.clone().builtin_deref(true).is_none() { let prov_ty = get_prov_ty(ty, offset); debug_log_println!( "DEBUG: GlobalAlloc::VTable with non-builtin-deref type; alloc_id={:?}, ty={:?}, offset={}, kind={:?}, recovered_prov_ty={:?}", From 964c893a1c446c2328b2f3e16ebd3316b7839fe2 Mon Sep 17 00:00:00 2001 From: Stevengre Date: Sat, 28 Feb 2026 21:28:47 +0800 Subject: [PATCH 06/10] chore(ci): trigger pull_request checks on latest head From 9aa617199a3c810df15d83db7d88bb6cfe2e8dd5 Mon Sep 17 00:00:00 2001 From: Stevengre Date: Mon, 2 Mar 2026 10:44:03 +0800 Subject: [PATCH 07/10] chore: drop local workspace override from PR --- Cargo.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c514c2f0..1ca983eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,5 +21,3 @@ debug_log = [] [package.metadata.rust-analyzer] # This package uses rustc crates. rustc_private=true - -[workspace] From 87d6fdcc79ac3b71a990025f56ee4d3848d2aefe Mon Sep 17 00:00:00 2001 From: Stevengre Date: Mon, 2 Mar 2026 10:48:50 +0800 Subject: [PATCH 08/10] revert: drop rustfmt-only refactor from this PR --- src/bin/cargo_stable_mir_json.rs | 16 ++--- src/driver.rs | 12 ++-- src/lib.rs | 3 +- src/main.rs | 8 +-- src/mk_graph/context.rs | 25 +++---- src/mk_graph/index.rs | 18 ++---- src/mk_graph/mod.rs | 21 +++--- src/mk_graph/output/d2.rs | 18 +++--- src/mk_graph/output/dot.rs | 48 ++++++-------- src/mk_graph/util.rs | 15 ++--- src/printer.rs | 108 ++++++++++++++----------------- 11 files changed, 125 insertions(+), 167 deletions(-) diff --git a/src/bin/cargo_stable_mir_json.rs b/src/bin/cargo_stable_mir_json.rs index edf5cad7..2ab3c9cd 100644 --- a/src/bin/cargo_stable_mir_json.rs +++ b/src/bin/cargo_stable_mir_json.rs @@ -1,12 +1,10 @@ -use { - anyhow::{bail, Result}, - std::{ - env, - io::Write, - os::unix::fs::PermissionsExt, - path::{Path, PathBuf}, - }, -}; +use std::env; + +use std::io::Write; +use std::os::unix::fs::PermissionsExt; +use std::path::{Path, PathBuf}; + +use anyhow::{bail, Result}; fn main() -> Result<()> { let args: Vec<_> = env::args().collect(); diff --git a/src/driver.rs b/src/driver.rs index 4afa649f..7522ebd8 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -1,7 +1,7 @@ //! This module provides a compiler driver such that: //! -//! 1. the rustc compiler context is available -//! 2. the rustc `stable_mir` APIs are available +//! 1. the rustc compiler context is available +//! 2. the rustc `stable_mir` APIs are available //! //! It exports a single function: //! @@ -22,10 +22,10 @@ extern crate rustc_interface; extern crate rustc_middle; extern crate rustc_session; extern crate rustc_smir; -use { - rustc_driver::Compilation, rustc_interface::interface::Compiler, rustc_middle::ty::TyCtxt, - rustc_smir::rustc_internal, -}; +use rustc_driver::Compilation; +use rustc_interface::interface::Compiler; +use rustc_middle::ty::TyCtxt; +use rustc_smir::rustc_internal; struct StableMirCallbacks { callback_fn: fn(TyCtxt) -> (), diff --git a/src/lib.rs b/src/lib.rs index a0c954af..86ca158f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,4 +2,5 @@ pub mod driver; pub mod mk_graph; pub mod printer; -pub use {driver::stable_mir_driver, printer::*}; +pub use driver::stable_mir_driver; +pub use printer::*; diff --git a/src/main.rs b/src/main.rs index 852d6fe6..deaaebb3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,11 +2,9 @@ use std::env; pub mod driver; pub mod printer; -use { - driver::stable_mir_driver, - printer::emit_smir, - stable_mir_json::mk_graph::{emit_d2file, emit_dotfile}, -}; +use driver::stable_mir_driver; +use printer::emit_smir; +use stable_mir_json::mk_graph::{emit_d2file, emit_dotfile}; fn main() { let mut args: Vec = env::args().collect(); diff --git a/src/mk_graph/context.rs b/src/mk_graph/context.rs index 38e956f2..e5507981 100644 --- a/src/mk_graph/context.rs +++ b/src/mk_graph/context.rs @@ -3,20 +3,16 @@ use std::collections::HashMap; extern crate stable_mir; -use { - super::{ - index::{AllocIndex, LayoutInfo, TypeEntry, TypeIndex, TypeKind}, - util::{function_string, short_fn_name, GraphLabelString}, - }, - crate::printer::SmirJson, - stable_mir::{ - mir::{ - BorrowKind, ConstOperand, Mutability, NonDivergingIntrinsic, Operand, Rvalue, - Statement, StatementKind, Terminator, TerminatorKind, - }, - ty::{ConstantKind, IndexedVal, MirConst, Ty}, - }, +use stable_mir::mir::{ + BorrowKind, ConstOperand, Mutability, NonDivergingIntrinsic, Operand, Rvalue, Statement, + StatementKind, Terminator, TerminatorKind, }; +use stable_mir::ty::{ConstantKind, IndexedVal, MirConst, Ty}; + +use crate::printer::SmirJson; + +use super::index::{AllocIndex, LayoutInfo, TypeEntry, TypeIndex, TypeKind}; +use super::util::{function_string, short_fn_name, GraphLabelString}; // ============================================================================= // GraphContext @@ -116,8 +112,7 @@ impl GraphContext { lines } - /// Resolve a call target to a function name if it's a constant function - /// pointer + /// Resolve a call target to a function name if it's a constant function pointer pub fn resolve_call_target(&self, func: &Operand) -> Option { match func { Operand::Constant(ConstOperand { const_, .. }) => { diff --git a/src/mk_graph/index.rs b/src/mk_graph/index.rs index eaa1a7f9..5b5e5739 100644 --- a/src/mk_graph/index.rs +++ b/src/mk_graph/index.rs @@ -3,15 +3,12 @@ use std::collections::HashMap; extern crate stable_mir; -use { - crate::printer::{AllocInfo, TypeMetadata}, - stable_mir::{ - abi::{FieldsShape, LayoutShape}, - mir::alloc::GlobalAlloc, - ty::{IndexedVal, Ty}, - CrateDef, - }, -}; +use stable_mir::abi::{FieldsShape, LayoutShape}; +use stable_mir::mir::alloc::GlobalAlloc; +use stable_mir::ty::{IndexedVal, Ty}; +use stable_mir::CrateDef; + +use crate::printer::{AllocInfo, TypeMetadata}; // ============================================================================= // Index Structures @@ -326,8 +323,7 @@ impl TypeEntry { .iter() .map(|&t| FieldInfo { ty: t, - offset: None, /* Enum variant offsets require variant-specific - * layout */ + offset: None, // Enum variant offsets require variant-specific layout }) .collect(), }) diff --git a/src/mk_graph/mod.rs b/src/mk_graph/mod.rs index a3b22ba0..cc67fab1 100644 --- a/src/mk_graph/mod.rs +++ b/src/mk_graph/mod.rs @@ -3,19 +3,16 @@ //! This module provides functionality to generate graph visualizations //! of Rust's MIR in various formats (DOT, D2). -use std::{ - fs::File, - io::{self, Write}, -}; +use std::fs::File; +use std::io::{self, Write}; extern crate rustc_middle; use rustc_middle::ty::TyCtxt; extern crate rustc_session; -use { - crate::printer::collect_smir, - rustc_session::config::{OutFileName, OutputType}, -}; +use rustc_session::config::{OutFileName, OutputType}; + +use crate::printer::collect_smir; // Sub-modules pub mod context; @@ -24,11 +21,9 @@ pub mod output; pub mod util; // Re-exports for convenience -pub use { - context::GraphContext, - index::{AllocEntry, AllocIndex, AllocKind, TypeIndex}, - util::GraphLabelString, -}; +pub use context::GraphContext; +pub use index::{AllocEntry, AllocIndex, AllocKind, TypeIndex}; +pub use util::GraphLabelString; // ============================================================================= // Entry Points diff --git a/src/mk_graph/output/d2.rs b/src/mk_graph/output/d2.rs index 0563f53a..e33697f6 100644 --- a/src/mk_graph/output/d2.rs +++ b/src/mk_graph/output/d2.rs @@ -1,16 +1,14 @@ //! D2 diagram format output for MIR graphs. extern crate stable_mir; -use { - crate::{ - mk_graph::{ - context::GraphContext, - util::{escape_d2, is_unqualified, name_lines, short_name, terminator_targets}, - }, - printer::SmirJson, - MonoItemKind, - }, - stable_mir::mir::TerminatorKind, +use stable_mir::mir::TerminatorKind; + +use crate::printer::SmirJson; +use crate::MonoItemKind; + +use crate::mk_graph::context::GraphContext; +use crate::mk_graph::util::{ + escape_d2, is_unqualified, name_lines, short_name, terminator_targets, }; impl SmirJson<'_> { diff --git a/src/mk_graph/output/dot.rs b/src/mk_graph/output/dot.rs index 696c6440..69b7d629 100644 --- a/src/mk_graph/output/dot.rs +++ b/src/mk_graph/output/dot.rs @@ -1,22 +1,17 @@ //! DOT (Graphviz) format output for MIR graphs. -use { - dot_writer::{Attributes, Color, DotWriter, Scope, Shape, Style}, - std::collections::HashSet, -}; +use std::collections::HashSet; + +use dot_writer::{Attributes, Color, DotWriter, Scope, Shape, Style}; extern crate stable_mir; -use { - crate::{ - mk_graph::{ - context::GraphContext, - util::{block_name, is_unqualified, name_lines, short_name, GraphLabelString}, - }, - printer::SmirJson, - MonoItemKind, - }, - stable_mir::mir::{BasicBlock, ConstOperand, Operand, TerminatorKind, UnwindAction}, -}; +use stable_mir::mir::{BasicBlock, ConstOperand, Operand, TerminatorKind, UnwindAction}; + +use crate::printer::SmirJson; +use crate::MonoItemKind; + +use crate::mk_graph::context::GraphContext; +use crate::mk_graph::util::{block_name, is_unqualified, name_lines, short_name, GraphLabelString}; impl SmirJson<'_> { /// Convert the MIR to DOT (Graphviz) format @@ -96,8 +91,7 @@ impl SmirJson<'_> { local_node.set("color", "palegreen3", false); drop(local_node); - // Cannot define local functions that capture env. variables. Instead we - // define _closures_. + // Cannot define local functions that capture env. variables. Instead we define _closures_. let process_block = |cluster: &mut Scope<'_, '_>, node_id: usize, b: &BasicBlock| { let name = &item.symbol_name; @@ -179,11 +173,8 @@ impl SmirJson<'_> { .set_label(&dest); } - // The call edge has to be drawn outside - // the cluster, outside this function - // (cluster borrows &mut graph)! - // Code for that is therefore separated - // into its own second function below. + // The call edge has to be drawn outside the cluster, outside this function (cluster borrows &mut graph)! + // Code for that is therefore separated into its own second function below. } Assert { cond, @@ -244,10 +235,9 @@ impl SmirJson<'_> { drop(c); // so we can borrow graph again - // call edges have to be added _outside_ the cluster of blocks for one - // function because they go between different - // clusters. Due to a scope/borrow issue, we have to - // make a 2nd pass over the bodies of the item. + // call edges have to be added _outside_ the cluster of blocks for one function + // because they go between different clusters. Due to a scope/borrow issue, we have + // to make a 2nd pass over the bodies of the item. let add_call_edges = |graph: &mut Scope<'_, '_>, offset: usize, bs: &Vec| { for (i, b) in bs.iter().enumerate() { @@ -262,8 +252,7 @@ impl SmirJson<'_> { if let Some(callee) = ctx.functions.get(&const_.ty()) { - // callee node/body will be added when its - // body is added, missing ones added before + // callee node/body will be added when its body is added, missing ones added before graph.edge( &this_block, block_name(callee, 0), @@ -271,8 +260,7 @@ impl SmirJson<'_> { } else { let unknown = format!("{}", const_.ty()); // pathological case, could panic! instead. - // all unknown callees will be collapsed - // into one `unknown` node + // all unknown callees will be collapsed into one `unknown` node graph.edge(&this_block, unknown) } } diff --git a/src/mk_graph/util.rs b/src/mk_graph/util.rs index d3f4c528..41d1fbec 100644 --- a/src/mk_graph/util.rs +++ b/src/mk_graph/util.rs @@ -3,16 +3,13 @@ use std::hash::{DefaultHasher, Hash, Hasher}; extern crate stable_mir; -use { - crate::printer::FnSymType, - stable_mir::{ - mir::{ - AggregateKind, BorrowKind, ConstOperand, Mutability, NonDivergingIntrinsic, NullOp, - Operand, Place, ProjectionElem, Rvalue, Terminator, TerminatorKind, UnwindAction, - }, - ty::{IndexedVal, RigidTy}, - }, +use stable_mir::mir::{ + AggregateKind, BorrowKind, ConstOperand, Mutability, NonDivergingIntrinsic, NullOp, Operand, + Place, ProjectionElem, Rvalue, Terminator, TerminatorKind, UnwindAction, }; +use stable_mir::ty::{IndexedVal, RigidTy}; + +use crate::printer::FnSymType; // ============================================================================= // GraphLabelString Trait diff --git a/src/printer.rs b/src/printer.rs index 4c347cbd..574fc38d 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -1,10 +1,11 @@ +use std::hash::Hash; +use std::io::Write; +use std::ops::ControlFlow; use std::{ collections::{HashMap, HashSet}, fs::File, - hash::Hash, - io::{self, Write}, + io, iter::Iterator, - ops::ControlFlow, str, vec::Vec, }; @@ -14,36 +15,32 @@ extern crate rustc_session; extern crate rustc_smir; extern crate rustc_span; extern crate stable_mir; -// HACK: typically, we would source serde/serde_json separately from the -// compiler However, due to issues matching crate versions when we have -// our own serde in addition to the rustc serde, we force ourselves to use -// rustc serde +// HACK: typically, we would source serde/serde_json separately from the compiler +// However, due to issues matching crate versions when we have our own serde +// in addition to the rustc serde, we force ourselves to use rustc serde extern crate serde; extern crate serde_json; -use { - rustc_middle::{ - self as middle, - ty::{EarlyBinder, FnSig, GenericArgs, List, Ty, TyCtxt, TypeFoldable, TypingEnv}, - }, - rustc_session::config::{OutFileName, OutputType}, - rustc_smir::rustc_internal::{self, internal}, - rustc_span::{ - def_id::{DefId, LOCAL_CRATE}, - symbol, - }, - serde::{Serialize, Serializer}, - stable_mir::{ - abi::{FieldsShape, LayoutShape}, - mir::{ - alloc::{AllocId, GlobalAlloc}, - mono::{Instance, InstanceKind, MonoItem}, - visit::MirVisitor, - Body, LocalDecl, Rvalue, Terminator, TerminatorKind, - }, - ty::{AdtDef, Allocation, ConstDef, ForeignItemKind, IndexedVal, RigidTy, TyKind}, - visitor::{Visitable, Visitor}, - CrateDef, CrateItem, ItemKind, +use rustc_middle as middle; +use rustc_middle::ty::{ + EarlyBinder, FnSig, GenericArgs, List, Ty, TyCtxt, TypeFoldable, TypingEnv, +}; +use rustc_session::config::{OutFileName, OutputType}; +use rustc_smir::rustc_internal::{self, internal}; +use rustc_span::{ + def_id::{DefId, LOCAL_CRATE}, + symbol, +}; +use serde::{Serialize, Serializer}; +use stable_mir::{ + abi::{FieldsShape, LayoutShape}, + mir::mono::{Instance, InstanceKind, MonoItem}, + mir::{ + alloc::AllocId, alloc::GlobalAlloc, visit::MirVisitor, Body, LocalDecl, Rvalue, Terminator, + TerminatorKind, }, + ty::{AdtDef, Allocation, ConstDef, ForeignItemKind, IndexedVal, RigidTy, TyKind}, + visitor::{Visitable, Visitor}, + CrateDef, CrateItem, ItemKind, }; // Structs for serializing extra details about mono items @@ -310,9 +307,8 @@ pub struct Item { } impl Item { - /// Returns the pre-collected body and appropriate locals slice, if - /// available. For functions, locals come from the body; for statics, - /// locals are empty. + /// Returns the pre-collected body and appropriate locals slice, if available. + /// For functions, locals come from the body; for statics, locals are empty. fn body_and_locals(&self) -> Option<(&Body, &[LocalDecl])> { match &self.mono_item_kind { MonoItemKind::MonoItemFn { @@ -528,13 +524,13 @@ type LinkMap<'tcx> = HashMap, (ItemSource, FnSymType)>; /// behavior in debug builds. This serves two purposes: /// /// 1. Duplicate detection: if the same AllocId is inserted twice, that -/// indicates a body was walked more than once (a regression the declarative -/// pipeline is designed to prevent). +/// indicates a body was walked more than once (a regression the +/// declarative pipeline is designed to prevent). /// -/// 2. Coherence verification (via `verify_coherence`): after collection, checks -/// that every AllocId in the stored Item bodies actually exists in this map. -/// A mismatch means the analysis walked a different body than what's stored -/// (the original alloc-id bug). +/// 2. Coherence verification (via `verify_coherence`): after collection, +/// checks that every AllocId in the stored Item bodies actually +/// exists in this map. A mismatch means the analysis walked a +/// different body than what's stored (the original alloc-id bug). /// /// In release builds the tracking fields are compiled out, making this /// a zero-cost wrapper. @@ -765,9 +761,9 @@ impl Visitor for TyCollector<'_> { } } -/// Single-pass body visitor that collects all derived information from a MIR -/// body: link map entries (calls, drops, fn pointers), allocations, types, -/// spans, and unevaluated constant references (for transitive item discovery). +/// Single-pass body visitor that collects all derived information from a MIR body: +/// link map entries (calls, drops, fn pointers), allocations, types, spans, +/// and unevaluated constant references (for transitive item discovery). /// /// By combining what was previously two separate visitors (BodyAnalyzer /// and UnevaluatedConstCollector), each body is walked exactly once. @@ -947,8 +943,7 @@ fn get_prov_ty(ty: stable_mir::ty::Ty, offset: &usize) -> Option - // FIXME we'd have to figure out which variant we are dealing with (requires the - // data) + // FIXME we'd have to figure out which variant we are dealing with (requires the data) { None } @@ -1137,10 +1132,8 @@ impl MirVisitor for BodyAnalyzer<'_, '_> { } fn visit_terminator(&mut self, term: &Terminator, loc: stable_mir::mir::visit::Location) { - use { - stable_mir::mir::{ConstOperand, Operand::Constant}, - TerminatorKind::*, - }; + use stable_mir::mir::{ConstOperand, Operand::Constant}; + use TerminatorKind::*; let fn_sym = match &term.kind { Call { func: Constant(ConstOperand { const_: cnst, .. }), @@ -1258,9 +1251,8 @@ impl MirVisitor for BodyAnalyzer<'_, '_> { } } -/// Result of collecting all mono items and analyzing their bodies in a single -/// pass. After this phase completes, no more inst.body() calls should be -/// needed. +/// Result of collecting all mono items and analyzing their bodies in a single pass. +/// After this phase completes, no more inst.body() calls should be needed. struct CollectedCrate { items: Vec, unevaluated_consts: HashMap, @@ -1273,8 +1265,8 @@ struct DerivedInfo<'tcx> { spans: SpanMap, } -/// Enqueue newly discovered unevaluated-const items into the fixpoint work -/// queue. Each new item calls mk_item (which calls inst.body() exactly once). +/// Enqueue newly discovered unevaluated-const items into the fixpoint work queue. +/// Each new item calls mk_item (which calls inst.body() exactly once). fn enqueue_unevaluated_consts( tcx: TyCtxt<'_>, discovered: Vec, @@ -1455,13 +1447,14 @@ fn mk_type_metadata( t: TyKind, layout: Option, ) -> Option<(stable_mir::ty::Ty, TypeMetadata)> { - use {stable_mir::ty::RigidTy::*, TyKind::RigidTy as T, TypeMetadata::*}; + use stable_mir::ty::RigidTy::*; + use TyKind::RigidTy as T; + use TypeMetadata::*; let name = format!("{k}"); // prints name with type parameters match t { T(prim_type) if t.is_primitive() => Some((k, PrimitiveType(prim_type))), // for enums, we need a mapping of variantIdx to discriminant - // this requires access to the internals and is not provided as an interface function at the - // moment + // this requires access to the internals and is not provided as an interface function at the moment T(Adt(adt_def, args)) if t.is_enum() => { let adt_internal = rustc_internal::internal(tcx, adt_def); let discriminants = adt_internal @@ -1574,8 +1567,7 @@ fn mk_type_metadata( )), // for tuples the element types are provided T(Tuple(types)) => Some((k, TupleType { types, layout })), - // opaque function types (fun ptrs, closures, FnDef) are only provided to avoid dangling ty - // references + // opaque function types (fun ptrs, closures, FnDef) are only provided to avoid dangling ty references T(FnDef(_, _)) | T(FnPtr(_)) | T(Closure(_, _)) => Some((k, FunType(name))), // other types are not provided either T(Foreign(_)) From 737b46bd955800b165fd6b675451df668de78ad2 Mon Sep 17 00:00:00 2001 From: Stevengre Date: Mon, 2 Mar 2026 11:26:25 +0800 Subject: [PATCH 09/10] split: keep closure metadata PR focused; drop static/vtable fallback --- src/printer.rs | 54 ++++++-------------------------------------------- 1 file changed, 6 insertions(+), 48 deletions(-) diff --git a/src/printer.rs b/src/printer.rs index 574fc38d..6f9cc9f9 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -1028,56 +1028,14 @@ fn collect_alloc( } } GlobalAlloc::Static(_) => { - if kind.clone().builtin_deref(true).is_none() { - let prov_ty = get_prov_ty(ty, offset); - debug_log_println!( - "DEBUG: GlobalAlloc::Static with non-builtin-deref type; alloc_id={:?}, ty={:?}, offset={}, kind={:?}, recovered_prov_ty={:?}", - val, - ty, - offset, - kind, - prov_ty - ); - if let Some(p_ty) = prov_ty { - val_collector - .visited_allocs - .insert(val, (p_ty, global_alloc.clone())); - } else { - val_collector - .visited_allocs - .insert(val, (stable_mir::ty::Ty::to_val(0), global_alloc.clone())); - } - } else { - val_collector - .visited_allocs - .insert(val, (ty, global_alloc.clone())); - } + val_collector + .visited_allocs + .insert(val, (ty, global_alloc.clone())); } GlobalAlloc::VTable(_, _) => { - if kind.clone().builtin_deref(true).is_none() { - let prov_ty = get_prov_ty(ty, offset); - debug_log_println!( - "DEBUG: GlobalAlloc::VTable with non-builtin-deref type; alloc_id={:?}, ty={:?}, offset={}, kind={:?}, recovered_prov_ty={:?}", - val, - ty, - offset, - kind, - prov_ty - ); - if let Some(p_ty) = prov_ty { - val_collector - .visited_allocs - .insert(val, (p_ty, global_alloc.clone())); - } else { - val_collector - .visited_allocs - .insert(val, (stable_mir::ty::Ty::to_val(0), global_alloc.clone())); - } - } else { - val_collector - .visited_allocs - .insert(val, (ty, global_alloc.clone())); - } + val_collector + .visited_allocs + .insert(val, (ty, global_alloc.clone())); } GlobalAlloc::Function(_) => { if !kind.is_fn_ptr() { From 40a690d9f7ad12d6f2ad3df79343a1887687e1a8 Mon Sep 17 00:00:00 2001 From: Stevengre Date: Mon, 2 Mar 2026 11:35:37 +0800 Subject: [PATCH 10/10] docs(printer): clarify closure Ty insertion condition --- src/printer.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/printer.rs b/src/printer.rs index 6f9cc9f9..d7d96478 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -699,6 +699,7 @@ impl Visitor for TyCollector<'_> { let instance = Instance::resolve_closure(def, args, stable_mir::ty::ClosureKind::Fn).unwrap(); let control = self.visit_instance(instance); + // Mirror other visit_ty branches: only record this Ty when traversal succeeds. if matches!(control, ControlFlow::Continue(_)) { let maybe_layout_shape = ty.layout().ok().map(|layout| layout.shape()); self.types.insert(*ty, (ty.kind(), maybe_layout_shape));