Skip to content

Commit 2d30dd6

Browse files
committed
Expose coercion logic in hir API
1 parent 6133e6a commit 2d30dd6

File tree

5 files changed

+67
-8
lines changed

5 files changed

+67
-8
lines changed

crates/hir/src/lib.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2900,7 +2900,7 @@ impl Type {
29002900
self.autoderef_(db).map(move |ty| self.derived(ty))
29012901
}
29022902

2903-
pub fn autoderef_<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Ty> + 'a {
2903+
fn autoderef_<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Ty> + 'a {
29042904
// There should be no inference vars in types passed here
29052905
let canonical = hir_ty::replace_errors_with_variables(&self.ty);
29062906
let environment = self.env.clone();
@@ -3238,7 +3238,12 @@ impl Type {
32383238

32393239
pub fn could_unify_with(&self, db: &dyn HirDatabase, other: &Type) -> bool {
32403240
let tys = hir_ty::replace_errors_with_variables(&(self.ty.clone(), other.ty.clone()));
3241-
could_unify(db, self.env.clone(), &tys)
3241+
hir_ty::could_unify(db, self.env.clone(), &tys)
3242+
}
3243+
3244+
pub fn could_coerce_to(&self, db: &dyn HirDatabase, to: &Type) -> bool {
3245+
let tys = hir_ty::replace_errors_with_variables(&(self.ty.clone(), to.ty.clone()));
3246+
hir_ty::could_coerce(db, self.env.clone(), &tys)
32423247
}
32433248
}
32443249

crates/hir_ty/src/infer.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ use crate::{
4545
// https://github.com/rust-lang/rust/issues/57411
4646
#[allow(unreachable_pub)]
4747
pub use unify::could_unify;
48+
pub use coerce::could_coerce;
4849

4950
pub(crate) mod unify;
5051
mod path;

crates/hir_ty/src/infer/coerce.rs

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
//! See <https://doc.rust-lang.org/nomicon/coercions.html> and
66
//! `librustc_typeck/check/coercion.rs`.
77
8-
use std::iter;
8+
use std::{iter, sync::Arc};
99

10-
use chalk_ir::{cast::Cast, Goal, Mutability, TyVariableKind};
10+
use chalk_ir::{cast::Cast, Goal, Mutability, TyVariableKind, BoundVar};
1111
use hir_def::{expr::ExprId, lang_item::LangItemTarget};
1212
use stdx::always;
1313
use syntax::SmolStr;
@@ -19,7 +19,7 @@ use crate::{
1919
PointerCast, TypeError, TypeMismatch,
2020
},
2121
static_lifetime, Canonical, DomainGoal, FnPointer, FnSig, Guidance, InEnvironment, Interner,
22-
Solution, Substitution, Ty, TyBuilder, TyExt, TyKind,
22+
Solution, Substitution, Ty, TyBuilder, TyExt, TyKind, db::HirDatabase, TraitEnvironment, GenericArgData,
2323
};
2424

2525
use super::unify::InferenceTable;
@@ -120,6 +120,45 @@ impl CoerceMany {
120120
}
121121
}
122122

