Skip to content

Commit 7d60a44

Browse files
Merge #3990
3990: Switch to Chalk recursive solver r=matklad a=flodiebold Before: ``` Expressions of unknown type: 5526 (3%) Expressions of partially unknown type: 5415 (3%) ``` After: ``` Expressions of unknown type: 4600 (2%) Expressions of partially unknown type: 4645 (2%) ``` On the other hand, ``` './target/release/rust-analyzer analysis-stats -q . # old solver' ran 1.24 ± 0.04 times faster than 'rust-analyzer analysis-stats -q . # new solver' ``` I think part of this just comes from the fact that we're inferring more types now; but apart from that, it should be possible to improve the performance a bunch, and I'll make looking into that a priority. Co-authored-by: Florian Diebold <[email protected]>
2 parents 364415b + 39fe3a6 commit 7d60a44

File tree

5 files changed

+121
-40
lines changed

5 files changed

+121
-40
lines changed

crates/ra_hir_ty/src/autoderef.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::{
1414
db::HirDatabase,
1515
traits::{InEnvironment, Solution},
1616
utils::generics,
17-
BoundVar, Canonical, DebruijnIndex, Substs, Ty,
17+
BoundVar, Canonical, DebruijnIndex, Obligation, Substs, TraitRef, Ty,
1818
};
1919

2020
const AUTODEREF_RECURSION_LIMIT: usize = 10;
@@ -66,6 +66,20 @@ fn deref_by_trait(
6666
let parameters =
6767
Substs::build_for_generics(&generic_params).push(ty.value.value.clone()).build();
6868

69+
// Check that the type implements Deref at all
70+
let trait_ref = TraitRef { trait_: deref_trait, substs: parameters.clone() };
71+
let implements_goal = super::Canonical {
72+
num_vars: ty.value.num_vars,
73+
value: InEnvironment {
74+
value: Obligation::Trait(trait_ref),
75+
environment: ty.environment.clone(),
76+
},
77+
};
78+
if db.trait_solve(krate, implements_goal).is_none() {
79+
return None;
80+
}
81+
82+
// Now do the assoc type projection
6983
let projection = super::traits::ProjectionPredicate {
7084
ty: Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, ty.value.num_vars)),
7185
projection_ty: super::ProjectionTy { associated_ty: target, parameters },
@@ -91,6 +105,11 @@ fn deref_by_trait(
91105
// they're just being 'passed through'. In the 'standard' case where
92106
// we have `impl<T> Deref for Foo<T> { Target = T }`, that should be
93107
// the case.
108+
109+
// FIXME: if the trait solver decides to truncate the type, these
110+
// assumptions will be broken. We would need to properly introduce
111+
// new variables in that case
112+
94113
for i in 1..vars.0.num_vars {
95114
if vars.0.value[i - 1] != Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, i - 1))
96115
{

crates/ra_hir_ty/src/infer/unify.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ where
3232
var_stack: Vec<TypeVarId>,
3333
}
3434

