Merge #1251
1251: Chalk integration improvements r=matklad a=flodiebold A few improvements that came up while working on where clause support: - turn `implements` into a query again to improve performance - allow skipping to a specific function with `analysis-stats`, e.g. `ra_cli analysis-stats --only world_symbols` - deduplicate impls in impls_for_trait -- previously many impls e.g. from std where repeated many times, this should help performance as well... - add a `HirDisplay` implementation for TraitRef (not used here anywhere, but useful for debugging) Co-authored-by: Florian Diebold <flodiebold@gmail.com>
This commit is contained in:
commit
70cd5ffbf5
7 changed files with 45 additions and 14 deletions
|
|
@ -7,7 +7,7 @@ use ra_syntax::AstNode;
|
|||
|
||||
use crate::Result;
|
||||
|
||||
pub fn run(verbose: bool) -> Result<()> {
|
||||
pub fn run(verbose: bool, only: Option<&str>) -> Result<()> {
|
||||
let db_load_time = Instant::now();
|
||||
let (db, roots) = BatchDatabase::load_cargo(".")?;
|
||||
println!("Database loaded, {} roots, {:?}", roots.len(), db_load_time.elapsed());
|
||||
|
|
@ -57,14 +57,19 @@ pub fn run(verbose: bool) -> Result<()> {
|
|||
let mut num_exprs_unknown = 0;
|
||||
let mut num_exprs_partially_unknown = 0;
|
||||
for f in funcs {
|
||||
let name = f.name(&db);
|
||||
if verbose {
|
||||
let (file_id, source) = f.source(&db);
|
||||
let original_file = file_id.original_file(&db);
|
||||
let path = db.file_relative_path(original_file);
|
||||
let syntax_range = source.syntax().range();
|
||||
let name = f.name(&db);
|
||||
println!("{} ({:?} {})", name, path, syntax_range);
|
||||
}
|
||||
if let Some(only_name) = only {
|
||||
if name.to_string() != only_name {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let body = f.body(&db);
|
||||
let inference_result = f.infer(&db);
|
||||
for (expr_id, _) in body.exprs() {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,9 @@ fn main() -> Result<()> {
|
|||
.subcommand(SubCommand::with_name("parse").arg(Arg::with_name("no-dump").long("--no-dump")))
|
||||
.subcommand(SubCommand::with_name("symbols"))
|
||||
.subcommand(
|
||||
SubCommand::with_name("analysis-stats").arg(Arg::with_name("verbose").short("v")),
|
||||
SubCommand::with_name("analysis-stats")
|
||||
.arg(Arg::with_name("verbose").short("v"))
|
||||
.arg(Arg::with_name("only").short("o").takes_value(true)),
|
||||
)
|
||||
.get_matches();
|
||||
match matches.subcommand() {
|
||||
|
|
@ -51,7 +53,8 @@ fn main() -> Result<()> {
|
|||
}
|
||||
("analysis-stats", Some(matches)) => {
|
||||
let verbose = matches.is_present("verbose");
|
||||
analysis_stats::run(verbose)?;
|
||||
let only = matches.value_of("only");
|
||||
analysis_stats::run(verbose, only)?;
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -161,6 +161,13 @@ pub trait HirDatabase: DefDatabase {
|
|||
#[salsa::invoke(crate::ty::traits::solver)]
|
||||
#[salsa::volatile]
|
||||
fn solver(&self, krate: Crate) -> Arc<Mutex<crate::ty::traits::Solver>>;
|
||||
|
||||
#[salsa::invoke(crate::ty::traits::implements)]
|
||||
fn implements(
|
||||
&self,
|
||||
krate: Crate,
|
||||
goal: crate::ty::Canonical<crate::ty::TraitRef>,
|
||||
) -> Option<crate::ty::traits::Solution>;
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -240,7 +240,7 @@ impl TraitRef {
|
|||
/// many there are. This is used to erase irrelevant differences between types
|
||||
/// before using them in queries.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct Canonical<T> {
|
||||
pub struct Canonical<T> {
|
||||
pub value: T,
|
||||
pub num_vars: usize,
|
||||
}
|
||||
|
|
@ -534,3 +534,20 @@ impl HirDisplay for Ty {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl HirDisplay for TraitRef {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}: {}",
|
||||
self.substs[0].display(f.db),
|
||||
self.trait_.name(f.db).unwrap_or_else(Name::missing)
|
||||
)?;
|
||||
if self.substs.len() > 1 {
|
||||
write!(f, "<")?;
|
||||
f.write_joined(&self.substs[1..], ", ")?;
|
||||
write!(f, ">")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -328,8 +328,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
Obligation::Trait(tr) => {
|
||||
let canonicalized = self.canonicalizer().canonicalize_trait_ref(tr.clone());
|
||||
(
|
||||
super::traits::implements(
|
||||
self.db,
|
||||
self.db.implements(
|
||||
self.resolver.krate().unwrap(),
|
||||
canonicalized.value.clone(),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -196,8 +196,7 @@ fn iterate_trait_method_candidates<T>(
|
|||
if name.map_or(true, |name| sig.name() == name) && sig.has_self_param() {
|
||||
if !known_implemented {
|
||||
let trait_ref = canonical_trait_ref(db, t, ty.clone());
|
||||
// FIXME cache this implements check (without solution) in a query?
|
||||
if super::traits::implements(db, krate, trait_ref).is_none() {
|
||||
if db.implements(krate, trait_ref).is_none() {
|
||||
continue 'traits;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//! Trait solving using Chalk.
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use rustc_hash::FxHashSet;
|
||||
use log::debug;
|
||||
use chalk_ir::cast::Cast;
|
||||
|
||||
|
|
@ -31,7 +32,7 @@ pub(crate) fn impls_for_trait(
|
|||
krate: Crate,
|
||||
trait_: Trait,
|
||||
) -> Arc<[ImplBlock]> {
|
||||
let mut impls = Vec::new();
|
||||
let mut impls = FxHashSet::default();
|
||||
// We call the query recursively here. On the one hand, this means we can
|
||||
// reuse results from queries for different crates; on the other hand, this
|
||||
// will only ever get called for a few crates near the root of the tree (the
|
||||
|
|
@ -42,7 +43,7 @@ pub(crate) fn impls_for_trait(
|
|||
}
|
||||
let crate_impl_blocks = db.impls_in_crate(krate);
|
||||
impls.extend(crate_impl_blocks.lookup_impl_blocks_for_trait(&trait_));
|
||||
impls.into()
|
||||
impls.into_iter().collect::<Vec<_>>().into()
|
||||
}
|
||||
|
||||
fn solve(
|
||||
|
|
@ -125,11 +126,11 @@ fn solution_from_chalk(db: &impl HirDatabase, solution: chalk_solve::Solution) -
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub(crate) struct SolutionVariables(pub Canonical<Vec<Ty>>);
|
||||
pub struct SolutionVariables(pub Canonical<Vec<Ty>>);
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
/// A (possible) solution for a proposed goal.
|
||||
pub(crate) enum Solution {
|
||||
pub enum Solution {
|
||||
/// The goal indeed holds, and there is a unique value for all existential
|
||||
/// variables.
|
||||
Unique(SolutionVariables),
|
||||
|
|
@ -144,7 +145,7 @@ pub(crate) enum Solution {
|
|||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
/// When a goal holds ambiguously (e.g., because there are multiple possible
|
||||
/// solutions), we issue a set of *guidance* back to type inference.
|
||||
pub(crate) enum Guidance {
|
||||
pub enum Guidance {
|
||||
/// The existential variables *must* have the given values if the goal is
|
||||
/// ever to hold, but that alone isn't enough to guarantee the goal will
|
||||
/// actually hold.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue