9975: minor: Fix panic caused by #9966 r=flodiebold a=flodiebold

Chalk can introduce new type variables when doing lazy normalization, so we have to do the proper 'fudging' after all.

Co-authored-by: Florian Diebold <flodiebold@gmail.com>
This commit is contained in:
bors[bot] 2021-08-21 18:07:36 +00:00 committed by GitHub
commit 9e3517f8f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 129 additions and 17 deletions

View File

@ -986,20 +986,21 @@ impl<'a> InferenceContext<'a> {
inputs: Vec<Ty>,
) -> Vec<Ty> {
if let Some(expected_ty) = expected_output.to_option(&mut self.table) {
let snapshot = self.table.snapshot();
let result = if self.table.try_unify(&expected_ty, &output).is_ok() {
// FIXME: the unification could introduce lifetime variables, which we'd need to handle here
self.table.resolve_with_fallback(inputs, |var, kind, _, _| match kind {
chalk_ir::VariableKind::Ty(tk) => var.to_ty(&Interner, tk).cast(&Interner),
chalk_ir::VariableKind::Lifetime => var.to_lifetime(&Interner).cast(&Interner),
chalk_ir::VariableKind::Const(ty) => {
var.to_const(&Interner, ty).cast(&Interner)
}
})
} else {
Vec::new()
};
self.table.rollback_to(snapshot);
let result = self.table.fudge_inference(|table| {
if table.try_unify(&expected_ty, &output).is_ok() {
table.resolve_with_fallback(inputs, |var, kind, _, _| match kind {
chalk_ir::VariableKind::Ty(tk) => var.to_ty(&Interner, tk).cast(&Interner),
chalk_ir::VariableKind::Lifetime => {
var.to_lifetime(&Interner).cast(&Interner)
}
chalk_ir::VariableKind::Const(ty) => {
var.to_const(&Interner, ty).cast(&Interner)
}
})
} else {
Vec::new()
}
});
result
} else {
Vec::new()

View File

@ -11,9 +11,9 @@ use ena::unify::UnifyKey;
use super::{InferOk, InferResult, InferenceContext, TypeError};
use crate::{
db::HirDatabase, fold_tys, static_lifetime, AliasEq, AliasTy, BoundVar, Canonical,
DebruijnIndex, GenericArg, Goal, Guidance, InEnvironment, InferenceVar, Interner, ProjectionTy,
Scalar, Solution, Substitution, TraitEnvironment, Ty, TyKind, VariableKind,
db::HirDatabase, fold_tys, static_lifetime, AliasEq, AliasTy, BoundVar, Canonical, Const,
DebruijnIndex, GenericArg, Goal, Guidance, InEnvironment, InferenceVar, Interner, Lifetime,
ProjectionTy, Scalar, Solution, Substitution, TraitEnvironment, Ty, TyKind, VariableKind,
};
impl<'a> InferenceContext<'a> {
@ -273,6 +273,16 @@ impl<'a> InferenceTable<'a> {
self.new_var(TyVariableKind::General, true)
}
pub(crate) fn new_const_var(&mut self, ty: Ty) -> Const {
let var = self.var_unification_table.new_variable(UniverseIndex::ROOT);
var.to_const(&Interner, ty)
}
pub(crate) fn new_lifetime_var(&mut self) -> Lifetime {
let var = self.var_unification_table.new_variable(UniverseIndex::ROOT);
var.to_lifetime(&Interner)
}
pub(crate) fn resolve_with_fallback<T>(
&mut self,
t: T,
@ -388,6 +398,76 @@ impl<'a> InferenceTable<'a> {
}
}
pub(crate) fn fudge_inference<T: Fold<Interner>>(
&mut self,
f: impl FnOnce(&mut Self) -> T,
) -> T::Result {
use chalk_ir::fold::Folder;
struct VarFudger<'a, 'b> {
table: &'a mut InferenceTable<'b>,
highest_known_var: InferenceVar,
}
impl<'a, 'b> Folder<'static, Interner> for VarFudger<'a, 'b> {
fn as_dyn(&mut self) -> &mut dyn Folder<'static, Interner> {
self
}
fn interner(&self) -> &'static Interner {
&Interner
}
fn fold_inference_ty(
&mut self,
var: chalk_ir::InferenceVar,
kind: TyVariableKind,
_outer_binder: chalk_ir::DebruijnIndex,
) -> chalk_ir::Fallible<chalk_ir::Ty<Interner>> {
Ok(if var < self.highest_known_var {
var.to_ty(&Interner, kind)
} else {
self.table.new_type_var()
})
}
fn fold_inference_lifetime(
&mut self,
var: chalk_ir::InferenceVar,
_outer_binder: chalk_ir::DebruijnIndex,
) -> chalk_ir::Fallible<chalk_ir::Lifetime<Interner>> {
Ok(if var < self.highest_known_var {
var.to_lifetime(&Interner)
} else {
self.table.new_lifetime_var()
})
}
fn fold_inference_const(
&mut self,
ty: chalk_ir::Ty<Interner>,
var: chalk_ir::InferenceVar,
_outer_binder: chalk_ir::DebruijnIndex,
) -> chalk_ir::Fallible<chalk_ir::Const<Interner>> {
Ok(if var < self.highest_known_var {
var.to_const(&Interner, ty)
} else {
self.table.new_const_var(ty)
})
}
}
let snapshot = self.snapshot();
let highest_known_var =
self.new_type_var().inference_var(&Interner).expect("inference_var");
let result = f(self);
self.rollback_to(snapshot);
let result = result
.fold_with(&mut VarFudger { table: self, highest_known_var }, DebruijnIndex::INNERMOST)
.expect("fold_with with VarFudger");
result
}
/// This checks whether any of the free variables in the `canonicalized`
/// have changed (either been unified with another variable, or with a
/// value). If this is not the case, we don't need to try to solve the goal

View File

@ -1114,3 +1114,34 @@ fn test() {
"#,
);
}
#[test]
fn coerce_diesel_panic() {
check_no_mismatches(
r#"
//- minicore: option
trait TypeMetadata {
type MetadataLookup;
}
pub struct Output<'a, T, DB>
where
DB: TypeMetadata,
DB::MetadataLookup: 'a,
{
out: T,
metadata_lookup: Option<&'a DB::MetadataLookup>,
}
impl<'a, T, DB: TypeMetadata> Output<'a, T, DB> {
pub fn new(out: T, metadata_lookup: &'a DB::MetadataLookup) -> Self {
Output {
out,
metadata_lookup: Some(metadata_lookup),
}
}
}
"#,
);
}