Skip to content

Commit d5d485e

Browse files
committed
Implement Chalk variable kinds
This means we need to keep track of the kinds (general/int/float) of variables in `Canonical`, which requires some more ceremony. (It also exposes some places where we're not really dealing with canonicalization correctly -- another thing to be cleaned up when we switch to using Chalk's types directly.) Should fix the last remaining issue of #2534.
1 parent 4a19d59 commit d5d485e

File tree

8 files changed

+140
-71
lines changed

8 files changed

+140
-71
lines changed

crates/ra_hir/src/code_model.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1187,7 +1187,7 @@ impl Type {
11871187
None => return false,
11881188
};
11891189

1190-
let canonical_ty = Canonical { value: self.ty.value.clone(), num_vars: 0 };
1190+
let canonical_ty = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
11911191
method_resolution::implements_trait(
11921192
&canonical_ty,
11931193
db,
@@ -1211,7 +1211,7 @@ impl Type {
12111211
self.ty.environment.clone(),
12121212
hir_ty::Obligation::Trait(trait_ref),
12131213
),
1214-
num_vars: 0,
1214+
kinds: Arc::new([]),
12151215
};
12161216

12171217
db.trait_solve(self.krate, goal).is_some()
@@ -1286,7 +1286,7 @@ impl Type {
12861286
pub fn autoderef<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Type> + 'a {
12871287
// There should be no inference vars in types passed here
12881288
// FIXME check that?
1289-
let canonical = Canonical { value: self.ty.value.clone(), num_vars: 0 };
1289+
let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
12901290
let environment = self.ty.environment.clone();
12911291
let ty = InEnvironment { value: canonical, environment };
12921292
autoderef(db, Some(self.krate), ty)
@@ -1327,7 +1327,7 @@ impl Type {
13271327
// There should be no inference vars in types passed here
13281328
// FIXME check that?
13291329
// FIXME replace Unknown by bound vars here
1330-
let canonical = Canonical { value: self.ty.value.clone(), num_vars: 0 };
1330+
let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
13311331

13321332
let env = self.ty.environment.clone();
13331333
let krate = krate.id;
@@ -1358,7 +1358,7 @@ impl Type {
13581358
// There should be no inference vars in types passed here
13591359
// FIXME check that?
13601360
// FIXME replace Unknown by bound vars here
1361-
let canonical = Canonical { value: self.ty.value.clone(), num_vars: 0 };
1361+
let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
13621362

13631363
let env = self.ty.environment.clone();
13641364
let krate = krate.id;

crates/ra_hir_ty/src/autoderef.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ pub(crate) fn deref(
3737
ty: InEnvironment<&Canonical<Ty>>,
3838
) -> Option<Canonical<Ty>> {
3939
if let Some(derefed) = ty.value.value.builtin_deref() {
40-
Some(Canonical { value: derefed, num_vars: ty.value.num_vars })
40+
Some(Canonical { value: derefed, kinds: ty.value.kinds.clone() })
4141
} else {
4242
deref_by_trait(db, krate, ty)
4343
}
@@ -68,8 +68,8 @@ fn deref_by_trait(
6868

6969
// Check that the type implements Deref at all
7070
let trait_ref = TraitRef { trait_: deref_trait, substs: parameters.clone() };
71-
let implements_goal = super::Canonical {
72-
num_vars: ty.value.num_vars,
71+
let implements_goal = Canonical {
72+
kinds: ty.value.kinds.clone(),
7373
value: InEnvironment {
7474
value: Obligation::Trait(trait_ref),
7575
environment: ty.environment.clone(),
@@ -81,15 +81,16 @@ fn deref_by_trait(
8181

8282
// Now do the assoc type projection
8383
let projection = super::traits::ProjectionPredicate {
84-
ty: Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, ty.value.num_vars)),
84+
ty: Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, ty.value.kinds.len())),
8585
projection_ty: super::ProjectionTy { associated_ty: target, parameters },
8686
};
8787

8888
let obligation = super::Obligation::Projection(projection);
8989

9090
let in_env = InEnvironment { value: obligation, environment: ty.environment };
9191

92-
let canonical = super::Canonical { num_vars: 1 + ty.value.num_vars, value: in_env };
92+
let canonical =
93+
Canonical::new(in_env, ty.value.kinds.iter().copied().chain(Some(super::TyKind::General)));
9394

9495
let solution = db.trait_solve(krate, canonical)?;
9596

@@ -110,7 +111,7 @@ fn deref_by_trait(
110111
// assumptions will be broken. We would need to properly introduce
111112
// new variables in that case
112113

113-
for i in 1..vars.0.num_vars {
114+
for i in 1..vars.0.kinds.len() {
114115
if vars.0.value[i - 1] != Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, i - 1))
115116
{
116117
warn!("complex solution for derefing {:?}: {:?}, ignoring", ty.value, solution);
@@ -119,7 +120,7 @@ fn deref_by_trait(
119120
}
120121
Some(Canonical {
121122
value: vars.0.value[vars.0.value.len() - 1].clone(),
122-
num_vars: vars.0.num_vars,
123+
kinds: vars.0.kinds.clone(),
123124
})
124125
}
125126
Solution::Ambig(_) => {

crates/ra_hir_ty/src/infer/unify.rs

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use test_utils::mark;
99
use super::{InferenceContext, Obligation};
1010
use crate::{
1111
BoundVar, Canonical, DebruijnIndex, GenericPredicate, InEnvironment, InferTy, Substs, Ty,
12-
TypeCtor, TypeWalk,
12+
TyKind, TypeCtor, TypeWalk,
1313
};
1414

1515
impl<'a> InferenceContext<'a> {
@@ -86,10 +86,20 @@ where
8686
}
8787

8888
fn into_canonicalized<T>(self, result: T) -> Canonicalized<T> {
89-
Canonicalized {
90-
value: Canonical { value: result, num_vars: self.free_vars.len() },
91-
free_vars: self.free_vars,
92-
}
89+
let kinds = self
90+
.free_vars
91+
.iter()
92+
.map(|v| match v {
93+
// mapping MaybeNeverTypeVar to the same kind as general ones
94+
// should be fine, because as opposed to int or float type vars,
95+
// they don't restrict what kind of type can go into them, they
96+
// just affect fallback.
97+
InferTy::TypeVar(_) | InferTy::MaybeNeverTypeVar(_) => TyKind::General,
98+
InferTy::IntVar(_) => TyKind::Integer,
99+
InferTy::FloatVar(_) => TyKind::Float,
100+
})
101+
.collect();
102+
Canonicalized { value: Canonical { value: result, kinds }, free_vars: self.free_vars }
93103
}
94104

95105
pub(crate) fn canonicalize_ty(mut self, ty: Ty) -> Canonicalized<Ty> {
@@ -131,26 +141,41 @@ impl<T> Canonicalized<T> {
131141
ty
132142
}
133143

134-
pub fn apply_solution(&self, ctx: &mut InferenceContext<'_>, solution: Canonical<Vec<Ty>>) {
144+
pub fn apply_solution(&self, ctx: &mut InferenceContext<'_>, solution: Canonical<Substs>) {
135145
// the solution may contain new variables, which we need to convert to new inference vars
136-
let new_vars = Substs((0..solution.num_vars).map(|_| ctx.table.new_type_var()).collect());
146+
let new_vars = Substs(
147+
solution
148+
.kinds
149+
.iter()
150+
.map(|k| match k {
151+
TyKind::General => ctx.table.new_type_var(),
152+
TyKind::Integer => ctx.table.new_integer_var(),
153+
TyKind::Float => ctx.table.new_float_var(),
154+
})
155+
.collect(),
156+
);
137157
for (i, ty) in solution.value.into_iter().enumerate() {
138158
let var = self.free_vars[i];
139159
// eagerly replace projections in the type; we may be getting types
140160
// e.g. from where clauses where this hasn't happened yet
141-
let ty = ctx.normalize_associated_types_in(ty.subst_bound_vars(&new_vars));
161+
let ty = ctx.normalize_associated_types_in(ty.clone().subst_bound_vars(&new_vars));
142162
ctx.table.unify(&Ty::Infer(var), &ty);
143163
}
144164
}
145165
}
146166

147-
pub fn unify(ty1: &Canonical<Ty>, ty2: &Canonical<Ty>) -> Option<Substs> {
167+
pub fn unify(tys: &Canonical<(Ty, Ty)>) -> Option<Substs> {
148168
let mut table = InferenceTable::new();
149-
let num_vars = ty1.num_vars.max(ty2.num_vars);
150-
let vars =
151-
Substs::builder(num_vars).fill(std::iter::repeat_with(|| table.new_type_var())).build();
152-
let ty1_with_vars = ty1.value.clone().subst_bound_vars(&vars);
153-
let ty2_with_vars = ty2.value.clone().subst_bound_vars(&vars);
169+
let vars = Substs(
170+
tys.kinds
171+
.iter()
172+
// we always use type vars here because we want everything to
173+
// fallback to Unknown in the end (kind of hacky, as below)
174+
.map(|_| table.new_type_var())
175+
.collect(),
176+
);
177+
let ty1_with_vars = tys.value.0.clone().subst_bound_vars(&vars);
178+
let ty2_with_vars = tys.value.1.clone().subst_bound_vars(&vars);
154179
if !table.unify(&ty1_with_vars, &ty2_with_vars) {
155180
return None;
156181
}
@@ -162,7 +187,7 @@ pub fn unify(ty1: &Canonical<Ty>, ty2: &Canonical<Ty>) -> Option<Substs> {
162187
}
163188
}
164189
Some(
165-
Substs::builder(ty1.num_vars)
190+
Substs::builder(tys.kinds.len())
166191
.fill(vars.iter().map(|v| table.resolve_ty_completely(v.clone())))
167192
.build(),
168193
)

crates/ra_hir_ty/src/lib.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -662,13 +662,27 @@ impl TypeWalk for GenericPredicate {
662662

663663
/// Basically a claim (currently not validated / checked) that the contained
664664
/// type / trait ref contains no inference variables; any inference variables it
665-
/// contained have been replaced by bound variables, and `num_vars` tells us how
666-
/// many there are. This is used to erase irrelevant differences between types
667-
/// before using them in queries.
665+
/// contained have been replaced by bound variables, and `kinds` tells us how
666+
/// many there are and whether they were normal or float/int variables. This is
667+
/// used to erase irrelevant differences between types before using them in
668+
/// queries.
668669
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
669670
pub struct Canonical<T> {
670671
pub value: T,
671-
pub num_vars: usize,
672+
pub kinds: Arc<[TyKind]>,
673+
}
674+
675+
impl<T> Canonical<T> {
676+
pub fn new(value: T, kinds: impl IntoIterator<Item = TyKind>) -> Self {
677+
Self { value, kinds: kinds.into_iter().collect() }
678+
}
679+
}
680+
681+
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
682+
pub enum TyKind {
683+
General,
684+
Integer,
685+
Float,
672686
}
673687

674688
/// A function signature as seen by type inference: Several parameter types and

crates/ra_hir_ty/src/method_resolution.rs

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//! For details about how this works in rustc, see the method lookup page in the
33
//! [rustc guide](https://rust-lang.github.io/rustc-guide/method-lookup.html)
44
//! and the corresponding code mostly in librustc_typeck/check/method/probe.rs.
5-
use std::sync::Arc;
5+
use std::{iter, sync::Arc};
66

77
use arrayvec::ArrayVec;
88
use hir_def::{
@@ -17,7 +17,8 @@ use rustc_hash::{FxHashMap, FxHashSet};
1717
use super::Substs;
1818
use crate::{
1919
autoderef, db::HirDatabase, primitive::FloatBitness, utils::all_super_traits, ApplicationTy,
20-
Canonical, DebruijnIndex, InEnvironment, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk,
20+
Canonical, DebruijnIndex, InEnvironment, TraitEnvironment, TraitRef, Ty, TyKind, TypeCtor,
21+
TypeWalk,
2122
};
2223

2324
/// This is used as a key for indexing impls.
@@ -377,7 +378,7 @@ fn iterate_method_candidates_with_autoref(
377378
return true;
378379
}
379380
let refed = Canonical {
380-
num_vars: deref_chain[0].num_vars,
381+
kinds: deref_chain[0].kinds.clone(),
381382
value: Ty::apply_one(TypeCtor::Ref(Mutability::Shared), deref_chain[0].value.clone()),
382383
};
383384
if iterate_method_candidates_by_receiver(
@@ -393,7 +394,7 @@ fn iterate_method_candidates_with_autoref(
393394
return true;
394395
}
395396
let ref_muted = Canonical {
396-
num_vars: deref_chain[0].num_vars,
397+
kinds: deref_chain[0].kinds.clone(),
397398
value: Ty::apply_one(TypeCtor::Ref(Mutability::Mut), deref_chain[0].value.clone()),
398399
};
399400
if iterate_method_candidates_by_receiver(
@@ -612,18 +613,19 @@ pub(crate) fn inherent_impl_substs(
612613
// we create a var for each type parameter of the impl; we need to keep in
613614
// mind here that `self_ty` might have vars of its own
614615
let vars = Substs::build_for_def(db, impl_id)
615-
.fill_with_bound_vars(DebruijnIndex::INNERMOST, self_ty.num_vars)
616+
.fill_with_bound_vars(DebruijnIndex::INNERMOST, self_ty.kinds.len())
616617
.build();
617618
let self_ty_with_vars = db.impl_self_ty(impl_id).subst(&vars);
618-
let self_ty_with_vars =
619-
Canonical { num_vars: vars.len() + self_ty.num_vars, value: self_ty_with_vars };
620-
let substs = super::infer::unify(&self_ty_with_vars, self_ty);
619+
let mut kinds = self_ty.kinds.to_vec();
620+
kinds.extend(iter::repeat(TyKind::General).take(vars.len()));
621+
let tys = Canonical { kinds: kinds.into(), value: (self_ty_with_vars, self_ty.value.clone()) };
622+
let substs = super::infer::unify(&tys);
621623
// We only want the substs for the vars we added, not the ones from self_ty.
622624
// Also, if any of the vars we added are still in there, we replace them by
623625
// Unknown. I think this can only really happen if self_ty contained
624626
// Unknown, and in that case we want the result to contain Unknown in those
625627
// places again.
626-
substs.map(|s| fallback_bound_vars(s.suffix(vars.len()), self_ty.num_vars))
628+
substs.map(|s| fallback_bound_vars(s.suffix(vars.len()), self_ty.kinds.len()))
627629
}
628630

629631
/// This replaces any 'free' Bound vars in `s` (i.e. those with indices past
@@ -683,15 +685,15 @@ fn generic_implements_goal(
683685
trait_: TraitId,
684686
self_ty: Canonical<Ty>,
685687
) -> Canonical<InEnvironment<super::Obligation>> {
686-
let num_vars = self_ty.num_vars;
688+
let mut kinds = self_ty.kinds.to_vec();
687689
let substs = super::Substs::build_for_def(db, trait_)
688690
.push(self_ty.value)
689-
.fill_with_bound_vars(DebruijnIndex::INNERMOST, num_vars)
691+
.fill_with_bound_vars(DebruijnIndex::INNERMOST, kinds.len())
690692
.build();
691-
let num_vars = substs.len() - 1 + self_ty.num_vars;
693+
kinds.extend(iter::repeat(TyKind::General).take(substs.len() - 1));
692694
let trait_ref = TraitRef { trait_, substs };
693695
let obligation = super::Obligation::Trait(trait_ref);
694-
Canonical { num_vars, value: InEnvironment::new(env, obligation) }
696+
Canonical { kinds: kinds.into(), value: InEnvironment::new(env, obligation) }
695697
}
696698

697699
fn autoderef_method_receiver(
@@ -704,9 +706,9 @@ fn autoderef_method_receiver(
704706
if let Some(Ty::Apply(ApplicationTy { ctor: TypeCtor::Array, parameters })) =
705707
deref_chain.last().map(|ty| &ty.value)
706708
{
707-
let num_vars = deref_chain.last().unwrap().num_vars;
709+
let kinds = deref_chain.last().unwrap().kinds.clone();
708710
let unsized_ty = Ty::apply(TypeCtor::Slice, parameters.clone());
709-
deref_chain.push(Canonical { value: unsized_ty, num_vars })
711+
deref_chain.push(Canonical { value: unsized_ty, kinds })
710712
}
711713
deref_chain
712714
}

crates/ra_hir_ty/src/tests/traits.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3029,3 +3029,21 @@ fn infer_dyn_fn_output() {
30293029
"###
30303030
);
30313031
}
3032+
3033+
#[test]
3034+
fn variable_kinds() {
3035+
check_types(
3036+
r#"
3037+
trait Trait<T> { fn get(self, t: T) -> T; }
3038+
struct S;
3039+
impl Trait<u128> for S {}
3040+
impl Trait<f32> for S {}
3041+
fn test() {
3042+
S.get(1);
3043+
//^^^^^^^^ u128
3044+
S.get(1.);
3045+
//^^^^^^^^ f32
3046+
}
3047+
"#,
3048+
);
3049+
}

crates/ra_hir_ty/src/traits.rs

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//! Trait solving using Chalk.
2-
use std::{panic, sync::Arc};
2+
use std::sync::Arc;
33

44
use chalk_ir::cast::Cast;
55
use hir_def::{
@@ -8,7 +8,7 @@ use hir_def::{
88
use ra_db::{impl_intern_key, salsa, CrateId};
99
use ra_prof::profile;
1010

11-
use crate::{db::HirDatabase, DebruijnIndex};
11+
use crate::{db::HirDatabase, DebruijnIndex, Substs};
1212

1313
use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk};
1414

@@ -190,15 +190,7 @@ fn solution_from_chalk(
190190
solution: chalk_solve::Solution<Interner>,
191191
) -> Solution {
192192
let convert_subst = |subst: chalk_ir::Canonical<chalk_ir::Substitution<Interner>>| {
193-
let value = subst
194-
.value
195-
.iter(&Interner)
196-
.map(|p| match p.ty(&Interner) {
197-
Some(ty) => from_chalk(db, ty.clone()),
198-
None => unimplemented!(),
199-
})
200-
.collect();
201-
let result = Canonical { value, num_vars: subst.binders.len(&Interner) };
193+
let result = from_chalk(db, subst);
202194
SolutionVariables(result)
203195
};
204196
match solution {
@@ -222,7 +214,7 @@ fn solution_from_chalk(
222214
}
223215

224216
#[derive(Clone, Debug, PartialEq, Eq)]
225-
pub struct SolutionVariables(pub Canonical<Vec<Ty>>);
217+
pub struct SolutionVariables(pub Canonical<Substs>);
226218

227219
#[derive(Clone, Debug, PartialEq, Eq)]
228220
/// A (possible) solution for a proposed goal.

0 commit comments

Comments
 (0)