Skip to content

Commit

Permalink
Implement Chalk variable kinds
Browse files Browse the repository at this point in the history
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 rust-lang#2534.
  • Loading branch information
flodiebold committed Jun 30, 2020
1 parent 4a19d59 commit 042f59f
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 71 deletions.
10 changes: 5 additions & 5 deletions crates/ra_hir/src/code_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1187,7 +1187,7 @@ impl Type {
None => return false,
};

let canonical_ty = Canonical { value: self.ty.value.clone(), num_vars: 0 };
let canonical_ty = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
method_resolution::implements_trait(
&canonical_ty,
db,
Expand All @@ -1211,7 +1211,7 @@ impl Type {
self.ty.environment.clone(),
hir_ty::Obligation::Trait(trait_ref),
),
num_vars: 0,
kinds: Arc::new([]),
};

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

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

let env = self.ty.environment.clone();
let krate = krate.id;
Expand Down
15 changes: 8 additions & 7 deletions crates/ra_hir_ty/src/autoderef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub(crate) fn deref(
ty: InEnvironment<&Canonical<Ty>>,
) -> Option<Canonical<Ty>> {
if let Some(derefed) = ty.value.value.builtin_deref() {
Some(Canonical { value: derefed, num_vars: ty.value.num_vars })
Some(Canonical { value: derefed, kinds: ty.value.kinds.clone() })
} else {
deref_by_trait(db, krate, ty)
}
Expand Down Expand Up @@ -68,8 +68,8 @@ fn deref_by_trait(

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

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

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

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

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

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

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

for i in 1..vars.0.num_vars {
for i in 1..vars.0.kinds.len() {
if vars.0.value[i - 1] != Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, i - 1))
{
warn!("complex solution for derefing {:?}: {:?}, ignoring", ty.value, solution);
Expand All @@ -119,7 +120,7 @@ fn deref_by_trait(
}
Some(Canonical {
value: vars.0.value[vars.0.value.len() - 1].clone(),
num_vars: vars.0.num_vars,
kinds: vars.0.kinds.clone(),
})
}
Solution::Ambig(_) => {
Expand Down
55 changes: 40 additions & 15 deletions crates/ra_hir_ty/src/infer/unify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use test_utils::mark;
use super::{InferenceContext, Obligation};
use crate::{
BoundVar, Canonical, DebruijnIndex, GenericPredicate, InEnvironment, InferTy, Substs, Ty,
TypeCtor, TypeWalk,
TyKind, TypeCtor, TypeWalk,
};

impl<'a> InferenceContext<'a> {
Expand Down Expand Up @@ -86,10 +86,20 @@ where
}

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

pub(crate) fn canonicalize_ty(mut self, ty: Ty) -> Canonicalized<Ty> {
Expand Down Expand Up @@ -131,26 +141,41 @@ impl<T> Canonicalized<T> {
ty
}

pub fn apply_solution(&self, ctx: &mut InferenceContext<'_>, solution: Canonical<Vec<Ty>>) {
pub fn apply_solution(&self, ctx: &mut InferenceContext<'_>, solution: Canonical<Substs>) {
// the solution may contain new variables, which we need to convert to new inference vars
let new_vars = Substs((0..solution.num_vars).map(|_| ctx.table.new_type_var()).collect());
let new_vars = Substs(
solution
.kinds
.iter()
.map(|k| match k {
TyKind::General => ctx.table.new_type_var(),
TyKind::Integer => ctx.table.new_integer_var(),
TyKind::Float => ctx.table.new_float_var(),
})
.collect(),
);
for (i, ty) in solution.value.into_iter().enumerate() {
let var = self.free_vars[i];
// eagerly replace projections in the type; we may be getting types
// e.g. from where clauses where this hasn't happened yet
let ty = ctx.normalize_associated_types_in(ty.subst_bound_vars(&new_vars));
let ty = ctx.normalize_associated_types_in(ty.clone().subst_bound_vars(&new_vars));
ctx.table.unify(&Ty::Infer(var), &ty);
}
}
}

pub fn unify(ty1: &Canonical<Ty>, ty2: &Canonical<Ty>) -> Option<Substs> {
pub fn unify(tys: &Canonical<(Ty, Ty)>) -> Option<Substs> {
let mut table = InferenceTable::new();
let num_vars = ty1.num_vars.max(ty2.num_vars);
let vars =
Substs::builder(num_vars).fill(std::iter::repeat_with(|| table.new_type_var())).build();
let ty1_with_vars = ty1.value.clone().subst_bound_vars(&vars);
let ty2_with_vars = ty2.value.clone().subst_bound_vars(&vars);
let vars = Substs(
tys.kinds
.iter()
// we always use type vars here because we want everything to
// fallback to Unknown in the end (kind of hacky, as below)
.map(|_| table.new_type_var())
.collect(),
);
let ty1_with_vars = tys.value.0.clone().subst_bound_vars(&vars);
let ty2_with_vars = tys.value.1.clone().subst_bound_vars(&vars);
if !table.unify(&ty1_with_vars, &ty2_with_vars) {
return None;
}
Expand All @@ -162,7 +187,7 @@ pub fn unify(ty1: &Canonical<Ty>, ty2: &Canonical<Ty>) -> Option<Substs> {
}
}
Some(
Substs::builder(ty1.num_vars)
Substs::builder(tys.kinds.len())
.fill(vars.iter().map(|v| table.resolve_ty_completely(v.clone())))
.build(),
)
Expand Down
22 changes: 18 additions & 4 deletions crates/ra_hir_ty/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -662,13 +662,27 @@ impl TypeWalk for GenericPredicate {

/// Basically a claim (currently not validated / checked) that the contained
/// type / trait ref contains no inference variables; any inference variables it
/// contained have been replaced by bound variables, and `num_vars` tells us how
/// many there are. This is used to erase irrelevant differences between types
/// before using them in queries.
/// contained have been replaced by bound variables, and `kinds` tells us how
/// many there are and whether they were normal or float/int variables. This is
/// used to erase irrelevant differences between types before using them in
/// queries.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Canonical<T> {
pub value: T,
pub num_vars: usize,
pub kinds: Arc<[TyKind]>,
}

impl<T> Canonical<T> {
pub fn new(value: T, kinds: impl IntoIterator<Item = TyKind>) -> Self {
Self { value, kinds: kinds.into_iter().collect() }
}
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum TyKind {
General,
Integer,
Float,
}

/// A function signature as seen by type inference: Several parameter types and
Expand Down
32 changes: 17 additions & 15 deletions crates/ra_hir_ty/src/method_resolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//! For details about how this works in rustc, see the method lookup page in the
//! [rustc guide](https://rust-lang.github.io/rustc-guide/method-lookup.html)
//! and the corresponding code mostly in librustc_typeck/check/method/probe.rs.
use std::sync::Arc;
use std::{iter, sync::Arc};

use arrayvec::ArrayVec;
use hir_def::{
Expand All @@ -17,7 +17,8 @@ use rustc_hash::{FxHashMap, FxHashSet};
use super::Substs;
use crate::{
autoderef, db::HirDatabase, primitive::FloatBitness, utils::all_super_traits, ApplicationTy,
Canonical, DebruijnIndex, InEnvironment, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk,
Canonical, DebruijnIndex, InEnvironment, TraitEnvironment, TraitRef, Ty, TyKind, TypeCtor,
TypeWalk,
};

/// This is used as a key for indexing impls.
Expand Down Expand Up @@ -377,7 +378,7 @@ fn iterate_method_candidates_with_autoref(
return true;
}
let refed = Canonical {
num_vars: deref_chain[0].num_vars,
kinds: deref_chain[0].kinds.clone(),
value: Ty::apply_one(TypeCtor::Ref(Mutability::Shared), deref_chain[0].value.clone()),
};
if iterate_method_candidates_by_receiver(
Expand All @@ -393,7 +394,7 @@ fn iterate_method_candidates_with_autoref(
return true;
}
let ref_muted = Canonical {
num_vars: deref_chain[0].num_vars,
kinds: deref_chain[0].kinds.clone(),
value: Ty::apply_one(TypeCtor::Ref(Mutability::Mut), deref_chain[0].value.clone()),
};
if iterate_method_candidates_by_receiver(
Expand Down Expand Up @@ -612,18 +613,19 @@ pub(crate) fn inherent_impl_substs(
// we create a var for each type parameter of the impl; we need to keep in
// mind here that `self_ty` might have vars of its own
let vars = Substs::build_for_def(db, impl_id)
.fill_with_bound_vars(DebruijnIndex::INNERMOST, self_ty.num_vars)
.fill_with_bound_vars(DebruijnIndex::INNERMOST, self_ty.kinds.len())
.build();
let self_ty_with_vars = db.impl_self_ty(impl_id).subst(&vars);
let self_ty_with_vars =
Canonical { num_vars: vars.len() + self_ty.num_vars, value: self_ty_with_vars };
let substs = super::infer::unify(&self_ty_with_vars, self_ty);
let mut kinds = self_ty.kinds.to_vec();
kinds.extend(iter::repeat(TyKind::General).take(vars.len()));
let tys = Canonical { kinds: kinds.into(), value: (self_ty_with_vars, self_ty.value.clone()) };
let substs = super::infer::unify(&tys);
// We only want the substs for the vars we added, not the ones from self_ty.
// Also, if any of the vars we added are still in there, we replace them by
// Unknown. I think this can only really happen if self_ty contained
// Unknown, and in that case we want the result to contain Unknown in those
// places again.
substs.map(|s| fallback_bound_vars(s.suffix(vars.len()), self_ty.num_vars))
substs.map(|s| fallback_bound_vars(s.suffix(vars.len()), self_ty.kinds.len()))
}

/// This replaces any 'free' Bound vars in `s` (i.e. those with indices past
Expand Down Expand Up @@ -683,15 +685,15 @@ fn generic_implements_goal(
trait_: TraitId,
self_ty: Canonical<Ty>,
) -> Canonical<InEnvironment<super::Obligation>> {
let num_vars = self_ty.num_vars;
let mut kinds = self_ty.kinds.to_vec();
let substs = super::Substs::build_for_def(db, trait_)
.push(self_ty.value)
.fill_with_bound_vars(DebruijnIndex::INNERMOST, num_vars)
.fill_with_bound_vars(DebruijnIndex::INNERMOST, kinds.len())
.build();
let num_vars = substs.len() - 1 + self_ty.num_vars;
kinds.extend(iter::repeat(TyKind::General).take(substs.len() - 1));
let trait_ref = TraitRef { trait_, substs };
let obligation = super::Obligation::Trait(trait_ref);
Canonical { num_vars, value: InEnvironment::new(env, obligation) }
Canonical { kinds: kinds.into(), value: InEnvironment::new(env, obligation) }
}

fn autoderef_method_receiver(
Expand All @@ -704,9 +706,9 @@ fn autoderef_method_receiver(
if let Some(Ty::Apply(ApplicationTy { ctor: TypeCtor::Array, parameters })) =
deref_chain.last().map(|ty| &ty.value)
{
let num_vars = deref_chain.last().unwrap().num_vars;
let kinds = deref_chain.last().unwrap().kinds.clone();
let unsized_ty = Ty::apply(TypeCtor::Slice, parameters.clone());
deref_chain.push(Canonical { value: unsized_ty, num_vars })
deref_chain.push(Canonical { value: unsized_ty, kinds })
}
deref_chain
}
31 changes: 31 additions & 0 deletions crates/ra_hir_ty/src/tests/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3029,3 +3029,34 @@ fn infer_dyn_fn_output() {
"###
);
}

#[test]
fn variable_kinds() {
assert_snapshot!(
infer(
r#"
trait Trait<T> { fn get(self, t: T) -> T; }
struct S;
impl Trait<u128> for S {}
impl Trait<f32> for S {}
fn test() {
S.get(1);
S.get(1.);
}
"#
),
@r###"
24..28 'self': Self
30..31 't': T
117..149 '{ ...1.); }': ()
123..124 'S': S
123..131 'S.get(1)': u128
129..130 '1': u128
137..138 'S': S
137..146 'S.get(1.)': f32
143..145 '1.': f32
"###
);
}
16 changes: 4 additions & 12 deletions crates/ra_hir_ty/src/traits.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! Trait solving using Chalk.
use std::{panic, sync::Arc};
use std::sync::Arc;

use chalk_ir::cast::Cast;
use hir_def::{
Expand All @@ -8,7 +8,7 @@ use hir_def::{
use ra_db::{impl_intern_key, salsa, CrateId};
use ra_prof::profile;

use crate::{db::HirDatabase, DebruijnIndex};
use crate::{db::HirDatabase, DebruijnIndex, Substs};

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

Expand Down Expand Up @@ -190,15 +190,7 @@ fn solution_from_chalk(
solution: chalk_solve::Solution<Interner>,
) -> Solution {
let convert_subst = |subst: chalk_ir::Canonical<chalk_ir::Substitution<Interner>>| {
let value = subst
.value
.iter(&Interner)
.map(|p| match p.ty(&Interner) {
Some(ty) => from_chalk(db, ty.clone()),
None => unimplemented!(),
})
.collect();
let result = Canonical { value, num_vars: subst.binders.len(&Interner) };
let result = from_chalk(db, subst);
SolutionVariables(result)
};
match solution {
Expand All @@ -222,7 +214,7 @@ fn solution_from_chalk(
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SolutionVariables(pub Canonical<Vec<Ty>>);
pub struct SolutionVariables(pub Canonical<Substs>);

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

0 comments on commit 042f59f

Please sign in to comment.