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 | 132 |
1 files changed, 84 insertions, 48 deletions
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 9acd5c0e0f..54f334b66d 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -34,11 +34,12 @@ use std::{cell::OnceCell, convert::identity, fmt, iter, ops::Deref}; use base_db::{Crate, FxIndexMap}; use either::Either; use hir_def::{ - AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, ExpressionStoreOwnerId, FieldId, - FunctionId, GenericDefId, GenericParamId, ItemContainerId, LocalFieldId, Lookup, TraitId, - TupleFieldId, TupleId, TypeAliasId, TypeOrConstParamId, VariantId, + AdtId, AssocItemId, AttrDefId, ConstId, ConstParamId, DefWithBodyId, ExpressionStoreOwnerId, + FieldId, FunctionId, GenericDefId, GenericParamId, HasModule, ItemContainerId, LocalFieldId, + Lookup, TraitId, TupleFieldId, TupleId, TypeAliasId, TypeOrConstParamId, VariantId, + attrs::AttrFlags, expr_store::{Body, ExpressionStore, HygieneId, RootExprOrigin, path::Path}, - hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId}, + hir::{BindingId, ExprId, ExprOrPatId, LabelId, PatId}, lang_item::LangItems, layout::Integer, resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs}, @@ -75,6 +76,7 @@ use crate::{ coerce::{CoerceMany, DynamicCoerceMany}, diagnostics::{Diagnostics, InferenceTyLoweringContext as TyLoweringContext}, expr::ExprIsRead, + pat::PatOrigin, }, lower::{ ImplTraitIdx, ImplTraitLoweringMode, LifetimeElisionKind, diagnostics::TyLoweringDiagnostic, @@ -283,25 +285,21 @@ fn infer_finalize(mut ctx: InferenceContext<'_, '_>) -> InferenceResult { ctx.resolve_all() } -/// Binding modes inferred for patterns. -/// <https://doc.rust-lang.org/reference/patterns.html#binding-modes> -#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)] -pub enum BindingMode { - #[default] - Move, - Ref(Mutability), -} -impl BindingMode { - fn convert(annotation: BindingAnnotation) -> BindingMode { - match annotation { - BindingAnnotation::Unannotated | BindingAnnotation::Mutable => BindingMode::Move, - BindingAnnotation::Ref => BindingMode::Ref(Mutability::Not), - BindingAnnotation::RefMut => BindingMode::Ref(Mutability::Mut), - } - } +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum ByRef { + Yes(Mutability), + No, } +/// The mode of a binding (`mut`, `ref mut`, etc). +/// Used for both the explicit binding annotations given in the HIR for a binding +/// and the final binding mode that we infer after type inference/match ergonomics. +/// `.0` is the by-reference mode (`ref`, `ref mut`, or by value), +/// `.1` is the mutability of the binding. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct BindingMode(pub ByRef, pub Mutability); + #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum InferenceTyDiagnosticSource { /// Diagnostics that come from types in the body. @@ -357,7 +355,7 @@ pub enum InferenceDiagnostic { found: usize, }, MismatchedTupleStructPatArgCount { - pat: ExprOrPatId, + pat: PatId, expected: usize, found: usize, }, @@ -576,6 +574,27 @@ pub enum PointerCast { Unsize, } +/// Represents an implicit coercion applied to the scrutinee of a match before testing a pattern +/// against it. Currently, this is used only for implicit dereferences. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PatAdjustment { + pub kind: PatAdjust, + /// The type of the scrutinee before the adjustment is applied, or the "adjusted type" of the + /// pattern. + pub source: StoredTy, +} + +/// Represents implicit coercions of patterns' types, rather than values' types. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum PatAdjust { + /// An implicit dereference before matching, such as when matching the pattern `0` against a + /// scrutinee of type `&u8` or `&mut u8`. + BuiltinDeref, + /// An implicit call to `Deref(Mut)::deref(_mut)` before matching, such as when matching the + /// pattern `[..]` against a scrutinee of type `Vec<T>`. + OverloadedDeref, +} + /// The result of type inference: A mapping from expressions and patterns to types. /// /// When you add a field that stores types (including `Substitution` and the like), don't forget @@ -621,7 +640,7 @@ pub struct InferenceResult { pub(crate) expr_adjustments: FxHashMap<ExprId, Box<[Adjustment]>>, /// Stores the types which were implicitly dereferenced in pattern binding modes. - pub(crate) pat_adjustments: FxHashMap<PatId, Vec<StoredTy>>, + pub(crate) pat_adjustments: FxHashMap<PatId, Vec<PatAdjustment>>, /// Stores the binding mode (`ref` in `let ref x = 2`) of bindings. /// /// This one is tied to the `PatId` instead of `BindingId`, because in some rare cases, a binding in an @@ -637,6 +656,10 @@ pub struct InferenceResult { /// the first `rest` has implicit `ref` binding mode, but the second `rest` binding mode is `move`. pub(crate) binding_modes: ArenaMap<PatId, BindingMode>, + /// Set of reference patterns that match against a match-ergonomics inserted reference + /// (as opposed to against a reference in the scrutinee type). + skipped_ref_pats: FxHashSet<PatId>, + pub(crate) coercion_casts: FxHashSet<ExprId>, pub closures_data: FxHashMap<ExprId, ClosureData>, @@ -909,6 +932,7 @@ impl InferenceResult { type_of_type_placeholder: Default::default(), type_of_opaque: Default::default(), type_mismatches: Default::default(), + skipped_ref_pats: Default::default(), has_errors: Default::default(), error_ty: error_ty.store(), pat_adjustments: Default::default(), @@ -1008,10 +1032,10 @@ impl InferenceResult { None => self.type_of_expr.get(id).map(|it| it.as_ref()), } } - pub fn type_of_pat_with_adjust<'db>(&self, id: PatId) -> Option<Ty<'db>> { + pub fn type_of_pat_with_adjust<'db>(&self, id: PatId) -> Ty<'db> { match self.pat_adjustments.get(&id).and_then(|adjustments| adjustments.last()) { - Some(adjusted) => Some(adjusted.as_ref()), - None => self.type_of_pat.get(id).map(|it| it.as_ref()), + Some(adjusted) => adjusted.source.as_ref(), + None => self.pat_ty(id), } } pub fn is_erroneous(&self) -> bool { @@ -1026,7 +1050,7 @@ impl InferenceResult { self.tuple_field_access_types[id.0 as usize].as_ref() } - pub fn pat_adjustment(&self, id: PatId) -> Option<&[StoredTy]> { + pub fn pat_adjustment(&self, id: PatId) -> Option<&[PatAdjustment]> { self.pat_adjustments.get(&id).map(|it| &**it) } @@ -1034,8 +1058,8 @@ impl InferenceResult { self.expr_adjustments.get(&id).map(|it| &**it) } - pub fn binding_mode(&self, id: PatId) -> Option<BindingMode> { - self.binding_modes.get(id).copied() + pub fn binding_mode(&self, id: PatId) -> BindingMode { + self.binding_modes[id] } // This method is consumed by external tools to run rust-analyzer as a library. Don't remove, please. @@ -1101,6 +1125,10 @@ impl InferenceResult { .values() .flat_map(|captures| captures.iter().map(|capture| capture.captured_ty(db))) } + + pub fn is_skipped_ref_pat(&self, pat: PatId) -> bool { + self.skipped_ref_pats.contains(&pat) + } } /// The inference context contains all information needed during type inference. @@ -1316,6 +1344,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { type_of_binding, type_of_type_placeholder, type_of_opaque, + skipped_ref_pats, type_mismatches, closures_data, has_errors, @@ -1328,6 +1357,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { diagnostics: _, } = &mut result; + skipped_ref_pats.shrink_to_fit(); for ty in type_of_expr.values_mut() { *ty = table.resolve_completely(ty.as_ref()).store(); *has_errors = *has_errors || ty.as_ref().references_non_lt_error(); @@ -1404,9 +1434,12 @@ impl<'body, 'db> InferenceContext<'body, 'db> { *has_errors = *has_errors || adjustment.target.as_ref().references_non_lt_error(); } expr_adjustments.shrink_to_fit(); - for adjustment in pat_adjustments.values_mut().flatten() { - *adjustment = table.resolve_completely(adjustment.as_ref()).store(); - *has_errors = *has_errors || adjustment.as_ref().references_non_lt_error(); + for adjustments in pat_adjustments.values_mut() { + for adjustment in &mut *adjustments { + adjustment.source = table.resolve_completely(adjustment.source.as_ref()).store(); + *has_errors = *has_errors || adjustment.source.as_ref().references_non_lt_error(); + } + adjustments.shrink_to_fit(); } pat_adjustments.shrink_to_fit(); for closure_data in closures_data.values_mut() { @@ -1515,7 +1548,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { for (ty, pat) in param_tys.zip(params) { let ty = self.process_user_written_ty(ty); - self.infer_top_pat(*pat, ty, None); + self.infer_top_pat(*pat, ty, PatOrigin::Param); } self.return_ty = match data.ret_type { Some(return_ty) => { @@ -1589,13 +1622,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> { } } - fn write_pat_adj(&mut self, pat: PatId, adjustments: Box<[StoredTy]>) { - if adjustments.is_empty() { - return; - } - self.result.pat_adjustments.entry(pat).or_default().extend(adjustments); - } - pub(crate) fn write_method_resolution( &mut self, expr: ExprId, @@ -1882,15 +1908,24 @@ impl<'body, 'db> InferenceContext<'body, 'db> { result.map_err(drop) } - fn demand_suptype(&mut self, expected: Ty<'db>, actual: Ty<'db>) { + fn demand_suptype( + &mut self, + id: ExprOrPatId, + expected: Ty<'db>, + actual: Ty<'db>, + ) -> Result<(), ()> { let result = self .table .at(&ObligationCause::new()) .sup(expected, actual) .map(|infer_ok| self.table.register_infer_ok(infer_ok)); - if let Err(_err) = result { - // FIXME: Emit diagnostic. + if result.is_err() { + self.result + .type_mismatches + .get_or_insert_default() + .insert(id, TypeMismatch { expected: expected.store(), actual: actual.store() }); } + result.map_err(drop) } fn demand_coerce( @@ -1901,7 +1936,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { allow_two_phase: AllowTwoPhase, expr_is_read: ExprIsRead, ) -> Ty<'db> { - let result = self.coerce(expr.into(), checked_ty, expected, allow_two_phase, expr_is_read); + let result = self.coerce(expr, checked_ty, expected, allow_two_phase, expr_is_read); if let Err(_err) = result { // FIXME: Emit diagnostic. } @@ -1966,13 +2001,9 @@ impl<'body, 'db> InferenceContext<'body, 'db> { fn resolve_variant( &mut self, node: ExprOrPatId, - path: Option<&Path>, + path: &Path, value_ns: bool, ) -> (Ty<'db>, Option<VariantId>) { - let path = match path { - Some(path) => path, - None => return (self.err_ty(), None), - }; let mut ctx = TyLoweringContext::new( self.db, &self.resolver, @@ -2373,6 +2404,11 @@ impl<'body, 'db> InferenceContext<'body, 'db> { Either::Right(&self.traits_in_scope) } } + + fn has_applicable_non_exhaustive(&self, def: AttrDefId) -> bool { + AttrFlags::query(self.db, def).contains(AttrFlags::NON_EXHAUSTIVE) + && def.krate(self.db) != self.krate() + } } /// When inferring an expression, we propagate downward whatever type hint we |