8938: internal: Fix #8931 r=flodiebold a=flodiebold

  - and add some better checking for similar bugs

Co-authored-by: Florian Diebold <flodiebold@gmail.com>
This commit is contained in:
bors[bot] 2021-05-23 11:03:18 +00:00 committed by GitHub
commit 0ec4ce1e9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 118 additions and 15 deletions

View File

@ -2053,7 +2053,7 @@ impl Type {
name: Option<&Name>,
mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
) -> Option<T> {
let canonical = hir_ty::replace_errors_with_variables(self.ty.clone());
let canonical = hir_ty::replace_errors_with_variables(&self.ty);
let env = self.env.clone();
let krate = krate.id;
@ -2222,7 +2222,7 @@ impl Type {
}
pub fn could_unify_with(&self, db: &dyn HirDatabase, other: &Type) -> bool {
let tys = hir_ty::replace_errors_with_variables((self.ty.clone(), other.ty.clone()));
let tys = hir_ty::replace_errors_with_variables(&(self.ty.clone(), other.ty.clone()));
could_unify(db, self.env.clone(), &tys)
}
}

View File

@ -505,9 +505,10 @@ impl<'db> SemanticsImpl<'db> {
}
fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> {
// FIXME: this erases Substs
// FIXME: this erases Substs, we should instead record the correct
// substitution during inference and use that
let func = self.resolve_method_call(call)?;
let (ty, _) = self.db.value_ty(func.into()).into_value_and_skipped_binders();
let ty = hir_ty::TyBuilder::value_ty(self.db, func.into()).fill_with_unknown().build();
let resolver = self.analyze(call.syntax()).resolver;
let ty = Type::new_with_resolver(self.db, &resolver, ty)?;
let mut res = ty.as_callable(self.db)?;

View File

@ -43,7 +43,6 @@ use hir_def::{
type_ref::{ConstScalar, Rawness},
TypeParamId,
};
use stdx::always;
use crate::{db::HirDatabase, utils::generics};
@ -329,14 +328,17 @@ pub(crate) fn fold_tys<T: HasInterner<Interner = Interner> + Fold<Interner>>(
t.fold_with(&mut TyFolder(f), binders).expect("fold failed unexpectedly")
}
pub fn replace_errors_with_variables<T>(t: T) -> Canonical<T::Result>
/// 'Canonicalizes' the `t` by replacing any errors with new variables. Also
/// ensures there are no unbound variables or inference variables anywhere in
/// the `t`.
pub fn replace_errors_with_variables<T>(t: &T) -> Canonical<T::Result>
where
T: HasInterner<Interner = Interner> + Fold<Interner>,
T: HasInterner<Interner = Interner> + Fold<Interner> + Clone,
T::Result: HasInterner<Interner = Interner>,
{
use chalk_ir::{
fold::{Folder, SuperFold},
Fallible,
Fallible, NoSolution,
};
struct ErrorReplacer {
vars: usize,
@ -363,18 +365,88 @@ where
fn fold_inference_ty(
&mut self,
var: InferenceVar,
kind: TyVariableKind,
_var: InferenceVar,
_kind: TyVariableKind,
_outer_binder: DebruijnIndex,
) -> Fallible<Ty> {
always!(false);
Ok(TyKind::InferenceVar(var, kind).intern(&Interner))
if cfg!(debug_assertions) {
// we don't want to just panic here, because then the error message
// won't contain the whole thing, which would not be very helpful
Err(NoSolution)
} else {
Ok(TyKind::Error.intern(&Interner))
}
}
fn fold_free_var_ty(
&mut self,
_bound_var: BoundVar,
_outer_binder: DebruijnIndex,
) -> Fallible<Ty> {
if cfg!(debug_assertions) {
// we don't want to just panic here, because then the error message
// won't contain the whole thing, which would not be very helpful
Err(NoSolution)
} else {
Ok(TyKind::Error.intern(&Interner))
}
}
fn fold_inference_const(
&mut self,
_ty: Ty,
_var: InferenceVar,
_outer_binder: DebruijnIndex,
) -> Fallible<Const> {
if cfg!(debug_assertions) {
Err(NoSolution)
} else {
Ok(dummy_usize_const())
}
}
fn fold_free_var_const(
&mut self,
_ty: Ty,
_bound_var: BoundVar,
_outer_binder: DebruijnIndex,
) -> Fallible<Const> {
if cfg!(debug_assertions) {
Err(NoSolution)
} else {
Ok(dummy_usize_const())
}
}
fn fold_inference_lifetime(
&mut self,
_var: InferenceVar,
_outer_binder: DebruijnIndex,
) -> Fallible<Lifetime> {
if cfg!(debug_assertions) {
Err(NoSolution)
} else {
Ok(static_lifetime())
}
}
fn fold_free_var_lifetime(
&mut self,
_bound_var: BoundVar,
_outer_binder: DebruijnIndex,
) -> Fallible<Lifetime> {
if cfg!(debug_assertions) {
Err(NoSolution)
} else {
Ok(static_lifetime())
}
}
}
let mut error_replacer = ErrorReplacer { vars: 0 };
let value = t
.fold_with(&mut error_replacer, DebruijnIndex::INNERMOST)
.expect("fold failed unexpectedly");
let value = match t.clone().fold_with(&mut error_replacer, DebruijnIndex::INNERMOST) {
Ok(t) => t,
Err(_) => panic!("Encountered unbound or inference vars in {:?}", t),
};
let kinds = (0..error_replacer.vars).map(|_| {
chalk_ir::CanonicalVarKind::new(
chalk_ir::VariableKind::Ty(TyVariableKind::General),

View File

@ -454,4 +454,34 @@ mod foo {
"#]],
);
}
#[test]
fn issue_8931() {
check(
r#"
#[lang = "fn_once"]
trait FnOnce<Args> {
type Output;
}
struct S;
struct Foo;
impl Foo {
fn foo(&self) -> &[u8] { loop {} }
}
impl S {
fn indented(&mut self, f: impl FnOnce(&mut Self)) {
}
fn f(&mut self, v: Foo) {
self.indented(|this| v.$0)
}
}
"#,
expect![[r#"
me foo() fn(&self) -> &[u8]
"#]],
);
}
}