123+
pub fn could_coerce(
124+
db: &dyn HirDatabase,
125+
env: Arc<TraitEnvironment>,
126+
tys: &Canonical<(Ty, Ty)>,
127+
) -> bool {
128+
coerce(db, env, tys).is_ok()
129+
}
130+
131+
pub(crate) fn coerce(
132+
db: &dyn HirDatabase,
133+
env: Arc<TraitEnvironment>,
134+
tys: &Canonical<(Ty, Ty)>,
135+
) -> Result<(Vec<Adjustment>, Ty), TypeError> {
136+
let mut table = InferenceTable::new(db, env);
137+
let vars = table.fresh_subst(tys.binders.as_slice(Interner));
138+
let ty1_with_vars = vars.apply(tys.value.0.clone(), Interner);
139+
let ty2_with_vars = vars.apply(tys.value.1.clone(), Interner);
140+
let (adjustments, ty) = table.coerce(&ty1_with_vars, &ty2_with_vars)?;
141+
// default any type vars that weren't unified back to their original bound vars
142+
// (kind of hacky)
143+
let find_var = |iv| {
144+
vars.iter(Interner).position(|v| match v.interned() {
145+
chalk_ir::GenericArgData::Ty(ty) => ty.inference_var(Interner),
146+
chalk_ir::GenericArgData::Lifetime(lt) => lt.inference_var(Interner),
147+
chalk_ir::GenericArgData::Const(c) => c.inference_var(Interner),
148+
} == Some(iv))
149+
};
150+
let fallback = |iv, kind, default, binder| match kind {
151+
chalk_ir::VariableKind::Ty(_ty_kind) => find_var(iv)
152+
.map_or(default, |i| BoundVar::new(binder, i).to_ty(Interner).cast(Interner)),
153+
chalk_ir::VariableKind::Lifetime => find_var(iv)
154+
.map_or(default, |i| BoundVar::new(binder, i).to_lifetime(Interner).cast(Interner)),
155+
chalk_ir::VariableKind::Const(ty) => find_var(iv)
156+
.map_or(default, |i| BoundVar::new(binder, i).to_const(Interner, ty).cast(Interner)),
157+
};
158+
// FIXME also map the types in the adjustments
159+
Ok((adjustments, table.resolve_with_fallback(ty, &fallback)))
160+
}
161+
123162
impl<'a> InferenceContext<'a> {
124163
/// Unify two types, but may coerce the first one to the second one
125164
/// using "implicit coercion rules" if needed.

crates/hir_ty/src/infer/unify.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::{fmt, mem, sync::Arc};
44

55
use chalk_ir::{
66
cast::Cast, fold::Fold, interner::HasInterner, zip::Zip, FloatTy, IntTy, NoSolution,
7-
TyVariableKind, UniverseIndex,
7+
TyVariableKind, UniverseIndex, CanonicalVarKind,
88
};
99
use chalk_solve::infer::ParameterEnaVariableExt;
1010
use ena::unify::UnifyKey;
@@ -299,11 +299,25 @@ impl<'a> InferenceTable<'a> {
299299
self.resolve_with_fallback_inner(&mut Vec::new(), t, &fallback)
300300
}
301301

302+
pub(crate) fn fresh_subst(
303+
&mut self,
304+
binders: &[CanonicalVarKind<Interner>],
305+
) -> Substitution {
306+
Substitution::from_iter(
307+
Interner,
308+
binders.iter().map(|kind| {
309+
let param_infer_var = kind.map_ref(|&ui| self.var_unification_table.new_variable(ui));
310+
param_infer_var.to_generic_arg(Interner)
311+
}),
312+
)
313+
}
314+
302315
pub(crate) fn instantiate_canonical<T>(&mut self, canonical: Canonical<T>) -> T::Result
303316
where
304317
T: HasInterner<Interner = Interner> + Fold<Interner> + std::fmt::Debug,
305318
{
306-
self.var_unification_table.instantiate_canonical(Interner, canonical)
319+
let subst = self.fresh_subst(canonical.binders.as_slice(Interner));
320+
subst.apply(canonical.value, Interner)
307321
}
308322

309323
fn resolve_with_fallback_inner<T>(

crates/hir_ty/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ pub use autoderef::autoderef;
5151
pub use builder::{ParamKind, TyBuilder};
5252
pub use chalk_ext::*;
5353
pub use infer::{
54-
could_unify, Adjust, Adjustment, AutoBorrow, InferenceDiagnostic, InferenceResult,
54+
could_coerce, could_unify, Adjust, Adjustment, AutoBorrow, InferenceDiagnostic, InferenceResult,
5555
};
5656
pub use interner::Interner;
5757
pub use lower::{

0 commit comments

Comments
 (0)