From df77e2448cf85ad33d07ceddb31922a133168895 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 21 Aug 2021 19:47:06 +0200 Subject: [PATCH] Fix panic caused by #9966 Chalk can introduce new type variables when doing lazy normalization, so we have to do the proper 'fudging' after all. --- crates/hir_ty/src/infer/expr.rs | 29 ++++----- crates/hir_ty/src/infer/unify.rs | 86 ++++++++++++++++++++++++++- crates/hir_ty/src/tests/regression.rs | 31 ++++++++++ 3 files changed, 129 insertions(+), 17 deletions(-) diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index d535e7f432..7d37e51244 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs @@ -985,20 +985,21 @@ impl<'a> InferenceContext<'a> { inputs: Vec, ) -> Vec { 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() diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index 4c470e5563..bb87e83ca0 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs @@ -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( &mut self, t: T, @@ -388,6 +398,76 @@ impl<'a> InferenceTable<'a> { } } + pub(crate) fn fudge_inference>( + &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> { + 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> { + 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, + var: chalk_ir::InferenceVar, + _outer_binder: chalk_ir::DebruijnIndex, + ) -> chalk_ir::Fallible> { + 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 diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs index 1919b1fc70..70a1c37dc0 100644 --- a/crates/hir_ty/src/tests/regression.rs +++ b/crates/hir_ty/src/tests/regression.rs @@ -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), + } + } +} + "#, + ); +}