Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/infer/pat.rs')
| -rw-r--r-- | crates/hir-ty/src/infer/pat.rs | 2052 |
1 files changed, 1529 insertions, 523 deletions
diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs index a437253672..bba69c758a 100644 --- a/crates/hir-ty/src/infer/pat.rs +++ b/crates/hir-ty/src/infer/pat.rs @@ -1,645 +1,1516 @@ //! Type inference for patterns. -use std::{cmp, iter}; +use std::{ + cmp, + collections::hash_map::Entry::{Occupied, Vacant}, + iter, +}; use hir_def::{ - HasModule as _, - expr_store::{ExpressionStore, path::Path}, - hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, Literal, Pat, PatId}, + AdtId, LocalFieldId, VariantId, + expr_store::path::Path, + hir::{ + BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId, + RecordFieldPat, + }, + resolver::ValueNs, signatures::VariantFields, }; -use hir_expand::name::Name; use rustc_ast_ir::Mutability; -use rustc_type_ir::inherent::{GenericArg as _, GenericArgs as _, IntoKind, Ty as _}; -use stdx::TupleExt; +use rustc_hash::FxHashMap; +use rustc_type_ir::{ + TypeVisitableExt as _, + inherent::{AdtDef as _, IntoKind as _, Ty as _}, +}; +use span::Edition; +use tracing::{debug, instrument, trace}; use crate::{ - DeclContext, DeclOrigin, InferenceDiagnostic, - consteval::{self, try_const_usize, usize_const}, + BindingMode, InferenceDiagnostic, infer::{ - AllowTwoPhase, BindingMode, Expectation, InferenceContext, TypeMismatch, expr::ExprIsRead, + AllowTwoPhase, ByRef, Expectation, InferenceContext, PatAdjust, PatAdjustment, + TypeMismatch, expr::ExprIsRead, + }, + next_solver::{ + Const, TraitRef, Ty, TyKind, Tys, + infer::{ + InferOk, + traits::{Obligation, ObligationCause}, + }, }, - lower::lower_mutability, - next_solver::{GenericArgs, Ty, TyKind, Tys, infer::traits::ObligationCause}, + utils::EnumerateAndAdjustIterator, }; -impl<'db> InferenceContext<'_, 'db> { - /// Infers type for tuple struct pattern or its corresponding assignee expression. - /// - /// Ellipses found in the original pattern or expression must be filtered out. - pub(super) fn infer_tuple_struct_pat_like( - &mut self, - path: Option<&Path>, - expected: Ty<'db>, - default_bm: BindingMode, - id: PatId, - ellipsis: Option<u32>, - subs: &[PatId], - decl: Option<DeclContext>, - ) -> Ty<'db> { - let (ty, def) = self.resolve_variant(id.into(), path, true); - let var_data = def.map(|it| it.fields(self.db)); - if let Some(variant) = def { - self.write_variant_resolution(id.into(), variant); - } - if let Some(var) = &var_data { - let cmp = if ellipsis.is_some() { usize::gt } else { usize::ne }; - - if cmp(&subs.len(), &var.fields().len()) { - self.push_diagnostic(InferenceDiagnostic::MismatchedTupleStructPatArgCount { - pat: id.into(), - expected: var.fields().len(), - found: subs.len(), - }); - } +impl ByRef { + #[must_use] + fn cap_ref_mutability(mut self, mutbl: Mutability) -> Self { + if let ByRef::Yes(old_mutbl) = &mut self { + *old_mutbl = cmp::min(*old_mutbl, mutbl); } + self + } +} - self.unify(ty, expected); +impl BindingMode { + fn from_annotation(annotation: BindingAnnotation) -> BindingMode { + match annotation { + BindingAnnotation::Unannotated => BindingMode(ByRef::No, Mutability::Not), + BindingAnnotation::Mutable => BindingMode(ByRef::No, Mutability::Mut), + BindingAnnotation::Ref => BindingMode(ByRef::Yes(Mutability::Not), Mutability::Not), + BindingAnnotation::RefMut => BindingMode(ByRef::Yes(Mutability::Mut), Mutability::Not), + } + } +} - match def { - _ if subs.is_empty() => {} - Some(def) => { - let field_types = self.db.field_types(def); - let variant_data = def.fields(self.db); - let visibilities = VariantFields::field_visibilities(self.db, def); +#[derive(Clone, Copy, PartialEq, Eq)] +pub(super) enum PatOrigin { + LetExpr, + LetStmt { has_else: bool }, + Param, + MatchArm, + DestructuringAssignment, +} - let (pre, post) = match ellipsis { - Some(idx) => subs.split_at(idx as usize), - None => (subs, &[][..]), - }; - let post_idx_offset = field_types.iter().count().saturating_sub(post.len()); - - let pre_iter = pre.iter().enumerate(); - let post_iter = (post_idx_offset..).zip(post.iter()); - - let substs = ty.as_adt().map(TupleExt::tail); - - for (i, &subpat) in pre_iter.chain(post_iter) { - let expected_ty = { - match variant_data.field(&Name::new_tuple_field(i)) { - Some(local_id) => { - if !visibilities[local_id] - .is_visible_from(self.db, self.resolver.module()) - { - // FIXME(DIAGNOSE): private tuple field - } - let f = field_types[local_id].get(); - let expected_ty = match substs { - Some(substs) => f.instantiate(self.interner(), substs), - None => f.instantiate(self.interner(), &[]), - }; - self.process_remote_user_written_ty(expected_ty) - } - None => self.err_ty(), - } - }; +impl PatOrigin { + fn default_binding_modes(self) -> bool { + self != PatOrigin::DestructuringAssignment + } +} - self.infer_pat(subpat, expected_ty, default_bm, decl); - } - } - None => { - let err_ty = self.err_ty(); - for &inner in subs { - self.infer_pat(inner, err_ty, default_bm, decl); - } - } +#[derive(Copy, Clone)] +struct PatInfo { + binding_mode: ByRef, + max_ref_mutbl: MutblCap, + pat_origin: PatOrigin, +} + +/// Mode for adjusting the expected type and binding mode. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum AdjustMode { + /// Peel off all immediate reference types. If the `deref_patterns` feature is enabled, this + /// also peels smart pointer ADTs. + Peel { kind: PeelKind }, + /// Pass on the input binding mode and expected type. + Pass, +} + +/// Restrictions on what types to peel when adjusting the expected type and binding mode. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum PeelKind { + /// Only peel reference types. This is used for explicit `deref!(_)` patterns, which dereference + /// any number of `&`/`&mut` references, plus a single smart pointer. + ExplicitDerefPat, + /// Implicitly peel references, and if `deref_patterns` is enabled, smart pointer ADTs. + Implicit { + /// The ADT the pattern is a constructor for, if applicable, so that we don't peel it. See + /// [`ResolvedPat`] for more information. + until_adt: Option<AdtId>, + /// The number of references at the head of the pattern's type, so we can leave that many + /// untouched. This is `1` for string literals, and `0` for most patterns. + pat_ref_layers: usize, + }, +} + +impl AdjustMode { + const fn peel_until_adt(opt_adt_def: Option<AdtId>) -> AdjustMode { + AdjustMode::Peel { kind: PeelKind::Implicit { until_adt: opt_adt_def, pat_ref_layers: 0 } } + } + const fn peel_all() -> AdjustMode { + AdjustMode::peel_until_adt(None) + } +} + +/// `ref mut` bindings (explicit or match-ergonomics) are not allowed behind an `&` reference. +/// Normally, the borrow checker enforces this, but for (currently experimental) match ergonomics, +/// we track this when typing patterns for two purposes: +/// +/// - For RFC 3627's Rule 3, when this would prevent us from binding with `ref mut`, we limit the +/// default binding mode to be by shared `ref` when it would otherwise be `ref mut`. +/// +/// - For RFC 3627's Rule 5, we allow `&` patterns to match against `&mut` references, treating them +/// as if they were shared references. Since the scrutinee is mutable in this case, the borrow +/// checker won't catch if we bind with `ref mut`, so we need to throw an error ourselves. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum MutblCap { + /// Mutability restricted to immutable. + Not, + + /// Mutability restricted to immutable, but only because of the pattern + /// (not the scrutinee type). + /// + /// The contained span, if present, points to an `&` pattern + /// that is the reason for the restriction, + /// and which will be reported in a diagnostic. + WeaklyNot, + + /// No restriction on mutability + Mut, +} + +impl MutblCap { + #[must_use] + fn cap_to_weakly_not(self) -> Self { + match self { + MutblCap::Not => MutblCap::Not, + _ => MutblCap::WeaklyNot, } + } - ty + #[must_use] + fn as_mutbl(self) -> Mutability { + match self { + MutblCap::Not | MutblCap::WeaklyNot => Mutability::Not, + MutblCap::Mut => Mutability::Mut, + } } +} - /// Infers type for record pattern or its corresponding assignee expression. - pub(super) fn infer_record_pat_like( - &mut self, - path: Option<&Path>, - expected: Ty<'db>, - default_bm: BindingMode, - id: PatId, - subs: impl ExactSizeIterator<Item = (Name, PatId)>, - decl: Option<DeclContext>, - ) -> Ty<'db> { - let (ty, def) = self.resolve_variant(id.into(), path, false); - if let Some(variant) = def { - self.write_variant_resolution(id.into(), variant); - } - - self.unify(ty, expected); - - match def { - _ if subs.len() == 0 => {} - Some(def) => { - let field_types = self.db.field_types(def); - let variant_data = def.fields(self.db); - let visibilities = VariantFields::field_visibilities(self.db, def); - - let substs = ty.as_adt().map(TupleExt::tail); - - for (name, inner) in subs { - let expected_ty = { - match variant_data.field(&name) { - Some(local_id) => { - if !visibilities[local_id] - .is_visible_from(self.db, self.resolver.module()) - { - self.push_diagnostic(InferenceDiagnostic::NoSuchField { - field: inner.into(), - private: Some(local_id), - variant: def, - }); - } - let f = field_types[local_id].get(); - let expected_ty = match substs { - Some(substs) => f.instantiate(self.interner(), substs), - None => f.instantiate(self.interner(), &[]), - }; - self.process_remote_user_written_ty(expected_ty) - } - None => { - self.push_diagnostic(InferenceDiagnostic::NoSuchField { - field: inner.into(), - private: None, - variant: def, - }); - self.err_ty() - } - } - }; +/// Variations on RFC 3627's Rule 4: when do reference patterns match against inherited references? +/// +/// "Inherited reference" designates the `&`/`&mut` types that arise from using match ergonomics, i.e. +/// from matching a reference type with a non-reference pattern. E.g. when `Some(x)` matches on +/// `&mut Option<&T>`, `x` gets type `&mut &T` and the outer `&mut` is considered "inherited". +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum InheritedRefMatchRule { + /// Reference patterns consume only the inherited reference if possible, regardless of whether + /// the underlying type being matched against is a reference type. If there is no inherited + /// reference, a reference will be consumed from the underlying type. + EatOuter, + /// Reference patterns consume only a reference from the underlying type if possible. If the + /// underlying type is not a reference type, the inherited reference will be consumed. + EatInner, + /// When the underlying type is a reference type, reference patterns consume both layers of + /// reference, i.e. they both reset the binding mode and consume the reference type. + EatBoth { + /// If `true`, an inherited reference will be considered when determining whether a reference + /// pattern matches a given type: + /// - If the underlying type is not a reference, a reference pattern may eat the inherited reference; + /// - If the underlying type is a reference, a reference pattern matches if it can eat either one + /// of the underlying and inherited references. E.g. a `&mut` pattern is allowed if either the + /// underlying type is `&mut` or the inherited reference is `&mut`. + /// + /// If `false`, a reference pattern is only matched against the underlying type. + /// This is `false` for stable Rust and `true` for both the `ref_pat_eat_one_layer_2024` and + /// `ref_pat_eat_one_layer_2024_structural` feature gates. + consider_inherited_ref: bool, + }, +} - self.infer_pat(inner, expected_ty, default_bm, decl); - } +/// When checking patterns containing paths, we need to know the path's resolution to determine +/// whether to apply match ergonomics and implicitly dereference the scrutinee. For instance, when +/// the `deref_patterns` feature is enabled and we're matching against a scrutinee of type +/// `Cow<'a, Option<u8>>`, we insert an implicit dereference to allow the pattern `Some(_)` to type, +/// but we must not dereference it when checking the pattern `Cow::Borrowed(_)`. +/// +/// `ResolvedPat` contains the information from resolution needed to determine match ergonomics +/// adjustments, and to finish checking the pattern once we know its adjusted type. +#[derive(Clone, Copy, Debug)] +struct ResolvedPat<'db> { + /// The type of the pattern, to be checked against the type of the scrutinee after peeling. This + /// is also used to avoid peeling the scrutinee's constructors (see the `Cow` example above). + ty: Ty<'db>, + kind: ResolvedPatKind, +} + +#[derive(Clone, Copy, Debug)] +enum ResolvedPatKind { + Path { res: ValueNs }, + Struct { variant: VariantId }, + TupleStruct { variant: VariantId }, +} + +impl<'db> ResolvedPat<'db> { + fn adjust_mode(&self) -> AdjustMode { + if let ResolvedPatKind::Path { res, .. } = self.kind + && matches!(res, ValueNs::ConstId(_)) + { + // These constants can be of a reference type, e.g. `const X: &u8 = &0;`. + // Peeling the reference types too early will cause type checking failures. + // Although it would be possible to *also* peel the types of the constants too. + AdjustMode::Pass + } else { + // The remaining possible resolutions for path, struct, and tuple struct patterns are + // ADT constructors. As such, we may peel references freely, but we must not peel the + // ADT itself from the scrutinee if it's a smart pointer. + AdjustMode::peel_until_adt(self.ty.as_adt().map(|(adt, _)| adt)) + } + } +} + +impl<'a, 'db> InferenceContext<'a, 'db> { + /// Experimental pattern feature: after matching against a shared reference, do we limit the + /// default binding mode in subpatterns to be `ref` when it would otherwise be `ref mut`? + /// This corresponds to Rule 3 of RFC 3627. + fn downgrade_mut_inside_shared(&self) -> bool { + // NB: RFC 3627 proposes stabilizing Rule 3 in all editions. If we adopt the same behavior + // across all editions, this may be removed. + self.features.ref_pat_eat_one_layer_2024_structural + } + + /// Experimental pattern feature: when do reference patterns match against inherited references? + /// This corresponds to variations on Rule 4 of RFC 3627. + fn ref_pat_matches_inherited_ref(&self, edition: Edition) -> InheritedRefMatchRule { + // NB: The particular rule used here is likely to differ across editions, so calls to this + // may need to become edition checks after match ergonomics stabilize. + if edition.at_least_2024() { + if self.features.ref_pat_eat_one_layer_2024 { + InheritedRefMatchRule::EatOuter + } else if self.features.ref_pat_eat_one_layer_2024_structural { + InheritedRefMatchRule::EatInner + } else { + // Currently, matching against an inherited ref on edition 2024 is an error. + // Use `EatBoth` as a fallback to be similar to stable Rust. + InheritedRefMatchRule::EatBoth { consider_inherited_ref: false } } - None => { - let err_ty = self.err_ty(); - for (_, inner) in subs { - self.infer_pat(inner, err_ty, default_bm, decl); - } + } else { + InheritedRefMatchRule::EatBoth { + consider_inherited_ref: self.features.ref_pat_eat_one_layer_2024 + || self.features.ref_pat_eat_one_layer_2024_structural, } } + } - ty + /// Experimental pattern feature: do `&` patterns match against `&mut` references, treating them + /// as if they were shared references? This corresponds to Rule 5 of RFC 3627. + fn ref_pat_matches_mut_ref(&self) -> bool { + // NB: RFC 3627 proposes stabilizing Rule 5 in all editions. If we adopt the same behavior + // across all editions, this may be removed. + self.features.ref_pat_eat_one_layer_2024 + || self.features.ref_pat_eat_one_layer_2024_structural } - /// Infers type for tuple pattern or its corresponding assignee expression. + /// Type check the given top level pattern against the `expected` type. + /// + /// If a `Some(span)` is provided and `origin_expr` holds, + /// then the `span` represents the scrutinee's span. + /// The scrutinee is found in e.g. `match scrutinee { ... }` and `let pat = scrutinee;`. /// - /// Ellipses found in the original pattern or expression must be filtered out. - pub(super) fn infer_tuple_pat_like( + /// Otherwise, `Some(span)` represents the span of a type expression + /// which originated the `expected` type. + pub(super) fn infer_top_pat(&mut self, pat: PatId, expected: Ty<'db>, pat_origin: PatOrigin) { + let pat_info = + PatInfo { binding_mode: ByRef::No, max_ref_mutbl: MutblCap::Mut, pat_origin }; + self.infer_pat(pat, expected, pat_info); + } + + /// Type check the given `pat` against the `expected` type + /// with the provided `binding_mode` (default binding mode). + /// + /// Outside of this module, `check_pat_top` should always be used. + /// Conversely, inside this module, `check_pat_top` should never be used. + #[instrument(level = "debug", skip(self, pat_info))] + fn infer_pat(&mut self, pat_id: PatId, expected: Ty<'db>, pat_info: PatInfo) { + // For patterns containing paths, we need the path's resolution to determine whether to + // implicitly dereference the scrutinee before matching. + let pat = &self.store[pat_id]; + let opt_path_res = match pat { + Pat::Path(path) => Some(self.resolve_pat_path(pat_id, path)), + Pat::Record { path, .. } => Some(self.resolve_record_pat(pat_id, path)), + Pat::TupleStruct { path, .. } => Some(self.resolve_tuple_struct_pat(pat_id, path)), + _ => None, + }; + let adjust_mode = self.calc_adjust_mode(pat, opt_path_res); + let ty = self.infer_pat_inner(pat_id, opt_path_res, adjust_mode, expected, pat_info); + self.write_pat_ty(pat_id, ty); + + // If we implicitly inserted overloaded dereferences before matching check the pattern to + // see if the dereferenced types need `DerefMut` bounds. + if let Some(derefed_tys) = self.result.pat_adjustment(pat_id) + && derefed_tys.iter().any(|adjust| adjust.kind == PatAdjust::OverloadedDeref) + { + let infer_ok = self.register_deref_mut_bounds_if_needed( + pat_id, + derefed_tys.iter().filter_map(|adjust| match adjust.kind { + PatAdjust::OverloadedDeref => Some(adjust.source.as_ref()), + PatAdjust::BuiltinDeref => None, + }), + ); + self.table.register_infer_ok(infer_ok); + } + + // (note_1): In most of the cases where (note_1) is referenced + // (literals and constants being the exception), we relate types + // using strict equality, even though subtyping would be sufficient. + // There are a few reasons for this, some of which are fairly subtle + // and which cost me (nmatsakis) an hour or two debugging to remember, + // so I thought I'd write them down this time. + // + // 1. There is no loss of expressiveness here, though it does + // cause some inconvenience. What we are saying is that the type + // of `x` becomes *exactly* what is expected. This can cause unnecessary + // errors in some cases, such as this one: + // + // ``` + // fn foo<'x>(x: &'x i32) { + // let a = 1; + // let mut z = x; + // z = &a; + // } + // ``` + // + // The reason we might get an error is that `z` might be + // assigned a type like `&'x i32`, and then we would have + // a problem when we try to assign `&a` to `z`, because + // the lifetime of `&a` (i.e., the enclosing block) is + // shorter than `'x`. + // + // HOWEVER, this code works fine. The reason is that the + // expected type here is whatever type the user wrote, not + // the initializer's type. In this case the user wrote + // nothing, so we are going to create a type variable `Z`. + // Then we will assign the type of the initializer (`&'x i32`) + // as a subtype of `Z`: `&'x i32 <: Z`. And hence we + // will instantiate `Z` as a type `&'0 i32` where `'0` is + // a fresh region variable, with the constraint that `'x : '0`. + // So basically we're all set. + // + // Note that there are two tests to check that this remains true + // (`regions-reassign-{match,let}-bound-pointer.rs`). + // + // 2. An outdated issue related to the old HIR borrowck. See the test + // `regions-relate-bound-regions-on-closures-to-inference-variables.rs`, + } + + // Helper to avoid resolving the same path pattern several times. + fn infer_pat_inner( &mut self, pat: PatId, + opt_path_res: Option<Result<ResolvedPat<'db>, ()>>, + adjust_mode: AdjustMode, expected: Ty<'db>, - default_bm: BindingMode, - ellipsis: Option<u32>, - elements: &[PatId], - decl: Option<DeclContext>, + pat_info: PatInfo, ) -> Ty<'db> { - let mut expected_len = elements.len(); - if ellipsis.is_some() { - // Require known type only when `..` is present. - if let TyKind::Tuple(tys) = self.table.structurally_resolve_type(expected).kind() { - expected_len = tys.len(); - } + #[cfg(debug_assertions)] + if matches!(pat_info.binding_mode, ByRef::Yes(Mutability::Mut)) + && pat_info.max_ref_mutbl != MutblCap::Mut + && self.downgrade_mut_inside_shared() + { + panic!("Pattern mutability cap violated!"); } - let max_len = cmp::max(expected_len, elements.len()); - let element_tys_iter = (0..max_len).map(|_| self.table.next_ty_var()); - let element_tys = Tys::new_from_iter(self.interner(), element_tys_iter); - let pat_ty = Ty::new(self.interner(), TyKind::Tuple(element_tys)); - if self.demand_eqtype(pat.into(), expected, pat_ty).is_err() - && let TyKind::Tuple(expected) = expected.kind() + // Resolve type if needed. + let expected = if let AdjustMode::Peel { .. } = adjust_mode + && pat_info.pat_origin.default_binding_modes() { - // Equate expected type with the infer vars, for better diagnostics. - for (expected, elem_ty) in iter::zip(expected, element_tys) { - _ = self - .table - .at(&ObligationCause::dummy()) - .eq(expected, elem_ty) - .map(|infer_ok| self.table.register_infer_ok(infer_ok)); - } - } - let (before_ellipsis, after_ellipsis) = match ellipsis { - Some(ellipsis) => { - let element_tys = element_tys.as_slice(); - // Don't check patterns twice. - let from_end_start = cmp::max( - element_tys.len().saturating_sub(elements.len() - ellipsis as usize), - ellipsis as usize, - ); - ( - element_tys.get(..ellipsis as usize).unwrap_or(element_tys), - element_tys.get(from_end_start..).unwrap_or_default(), + self.table.try_structurally_resolve_type(expected) + } else { + expected + }; + + match self.store[pat] { + // Peel off a `&` or `&mut`from the scrutinee type. See the examples in + // `tests/ui/rfcs/rfc-2005-default-binding-mode`. + _ if let AdjustMode::Peel { kind: peel_kind } = adjust_mode + && pat_info.pat_origin.default_binding_modes() + && let TyKind::Ref(_, inner_ty, inner_mutability) = expected.kind() + && self.should_peel_ref(peel_kind, expected) => + { + debug!("inspecting {:?}", expected); + + debug!("current discriminant is Ref, inserting implicit deref"); + // Preserve the reference type. We'll need it later during THIR lowering. + self.result.pat_adjustments.entry(pat).or_default().push(PatAdjustment { + kind: PatAdjust::BuiltinDeref, + source: expected.store(), + }); + + // Use the old pat info to keep `current_depth` to its old value. + let new_pat_info = self.adjust_pat_info(inner_mutability, pat_info); + + // Recurse with the new expected type. + self.infer_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, new_pat_info) + } + // If `deref_patterns` is enabled, peel a smart pointer from the scrutinee type. See the + // examples in `tests/ui/pattern/deref_patterns/`. + _ if self.features.deref_patterns + && let AdjustMode::Peel { kind: peel_kind } = adjust_mode + && pat_info.pat_origin.default_binding_modes() + && self.should_peel_smart_pointer(peel_kind, expected) => + { + debug!("scrutinee ty {expected:?} is a smart pointer, inserting pin deref"); + + // The scrutinee is a smart pointer; implicitly dereference it. This adds a + // requirement that `expected: DerefPure`. + let inner_ty = self.deref_pat_target(expected); + // Once we've checked `pat`, we'll add a `DerefMut` bound if it contains any + // `ref mut` bindings. See `Self::register_deref_mut_bounds_if_needed`. + + self.check_deref_pattern( + pat, + opt_path_res, + adjust_mode, + expected, + inner_ty, + PatAdjust::OverloadedDeref, + pat_info, ) } - None => (element_tys.as_slice(), &[][..]), - }; - for (&elem, &elem_ty) in iter::zip(elements, before_ellipsis.iter().chain(after_ellipsis)) { - self.infer_pat(elem, elem_ty, default_bm, decl); - } - if let Some(uncovered) = elements.get(element_tys.len()..) { - for &elem in uncovered { - self.infer_pat(elem, self.types.types.error, default_bm, decl); + Pat::Missing | Pat::Wild => expected, + // We allow any type here; we ensure that the type is uninhabited during match checking. + // Pat::Never => expected, + Pat::Path(_) => { + let ty = match opt_path_res.unwrap() { + Ok(ref pr) => self.infer_pat_path(pat, pr, expected), + Err(()) => self.types.types.error, + }; + self.write_pat_ty(pat, ty); + ty + } + Pat::Lit(expr) => self.infer_lit_pat(expr, expected), + Pat::Range { start: lhs, end: rhs, .. } => self.infer_range_pat(lhs, rhs, expected), + Pat::Bind { id: var_id, subpat } => { + self.infer_bind_pat(pat, var_id, subpat, expected, pat_info) + } + Pat::TupleStruct { args: ref subpats, ellipsis: ddpos, .. } => match opt_path_res + .unwrap() + { + Ok(ResolvedPat { ty, kind: ResolvedPatKind::TupleStruct { variant } }) => self + .infer_tuple_struct_pat(pat, subpats, ddpos, ty, variant, expected, pat_info), + Err(()) => { + let ty_err = self.types.types.error; + for &subpat in subpats { + self.infer_pat(subpat, ty_err, pat_info); + } + ty_err + } + Ok(pr) => panic!("tuple struct pattern resolved to {pr:?}"), + }, + Pat::Record { args: ref fields, ellipsis: has_rest_pat, .. } => { + match opt_path_res.unwrap() { + Ok(ResolvedPat { ty, kind: ResolvedPatKind::Struct { variant } }) => self + .infer_record_pat( + pat, + fields, + has_rest_pat, + ty, + variant, + expected, + pat_info, + ), + Err(()) => { + let ty_err = self.types.types.error; + for field in fields { + self.infer_pat(field.pat, ty_err, pat_info); + } + ty_err + } + Ok(pr) => panic!("struct pattern resolved to {pr:?}"), + } + } + // Pat::Guard(pat, cond) => { + // self.infer_pat(pat, expected, pat_info); + // self.check_expr_has_type_or_error(cond, self.tcx.types.bool, |_| {}); + // expected + // } + Pat::Or(ref pats) => { + for &pat in pats { + self.infer_pat(pat, expected, pat_info); + } + expected + } + Pat::Tuple { args: ref elements, ellipsis: ddpos } => { + self.infer_tuple_pat(pat, elements, ddpos, expected, pat_info) + } + Pat::Box { inner } => self.infer_box_pat(pat, inner, expected, pat_info), + // Pat::Deref(inner) => self.infer_deref_pat(pat.span, inner, expected, pat_info), + Pat::Ref { pat: inner, mutability: mutbl } => self.infer_ref_pat( + pat, + inner, + if mutbl.is_mut() { Mutability::Mut } else { Mutability::Not }, + expected, + pat_info, + ), + Pat::Slice { prefix: ref before, slice, suffix: ref after } => { + self.infer_slice_pat(pat, before, slice, after, expected, pat_info) + } + Pat::Expr(expr) => self.infer_destructuring_assignment_expr(expr, expected), + Pat::ConstBlock(expr) => { + self.infer_expr(expr, &Expectation::has_type(expected), ExprIsRead::Yes) } } - pat_ty } - pub(super) fn infer_top_pat( - &mut self, - pat: PatId, - expected: Ty<'db>, - decl: Option<DeclContext>, - ) { - self.infer_pat(pat, expected, BindingMode::default(), decl); + fn adjust_pat_info(&self, inner_mutability: Mutability, pat_info: PatInfo) -> PatInfo { + let mut binding_mode = match pat_info.binding_mode { + // If default binding mode is by value, make it `ref`, `ref mut`, `ref pin const` + // or `ref pin mut` (depending on whether we observe `&`, `&mut`, `&pin const` or + // `&pin mut`). + ByRef::No => ByRef::Yes(inner_mutability), + ByRef::Yes(mutability) => { + let mutability = match mutability { + // When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`). + Mutability::Mut => inner_mutability, + // Once a `ref`, always a `ref`. + // This is because a `& &mut` cannot mutate the underlying value. + Mutability::Not => Mutability::Not, + }; + ByRef::Yes(mutability) + } + }; + + let PatInfo { mut max_ref_mutbl, .. } = pat_info; + if self.downgrade_mut_inside_shared() { + binding_mode = binding_mode.cap_ref_mutability(max_ref_mutbl.as_mutbl()); + } + match binding_mode { + ByRef::Yes(Mutability::Not) => max_ref_mutbl = MutblCap::Not, + _ => {} + } + debug!("default binding mode is now {:?}", binding_mode); + PatInfo { binding_mode, max_ref_mutbl, ..pat_info } } - fn infer_pat( + fn check_deref_pattern( &mut self, pat: PatId, + opt_path_res: Option<Result<ResolvedPat<'db>, ()>>, + adjust_mode: AdjustMode, expected: Ty<'db>, - mut default_bm: BindingMode, - decl: Option<DeclContext>, + mut inner_ty: Ty<'db>, + pat_adjust_kind: PatAdjust, + pat_info: PatInfo, ) -> Ty<'db> { - let mut expected = self.table.structurally_resolve_type(expected); - - if matches!(&self.store[pat], Pat::Ref { .. }) || self.inside_assignment { - cov_mark::hit!(match_ergonomics_ref); - // When you encounter a `&pat` pattern, reset to Move. - // This is so that `w` is by value: `let (_, &w) = &(1, &2);` - // Destructuring assignments also reset the binding mode and - // don't do match ergonomics. - default_bm = BindingMode::Move; - } else if self.is_non_ref_pat(self.store, pat) { - let mut pat_adjustments = Vec::new(); - while let TyKind::Ref(_lifetime, inner, mutability) = expected.kind() { - pat_adjustments.push(expected.store()); - expected = self.table.try_structurally_resolve_type(inner); - default_bm = match default_bm { - BindingMode::Move => BindingMode::Ref(mutability), - BindingMode::Ref(Mutability::Not) => BindingMode::Ref(Mutability::Not), - BindingMode::Ref(Mutability::Mut) => BindingMode::Ref(mutability), - } - } + debug_assert!( + !matches!(pat_adjust_kind, PatAdjust::BuiltinDeref), + "unexpected deref pattern for builtin reference type {expected:?}", + ); - if !pat_adjustments.is_empty() { - pat_adjustments.shrink_to_fit(); - self.result.pat_adjustments.insert(pat, pat_adjustments); - } + let pat_adjustments = self.result.pat_adjustments.entry(pat).or_default(); + // We may reach the recursion limit if a user matches on a type `T` satisfying + // `T: Deref<Target = T>`; error gracefully in this case. + // FIXME(deref_patterns): If `deref_patterns` stabilizes, it may make sense to move + // this check out of this branch. Alternatively, this loop could be implemented with + // autoderef and this check removed. For now though, don't break code compiling on + // stable with lots of `&`s and a low recursion limit, if anyone's done that. + if pat_adjustments.len() < self.resolver.top_level_def_map().recursion_limit() as usize { + // Preserve the smart pointer type for THIR lowering and closure upvar analysis. + pat_adjustments.push(PatAdjustment { kind: pat_adjust_kind, source: expected.store() }); + } else { + // FIXME: Emit an error. + inner_ty = self.types.types.error; } - // Lose mutability. - let default_bm = default_bm; - let expected = expected; + // Recurse, using the old pat info to keep `current_depth` to its old value. + // Peeling smart pointers does not update the default binding mode. + self.infer_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, pat_info) + } - let ty = match &self.store[pat] { - Pat::Tuple { args, ellipsis } => { - self.infer_tuple_pat_like(pat, expected, default_bm, *ellipsis, args, decl) - } - Pat::Or(pats) => { - for pat in pats.iter() { - self.infer_pat(*pat, expected, default_bm, decl); - } - expected + /// How should the binding mode and expected type be adjusted? + /// + /// When the pattern contains a path, `opt_path_res` must be `Some(path_res)`. + fn calc_adjust_mode( + &mut self, + pat: &Pat, + opt_path_res: Option<Result<ResolvedPat<'db>, ()>>, + ) -> AdjustMode { + match pat { + // Type checking these product-like types successfully always require + // that the expected type be of those types and not reference types. + Pat::Tuple { .. } | Pat::Range { .. } | Pat::Slice { .. } => AdjustMode::peel_all(), + // When checking an explicit deref pattern, only peel reference types. + // FIXME(deref_patterns): If box patterns and deref patterns need to coexist, box + // patterns may want `PeelKind::Implicit`, stopping on encountering a box. + Pat::Box { .. } /* | Pat::Deref(_) */ => { + AdjustMode::Peel { kind: PeelKind::ExplicitDerefPat } } - &Pat::Ref { pat, mutability } => { - self.infer_ref_pat(pat, lower_mutability(mutability), expected, default_bm, decl) + // A never pattern behaves somewhat like a literal or unit variant. + // Pat::Never => AdjustMode::peel_all(), + // For patterns with paths, how we peel the scrutinee depends on the path's resolution. + Pat::Record { .. } + | Pat::TupleStruct { .. } + | Pat::Path(_) => { + // If there was an error resolving the path, default to peeling everything. + opt_path_res.unwrap().map_or(AdjustMode::peel_all(), |pr| pr.adjust_mode()) } - Pat::TupleStruct { path: p, args: subpats, ellipsis } => self - .infer_tuple_struct_pat_like( - p.as_deref(), - expected, - default_bm, - pat, - *ellipsis, - subpats, - decl, - ), - Pat::Record { path: p, args: fields, ellipsis: _ } => { - let subs = fields.iter().map(|f| (f.name.clone(), f.pat)); - self.infer_record_pat_like(p.as_deref(), expected, default_bm, pat, subs, decl) - } - Pat::Path(path) => { - let ty = self.infer_path(path, pat.into()).unwrap_or_else(|| self.err_ty()); - let ty_inserted_vars = self.insert_type_vars_shallow(ty); - match self.coerce( - pat.into(), - expected, - ty_inserted_vars, - AllowTwoPhase::No, - ExprIsRead::No, - ) { - Ok(coerced_ty) => { - self.write_pat_ty(pat, coerced_ty); - return self.pat_ty_after_adjustment(pat); + + // String and byte-string literals result in types `&str` and `&[u8]` respectively. + // All other literals result in non-reference types. + // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo" {}` unless + // `deref_patterns` is enabled. + &Pat::Lit(expr) | &Pat::ConstBlock(expr) => { + let lit_ty = self.infer_expr_pat_unadjusted(expr); + // Call `resolve_vars_if_possible` here for inline const blocks. + let lit_ty = self.infcx().resolve_vars_if_possible(lit_ty); + // If `deref_patterns` is enabled, allow `if let "foo" = &&"foo" {}`. + if self.features.deref_patterns { + let mut peeled_ty = lit_ty; + let mut pat_ref_layers = 0; + while let TyKind::Ref(_, inner_ty, mutbl) = + self.table.try_structurally_resolve_type(peeled_ty).kind() + { + // We rely on references at the head of constants being immutable. + debug_assert!(mutbl.is_not()); + pat_ref_layers += 1; + peeled_ty = inner_ty; } - Err(_) => { - self.result.type_mismatches.get_or_insert_default().insert( - pat.into(), - TypeMismatch { - expected: expected.store(), - actual: ty_inserted_vars.store(), - }, - ); - self.write_pat_ty(pat, ty); - // We return `expected` to prevent cascading errors. I guess an alternative is to - // not emit type mismatches for error types and emit an error type here. - return expected; + AdjustMode::Peel { + kind: PeelKind::Implicit { until_adt: None, pat_ref_layers }, } + } else { + if lit_ty.is_ref() { AdjustMode::Pass } else { AdjustMode::peel_all() } } } - Pat::Bind { id, subpat } => { - return self.infer_bind_pat(pat, *id, default_bm, *subpat, expected, decl); - } - Pat::Slice { prefix, slice, suffix } => { - self.infer_slice_pat(expected, prefix, *slice, suffix, default_bm, decl) + + // Ref patterns are complicated, we handle them in `check_pat_ref`. + Pat::Ref { .. } + // No need to do anything on a missing pattern. + | Pat::Missing + // A `_` pattern works with any expected type, so there's no need to do anything. + | Pat::Wild + // Bindings also work with whatever the expected type is, + // and moreover if we peel references off, that will give us the wrong binding type. + // Also, we can have a subpattern `binding @ pat`. + // Each side of the `@` should be treated independently (like with OR-patterns). + | Pat::Bind { .. } + // `Pat::Expr(_)` inside assignments becomes a binding in rustc, therefore should be + // the same as `Pat::Bind`. + | Pat::Expr(_) + // An OR-pattern just propagates to each individual alternative. + // This is maximally flexible, allowing e.g., `Some(mut x) | &Some(mut x)`. + // In that example, `Some(mut x)` results in `Peel` whereas `&Some(mut x)` in `Reset`. + | Pat::Or(_) + // Like or-patterns, guard patterns just propagate to their subpatterns. + /* | Pat::Guard(..) */ => AdjustMode::Pass, + } + } + + /// Assuming `expected` is a reference type, determine whether to peel it before matching. + fn should_peel_ref(&self, peel_kind: PeelKind, mut expected: Ty<'db>) -> bool { + debug_assert!(expected.is_ref()); + let pat_ref_layers = match peel_kind { + PeelKind::ExplicitDerefPat => 0, + PeelKind::Implicit { pat_ref_layers, .. } => pat_ref_layers, + }; + + // Most patterns don't have reference types, so we'll want to peel all references from the + // scrutinee before matching. To optimize for the common case, return early. + if pat_ref_layers == 0 { + return true; + } + debug_assert!( + self.features.deref_patterns, + "Peeling for patterns with reference types is gated by `deref_patterns`." + ); + + // If the pattern has as many or more layers of reference as the expected type, we can match + // without peeling more, unless we find a smart pointer or `&mut` that we also need to peel. + // We don't treat `&` and `&mut` as interchangeable, but by peeling `&mut`s before matching, + // we can still, e.g., match on a `&mut str` with a string literal pattern. This is because + // string literal patterns may be used where `str` is expected. + let mut expected_ref_layers = 0; + while let TyKind::Ref(_, inner_ty, mutbl) = expected.kind() { + if mutbl.is_mut() { + // Mutable references can't be in the final value of constants, thus they can't be + // at the head of their types, thus we should always peel `&mut`. + return true; } - Pat::Wild => expected, - Pat::Range { start, end, range_type: _ } => { - if let Some(start) = *start { - let start_ty = self.infer_expr(start, &Expectation::None, ExprIsRead::Yes); - _ = self.demand_eqtype(start.into(), expected, start_ty); + expected_ref_layers += 1; + expected = inner_ty; + } + pat_ref_layers < expected_ref_layers || self.should_peel_smart_pointer(peel_kind, expected) + } + + /// Determine whether `expected` is a smart pointer type that should be peeled before matching. + fn should_peel_smart_pointer(&self, peel_kind: PeelKind, expected: Ty<'db>) -> bool { + // Explicit `deref!(_)` patterns match against smart pointers; don't peel in that case. + if let PeelKind::Implicit { until_adt, .. } = peel_kind + // For simplicity, only apply overloaded derefs if `expected` is a known ADT. + // FIXME(deref_patterns): we'll get better diagnostics for users trying to + // implicitly deref generics if we allow them here, but primitives, tuples, and + // inference vars definitely should be stopped. Figure out what makes most sense. + && let TyKind::Adt(scrutinee_adt, _) = expected.kind() + // Don't peel if the pattern type already matches the scrutinee. E.g., stop here if + // matching on a `Cow<'a, T>` scrutinee with a `Cow::Owned(_)` pattern. + && until_adt != Some(scrutinee_adt.def_id().0) + // At this point, the pattern isn't able to match `expected` without peeling. Check + // that it implements `Deref` before assuming it's a smart pointer, to get a normal + // type error instead of a missing impl error if not. This only checks for `Deref`, + // not `DerefPure`: we require that too, but we want a trait error if it's missing. + && let Some(deref_trait) = self.lang_items.Deref + && self.infcx().type_implements_trait(deref_trait, [expected], self.table.param_env).may_apply() + { + true + } else { + false + } + } + + fn infer_expr_pat_unadjusted(&mut self, expr: ExprId) -> Ty<'db> { + self.infer_expr_no_expect(expr, ExprIsRead::Yes) + } + + fn infer_lit_pat(&mut self, expr: ExprId, expected: Ty<'db>) -> Ty<'db> { + let literal = match &self.store[expr] { + Expr::Literal(literal) => literal, + _ => panic!("expected a literal"), + }; + + // We've already computed the type above (when checking for a non-ref pat), + // so avoid computing it again. + let ty = self.expr_ty(expr); + + // Byte string patterns behave the same way as array patterns + // They can denote both statically and dynamically-sized byte arrays. + // Additionally, when `deref_patterns` is enabled, byte string literal patterns may have + // types `[u8]` or `[u8; N]`, in order to type, e.g., `deref!(b"..."): Vec<u8>`. + let mut pat_ty = ty; + if matches!(literal, Literal::ByteString(_)) { + let expected = self.table.structurally_resolve_type(expected); + match expected.kind() { + // Allow `b"...": &[u8]` + TyKind::Ref(_, inner_ty, _) + if self.table.try_structurally_resolve_type(inner_ty).is_slice() => + { + trace!(?expr, "polymorphic byte string lit"); + pat_ty = self.types.types.static_u8_slice; } - if let Some(end) = *end { - let end_ty = self.infer_expr(end, &Expectation::None, ExprIsRead::Yes); - _ = self.demand_eqtype(end.into(), expected, end_ty); + // Allow `b"...": [u8; 3]` for `deref_patterns` + TyKind::Array(..) if self.features.deref_patterns => { + pat_ty = match ty.kind() { + TyKind::Ref(_, inner_ty, _) => inner_ty, + _ => panic!("found byte string literal with non-ref type {ty:?}"), + } } - expected - } - &Pat::Lit(expr) => { - // Don't emit type mismatches again, the expression lowering already did that. - let ty = self.infer_lit_pat(expr, expected); - self.write_pat_ty(pat, ty); - return self.pat_ty_after_adjustment(pat); - } - Pat::Box { inner } => match self.resolve_boxed_box() { - Some(box_adt) => { - let (inner_ty, alloc_ty) = match expected.as_adt() { - Some((adt, subst)) if adt == box_adt => { - (subst.type_at(0), subst.as_slice().get(1).and_then(|a| a.as_type())) - } - _ => (self.types.types.error, None), - }; - - let inner_ty = self.infer_pat(*inner, inner_ty, default_bm, decl); - Ty::new_adt( - self.interner(), - box_adt, - GenericArgs::fill_with_defaults( - self.interner(), - box_adt.into(), - iter::once(inner_ty.into()).chain(alloc_ty.map(Into::into)), - |_, id, _| self.table.next_var_for_param(id), - ), - ) + // Allow `b"...": [u8]` for `deref_patterns` + TyKind::Slice(..) if self.features.deref_patterns => { + pat_ty = self.types.types.u8_slice; } - None => self.err_ty(), - }, - Pat::ConstBlock(expr) => { - let old_inside_assign = std::mem::replace(&mut self.inside_assignment, false); - let result = - self.infer_expr(*expr, &Expectation::has_type(expected), ExprIsRead::Yes); - self.inside_assignment = old_inside_assign; - result - } - Pat::Expr(expr) => { - let old_inside_assign = std::mem::replace(&mut self.inside_assignment, false); - // LHS of assignment doesn't constitute reads. - let expr_is_read = ExprIsRead::No; - let result = - self.infer_expr_inner(*expr, &Expectation::has_type(expected), expr_is_read); - // We are returning early to avoid the unifiability check below. - let lhs_ty = self.insert_type_vars_shallow(result); - let ty = match self.coerce( - (*expr).into(), - expected, - lhs_ty, - AllowTwoPhase::No, - expr_is_read, - ) { - Ok(ty) => ty, - Err(_) => { - self.result.type_mismatches.get_or_insert_default().insert( - pat.into(), - TypeMismatch { expected: expected.store(), actual: lhs_ty.store() }, - ); - // `rhs_ty` is returned so no further type mismatches are - // reported because of this mismatch. - expected - } - }; - self.write_pat_ty(pat, ty); - self.inside_assignment = old_inside_assign; - return ty; + // Otherwise, `b"...": &[u8; 3]` + _ => {} } - Pat::Missing => self.err_ty(), - }; - // use a new type variable if we got error type here - let ty = self.insert_type_vars_shallow(ty); - // FIXME: This never check is odd, but required with out we do inference right now - if !expected.is_never() && !self.unify(ty, expected) { - self.result.type_mismatches.get_or_insert_default().insert( - pat.into(), - TypeMismatch { expected: expected.store(), actual: ty.store() }, - ); } - self.write_pat_ty(pat, ty); - self.pat_ty_after_adjustment(pat) - } - fn pat_ty_after_adjustment(&self, pat: PatId) -> Ty<'db> { - self.result - .pat_adjustments - .get(&pat) - .and_then(|it| it.last()) - .unwrap_or_else(|| &self.result.type_of_pat[pat]) - .as_ref() + // When `deref_patterns` is enabled, in order to allow `deref!("..."): String`, we allow + // string literal patterns to have type `str`. This is accounted for when lowering to MIR. + if self.features.deref_patterns + && matches!(literal, Literal::String(_)) + && self.table.try_structurally_resolve_type(expected).is_str() + { + pat_ty = self.types.types.str; + } + + // Somewhat surprising: in this case, the subtyping relation goes the + // opposite way as the other cases. Actually what we really want is not + // a subtyping relation at all but rather that there exists a LUB + // (so that they can be compared). However, in practice, constants are + // always scalars or strings. For scalars subtyping is irrelevant, + // and for strings `ty` is type is `&'static str`, so if we say that + // + // &'static str <: expected + // + // then that's equivalent to there existing a LUB. + _ = self.demand_suptype(expr.into(), expected, pat_ty); + + pat_ty } - fn infer_ref_pat( + fn infer_range_pat( &mut self, - inner_pat: PatId, - mutability: Mutability, + lhs: Option<ExprId>, + rhs: Option<ExprId>, expected: Ty<'db>, - default_bm: BindingMode, - decl: Option<DeclContext>, ) -> Ty<'db> { - let (expectation_type, expectation_lt) = match expected.kind() { - TyKind::Ref(lifetime, inner_ty, _exp_mut) => (inner_ty, lifetime), - _ => { - let inner_ty = self.table.next_ty_var(); - let inner_lt = self.table.next_region_var(); - let ref_ty = Ty::new_ref(self.interner(), inner_lt, inner_ty, mutability); - // Unification failure will be reported by the caller. - self.unify(ref_ty, expected); - (inner_ty, inner_lt) + let mut calc_side = |opt_expr: Option<ExprId>| match opt_expr { + None => None, + Some(expr) => { + let ty = self.infer_expr_pat_unadjusted(expr); + // Check that the end-point is possibly of numeric or char type. + // The early check here is not for correctness, but rather better + // diagnostics (e.g. when `&str` is being matched, `expected` will + // be peeled to `str` while ty here is still `&str`, if we don't + // err early here, a rather confusing unification error will be + // emitted instead). + let ty = self.table.try_structurally_resolve_type(ty); + let fail = + !(ty.is_numeric() || ty.is_char() || ty.is_ty_var() || ty.references_error()); + Some((fail, ty, expr)) } }; - let subty = self.infer_pat(inner_pat, expectation_type, default_bm, decl); - Ty::new_ref(self.interner(), expectation_lt, subty, mutability) + let mut lhs = calc_side(lhs); + let mut rhs = calc_side(rhs); + + if let (Some((true, ..)), _) | (_, Some((true, ..))) = (lhs, rhs) { + // There exists a side that didn't meet our criteria that the end-point + // be of a numeric or char type, as checked in `calc_side` above. + // FIXME: Emit an error. + return self.types.types.error; + } + + // Unify each side with `expected`. + // Subtyping doesn't matter here, as the value is some kind of scalar. + let mut demand_eqtype = |x: &mut _| { + if let Some((_, x_ty, x_expr)) = *x { + _ = self.demand_eqtype(ExprOrPatId::from(x_expr), expected, x_ty); + } + }; + demand_eqtype(&mut lhs); + demand_eqtype(&mut rhs); + + if let (Some((true, ..)), _) | (_, Some((true, ..))) = (lhs, rhs) { + return self.types.types.error; + } + + // Find the unified type and check if it's of numeric or char type again. + // This check is needed if both sides are inference variables. + // We require types to be resolved here so that we emit inference failure + // rather than "_ is not a char or numeric". + let ty = self.table.structurally_resolve_type(expected); + if !(ty.is_numeric() || ty.is_char() || ty.references_error()) { + // FIXME: Emit an error. + return self.types.types.error; + } + ty } fn infer_bind_pat( &mut self, pat: PatId, - binding: BindingId, - default_bm: BindingMode, - subpat: Option<PatId>, + var_id: BindingId, + sub: Option<PatId>, expected: Ty<'db>, - decl: Option<DeclContext>, + pat_info: PatInfo, ) -> Ty<'db> { - let Binding { mode, .. } = self.store[binding]; - let mode = if mode == BindingAnnotation::Unannotated { - default_bm - } else { - BindingMode::convert(mode) + let PatInfo { binding_mode: def_br, .. } = pat_info; + let binding_data = &self.store[var_id]; + + // Determine the binding mode... + let user_bind_annot = BindingMode::from_annotation(binding_data.mode); + let bm = match user_bind_annot { + BindingMode(ByRef::No, Mutability::Mut) if let ByRef::Yes(_) = def_br => { + // Only mention the experimental `mut_ref` feature if if we're in edition 2024 and + // using other experimental matching features compatible with it. + if self.edition.at_least_2024() + && (self.features.ref_pat_eat_one_layer_2024 + || self.features.ref_pat_eat_one_layer_2024_structural) + { + if !self.features.mut_ref { + // FIXME: Emit an error: binding cannot be both mutable and by-reference. + } + + BindingMode(def_br, Mutability::Mut) + } else { + // `mut` resets the binding mode on edition <= 2021 + BindingMode(ByRef::No, Mutability::Mut) + } + } + BindingMode(ByRef::No, mutbl) => BindingMode(def_br, mutbl), + BindingMode(ByRef::Yes(_), _) => user_bind_annot, }; - self.result.binding_modes.insert(pat, mode); - let inner_ty = match subpat { - Some(subpat) => self.infer_pat(subpat, expected, default_bm, decl), - None => expected, + if matches!(bm.0, ByRef::Yes(Mutability::Mut)) + && let MutblCap::WeaklyNot = pat_info.max_ref_mutbl + { + // FIXME: Emit an error: cannot borrow as mutable inside an `&` pattern. + } + + // ...and store it in a side table: + self.result.binding_modes.insert(pat, bm); + + debug!("check_pat_ident: pat.hir_id={:?} bm={:?}", pat, bm); + + let local_ty = match bm.0 { + ByRef::Yes(mutbl) => { + // If the binding is like `ref x | ref mut x`, + // then `x` is assigned a value of type `&M T` where M is the + // mutability and T is the expected type. + // + // Under pin ergonomics, if the binding is like `ref pin const|mut x`, + // then `x` is assigned a value of type `&pin M T` where M is the + // mutability and T is the expected type. + // + // `x` is assigned a value of type `&M T`, hence `&M T <: typeof(x)` + // is required. However, we use equality, which is stronger. + // See (note_1) for an explanation. + self.new_ref_ty(mutbl, expected) + } + // Otherwise, the type of x is the expected type `T`. + ByRef::No => expected, // As above, `T <: typeof(x)` is required, but we use equality, see (note_1). }; - let inner_ty = self.insert_type_vars_shallow(inner_ty); - let bound_ty = match mode { - BindingMode::Ref(mutability) => { - let inner_lt = self.table.next_region_var(); - Ty::new_ref(self.interner(), inner_lt, expected, mutability) + // We have a concrete type for the local, so we do not need to taint it and hide follow up errors *using* the local. + if let Some(existing_local_ty) = self.result.type_of_binding.get(var_id) { + // If there are multiple arms, make sure they all agree on + // what the type of the binding `x` ought to be. + _ = self.demand_eqtype(pat.into(), existing_local_ty.as_ref(), local_ty); + } else { + self.write_binding_ty(var_id, local_ty); + } + + if let Some(p) = sub { + self.infer_pat(p, expected, pat_info); + } + + local_ty + } + + fn check_dereferenceable(&self, expected: Ty<'db>, inner: PatId) -> Result<(), ()> { + if let Pat::Bind { .. } = self.store[inner] + && let Some(pointee_ty) = self.shallow_resolve(expected).builtin_deref(true) + && let TyKind::Dynamic(..) = pointee_ty.kind() + { + // This is "x = dyn SomeTrait" being reduced from + // "let &x = &dyn SomeTrait" or "let box x = Box<dyn SomeTrait>", an error. + // FIXME: Emit an error. rustc emits this message: + const _CANNOT_IMPLICITLY_DEREF_POINTER_TRAIT_OBJ: &str = "\ +This error indicates that a pointer to a trait type cannot be implicitly dereferenced by a \ +pattern. Every trait defines a type, but because the size of trait implementors isn't fixed, \ +this type has no compile-time size. Therefore, all accesses to trait types must be through \ +pointers. If you encounter this error you should try to avoid dereferencing the pointer. + +You can read more about trait objects in the Trait Objects section of the Reference: \ +https://doc.rust-lang.org/reference/types.html#trait-objects"; + } + Ok(()) + } + + fn resolve_record_pat(&mut self, pat: PatId, path: &Path) -> Result<ResolvedPat<'db>, ()> { + // Resolve the path and check the definition for errors. + let (pat_ty, Some(variant)) = self.resolve_variant(pat.into(), path, false) else { + return Err(()); + }; + self.write_variant_resolution(pat.into(), variant); + Ok(ResolvedPat { ty: pat_ty, kind: ResolvedPatKind::Struct { variant } }) + } + + fn infer_record_pat( + &mut self, + pat: PatId, + fields: &[RecordFieldPat], + has_rest_pat: bool, + pat_ty: Ty<'db>, + variant: VariantId, + expected: Ty<'db>, + pat_info: PatInfo, + ) -> Ty<'db> { + // Type-check the path. + let _ = self.demand_eqtype(pat.into(), expected, pat_ty); + + // Type-check subpatterns. + self.check_record_pat_fields(pat_ty, pat, variant, fields, has_rest_pat, pat_info); + pat_ty + } + + fn resolve_pat_path(&mut self, pat: PatId, path: &Path) -> Result<ResolvedPat<'db>, ()> { + let (res, pat_ty) = self.infer_path(path, pat.into()).ok_or(())?; + match res { + ValueNs::FunctionId(_) + | ValueNs::GenericParam(_) + | ValueNs::ImplSelf(_) + | ValueNs::LocalBinding(_) + | ValueNs::StaticId(_) => { + // FIXME: Emit an error. + return Err(()); } - BindingMode::Move => expected, + ValueNs::ConstId(_) | ValueNs::EnumVariantId(_) | ValueNs::StructId(_) => {} // OK + } + + Ok(ResolvedPat { ty: pat_ty, kind: ResolvedPatKind::Path { res } }) + } + + fn infer_pat_path( + &mut self, + pat: PatId, + resolved: &ResolvedPat<'db>, + expected: Ty<'db>, + ) -> Ty<'db> { + _ = self.demand_suptype(pat.into(), expected, resolved.ty); + resolved.ty + } + + fn resolve_tuple_struct_pat( + &mut self, + pat: PatId, + path: &Path, + ) -> Result<ResolvedPat<'db>, ()> { + // Resolve the path and check the definition for errors. + let (pat_ty, Some(variant)) = self.resolve_variant(pat.into(), path, true) else { + return Err(()); }; - self.write_pat_ty(pat, inner_ty); - self.write_binding_ty(binding, bound_ty); - inner_ty + self.write_variant_resolution(pat.into(), variant); + Ok(ResolvedPat { ty: pat_ty, kind: ResolvedPatKind::TupleStruct { variant } }) } - fn infer_slice_pat( + fn infer_tuple_struct_pat( &mut self, + pat: PatId, + subpats: &[PatId], + ddpos: Option<u32>, + pat_ty: Ty<'db>, + variant: VariantId, expected: Ty<'db>, - prefix: &[PatId], - slice: Option<PatId>, - suffix: &[PatId], - default_bm: BindingMode, - decl: Option<DeclContext>, + pat_info: PatInfo, ) -> Ty<'db> { - let expected = self.table.structurally_resolve_type(expected); + let interner = self.interner(); - // If `expected` is an infer ty, we try to equate it to an array if the given pattern - // allows it. See issue #16609 - if self.pat_is_irrefutable(decl) - && expected.is_ty_var() - && let Some(resolved_array_ty) = - self.try_resolve_slice_ty_to_array_ty(prefix, suffix, slice) + // Type-check the tuple struct pattern against the expected type. + let had_err = self.demand_eqtype(pat.into(), expected, pat_ty); + + let variant_fields = variant.fields(self.db); + let variant_field_tys = self.db.field_types(variant); + let TyKind::Adt(_, args) = pat_ty.kind() else { + panic!("unexpected pattern type {:?}", pat_ty); + }; + // Type-check subpatterns. + if subpats.len() == variant_fields.len() + || subpats.len() < variant_fields.len() && ddpos.is_some() { - self.unify(expected, resolved_array_ty); + for (i, &subpat) in subpats.iter().enumerate_and_adjust(variant_fields.len(), ddpos) { + let field_id = LocalFieldId::from_raw(la_arena::RawIdx::from_u32(i as u32)); + let field_ty = variant_field_tys[field_id].get().instantiate(interner, args); + self.infer_pat(subpat, field_ty, pat_info); + } + if let Err(()) = had_err { + for &pat in subpats { + self.infer_pat(pat, self.types.types.error, pat_info); + } + return self.types.types.error; + } + } else { + self.push_diagnostic(InferenceDiagnostic::MismatchedTupleStructPatArgCount { + pat, + expected: variant_fields.len(), + found: subpats.len(), + }); + + for (i, &pat) in subpats.iter().enumerate() { + let field_id = LocalFieldId::from_raw(la_arena::RawIdx::from_u32(i as u32)); + let expected = match variant_field_tys.get(field_id) { + Some(field_ty) => field_ty.get().instantiate(interner, args), + None => self.types.types.error, + }; + self.infer_pat(pat, expected, pat_info); + } } + pat_ty + } - let expected = self.table.try_structurally_resolve_type(expected); - let elem_ty = match expected.kind() { - TyKind::Array(st, _) | TyKind::Slice(st) => st, - _ => self.err_ty(), + fn infer_tuple_pat( + &mut self, + pat: PatId, + elements: &[PatId], + ddpos: Option<u32>, + expected: Ty<'db>, + pat_info: PatInfo, + ) -> Ty<'db> { + let interner = self.interner(); + let mut expected_len = elements.len(); + if ddpos.is_some() { + // Require known type only when `..` is present. + if let TyKind::Tuple(tys) = self.table.structurally_resolve_type(expected).kind() { + expected_len = tys.len(); + } + } + let max_len = cmp::max(expected_len, elements.len()); + + let element_tys_iter = (0..max_len).map(|_| self.table.next_ty_var()); + let element_tys = Tys::new_from_iter(interner, element_tys_iter); + let pat_ty = Ty::new(interner, TyKind::Tuple(element_tys)); + if self.demand_eqtype(pat.into(), expected, pat_ty).is_err() { + let expected = if let TyKind::Tuple(tys) = + self.table.try_structurally_resolve_type(expected).kind() + { + for (expected_var, found) in iter::zip(element_tys, tys) { + // Constrain the infer var so that the type mismatch error message, which contains it, + // will be better. + _ = self.demand_eqtype(pat.into(), expected_var, found); + } + tys + } else { + self.types.empty.tys + }; + let expected = expected.iter().chain(iter::repeat(self.types.types.error)); + Ty::new_tup_from_iter( + interner, + iter::zip(expected, elements).map(|(expected, &elem)| { + self.infer_pat(elem, expected, pat_info); + self.result.type_of_pat_with_adjust(elem) + }), + ) + } else { + for (i, &elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) { + self.infer_pat(elem, element_tys[i], pat_info); + } + pat_ty + } + } + + fn check_record_pat_fields( + &mut self, + adt_ty: Ty<'db>, + _pat: PatId, + variant: VariantId, + fields: &[RecordFieldPat], + has_rest_pat: bool, + pat_info: PatInfo, + ) { + let interner = self.interner(); + + let TyKind::Adt(_, args) = adt_ty.kind() else { + panic!("struct pattern is not an ADT"); }; - for &pat_id in prefix.iter().chain(suffix.iter()) { - self.infer_pat(pat_id, elem_ty, default_bm, decl); - } - - if let Some(slice_pat_id) = slice { - let rest_pat_ty = match expected.kind() { - TyKind::Array(_, length) => { - let len = try_const_usize(self.db, length); - let len = - len.and_then(|len| len.checked_sub((prefix.len() + suffix.len()) as u128)); - Ty::new_array_with_const_len( - self.interner(), - elem_ty, - usize_const(self.db, len, self.resolver.krate()), - ) + // Index the struct fields' types. + let variant_fields = variant.fields(self.db); + let field_map = variant_fields + .fields() + .iter() + .map(|(i, field)| (field.name.clone(), i)) + .collect::<FxHashMap<_, _>>(); + let variant_field_tys = self.db.field_types(variant); + let variant_fields_vis = VariantFields::field_visibilities(self.db, variant); + + // Keep track of which fields have already appeared in the pattern. + let mut used_fields = FxHashMap::default(); + + let mut inexistent_fields = vec![]; + // Typecheck each field. + for (field_idx, field) in fields.iter().enumerate() { + match used_fields.entry(field.name.clone()) { + Occupied(_occupied) => { + // FIXME: Emit an error, field specified twice. + } + Vacant(vacant) => { + vacant.insert(field_idx); + } + }; + let field_idx = field_map.get(&field.name).copied(); + let field_ty = match field_idx { + Some(field_idx) => { + if !self.resolver.is_visible(self.db, variant_fields_vis[field_idx]) { + self.push_diagnostic(InferenceDiagnostic::NoSuchField { + field: field.pat.into(), + private: Some(field_idx), + variant, + }); + } + + variant_field_tys[field_idx].get().instantiate(interner, args) + } + None => { + inexistent_fields.push(field); + self.types.types.error } - _ => Ty::new_slice(self.interner(), elem_ty), }; - self.infer_pat(slice_pat_id, rest_pat_ty, default_bm, decl); + + self.infer_pat(field.pat, field_ty, pat_info); } - match expected.kind() { - TyKind::Array(_, const_) => { - Ty::new_array_with_const_len(self.interner(), elem_ty, const_) + let unmentioned_fields = variant_fields + .fields() + .iter() + .filter(|(_, field)| !used_fields.contains_key(&field.name)) + .collect::<Vec<_>>(); + + for inexistent_field in inexistent_fields { + self.push_diagnostic(InferenceDiagnostic::NoSuchField { + field: inexistent_field.pat.into(), + private: None, + variant, + }); + } + + // Require `..` if struct has non_exhaustive attribute. + let non_exhaustive = self.has_applicable_non_exhaustive(variant.into()); + if non_exhaustive && !has_rest_pat { + // FIXME: Emit an error. + } + + // Report an error if an incorrect number of fields was specified. + if matches!(variant, VariantId::UnionId(_)) { + if fields.len() != 1 { + // FIXME: Emit an error, unions can't have more than one field. + } + if has_rest_pat { + // FIXME: Emit an error, unions can't have a rest pat. } - _ => Ty::new_slice(self.interner(), elem_ty), + } else if !unmentioned_fields.is_empty() && !has_rest_pat { + // FIXME: Emit an error. } } - fn infer_lit_pat(&mut self, expr: ExprId, expected: Ty<'db>) -> Ty<'db> { - // Like slice patterns, byte string patterns can denote both `&[u8; N]` and `&[u8]`. - if let Expr::Literal(Literal::ByteString(_)) = self.store[expr] - && let TyKind::Ref(_, inner, _) = expected.kind() - { - let inner = self.table.try_structurally_resolve_type(inner); - if matches!(inner.kind(), TyKind::Slice(_)) { - let elem_ty = self.types.types.u8; - let slice_ty = Ty::new_slice(self.interner(), elem_ty); - let ty = Ty::new_ref( - self.interner(), - self.types.regions.statik, - slice_ty, - Mutability::Not, - ); - self.write_expr_ty(expr, ty); - return ty; + fn infer_box_pat( + &mut self, + pat: PatId, + inner: PatId, + expected: Ty<'db>, + pat_info: PatInfo, + ) -> Ty<'db> { + let interner = self.interner(); + let (box_ty, inner_ty) = self + .check_dereferenceable(expected, inner) + .map(|()| { + // Here, `demand::subtype` is good enough, but I don't + // think any errors can be introduced by using `demand::eqtype`. + let inner_ty = self.table.next_ty_var(); + let box_ty = Ty::new_box(interner, inner_ty); + _ = self.demand_eqtype(pat.into(), expected, box_ty); + (box_ty, inner_ty) + }) + .unwrap_or_else(|()| { + let err = self.types.types.error; + (err, err) + }); + self.infer_pat(inner, inner_ty, pat_info); + box_ty + } + + fn _infer_deref_pat(&mut self, inner: PatId, expected: Ty<'db>, pat_info: PatInfo) -> Ty<'db> { + let target_ty = self.deref_pat_target(expected); + self.infer_pat(inner, target_ty, pat_info); + let infer_ok = self.register_deref_mut_bounds_if_needed(inner, [expected]); + self.table.register_infer_ok(infer_ok); + expected + } + + fn deref_pat_target(&mut self, source_ty: Ty<'db>) -> Ty<'db> { + let (Some(deref_pure), Some(deref_target)) = + (self.lang_items.DerefPure, self.lang_items.DerefTarget) + else { + return self.types.types.error; + }; + // Register a `DerefPure` bound, which is required by all `deref!()` pats. + let interner = self.interner(); + self.table.register_bound(source_ty, deref_pure, ObligationCause::new()); + // The expected type for the deref pat's inner pattern is `<expected as Deref>::Target`. + let target_ty = Ty::new_projection(interner, deref_target.into(), [source_ty]); + self.table.try_structurally_resolve_type(target_ty) + } + + /// Check if the interior of a deref pattern (either explicit or implicit) has any `ref mut` + /// bindings, which would require `DerefMut` to be emitted in MIR building instead of just + /// `Deref`. We do this *after* checking the inner pattern, since we want to make sure to + /// account for `ref mut` binding modes inherited from implicitly dereferencing `&mut` refs. + fn register_deref_mut_bounds_if_needed( + &self, + inner: PatId, + derefed_tys: impl IntoIterator<Item = Ty<'db>>, + ) -> InferOk<'db, ()> { + let mut infer_ok = InferOk { value: (), obligations: Vec::new() }; + if self.pat_has_ref_mut_binding(inner) { + let Some(deref_mut) = self.lang_items.DerefMut else { return infer_ok }; + let interner = self.interner(); + for mutably_derefed_ty in derefed_tys { + infer_ok.obligations.push(Obligation::new( + interner, + ObligationCause::new(), + self.table.param_env, + TraitRef::new(interner, deref_mut.into(), [mutably_derefed_ty]), + )); } } + infer_ok + } - self.infer_expr(expr, &Expectation::has_type(expected), ExprIsRead::Yes) + /// Does the pattern recursively contain a `ref mut` binding in it? + /// + /// This is used to determined whether a `deref` pattern should emit a `Deref` + /// or `DerefMut` call for its pattern scrutinee. + /// + /// This is computed from the typeck results since we want to make + /// sure to apply any match-ergonomics adjustments, which we cannot + /// determine from the HIR alone. + fn pat_has_ref_mut_binding(&self, pat: PatId) -> bool { + let mut has_ref_mut = false; + self.store.walk_pats(pat, &mut |pat| { + if let Some(BindingMode(ByRef::Yes(Mutability::Mut), _)) = + self.result.binding_modes.get(pat) + { + has_ref_mut = true; + } + }); + has_ref_mut } - fn is_non_ref_pat(&mut self, store: &hir_def::expr_store::ExpressionStore, pat: PatId) -> bool { - match &store[pat] { - Pat::Tuple { .. } - | Pat::TupleStruct { .. } - | Pat::Record { .. } - | Pat::Range { .. } - | Pat::Slice { .. } => true, - Pat::Or(pats) => pats.iter().all(|p| self.is_non_ref_pat(store, *p)), - Pat::Path(path) => { - // A const is a reference pattern, but other value ns things aren't (see #16131). - let resolved = self.resolve_value_path_inner(path, pat.into(), true); - resolved.is_some_and(|it| !matches!(it.0, hir_def::resolver::ValueNs::ConstId(_))) - } - Pat::ConstBlock(..) => false, - Pat::Lit(expr) => !matches!( - store[*expr], - Expr::Literal(Literal::String(..) | Literal::CString(..) | Literal::ByteString(..)) - ), - Pat::Wild - | Pat::Bind { .. } - | Pat::Ref { .. } - | Pat::Box { .. } - | Pat::Missing - | Pat::Expr(_) => false, + // Precondition: Pat is Ref(inner) + fn infer_ref_pat( + &mut self, + pat: PatId, + inner: PatId, + pat_mutbl: Mutability, + mut expected: Ty<'db>, + mut pat_info: PatInfo, + ) -> Ty<'db> { + let ref_pat_matches_mut_ref = self.ref_pat_matches_mut_ref(); + if ref_pat_matches_mut_ref && pat_mutbl == Mutability::Not { + // If `&` patterns can match against mutable reference types (RFC 3627, Rule 5), we need + // to prevent subpatterns from binding with `ref mut`. Subpatterns of a shared reference + // pattern should have read-only access to the scrutinee, and the borrow checker won't + // catch it in this case. + pat_info.max_ref_mutbl = pat_info.max_ref_mutbl.cap_to_weakly_not(); + } + + expected = self.table.try_structurally_resolve_type(expected); + // Determine whether we're consuming an inherited reference and resetting the default + // binding mode, based on edition and enabled experimental features. + if let ByRef::Yes(inh_mut) = pat_info.binding_mode { + match self.ref_pat_matches_inherited_ref(self.edition) { + InheritedRefMatchRule::EatOuter => { + // ref pattern attempts to consume inherited reference + if pat_mutbl > inh_mut { + // Tried to match inherited `ref` with `&mut` + // NB: This assumes that `&` patterns can match against mutable references + // (RFC 3627, Rule 5). If we implement a pattern typing ruleset with Rule 4E + // but not Rule 5, we'll need to check that here. + debug_assert!(ref_pat_matches_mut_ref); + // FIXME: Emit an error. + } + + pat_info.binding_mode = ByRef::No; + self.result.skipped_ref_pats.insert(pat); + self.infer_pat(inner, expected, pat_info); + return expected; + } + InheritedRefMatchRule::EatInner => { + if let TyKind::Ref(_, _, r_mutbl) = expected.kind() + && pat_mutbl <= r_mutbl + { + // Match against the reference type; don't consume the inherited ref. + // NB: The check for compatible pattern and ref type mutability assumes that + // `&` patterns can match against mutable references (RFC 3627, Rule 5). If + // we implement a pattern typing ruleset with Rule 4 (including the fallback + // to matching the inherited ref when the inner ref can't match) but not + // Rule 5, we'll need to check that here. + debug_assert!(ref_pat_matches_mut_ref); + // NB: For RFC 3627's Rule 3, we limit the default binding mode's ref + // mutability to `pat_info.max_ref_mutbl`. If we implement a pattern typing + // ruleset with Rule 4 but not Rule 3, we'll need to check that here. + debug_assert!(self.downgrade_mut_inside_shared()); + let mutbl_cap = cmp::min(r_mutbl, pat_info.max_ref_mutbl.as_mutbl()); + pat_info.binding_mode = pat_info.binding_mode.cap_ref_mutability(mutbl_cap); + } else { + // The reference pattern can't match against the expected type, so try + // matching against the inherited ref instead. + if pat_mutbl > inh_mut { + // We can't match an inherited shared reference with `&mut`. + // NB: This assumes that `&` patterns can match against mutable + // references (RFC 3627, Rule 5). If we implement a pattern typing + // ruleset with Rule 4 but not Rule 5, we'll need to check that here. + // FIXME(ref_pat_eat_one_layer_2024_structural): If we already tried + // matching the real reference, the error message should explain that + // falling back to the inherited reference didn't work. This should be + // the same error as the old-Edition version below. + debug_assert!(ref_pat_matches_mut_ref); + // FIXME: Emit an error. + } + + pat_info.binding_mode = ByRef::No; + self.result.skipped_ref_pats.insert(pat); + self.infer_pat(inner, expected, pat_info); + return expected; + } + } + InheritedRefMatchRule::EatBoth { consider_inherited_ref: true } => { + // Reset binding mode on old editions + pat_info.binding_mode = ByRef::No; + + if let TyKind::Ref(_, inner_ty, _) = expected.kind() { + // Consume both the inherited and inner references. + if pat_mutbl.is_mut() && inh_mut.is_mut() { + // As a special case, a `&mut` reference pattern will be able to match + // against a reference type of any mutability if the inherited ref is + // mutable. Since this allows us to match against a shared reference + // type, we refer to this as "falling back" to matching the inherited + // reference, though we consume the real reference as well. We handle + // this here to avoid adding this case to the common logic below. + self.infer_pat(inner, inner_ty, pat_info); + return expected; + } else { + // Otherwise, use the common logic below for matching the inner + // reference type. + // FIXME(ref_pat_eat_one_layer_2024_structural): If this results in a + // mutability mismatch, the error message should explain that falling + // back to the inherited reference didn't work. This should be the same + // error as the Edition 2024 version above. + } + } else { + // The expected type isn't a reference type, so only match against the + // inherited reference. + if pat_mutbl > inh_mut { + // We can't match a lone inherited shared reference with `&mut`. + // FIXME: Emit an error. + } + + self.result.skipped_ref_pats.insert(pat); + self.infer_pat(inner, expected, pat_info); + return expected; + } + } + InheritedRefMatchRule::EatBoth { consider_inherited_ref: false } => { + // Reset binding mode on stable Rust. This will be a type error below if + // `expected` is not a reference type. + pat_info.binding_mode = ByRef::No; + } + } } + + let (ref_ty, inner_ty) = match self.check_dereferenceable(expected, inner) { + Ok(()) => { + // `demand::subtype` would be good enough, but using `eqtype` turns + // out to be equally general. See (note_1) for details. + + // Take region, inner-type from expected type if we can, + // to avoid creating needless variables. This also helps with + // the bad interactions of the given hack detailed in (note_1). + debug!("check_pat_ref: expected={:?}", expected); + match expected.as_reference() { + Some((r_ty, _, r_mutbl)) + if ((ref_pat_matches_mut_ref && r_mutbl >= pat_mutbl) + || r_mutbl == pat_mutbl) => + { + if r_mutbl == Mutability::Not { + pat_info.max_ref_mutbl = MutblCap::Not; + } + + (expected, r_ty) + } + _ => { + let inner_ty = self.table.next_ty_var(); + let ref_ty = self.new_ref_ty(pat_mutbl, inner_ty); + debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty); + _ = self.demand_eqtype(pat.into(), expected, ref_ty); + + (ref_ty, inner_ty) + } + } + } + Err(()) => { + let err = self.types.types.error; + (err, err) + } + }; + + self.infer_pat(inner, inner_ty, pat_info); + ref_ty + } + + /// Create a reference or pinned reference type with a fresh region variable. + fn new_ref_ty(&self, mutbl: Mutability, ty: Ty<'db>) -> Ty<'db> { + let region = self.table.next_region_var(); + Ty::new_ref(self.interner(), region, ty, mutbl) } fn try_resolve_slice_ty_to_array_ty( - &mut self, + &self, before: &[PatId], - suffix: &[PatId], slice: Option<PatId>, ) -> Option<Ty<'db>> { if slice.is_some() { return None; } - let len = before.len() + suffix.len(); - let size = consteval::usize_const(self.db, Some(len as u128), self.owner.krate(self.db)); + let interner = self.interner(); + let len = before.len(); + let inner_ty = self.table.next_ty_var(); - let elem_ty = self.table.next_ty_var(); - let array_ty = Ty::new_array_with_const_len(self.interner(), elem_ty, size); - Some(array_ty) + Some(Ty::new_array(interner, inner_ty, len.try_into().unwrap())) } - /// Used to determine whether we can infer the expected type in the slice pattern to be of type array. + /// Used to determines whether we can infer the expected type in the slice pattern to be of type array. /// This is only possible if we're in an irrefutable pattern. If we were to allow this in refutable /// patterns we wouldn't e.g. report ambiguity in the following situation: /// /// ```ignore(rust) - /// struct Zeroes; + /// struct Zeroes; /// const ARR: [usize; 2] = [0; 2]; /// const ARR2: [usize; 2] = [2; 2]; /// @@ -664,15 +1535,150 @@ impl<'db> InferenceContext<'_, 'db> { /// /// If we're in an irrefutable pattern we prefer the array impl candidate given that /// the slice impl candidate would be rejected anyway (if no ambiguity existed). - fn pat_is_irrefutable(&self, decl_ctxt: Option<DeclContext>) -> bool { - matches!(decl_ctxt, Some(DeclContext { origin: DeclOrigin::LocalDecl { has_else: false } })) + fn pat_is_irrefutable(&self, pat_origin: PatOrigin) -> bool { + match pat_origin { + PatOrigin::LetExpr | PatOrigin::MatchArm => false, + PatOrigin::LetStmt { has_else } => !has_else, + PatOrigin::DestructuringAssignment | PatOrigin::Param => true, + } } -} -pub(super) fn contains_explicit_ref_binding(store: &ExpressionStore, pat_id: PatId) -> bool { - let mut res = false; - store.walk_pats(pat_id, &mut |pat| { - res |= matches!(store[pat], Pat::Bind { id, .. } if matches!(store[id].mode, BindingAnnotation::Ref | BindingAnnotation::RefMut)); - }); - res + /// Type check a slice pattern. + /// + /// Syntactically, these look like `[pat_0, ..., pat_n]`. + /// Semantically, we are type checking a pattern with structure: + /// ```ignore (not-rust) + /// [before_0, ..., before_n, (slice, after_0, ... after_n)?] + /// ``` + /// The type of `slice`, if it is present, depends on the `expected` type. + /// If `slice` is missing, then so is `after_i`. + /// If `slice` is present, it can still represent 0 elements. + fn infer_slice_pat( + &mut self, + pat: PatId, + before: &[PatId], + slice: Option<PatId>, + after: &[PatId], + expected: Ty<'db>, + pat_info: PatInfo, + ) -> Ty<'db> { + let expected = self.table.try_structurally_resolve_type(expected); + + // If the pattern is irrefutable and `expected` is an infer ty, we try to equate it + // to an array if the given pattern allows it. See issue #76342 + if self.pat_is_irrefutable(pat_info.pat_origin) + && expected.is_ty_var() + && let Some(resolved_arr_ty) = self.try_resolve_slice_ty_to_array_ty(before, slice) + { + debug!(?resolved_arr_ty); + let _ = self.demand_eqtype(pat.into(), expected, resolved_arr_ty); + } + + let expected = self.table.structurally_resolve_type(expected); + debug!(?expected); + + let (element_ty, opt_slice_ty, inferred) = match expected.kind() { + // An array, so we might have something like `let [a, b, c] = [0, 1, 2];`. + TyKind::Array(element_ty, len) => { + let min = before.len() as u64 + after.len() as u64; + let (opt_slice_ty, expected) = + self.check_array_pat_len(pat, element_ty, expected, slice, len, min.into()); + // `opt_slice_ty.is_none()` => `slice.is_none()`. + // Note, though, that opt_slice_ty could be `Some(error_ty)`. + assert!(opt_slice_ty.is_some() || slice.is_none()); + (element_ty, opt_slice_ty, expected) + } + TyKind::Slice(element_ty) => (element_ty, Some(expected), expected), + // The expected type must be an array or slice, but was neither, so error. + _ => { + // FIXME: Emit an error: expected an array or a slice. + let err = self.types.types.error; + (err, Some(err), err) + } + }; + + // Type check all the patterns before `slice`. + for &elt in before { + self.infer_pat(elt, element_ty, pat_info); + } + // Type check the `slice`, if present, against its expected type. + if let Some(slice) = slice { + self.infer_pat(slice, opt_slice_ty.unwrap(), pat_info); + } + // Type check the elements after `slice`, if present. + for &elt in after { + self.infer_pat(elt, element_ty, pat_info); + } + inferred + } + + /// Type check the length of an array pattern. + /// + /// Returns both the type of the variable length pattern (or `None`), and the potentially + /// inferred array type. We only return `None` for the slice type if `slice.is_none()`. + fn check_array_pat_len( + &mut self, + pat: PatId, + element_ty: Ty<'db>, + arr_ty: Ty<'db>, + slice: Option<PatId>, + len: Const<'db>, + min_len: u128, + ) -> (Option<Ty<'db>>, Ty<'db>) { + let len = crate::consteval::try_const_usize(self.db, len); + + if let Some(len) = len { + // Now we know the length... + if slice.is_none() { + // ...and since there is no variable-length pattern, + // we require an exact match between the number of elements + // in the array pattern and as provided by the matched type. + if min_len == len { + return (None, arr_ty); + } + + // FIXME: Emit an error: incorrect length. + } else if let Some(pat_len) = len.checked_sub(min_len) { + // The variable-length pattern was there, + // so it has an array type with the remaining elements left as its size... + return (Some(Ty::new_array(self.interner(), element_ty, pat_len)), arr_ty); + } else { + // ...however, in this case, there were no remaining elements. + // That is, the slice pattern requires more than the array type offers. + // FIXME: Emit an error. + } + } else if slice.is_none() { + // We have a pattern with a fixed length, + // which we can use to infer the length of the array. + let updated_arr_ty = Ty::new_array(self.interner(), element_ty, min_len); + _ = self.demand_eqtype(pat.into(), updated_arr_ty, arr_ty); + return (None, updated_arr_ty); + } else { + // We have a variable-length pattern and don't know the array length. + // This happens if we have e.g., + // `let [a, b, ..] = arr` where `arr: [T; N]` where `const N: usize`. + // FIXME: Emit an error: cannot pattern-match on an array without a fixed length. + }; + + // If we get here, we must have emitted an error. + (Some(self.types.types.error), arr_ty) + } + + fn infer_destructuring_assignment_expr(&mut self, expr: ExprId, expected: Ty<'db>) -> Ty<'db> { + // LHS of assignment doesn't constitute reads. + let expr_is_read = ExprIsRead::No; + let lhs_ty = self.infer_expr_inner(expr, &Expectation::has_type(expected), expr_is_read); + match self.coerce(expr, expected, lhs_ty, AllowTwoPhase::No, expr_is_read) { + Ok(ty) => ty, + Err(_) => { + self.result.type_mismatches.get_or_insert_default().insert( + expr.into(), + TypeMismatch { expected: expected.store(), actual: lhs_ty.store() }, + ); + // `rhs_ty` is returned so no further type mismatches are + // reported because of this mismatch. + expected + } + } + } } |