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 | 215 |
1 files changed, 144 insertions, 71 deletions
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 767afdf9eb..7de5b4295f 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -17,11 +17,12 @@ use std::ops::Index; use std::sync::Arc; use chalk_ir::{cast::Cast, ConstValue, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags}; +use either::Either; use hir_def::{ body::Body, builtin_type::{BuiltinInt, BuiltinType, BuiltinUint}, data::{ConstData, StaticData}, - expr::{BindingAnnotation, ExprId, ExprOrPatId, PatId}, + expr::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId}, lang_item::{LangItem, LangItemTarget}, layout::Integer, path::Path, @@ -30,10 +31,9 @@ use hir_def::{ AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule, ItemContainerId, Lookup, TraitId, TypeAliasId, VariantId, }; -use hir_expand::name::name; -use itertools::Either; +use hir_expand::name::{name, Name}; use la_arena::ArenaMap; -use rustc_hash::FxHashMap; +use rustc_hash::{FxHashMap, FxHashSet}; use stdx::always; use crate::{ @@ -66,8 +66,10 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer let mut ctx = InferenceContext::new(db, def, &body, resolver); match def { + DefWithBodyId::FunctionId(f) => { + ctx.collect_fn(f); + } DefWithBodyId::ConstId(c) => ctx.collect_const(&db.const_data(c)), - DefWithBodyId::FunctionId(f) => ctx.collect_fn(f), DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_data(s)), DefWithBodyId::VariantId(v) => { ctx.return_ty = TyBuilder::builtin(match db.enum_data(v.parent).variant_body_type() { @@ -144,44 +146,6 @@ impl Default for BindingMode { } } -/// Used to generalize patterns and assignee expressions. -trait PatLike: Into<ExprOrPatId> + Copy { - type BindingMode: Copy; - - fn infer( - this: &mut InferenceContext<'_>, - id: Self, - expected_ty: &Ty, - default_bm: Self::BindingMode, - ) -> Ty; -} - -impl PatLike for ExprId { - type BindingMode = (); - - fn infer( - this: &mut InferenceContext<'_>, - id: Self, - expected_ty: &Ty, - _: Self::BindingMode, - ) -> Ty { - this.infer_assignee_expr(id, expected_ty) - } -} - -impl PatLike for PatId { - type BindingMode = BindingMode; - - fn infer( - this: &mut InferenceContext<'_>, - id: Self, - expected_ty: &Ty, - default_bm: Self::BindingMode, - ) -> Ty { - this.infer_pat(id, expected_ty, default_bm) - } -} - #[derive(Debug)] pub(crate) struct InferOk<T> { value: T, @@ -200,11 +164,45 @@ pub(crate) type InferResult<T> = Result<InferOk<T>, TypeError>; #[derive(Debug, PartialEq, Eq, Clone)] pub enum InferenceDiagnostic { - NoSuchField { expr: ExprId }, - PrivateField { expr: ExprId, field: FieldId }, - PrivateAssocItem { id: ExprOrPatId, item: AssocItemId }, - BreakOutsideOfLoop { expr: ExprId, is_break: bool }, - MismatchedArgCount { call_expr: ExprId, expected: usize, found: usize }, + NoSuchField { + expr: ExprId, + }, + PrivateField { + expr: ExprId, + field: FieldId, + }, + PrivateAssocItem { + id: ExprOrPatId, + item: AssocItemId, + }, + UnresolvedField { + expr: ExprId, + receiver: Ty, + name: Name, + method_with_same_name_exists: bool, + }, + UnresolvedMethodCall { + expr: ExprId, + receiver: Ty, + name: Name, + /// Contains the type the field resolves to + field_with_same_name: Option<Ty>, + }, + // FIXME: Make this proper + BreakOutsideOfLoop { + expr: ExprId, + is_break: bool, + bad_value_break: bool, + }, + MismatchedArgCount { + call_expr: ExprId, + expected: usize, + found: usize, + }, + ExpectedFunction { + call_expr: ExprId, + found: Ty, + }, } /// A mismatch between an expected and an inferred type. @@ -293,8 +291,10 @@ pub enum Adjust { /// call, with the signature `&'a T -> &'a U` or `&'a mut T -> &'a mut U`. /// The target type is `U` in both cases, with the region and mutability /// being those shared by both the receiver and the returned reference. +/// +/// Mutability is `None` when we are not sure. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub struct OverloadedDeref(pub Mutability); +pub struct OverloadedDeref(pub Option<Mutability>); #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum AutoBorrow { @@ -354,7 +354,10 @@ pub struct InferenceResult { /// **Note**: When a pattern type is resolved it may still contain /// unresolved or missing subpatterns or subpatterns of mismatched types. pub type_of_pat: ArenaMap<PatId, Ty>, + pub type_of_binding: ArenaMap<BindingId, Ty>, pub type_of_rpit: ArenaMap<RpitId, Ty>, + /// Type of the result of `.into_iter()` on the for. `ExprId` is the one of the whole for loop. + pub type_of_for_iterator: FxHashMap<ExprId, Ty>, type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>, /// Interned common types to return references to. standard_types: InternedStandardTypes, @@ -389,18 +392,15 @@ impl InferenceResult { pub fn type_mismatch_for_pat(&self, pat: PatId) -> Option<&TypeMismatch> { self.type_mismatches.get(&pat.into()) } + pub fn type_mismatches(&self) -> impl Iterator<Item = (ExprOrPatId, &TypeMismatch)> { + self.type_mismatches.iter().map(|(expr_or_pat, mismatch)| (*expr_or_pat, mismatch)) + } pub fn expr_type_mismatches(&self) -> impl Iterator<Item = (ExprId, &TypeMismatch)> { self.type_mismatches.iter().filter_map(|(expr_or_pat, mismatch)| match *expr_or_pat { ExprOrPatId::ExprId(expr) => Some((expr, mismatch)), _ => None, }) } - pub fn pat_type_mismatches(&self) -> impl Iterator<Item = (PatId, &TypeMismatch)> { - self.type_mismatches.iter().filter_map(|(expr_or_pat, mismatch)| match *expr_or_pat { - ExprOrPatId::PatId(pat) => Some((pat, mismatch)), - _ => None, - }) - } } impl Index<ExprId> for InferenceResult { @@ -419,6 +419,14 @@ impl Index<PatId> for InferenceResult { } } +impl Index<BindingId> for InferenceResult { + type Output = Ty; + + fn index(&self, b: BindingId) -> &Ty { + self.type_of_binding.get(b).unwrap_or(&self.standard_types.unknown) + } +} + /// The inference context contains all information needed during type inference. #[derive(Clone, Debug)] pub(crate) struct InferenceContext<'a> { @@ -428,14 +436,19 @@ pub(crate) struct InferenceContext<'a> { pub(crate) resolver: Resolver, table: unify::InferenceTable<'a>, trait_env: Arc<TraitEnvironment>, + /// The traits in scope, disregarding block modules. This is used for caching purposes. + traits_in_scope: FxHashSet<TraitId>, pub(crate) result: InferenceResult, /// The return type of the function being inferred, the closure or async block if we're /// currently within one. /// /// We might consider using a nested inference context for checking - /// closures, but currently this is the only field that will change there, - /// so it doesn't make sense. + /// closures so we can swap all shared things out at once. return_ty: Ty, + /// If `Some`, this stores coercion information for returned + /// expressions. If `None`, this is in a context where return is + /// inappropriate, such as a const expression. + return_coercion: Option<CoerceMany>, /// The resume type and the yield type, respectively, of the generator being inferred. resume_yield_tys: Option<(Ty, Ty)>, diverges: Diverges, @@ -447,7 +460,7 @@ struct BreakableContext { /// Whether this context contains at least one break expression. may_break: bool, /// The coercion target of the context. - coerce: CoerceMany, + coerce: Option<CoerceMany>, /// The optional label of the context. label: Option<name::Name>, kind: BreakableKind, @@ -503,16 +516,22 @@ impl<'a> InferenceContext<'a> { trait_env, return_ty: TyKind::Error.intern(Interner), // set in collect_* calls resume_yield_tys: None, + return_coercion: None, db, owner, body, + traits_in_scope: resolver.traits_in_scope(db.upcast()), resolver, diverges: Diverges::Maybe, breakables: Vec::new(), } } - fn resolve_all(self) -> InferenceResult { + // FIXME: This function should be private in module. It is currently only used in the consteval, since we need + // `InferenceResult` in the middle of inference. See the fixme comment in `consteval::eval_to_const`. If you + // used this function for another workaround, mention it here. If you really need this function and believe that + // there is no problem in it being `pub(crate)`, remove this comment. + pub(crate) fn resolve_all(self) -> InferenceResult { let InferenceContext { mut table, mut result, .. } = self; table.fallback_if_possible(); @@ -528,13 +547,46 @@ impl<'a> InferenceContext<'a> { for ty in result.type_of_pat.values_mut() { *ty = table.resolve_completely(ty.clone()); } - for ty in result.type_of_rpit.iter_mut().map(|x| x.1) { + for ty in result.type_of_binding.values_mut() { + *ty = table.resolve_completely(ty.clone()); + } + for ty in result.type_of_rpit.values_mut() { + *ty = table.resolve_completely(ty.clone()); + } + for ty in result.type_of_for_iterator.values_mut() { *ty = table.resolve_completely(ty.clone()); } for mismatch in result.type_mismatches.values_mut() { mismatch.expected = table.resolve_completely(mismatch.expected.clone()); mismatch.actual = table.resolve_completely(mismatch.actual.clone()); } + result.diagnostics.retain_mut(|diagnostic| { + if let InferenceDiagnostic::ExpectedFunction { found: ty, .. } + | InferenceDiagnostic::UnresolvedField { receiver: ty, .. } + | InferenceDiagnostic::UnresolvedMethodCall { receiver: ty, .. } = diagnostic + { + *ty = table.resolve_completely(ty.clone()); + // FIXME: Remove this when we are on par with rustc in terms of inference + if ty.contains_unknown() { + return false; + } + + if let InferenceDiagnostic::UnresolvedMethodCall { field_with_same_name, .. } = + diagnostic + { + let clear = if let Some(ty) = field_with_same_name { + *ty = table.resolve_completely(ty.clone()); + ty.contains_unknown() + } else { + false + }; + if clear { + *field_with_same_name = None; + } + } + } + true + }); for (_, subst) in result.method_resolutions.values_mut() { *subst = table.resolve_completely(subst.clone()); } @@ -580,7 +632,7 @@ impl<'a> InferenceContext<'a> { let ty = self.insert_type_vars(ty); let ty = self.normalize_associated_types_in(ty); - self.infer_pat(*pat, &ty, BindingMode::default()); + self.infer_top_pat(*pat, &ty); } let error_ty = &TypeRef::Error; let return_ty = if data.has_async_kw() { @@ -632,10 +684,19 @@ impl<'a> InferenceContext<'a> { }; self.return_ty = self.normalize_associated_types_in(return_ty); + self.return_coercion = Some(CoerceMany::new(self.return_ty.clone())); } fn infer_body(&mut self) { - self.infer_expr_coerce(self.body.body_expr, &Expectation::has_type(self.return_ty.clone())); + match self.return_coercion { + Some(_) => self.infer_return(self.body.body_expr), + None => { + _ = self.infer_expr_coerce( + self.body.body_expr, + &Expectation::has_type(self.return_ty.clone()), + ) + } + } } fn write_expr_ty(&mut self, expr: ExprId, ty: Ty) { @@ -662,12 +723,15 @@ impl<'a> InferenceContext<'a> { self.result.type_of_pat.insert(pat, ty); } + fn write_binding_ty(&mut self, id: BindingId, ty: Ty) { + self.result.type_of_binding.insert(id, ty); + } + fn push_diagnostic(&mut self, diagnostic: InferenceDiagnostic) { self.result.diagnostics.push(diagnostic); } fn make_ty(&mut self, type_ref: &TypeRef) -> Ty { - // FIXME use right resolver for block let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver); let ty = ctx.lower_ty(type_ref); let ty = self.insert_type_vars(ty); @@ -681,11 +745,9 @@ impl<'a> InferenceContext<'a> { /// Replaces ConstScalar::Unknown by a new type var, so we can maybe still infer it. fn insert_const_vars_shallow(&mut self, c: Const) -> Const { let data = c.data(Interner); - match data.value { + match &data.value { ConstValue::Concrete(cc) => match cc.interned { - hir_def::type_ref::ConstScalar::Unknown => { - self.table.new_const_var(data.ty.clone()) - } + crate::ConstScalar::Unknown => self.table.new_const_var(data.ty.clone()), _ => c, }, _ => c, @@ -785,12 +847,11 @@ impl<'a> InferenceContext<'a> { Some(path) => path, None => return (self.err_ty(), None), }; - let resolver = &self.resolver; let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver); // FIXME: this should resolve assoc items as well, see this example: // https://play.rust-lang.org/?gist=087992e9e22495446c01c0d4e2d69521 let (resolution, unresolved) = if value_ns { - match resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path()) { + match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path()) { Some(ResolveValueResult::ValueNs(value)) => match value { ValueNs::EnumVariantId(var) => { let substs = ctx.substs_from_path(path, var.into(), true); @@ -811,7 +872,7 @@ impl<'a> InferenceContext<'a> { None => return (self.err_ty(), None), } } else { - match resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) { + match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) { Some(it) => it, None => return (self.err_ty(), None), } @@ -866,7 +927,10 @@ impl<'a> InferenceContext<'a> { // FIXME potentially resolve assoc type (self.err_ty(), None) } - TypeNs::AdtId(AdtId::EnumId(_)) | TypeNs::BuiltinType(_) | TypeNs::TraitId(_) => { + TypeNs::AdtId(AdtId::EnumId(_)) + | TypeNs::BuiltinType(_) + | TypeNs::TraitId(_) + | TypeNs::TraitAliasId(_) => { // FIXME diagnostic (self.err_ty(), None) } @@ -1018,6 +1082,15 @@ impl<'a> InferenceContext<'a> { let struct_ = self.resolve_lang_item(LangItem::VaList)?.as_struct()?; Some(struct_.into()) } + + fn get_traits_in_scope(&self) -> Either<FxHashSet<TraitId>, &FxHashSet<TraitId>> { + let mut b_traits = self.resolver.traits_in_scope_from_block_scopes().peekable(); + if b_traits.peek().is_some() { + Either::Left(self.traits_in_scope.iter().copied().chain(b_traits).collect()) + } else { + Either::Right(&self.traits_in_scope) + } + } } /// When inferring an expression, we propagate downward whatever type hint we |