Unnamed repository; edit this file 'description' to name the repository.
Apply fallback to scalar type variables before final obligation resolution
Ryo Yoshida 2023-01-06
parent b183612 · commit d01630c
-rw-r--r--crates/hir-ty/src/infer.rs2
-rw-r--r--crates/hir-ty/src/infer/unify.rs45
-rw-r--r--crates/hir-ty/src/tests/traits.rs65
3 files changed, 112 insertions, 0 deletions
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 7b54886d53..6b59f1c20d 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -512,6 +512,8 @@ impl<'a> InferenceContext<'a> {
fn resolve_all(self) -> InferenceResult {
let InferenceContext { mut table, mut result, .. } = self;
+ table.fallback_if_possible();
+
// FIXME resolve obligations as well (use Guidance if necessary)
table.resolve_obligations_as_possible();
diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs
index a1fef48f25..e7ddd1591f 100644
--- a/crates/hir-ty/src/infer/unify.rs
+++ b/crates/hir-ty/src/infer/unify.rs
@@ -350,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) {
diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs
index a9fd01ee01..d01fe06328 100644
--- a/crates/hir-ty/src/tests/traits.rs
+++ b/crates/hir-ty/src/tests/traits.rs
@@ -4100,3 +4100,68 @@ where
"#,
);
}
+
+#[test]
+fn bin_op_with_scalar_fallback() {
+ // Extra impls are significant so that chalk doesn't give us definite guidances.
+ check_types(
+ r#"
+//- minicore: add
+use core::ops::Add;
+
+struct Vec2<T>(T, T);
+
+impl Add for Vec2<i32> {
+ type Output = Self;
+ fn add(self, rhs: Self) -> Self::Output { loop {} }
+}
+impl Add for Vec2<u32> {
+ type Output = Self;
+ fn add(self, rhs: Self) -> Self::Output { loop {} }
+}
+impl Add for Vec2<f32> {
+ type Output = Self;
+ fn add(self, rhs: Self) -> Self::Output { loop {} }
+}
+impl Add for Vec2<f64> {
+ type Output = Self;
+ fn add(self, rhs: Self) -> Self::Output { loop {} }
+}
+
+fn test() {
+ let a = Vec2(1, 2);
+ let b = Vec2(3, 4);
+ let c = a + b;
+ //^ Vec2<i32>
+ let a = Vec2(1., 2.);
+ let b = Vec2(3., 4.);
+ let c = a + b;
+ //^ Vec2<f64>
+}
+"#,
+ );
+}
+
+#[test]
+fn trait_method_with_scalar_fallback() {
+ check_types(
+ r#"
+trait Trait {
+ type Output;
+ fn foo(&self) -> Self::Output;
+}
+impl<T> Trait for T {
+ type Output = T;
+ fn foo(&self) -> Self::Output { loop {} }
+}
+fn test() {
+ let a = 42;
+ let b = a.foo();
+ //^ i32
+ let a = 3.14;
+ let b = a.foo();
+ //^ f64
+}
+"#,
+ );
+}