Apply fallback to scalar type variables before final obligation resolution

This commit is contained in:
Ryo Yoshida 2023-01-05 21:31:10 +09:00
parent b183612610
commit d01630c8f3
No known key found for this signature in database
GPG Key ID: E25698A930586171
3 changed files with 112 additions and 0 deletions

View File

@ -512,6 +512,8 @@ fn new(
fn resolve_all(self) -> InferenceResult { fn resolve_all(self) -> InferenceResult {
let InferenceContext { mut table, mut result, .. } = self; let InferenceContext { mut table, mut result, .. } = self;
table.fallback_if_possible();
// FIXME resolve obligations as well (use Guidance if necessary) // FIXME resolve obligations as well (use Guidance if necessary)
table.resolve_obligations_as_possible(); table.resolve_obligations_as_possible();

View File

@ -350,6 +350,51 @@ pub(crate) fn resolve_completely<T>(&mut self, t: T) -> T
self.resolve_with_fallback(t, &|_, _, d, _| d) 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. /// 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 { pub(crate) fn unify<T: ?Sized + Zip<Interner>>(&mut self, ty1: &T, ty2: &T) -> bool {
let result = match self.try_unify(ty1, ty2) { let result = match self.try_unify(ty1, ty2) {

View File

@ -4100,3 +4100,68 @@ fn f<T>(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, 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
}
"#,
);
}