summaryrefslogtreecommitdiffstats
path: root/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs')
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs85
1 files changed, 70 insertions, 15 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
index 12f45f00f..e7ddd1591 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
@@ -1,6 +1,6 @@
//! Unification and canonicalization logic.
-use std::{fmt, mem, sync::Arc};
+use std::{fmt, iter, mem, sync::Arc};
use chalk_ir::{
cast::Cast, fold::TypeFoldable, interner::HasInterner, zip::Zip, CanonicalVarKind, FloatTy,
@@ -128,9 +128,13 @@ pub(crate) fn unify(
))
}
-#[derive(Copy, Clone, Debug)]
-pub(crate) struct TypeVariableData {
- diverging: bool,
+bitflags::bitflags! {
+ #[derive(Default)]
+ pub(crate) struct TypeVariableFlags: u8 {
+ const DIVERGING = 1 << 0;
+ const INTEGER = 1 << 1;
+ const FLOAT = 1 << 2;
+ }
}
type ChalkInferenceTable = chalk_solve::infer::InferenceTable<Interner>;
@@ -140,14 +144,14 @@ pub(crate) struct InferenceTable<'a> {
pub(crate) db: &'a dyn HirDatabase,
pub(crate) trait_env: Arc<TraitEnvironment>,
var_unification_table: ChalkInferenceTable,
- type_variable_table: Vec<TypeVariableData>,
+ type_variable_table: Vec<TypeVariableFlags>,
pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>,
}
pub(crate) struct InferenceTableSnapshot {
var_table_snapshot: chalk_solve::infer::InferenceSnapshot<Interner>,
pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>,
- type_variable_table_snapshot: Vec<TypeVariableData>,
+ type_variable_table_snapshot: Vec<TypeVariableFlags>,
}
impl<'a> InferenceTable<'a> {
@@ -169,19 +173,19 @@ impl<'a> InferenceTable<'a> {
/// result.
pub(super) fn propagate_diverging_flag(&mut self) {
for i in 0..self.type_variable_table.len() {
- if !self.type_variable_table[i].diverging {
+ if !self.type_variable_table[i].contains(TypeVariableFlags::DIVERGING) {
continue;
}
let v = InferenceVar::from(i as u32);
let root = self.var_unification_table.inference_var_root(v);
if let Some(data) = self.type_variable_table.get_mut(root.index() as usize) {
- data.diverging = true;
+ *data |= TypeVariableFlags::DIVERGING;
}
}
}
pub(super) fn set_diverging(&mut self, iv: InferenceVar, diverging: bool) {
- self.type_variable_table[iv.index() as usize].diverging = diverging;
+ self.type_variable_table[iv.index() as usize].set(TypeVariableFlags::DIVERGING, diverging);
}
fn fallback_value(&self, iv: InferenceVar, kind: TyVariableKind) -> Ty {
@@ -189,7 +193,7 @@ impl<'a> InferenceTable<'a> {
_ if self
.type_variable_table
.get(iv.index() as usize)
- .map_or(false, |data| data.diverging) =>
+ .map_or(false, |data| data.contains(TypeVariableFlags::DIVERGING)) =>
{
TyKind::Never
}
@@ -247,10 +251,8 @@ impl<'a> InferenceTable<'a> {
}
fn extend_type_variable_table(&mut self, to_index: usize) {
- self.type_variable_table.extend(
- (0..1 + to_index - self.type_variable_table.len())
- .map(|_| TypeVariableData { diverging: false }),
- );
+ let count = to_index - self.type_variable_table.len() + 1;
+ self.type_variable_table.extend(iter::repeat(TypeVariableFlags::default()).take(count));
}
fn new_var(&mut self, kind: TyVariableKind, diverging: bool) -> Ty {
@@ -258,7 +260,15 @@ impl<'a> InferenceTable<'a> {
// Chalk might have created some type variables for its own purposes that we don't know about...
self.extend_type_variable_table(var.index() as usize);
assert_eq!(var.index() as usize, self.type_variable_table.len() - 1);
- self.type_variable_table[var.index() as usize].diverging = diverging;
+ let flags = self.type_variable_table.get_mut(var.index() as usize).unwrap();
+ if diverging {
+ *flags |= TypeVariableFlags::DIVERGING;
+ }
+ if matches!(kind, TyVariableKind::Integer) {
+ *flags |= TypeVariableFlags::INTEGER;
+ } else if matches!(kind, TyVariableKind::Float) {
+ *flags |= TypeVariableFlags::FLOAT;
+ }
var.to_ty_with_kind(Interner, kind)
}
@@ -340,6 +350,51 @@ impl<'a> InferenceTable<'a> {
self.resolve_with_fallback(t, &|_, _, d, _| d)
}
+ /// Apply a fallback to unresolved scalar types. Integer type variables and float type
+ /// variables are replaced with i32 and f64, respectively.
+ ///
+ /// This method is only intended to be called just before returning inference results (i.e. in
+ /// `InferenceContext::resolve_all()`).
+ ///
+ /// FIXME: This method currently doesn't apply fallback to unconstrained general type variables
+ /// whereas rustc replaces them with `()` or `!`.
+ pub(super) fn fallback_if_possible(&mut self) {
+ let int_fallback = TyKind::Scalar(Scalar::Int(IntTy::I32)).intern(Interner);
+ let float_fallback = TyKind::Scalar(Scalar::Float(FloatTy::F64)).intern(Interner);
+
+ let scalar_vars: Vec<_> = self
+ .type_variable_table
+ .iter()
+ .enumerate()
+ .filter_map(|(index, flags)| {
+ let kind = if flags.contains(TypeVariableFlags::INTEGER) {
+ TyVariableKind::Integer
+ } else if flags.contains(TypeVariableFlags::FLOAT) {
+ TyVariableKind::Float
+ } else {
+ return None;
+ };
+
+ // FIXME: This is not really the nicest way to get `InferenceVar`s. Can we get them
+ // without directly constructing them from `index`?
+ let var = InferenceVar::from(index as u32).to_ty(Interner, kind);
+ Some(var)
+ })
+ .collect();
+
+ for var in scalar_vars {
+ let maybe_resolved = self.resolve_ty_shallow(&var);
+ if let TyKind::InferenceVar(_, kind) = maybe_resolved.kind(Interner) {
+ let fallback = match kind {
+ TyVariableKind::Integer => &int_fallback,
+ TyVariableKind::Float => &float_fallback,
+ TyVariableKind::General => unreachable!(),
+ };
+ self.unify(&var, fallback);
+ }
+ }
+ }
+
/// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that.
pub(crate) fn unify<T: ?Sized + Zip<Interner>>(&mut self, ty1: &T, ty2: &T) -> bool {
let result = match self.try_unify(ty1, ty2) {