From a5d85a6356dc761d047f46bf04eae09dc9ab80f9 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 23 May 2021 12:52:41 +0200 Subject: Add test for #8931 and better checking --- crates/hir/src/lib.rs | 4 +- crates/hir_ty/src/lib.rs | 94 ++++++++++++++++++++++++---- crates/ide_completion/src/completions/dot.rs | 29 +++++++++ 3 files changed, 114 insertions(+), 13 deletions(-) (limited to 'crates') diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 52d72c3c5..800101c91 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -2053,7 +2053,7 @@ impl Type { name: Option<&Name>, mut callback: impl FnMut(&Ty, AssocItem) -> Option, ) -> Option { - 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) } } diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs index 72093d75a..ef021978a 100644 --- a/crates/hir_ty/src/lib.rs +++ b/crates/hir_ty/src/lib.rs @@ -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 + Fold>( t.fold_with(&mut TyFolder(f), binders).expect("fold failed unexpectedly") } -pub fn replace_errors_with_variables(t: T) -> Canonical +/// '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) -> Canonical where - T: HasInterner + Fold, + T: HasInterner + Fold + Clone, T::Result: HasInterner, { 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 { + 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 { - 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_inference_const( + &mut self, + _ty: Ty, + _var: InferenceVar, + _outer_binder: DebruijnIndex, + ) -> Fallible { + 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 { + if cfg!(debug_assertions) { + Err(NoSolution) + } else { + Ok(dummy_usize_const()) + } + } + + fn fold_inference_lifetime( + &mut self, + _var: InferenceVar, + _outer_binder: DebruijnIndex, + ) -> Fallible { + if cfg!(debug_assertions) { + Err(NoSolution) + } else { + Ok(static_lifetime()) + } + } + + fn fold_free_var_lifetime( + &mut self, + _bound_var: BoundVar, + _outer_binder: DebruijnIndex, + ) -> Fallible { + 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), diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs index 7e4efe589..1bff55936 100644 --- a/crates/ide_completion/src/completions/dot.rs +++ b/crates/ide_completion/src/completions/dot.rs @@ -454,4 +454,33 @@ mod foo { "#]], ); } + + #[test] + fn issue_8931() { + check( + r#" +#[lang = "fn_once"] +trait FnOnce { + 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#" + "#]], + ); + } } -- cgit v1.2.3 From 34a3bc4196db302ea5b31c51b6d555336965be5f Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 23 May 2021 13:00:14 +0200 Subject: Paper over #8931 a bit more The problem was the skipping of binders in `resolve_method_call_as_callable`; this still doesn't use the _correct_ substitution, but at least it doesn't return a type with free variables in it. Fixes #8931. --- crates/hir/src/semantics.rs | 5 +++-- crates/ide_completion/src/completions/dot.rs | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'crates') diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 38bd376bc..1b5064b5a 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -505,9 +505,10 @@ impl<'db> SemanticsImpl<'db> { } fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option { - // 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)?; diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs index 1bff55936..fd9738743 100644 --- a/crates/ide_completion/src/completions/dot.rs +++ b/crates/ide_completion/src/completions/dot.rs @@ -480,6 +480,7 @@ impl S { } "#, expect![[r#" + me foo() fn(&self) -> &[u8] "#]], ); } -- cgit v1.2.3