From d01630c8f39e88aa175db9dbe92a9e2a188007ab Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Thu, 5 Jan 2023 21:31:10 +0900 Subject: [PATCH] Apply fallback to scalar type variables before final obligation resolution --- crates/hir-ty/src/infer.rs | 2 + crates/hir-ty/src/infer/unify.rs | 45 +++++++++++++++++++++ crates/hir-ty/src/tests/traits.rs | 65 +++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+) diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 7b54886d53f..6b59f1c20da 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -512,6 +512,8 @@ fn new( 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 a1fef48f25a..e7ddd1591fe 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs @@ -350,6 +350,51 @@ pub(crate) fn resolve_completely(&mut self, t: T) -> T 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>(&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 a9fd01ee011..d01fe063285 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -4100,3 +4100,68 @@ fn f(t: T) "#, ); } + +#[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); + +impl Add for Vec2 { + type Output = Self; + fn add(self, rhs: Self) -> Self::Output { loop {} } +} +impl Add for Vec2 { + type Output = Self; + fn add(self, rhs: Self) -> Self::Output { loop {} } +} +impl Add for Vec2 { + type Output = Self; + fn add(self, rhs: Self) -> Self::Output { loop {} } +} +impl Add for Vec2 { + 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 + let a = Vec2(1., 2.); + let b = Vec2(3., 4.); + let c = a + b; + //^ Vec2 +} +"#, + ); +} + +#[test] +fn trait_method_with_scalar_fallback() { + check_types( + r#" +trait Trait { + type Output; + fn foo(&self) -> Self::Output; +} +impl 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 +} +"#, + ); +}