Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/infer.rs')
| -rw-r--r-- | crates/hir-ty/src/infer.rs | 54 |
1 files changed, 44 insertions, 10 deletions
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 82c5834e64..8b0702c483 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -401,6 +401,9 @@ pub enum InferenceDiagnostic { InvalidLhsOfAssignment { lhs: ExprId, }, + TypeMustBeKnown { + at_point: ExprOrPatId, + }, } /// A mismatch between an expected and an inferred type. @@ -1178,6 +1181,7 @@ pub(crate) struct InferenceContext<'body, 'db> { deferred_call_resolutions: FxHashMap<ExprId, Vec<DeferredCallResolution<'db>>>, diagnostics: Diagnostics, + vars_emitted_type_must_be_known_for: FxHashSet<Ty<'db>>, } #[derive(Clone, Debug)] @@ -1267,6 +1271,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { deferred_cast_checks: Vec::new(), inside_assignment: false, diagnostics: Diagnostics::default(), + vars_emitted_type_must_be_known_for: FxHashSet::default(), deferred_call_resolutions: FxHashMap::default(), } } @@ -1581,6 +1586,33 @@ impl<'body, 'db> InferenceContext<'body, 'db> { &self.table.infer_ctxt } + /// If `ty` is an error, returns an infer var instead. Otherwise, returns it. + /// + /// "Refreshing" types like this is useful for getting better types, but it is also + /// very dangerous: we might create duplicate diagnostics, for example if we try + /// to resolve it and fail. rustc doesn't do that for this reason (and is in general + /// more strict with how it uses error types; an error type in inputs will almost + /// always cause it to infer an error type in output, while we infer some type as much + /// as we can). + /// + /// Unfortunately, we cannot allow ourselves to do that. Not only we more often work + /// with incomplete code, we also have assists, for example "Generate constant", that + /// will assume the inferred type is the expected type even if the expression itself + /// cannot be inferred. Therefore, we choose a middle ground: refresh the type, + /// but if we return a new var, mark it so that no diagnostics will be issued on it. + fn insert_type_vars_shallow(&mut self, ty: Ty<'db>) -> Ty<'db> { + if ty.is_ty_error() { + let var = self.table.next_ty_var(); + + // Suppress future errors on this var. Add more things here when we add more diagnostics. + self.vars_emitted_type_must_be_known_for.insert(var); + + var + } else { + ty + } + } + fn infer_body(&mut self, body_expr: ExprId) { match self.return_coercion { Some(_) => self.infer_return(body_expr), @@ -1781,11 +1813,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> { self.insert_type_vars(lt) } - /// Replaces `Ty::Error` by a new type var, so we can maybe still infer it. - fn insert_type_vars_shallow(&mut self, ty: Ty<'db>) -> Ty<'db> { - self.table.insert_type_vars_shallow(ty) - } - fn insert_type_vars<T>(&mut self, ty: T) -> T where T: TypeFoldable<DbInterner<'db>>, @@ -1873,6 +1900,11 @@ impl<'body, 'db> InferenceContext<'body, 'db> { self.table.resolve_vars_if_possible(t) } + pub(crate) fn structurally_resolve_type(&mut self, node: ExprOrPatId, ty: Ty<'db>) -> Ty<'db> { + let result = self.table.try_structurally_resolve_type(ty); + if result.is_ty_var() { self.type_must_be_known_at_this_point(node, ty) } else { result } + } + fn demand_eqtype( &mut self, id: ExprOrPatId, @@ -1938,11 +1970,13 @@ impl<'body, 'db> InferenceContext<'body, 'db> { } pub(crate) fn type_must_be_known_at_this_point( - &self, - _id: ExprOrPatId, - _ty: Ty<'db>, + &mut self, + node: ExprOrPatId, + ty: Ty<'db>, ) -> Ty<'db> { - // FIXME: Emit an diagnostic. + if self.vars_emitted_type_must_be_known_for.insert(ty) { + self.push_diagnostic(InferenceDiagnostic::TypeMustBeKnown { at_point: node }); + } self.types.types.error } @@ -2477,7 +2511,7 @@ impl<'db> Expectation<'db> { fn adjust_for_branches(&self, table: &mut unify::InferenceTable<'db>) -> Expectation<'db> { match *self { Expectation::HasType(ety) => { - let ety = table.structurally_resolve_type(ety); + let ety = table.try_structurally_resolve_type(ety); if ety.is_ty_var() { Expectation::None } else { Expectation::HasType(ety) } } Expectation::RValueLikeUnsized(ety) => Expectation::RValueLikeUnsized(ety), |