Unnamed repository; edit this file 'description' to name the repository.
Auto merge of #14808 - HKalbasi:metrics, r=HKalbasi
Add metrics for unevaluated constants, failed mir bodies, and failed data layouts
fix #14803
| -rw-r--r-- | crates/hir-ty/src/consteval/tests.rs | 19 | ||||
| -rw-r--r-- | crates/hir-ty/src/mir/lower.rs | 4 | ||||
| -rw-r--r-- | crates/rust-analyzer/src/cli/analysis_stats.rs | 103 | ||||
| -rw-r--r-- | crates/rust-analyzer/src/cli/flags.rs | 6 |
4 files changed, 113 insertions, 19 deletions
diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index 12b15065dd..2aac1cc057 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -2066,3 +2066,22 @@ fn type_error() { |e| matches!(e, ConstEvalError::MirLowerError(MirLowerError::TypeMismatch(_))), ); } + +#[test] +fn unsized_local() { + check_fail( + r#" + //- minicore: coerce_unsized, index, slice + const fn x() -> SomeUnknownTypeThatDereferenceToSlice { + SomeUnknownTypeThatDereferenceToSlice + } + + const GOAL: u16 = { + let y = x(); + let z: &[u16] = &y; + z[1] + }; + "#, + |e| matches!(e, ConstEvalError::MirLowerError(MirLowerError::UnsizedTemporary(_))), + ); +} diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 66b1d840bd..627c36dca9 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -75,6 +75,7 @@ pub enum MirLowerError { RecordLiteralWithoutPath, UnresolvedMethod(String), UnresolvedField, + UnsizedTemporary(Ty), MissingFunctionDefinition, TypeMismatch(TypeMismatch), /// This should be never happen. Type mismatch should catch everything. @@ -108,6 +109,7 @@ impl MirLowerError { } } MirLowerError::LayoutError(_) + | MirLowerError::UnsizedTemporary(_) | MirLowerError::IncompleteExpr | MirLowerError::UnaccessableLocal | MirLowerError::TraitFunctionDefinition(_, _) @@ -199,7 +201,7 @@ impl<'ctx> MirLowerCtx<'ctx> { fn temp(&mut self, ty: Ty) -> Result<LocalId> { if matches!(ty.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) { - implementation_error!("unsized temporaries"); + return Err(MirLowerError::UnsizedTemporary(ty)); } Ok(self.result.locals.alloc(Local { ty })) } diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index cdf40777ba..b12568b0bd 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -2,7 +2,6 @@ //! errors. use std::{ - collections::HashMap, env, time::{SystemTime, UNIX_EPOCH}, }; @@ -16,7 +15,7 @@ use hir_def::{ hir::{ExprId, PatId}, FunctionId, }; -use hir_ty::{Interner, TyExt, TypeFlags}; +use hir_ty::{Interner, Substitution, TyExt, TypeFlags}; use ide::{Analysis, AnalysisHost, LineCol, RootDatabase}; use ide_db::base_db::{ salsa::{self, debug::DebugQueryTable, ParallelDatabase}, @@ -122,14 +121,19 @@ impl flags::AnalysisStats { eprint!(" crates: {num_crates}"); let mut num_decls = 0; let mut funcs = Vec::new(); + let mut adts = Vec::new(); + let mut consts = Vec::new(); while let Some(module) = visit_queue.pop() { if visited_modules.insert(module) { visit_queue.extend(module.children(db)); for decl in module.declarations(db) { num_decls += 1; - if let ModuleDef::Function(f) = decl { - funcs.push(f); + match decl { + ModuleDef::Function(f) => funcs.push(f), + ModuleDef::Adt(a) => adts.push(a), + ModuleDef::Const(c) => consts.push(c), + _ => (), } } @@ -154,10 +158,13 @@ impl flags::AnalysisStats { self.run_inference(&host, db, &vfs, &funcs, verbosity); } - if self.mir_stats { - self.lower_mir(db, &funcs); + if !self.skip_mir_stats { + self.run_mir_lowering(db, &funcs, verbosity); } + self.run_data_layout(db, &adts, verbosity); + self.run_const_eval(db, &consts, verbosity); + let total_span = analysis_sw.elapsed(); eprintln!("{:<20} {total_span}", "Total:"); report_metric("total time", total_span.time.as_millis() as u64, "ms"); @@ -193,22 +200,88 @@ impl flags::AnalysisStats { Ok(()) } - fn lower_mir(&self, db: &RootDatabase, funcs: &[Function]) { - let all = funcs.len(); + fn run_data_layout(&self, db: &RootDatabase, adts: &[hir::Adt], verbosity: Verbosity) { + let mut sw = self.stop_watch(); + let mut all = 0; + let mut fail = 0; + for &a in adts { + if db.generic_params(a.into()).iter().next().is_some() { + // Data types with generics don't have layout. + continue; + } + all += 1; + let Err(e) = db.layout_of_adt(hir_def::AdtId::from(a).into(), Substitution::empty(Interner)) else { + continue; + }; + if verbosity.is_spammy() { + let full_name = a + .module(db) + .path_to_root(db) + .into_iter() + .rev() + .filter_map(|it| it.name(db)) + .chain(Some(a.name(db))) + .join("::"); + println!("Data layout for {full_name} failed due {e:?}"); + } + fail += 1; + } + eprintln!("{:<20} {}", "Data layouts:", sw.elapsed()); + eprintln!("Failed data layouts: {fail} ({}%)", fail * 100 / all); + report_metric("failed data layouts", fail, "#"); + } + + fn run_const_eval(&self, db: &RootDatabase, consts: &[hir::Const], verbosity: Verbosity) { + let mut sw = self.stop_watch(); + let mut all = 0; + let mut fail = 0; + for &c in consts { + all += 1; + let Err(e) = c.render_eval(db) else { + continue; + }; + if verbosity.is_spammy() { + let full_name = c + .module(db) + .path_to_root(db) + .into_iter() + .rev() + .filter_map(|it| it.name(db)) + .chain(c.name(db)) + .join("::"); + println!("Const eval for {full_name} failed due {e:?}"); + } + fail += 1; + } + eprintln!("{:<20} {}", "Const evaluation:", sw.elapsed()); + eprintln!("Failed const evals: {fail} ({}%)", fail * 100 / all); + report_metric("failed const evals", fail, "#"); + } + + fn run_mir_lowering(&self, db: &RootDatabase, funcs: &[Function], verbosity: Verbosity) { + let mut sw = self.stop_watch(); + let all = funcs.len() as u64; let mut fail = 0; - let mut h: HashMap<String, usize> = HashMap::new(); for f in funcs { - let f = FunctionId::from(*f); - let Err(e) = db.mir_body(f.into()) else { + let Err(e) = db.mir_body(FunctionId::from(*f).into()) else { continue; }; - let es = format!("{:?}", e); - *h.entry(es).or_default() += 1; + if verbosity.is_spammy() { + let full_name = f + .module(db) + .path_to_root(db) + .into_iter() + .rev() + .filter_map(|it| it.name(db)) + .chain(Some(f.name(db))) + .join("::"); + println!("Mir body for {full_name} failed due {e:?}"); + } fail += 1; } - let h = h.into_iter().sorted_by_key(|x| x.1).collect::<Vec<_>>(); - eprintln!("Mir failed reasons: {:#?}", h); + eprintln!("{:<20} {}", "MIR lowering:", sw.elapsed()); eprintln!("Mir failed bodies: {fail} ({}%)", fail * 100 / all); + report_metric("mir failed bodies", fail, "#"); } fn run_inference( diff --git a/crates/rust-analyzer/src/cli/flags.rs b/crates/rust-analyzer/src/cli/flags.rs index 891d99c5ad..6b5a79b431 100644 --- a/crates/rust-analyzer/src/cli/flags.rs +++ b/crates/rust-analyzer/src/cli/flags.rs @@ -66,8 +66,8 @@ xflags::xflags! { optional --memory-usage /// Print the total length of all source and macro files (whitespace is not counted). optional --source-stats - /// Print the number of bodies that fail to lower to mir, in addition to failed reasons. - optional --mir-stats + /// Only type check, skip lowering to mir + optional --skip-mir-stats /// Only analyze items matching this path. optional -o, --only path: String @@ -171,7 +171,7 @@ pub struct AnalysisStats { pub parallel: bool, pub memory_usage: bool, pub source_stats: bool, - pub mir_stats: bool, + pub skip_mir_stats: bool, pub only: Option<String>, pub with_deps: bool, pub no_sysroot: bool, |