35+
#[derive(Debug)]
3536
pub(super) struct Canonicalized<T> {
3637
pub value: Canonical<T>,
3738
free_vars: Vec<InferTy>,

crates/ra_hir_ty/src/tests/traits.rs

Lines changed: 89 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,6 @@ trait Trait: SuperTrait {
349349

350350
#[test]
351351
fn infer_project_associated_type() {
352-
// y, z, a don't yet work because of https://github.com/rust-lang/chalk/issues/234
353352
assert_snapshot!(
354353
infer(r#"
355354
trait Iterable {
@@ -368,12 +367,12 @@ fn test<T: Iterable>() {
368367
[108; 261) '{ ...ter; }': ()
369368
[118; 119) 'x': u32
370369
[145; 146) '1': u32
371-
[156; 157) 'y': {unknown}
372-
[183; 192) 'no_matter': {unknown}
373-
[202; 203) 'z': {unknown}
374-
[215; 224) 'no_matter': {unknown}
375-
[234; 235) 'a': {unknown}
376-
[249; 258) 'no_matter': {unknown}
370+
[156; 157) 'y': Iterable::Item<T>
371+
[183; 192) 'no_matter': Iterable::Item<T>
372+
[202; 203) 'z': Iterable::Item<T>
373+
[215; 224) 'no_matter': Iterable::Item<T>
374+
[234; 235) 'a': Iterable::Item<T>
375+
[249; 258) 'no_matter': Iterable::Item<T>
377376
"###
378377
);
379378
}
@@ -433,8 +432,8 @@ fn test<T: Iterable<Item=u32>>() {
433432
"#),
434433
@r###"
435434
[67; 100) '{ ...own; }': ()
436-
[77; 78) 'y': {unknown}
437-
[90; 97) 'unknown': {unknown}
435+
[77; 78) 'y': u32
436+
[90; 97) 'unknown': u32
438437
"###
439438
);
440439
}
@@ -549,7 +548,7 @@ impl std::ops::Index<u32> for Bar {
549548
550549
fn test() {
551550
let a = Bar;
552-
let b = a[1];
551+
let b = a[1u32];
553552
b<|>;
554553
}
555554
@@ -574,7 +573,7 @@ fn infer_ops_index_autoderef() {
574573
//- /main.rs crate:main deps:std
575574
fn test() {
576575
let a = &[1u32, 2, 3];
577-
let b = a[1];
576+
let b = a[1u32];
578577
b<|>;
579578
}
580579
@@ -916,11 +915,7 @@ fn test<T: ApplyL>(t: T) {
916915
}
917916
"#,
918917
);
919-
// FIXME here Chalk doesn't normalize the type to a placeholder. I think we
920-
// need to add a rule like Normalize(<T as ApplyL>::Out -> ApplyL::Out<T>)
921-
// to the trait env ourselves here; probably Chalk can't do this by itself.
922-
// assert_eq!(t, "ApplyL::Out<[missing name]>");
923-
assert_eq!(t, "{unknown}");
918+
assert_eq!(t, "ApplyL::Out<T>");
924919
}
925920

926921
#[test]
@@ -1329,16 +1324,16 @@ fn test<T: Trait<Type = u32>>(x: T, y: impl Trait<Type = i64>) {
13291324
[263; 264) 'y': impl Trait<Type = i64>
13301325
[290; 398) '{ ...r>); }': ()
13311326
[296; 299) 'get': fn get<T>(T) -> <T as Trait>::Type
1332-
[296; 302) 'get(x)': {unknown}
1327+
[296; 302) 'get(x)': u32
13331328
[300; 301) 'x': T
1334-
[308; 312) 'get2': fn get2<{unknown}, T>(T) -> {unknown}
1335-
[308; 315) 'get2(x)': {unknown}
1329+
[308; 312) 'get2': fn get2<u32, T>(T) -> u32
1330+
[308; 315) 'get2(x)': u32
13361331
[313; 314) 'x': T
13371332
[321; 324) 'get': fn get<impl Trait<Type = i64>>(impl Trait<Type = i64>) -> <impl Trait<Type = i64> as Trait>::Type
1338-
[321; 327) 'get(y)': {unknown}
1333+
[321; 327) 'get(y)': i64
13391334
[325; 326) 'y': impl Trait<Type = i64>
1340-
[333; 337) 'get2': fn get2<{unknown}, impl Trait<Type = i64>>(impl Trait<Type = i64>) -> {unknown}
1341-
[333; 340) 'get2(y)': {unknown}
1335+
[333; 337) 'get2': fn get2<i64, impl Trait<Type = i64>>(impl Trait<Type = i64>) -> i64
1336+
[333; 340) 'get2(y)': i64
13421337
[338; 339) 'y': impl Trait<Type = i64>
13431338
[346; 349) 'get': fn get<S<u64>>(S<u64>) -> <S<u64> as Trait>::Type
13441339
[346; 357) 'get(set(S))': u64
@@ -1402,7 +1397,6 @@ mod iter {
14021397

14031398
#[test]
14041399
fn projection_eq_within_chalk() {
1405-
// std::env::set_var("CHALK_DEBUG", "1");
14061400
assert_snapshot!(
14071401
infer(r#"
14081402
trait Trait1 {
@@ -1422,7 +1416,7 @@ fn test<T: Trait1<Type = u32>>(x: T) {
14221416
[164; 165) 'x': T
14231417
[170; 186) '{ ...o(); }': ()
14241418
[176; 177) 'x': T
1425-
[176; 183) 'x.foo()': {unknown}
1419+
[176; 183) 'x.foo()': u32
14261420
"###
14271421
);
14281422
}
@@ -1578,7 +1572,7 @@ fn test<F: FnOnce(u32, u64) -> u128>(f: F) {
15781572
[150; 151) 'f': F
15791573
[156; 184) '{ ...2)); }': ()
15801574
[162; 163) 'f': F
1581-
[162; 181) 'f.call...1, 2))': {unknown}
1575+
[162; 181) 'f.call...1, 2))': u128
15821576
[174; 180) '(1, 2)': (u32, u64)
15831577
[175; 176) '1': u32
15841578
[178; 179) '2': u64
@@ -1829,7 +1823,7 @@ impl Trait for S2 {
18291823
"#,
18301824
), @r###"
18311825
[54; 58) 'self': &Self
1832-
[60; 61) 'x': {unknown}
1826+
[60; 61) 'x': Trait::Item<Self>
18331827
[140; 144) 'self': &S
18341828
[146; 147) 'x': u32
18351829
[161; 175) '{ let y = x; }': ()
@@ -1989,9 +1983,75 @@ fn test<I: Iterator<Item: Iterator<Item = u32>>>() {
19891983
}
19901984
"#,
19911985
);
1992-
// assert_eq!(t, "u32");
1993-
// doesn't currently work, Chalk #234
1994-
assert_eq!(t, "{unknown}");
1986+
assert_eq!(t, "u32");
1987+
}
1988+
1989+
#[test]
1990+
fn proc_macro_server_types() {
1991+
assert_snapshot!(
1992+
infer_with_mismatches(r#"
1993+
macro_rules! with_api {
1994+
($S:ident, $self:ident, $m:ident) => {
1995+
$m! {
1996+
TokenStream {
1997+
fn new() -> $S::TokenStream;
1998+
},
1999+
Group {
2000+
},
2001+
}
2002+
};
2003+
}
2004+
macro_rules! associated_item {
2005+
(type TokenStream) =>
2006+
(type TokenStream: 'static + Clone;);
2007+
(type Group) =>
2008+
(type Group: 'static + Clone;);
2009+
($($item:tt)*) => ($($item)*;)
2010+
}
2011+
macro_rules! declare_server_traits {
2012+
($($name:ident {
2013+
$(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)*
2014+
}),* $(,)?) => {
2015+
pub trait Types {
2016+
$(associated_item!(type $name);)*
2017+
}
2018+
2019+
$(pub trait $name: Types {
2020+
$(associated_item!(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)?);)*
2021+
})*
2022+
2023+
pub trait Server: Types $(+ $name)* {}
2024+
impl<S: Types $(+ $name)*> Server for S {}
2025+
}
2026+
}
2027+
with_api!(Self, self_, declare_server_traits);
2028+
struct Group {}
2029+
struct TokenStream {}
2030+
struct Rustc;
2031+
impl Types for Rustc {
2032+
type TokenStream = TokenStream;
2033+
type Group = Group;
2034+
}
2035+
fn make<T>() -> T { loop {} }
2036+
impl TokenStream for Rustc {
2037+
fn new() -> Self::TokenStream {
2038+
let group: Self::Group = make();
2039+
make()
2040+
}
2041+
}
2042+
"#, true),
2043+
@r###"
2044+
[1115; 1126) '{ loop {} }': T
2045+
[1117; 1124) 'loop {}': !
2046+
[1122; 1124) '{}': ()
2047+
[1190; 1253) '{ ... }': {unknown}
2048+
[1204; 1209) 'group': {unknown}
2049+
[1225; 1229) 'make': fn make<{unknown}>() -> {unknown}
2050+
[1225; 1231) 'make()': {unknown}
2051+
[1241; 1245) 'make': fn make<{unknown}>() -> {unknown}
2052+
[1241; 1247) 'make()': {unknown}
2053+
"###
2054+
);
19952055
}
19962056

19972057
#[test]

crates/ra_hir_ty/src/traits.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@ use self::chalk::{from_chalk, Interner, ToChalk};
1616
pub(crate) mod chalk;
1717
mod builtin;
1818

19-
/// This controls the maximum size of types Chalk considers. If we set this too
20-
/// high, we can run into slow edge cases; if we set it too low, Chalk won't
21-
/// find some solutions.
22-
const CHALK_SOLVER_MAX_SIZE: usize = 10;
19+
// This controls the maximum size of types Chalk considers. If we set this too
20+
// high, we can run into slow edge cases; if we set it too low, Chalk won't
21+
// find some solutions.
22+
// FIXME this is currently hardcoded in the recursive solver
23+
// const CHALK_SOLVER_MAX_SIZE: usize = 10;
24+
2325
/// This controls how much 'time' we give the Chalk solver before giving up.
2426
const CHALK_SOLVER_FUEL: i32 = 100;
2527

@@ -30,8 +32,7 @@ struct ChalkContext<'a> {
3032
}
3133

3234
fn create_chalk_solver() -> chalk_solve::Solver<Interner> {
33-
let solver_choice =
34-
chalk_solve::SolverChoice::SLG { max_size: CHALK_SOLVER_MAX_SIZE, expected_answers: None };
35+
let solver_choice = chalk_solve::SolverChoice::recursive();
3536
solver_choice.into_solver()
3637
}
3738

crates/ra_hir_ty/src/traits/chalk.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -511,13 +511,13 @@ impl ToChalk for ProjectionTy {
511511
}
512512

513513
impl ToChalk for super::ProjectionPredicate {
514-
type Chalk = chalk_ir::Normalize<Interner>;
514+
type Chalk = chalk_ir::AliasEq<Interner>;
515515

516-
fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Normalize<Interner> {
517-
chalk_ir::Normalize { alias: self.projection_ty.to_chalk(db), ty: self.ty.to_chalk(db) }
516+
fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::AliasEq<Interner> {
517+
chalk_ir::AliasEq { alias: self.projection_ty.to_chalk(db), ty: self.ty.to_chalk(db) }
518518
}
519519

520-
fn from_chalk(_db: &dyn HirDatabase, _normalize: chalk_ir::Normalize<Interner>) -> Self {
520+
fn from_chalk(_db: &dyn HirDatabase, _normalize: chalk_ir::AliasEq<Interner>) -> Self {
521521
unimplemented!()
522522
}
523523
}

0 commit comments

Comments
 (0)