Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #22134 from ChayimFriedman2/port-pats
fix: Port pattern inference from rustc
43 files changed, 1966 insertions, 817 deletions
diff --git a/.typos.toml b/.typos.toml index e954b08fb1..873daa3bf3 100644 --- a/.typos.toml +++ b/.typos.toml @@ -34,6 +34,7 @@ thir = "thir" jod = "jod" tructure = "tructure" taits = "taits" +inh = "inh" [default.extend-identifiers] anc = "anc" diff --git a/bench_data/glorious_old_parser b/bench_data/glorious_old_parser index 5022514924..a6de9daa86 100644 --- a/bench_data/glorious_old_parser +++ b/bench_data/glorious_old_parser @@ -1,4 +1,4 @@ -//- minicore: fn +//- minicore: fn, iterator, try, panic use crate::ast::{AngleBracketedArgs, ParenthesizedArgs, AttrStyle, BareFnTy}; use crate::ast::{GenericBound, TraitBoundModifier}; use crate::ast::Unsafety; diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs index 4ace4aef16..3440fbee6d 100644 --- a/crates/hir-def/src/expr_store/lower.rs +++ b/crates/hir-def/src/expr_store/lower.rs @@ -1348,8 +1348,10 @@ impl<'db> ExprCollector<'db> { ast::Expr::RecordExpr(e) => { let path = e .path() - .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)) - .map(Box::new); + .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)); + let Some(path) = path else { + return Some(self.missing_expr()); + }; let record_lit = if let Some(nfl) = e.record_expr_field_list() { let fields = nfl .fields() @@ -1702,8 +1704,10 @@ impl<'db> ExprCollector<'db> { let path = collect_path(self, e.expr()?)?; let path = path .path() - .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)) - .map(Box::new); + .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)); + let Some(path) = path else { + return Some(self.missing_pat()); + }; let (ellipsis, args) = collect_tuple(self, e.arg_list()?.args()); self.alloc_pat_from_expr(Pat::TupleStruct { path, args, ellipsis }, syntax_ptr) } @@ -1733,8 +1737,10 @@ impl<'db> ExprCollector<'db> { ast::Expr::RecordExpr(e) => { let path = e .path() - .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)) - .map(Box::new); + .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)); + let Some(path) = path else { + return Some(self.missing_pat()); + }; let record_field_list = e.record_expr_field_list()?; let ellipsis = record_field_list.dotdot_token().is_some(); // FIXME: Report an error here if `record_field_list.spread().is_some()`. @@ -1988,24 +1994,27 @@ impl<'db> ExprCollector<'db> { /// ``` fn collect_for_loop(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::ForExpr) -> ExprId { let lang_items = self.lang_items(); - let into_iter_fn = self.lang_path(lang_items.IntoIterIntoIter); - let iter_next_fn = self.lang_path(lang_items.IteratorNext); - let option_some = self.lang_path(lang_items.OptionSome); - let option_none = self.lang_path(lang_items.OptionNone); + let (Some(into_iter_fn), Some(iter_next_fn), Some(option_some), Some(option_none)) = ( + self.lang_path(lang_items.IntoIterIntoIter), + self.lang_path(lang_items.IteratorNext), + self.lang_path(lang_items.OptionSome), + self.lang_path(lang_items.OptionNone), + ) else { + return self.missing_expr(); + }; let head = self.collect_expr_opt(e.iterable()); - let into_iter_fn_expr = - self.alloc_expr(into_iter_fn.map_or(Expr::Missing, Expr::Path), syntax_ptr); + let into_iter_fn_expr = self.alloc_expr(Expr::Path(into_iter_fn), syntax_ptr); let iterator = self.alloc_expr( Expr::Call { callee: into_iter_fn_expr, args: Box::new([head]) }, syntax_ptr, ); let none_arm = MatchArm { - pat: self.alloc_pat_desugared(option_none.map_or(Pat::Missing, Pat::Path)), + pat: self.alloc_pat_desugared(Pat::Path(option_none)), guard: None, expr: self.alloc_expr(Expr::Break { expr: None, label: None }, syntax_ptr), }; let some_pat = Pat::TupleStruct { - path: option_some.map(Box::new), + path: option_some, args: Box::new([self.collect_pat_top(e.pat())]), ellipsis: None, }; @@ -2025,8 +2034,7 @@ impl<'db> ExprCollector<'db> { Expr::Ref { expr: iter_expr, rawness: Rawness::Ref, mutability: Mutability::Mut }, syntax_ptr, ); - let iter_next_fn_expr = - self.alloc_expr(iter_next_fn.map_or(Expr::Missing, Expr::Path), syntax_ptr); + let iter_next_fn_expr = self.alloc_expr(Expr::Path(iter_next_fn), syntax_ptr); let iter_next_expr = self.alloc_expr( Expr::Call { callee: iter_next_fn_expr, args: Box::new([iter_expr_mut]) }, syntax_ptr, @@ -2074,11 +2082,15 @@ impl<'db> ExprCollector<'db> { /// ``` fn collect_try_operator(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::TryExpr) -> ExprId { let lang_items = self.lang_items(); - let try_branch = self.lang_path(lang_items.TryTraitBranch); - let cf_continue = self.lang_path(lang_items.ControlFlowContinue); - let cf_break = self.lang_path(lang_items.ControlFlowBreak); + let (Some(try_branch), Some(cf_continue), Some(cf_break)) = ( + self.lang_path(lang_items.TryTraitBranch), + self.lang_path(lang_items.ControlFlowContinue), + self.lang_path(lang_items.ControlFlowBreak), + ) else { + return self.missing_expr(); + }; let operand = self.collect_expr_opt(e.expr()); - let try_branch = self.alloc_expr(try_branch.map_or(Expr::Missing, Expr::Path), syntax_ptr); + let try_branch = self.alloc_expr(Expr::Path(try_branch), syntax_ptr); let expr = self .alloc_expr(Expr::Call { callee: try_branch, args: Box::new([operand]) }, syntax_ptr); let continue_name = self.generate_new_name(); @@ -2092,7 +2104,7 @@ impl<'db> ExprCollector<'db> { self.add_definition_to_binding(continue_binding, continue_bpat); let continue_arm = MatchArm { pat: self.alloc_pat_desugared(Pat::TupleStruct { - path: cf_continue.map(Box::new), + path: cf_continue, args: Box::new([continue_bpat]), ellipsis: None, }), @@ -2106,7 +2118,7 @@ impl<'db> ExprCollector<'db> { self.add_definition_to_binding(break_binding, break_bpat); let break_arm = MatchArm { pat: self.alloc_pat_desugared(Pat::TupleStruct { - path: cf_break.map(Box::new), + path: cf_break, args: Box::new([break_bpat]), ellipsis: None, }), @@ -2499,8 +2511,10 @@ impl<'db> ExprCollector<'db> { ast::Pat::TupleStructPat(p) => { let path = p .path() - .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)) - .map(Box::new); + .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)); + let Some(path) = path else { + return self.missing_pat(); + }; let (args, ellipsis) = self.collect_tuple_pat( p.fields(), comma_follows_token(p.l_paren_token()), @@ -2565,8 +2579,10 @@ impl<'db> ExprCollector<'db> { ast::Pat::RecordPat(p) => { let path = p .path() - .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)) - .map(Box::new); + .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)); + let Some(path) = path else { + return self.missing_pat(); + }; let record_pat_field_list = &p.record_pat_field_list().expect("every struct should have a field list"); let args = record_pat_field_list diff --git a/crates/hir-def/src/expr_store/lower/format_args.rs b/crates/hir-def/src/expr_store/lower/format_args.rs index 51616afb38..beb1267173 100644 --- a/crates/hir-def/src/expr_store/lower/format_args.rs +++ b/crates/hir-def/src/expr_store/lower/format_args.rs @@ -867,11 +867,14 @@ impl<'db> ExprCollector<'db> { }; let width = RecordLitField { name: Name::new_symbol_root(sym::width), expr: width_expr }; - self.alloc_expr_desugared(Expr::RecordLit { - path: self.lang_path(lang_items.FormatPlaceholder).map(Box::new), - fields: Box::new([position, flags, precision, width]), - spread: RecordSpread::None, - }) + match self.lang_path(lang_items.FormatPlaceholder) { + Some(path) => self.alloc_expr_desugared(Expr::RecordLit { + path, + fields: Box::new([position, flags, precision, width]), + spread: RecordSpread::None, + }), + None => self.missing_expr(), + } } else { let format_placeholder_new = self.ty_rel_lang_path_desugared_expr(lang_items.FormatPlaceholder, sym::new); diff --git a/crates/hir-def/src/expr_store/pretty.rs b/crates/hir-def/src/expr_store/pretty.rs index 70ea54c734..fdd0654508 100644 --- a/crates/hir-def/src/expr_store/pretty.rs +++ b/crates/hir-def/src/expr_store/pretty.rs @@ -668,10 +668,7 @@ impl Printer<'_> { } } Expr::RecordLit { path, fields, spread } => { - match path { - Some(path) => self.print_path(path), - None => w!(self, "�"), - } + self.print_path(path); w!(self, "{{"); let edition = self.edition; @@ -923,10 +920,7 @@ impl Printer<'_> { w!(self, ")"); } Pat::Record { path, args, ellipsis } => { - match path { - Some(path) => self.print_path(path), - None => w!(self, "�"), - } + self.print_path(path); w!(self, " {{"); let edition = self.edition; @@ -1004,10 +998,7 @@ impl Printer<'_> { } } Pat::TupleStruct { path, args, ellipsis } => { - match path { - Some(path) => self.print_path(path), - None => w!(self, "�"), - } + self.print_path(path); w!(self, "("); for (i, arg) in args.iter().enumerate() { if i != 0 { diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs index 031280b6e9..a1a346cabc 100644 --- a/crates/hir-def/src/hir.rs +++ b/crates/hir-def/src/hir.rs @@ -259,7 +259,7 @@ pub enum Expr { expr: Option<ExprId>, }, RecordLit { - path: Option<Box<Path>>, + path: Path, fields: Box<[RecordLitField]>, spread: RecordSpread, }, @@ -673,7 +673,7 @@ pub enum Pat { }, Or(Box<[PatId]>), Record { - path: Option<Box<Path>>, + path: Path, args: Box<[RecordFieldPat]>, ellipsis: bool, }, @@ -694,7 +694,7 @@ pub enum Pat { subpat: Option<PatId>, }, TupleStruct { - path: Option<Box<Path>>, + path: Path, args: Box<[PatId]>, ellipsis: Option<u32>, }, diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs index 5b6ebe2a0b..6071ed2981 100644 --- a/crates/hir-def/src/lang_item.rs +++ b/crates/hir-def/src/lang_item.rs @@ -365,9 +365,10 @@ language_item_table! { LangItems => Deref, sym::deref, TraitId; DerefMut, sym::deref_mut, TraitId; + DerefPure, sym::deref_pure, TraitId; DerefTarget, sym::deref_target, TypeAliasId; Receiver, sym::receiver, TraitId; - ReceiverTarget, sym::receiver_target, TypeAliasId; + ReceiverTarget, sym::receiver_target, TypeAliasId; Fn, sym::fn_, TraitId; FnMut, sym::fn_mut, TraitId; diff --git a/crates/hir-def/src/unstable_features.rs b/crates/hir-def/src/unstable_features.rs index 3b7f83081f..559726fe9b 100644 --- a/crates/hir-def/src/unstable_features.rs +++ b/crates/hir-def/src/unstable_features.rs @@ -88,4 +88,8 @@ define_unstable_features! { never_type_fallback, specialization, min_specialization, + ref_pat_eat_one_layer_2024, + ref_pat_eat_one_layer_2024_structural, + deref_patterns, + mut_ref, } diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs index 068118c705..99dddf0bf4 100644 --- a/crates/hir-ty/src/diagnostics/expr.rs +++ b/crates/hir-ty/src/diagnostics/expr.rs @@ -218,9 +218,7 @@ impl<'db> ExprValidator<'db> { // Note: Skipping the entire diagnostic rather than just not including a faulty match arm is // preferred to avoid the chance of false positives. for arm in arms { - let Some(pat_ty) = self.infer.type_of_pat_with_adjust(arm.pat) else { - return; - }; + let pat_ty = self.infer.type_of_pat_with_adjust(arm.pat); if pat_ty.references_non_lt_error() { return; } diff --git a/crates/hir-ty/src/diagnostics/match_check.rs b/crates/hir-ty/src/diagnostics/match_check.rs index f559c26bf5..8356329d96 100644 --- a/crates/hir-ty/src/diagnostics/match_check.rs +++ b/crates/hir-ty/src/diagnostics/match_check.rs @@ -22,7 +22,7 @@ use span::Edition; use stdx::{always, never, variance::PhantomCovariantLifetime}; use crate::{ - InferenceResult, + ByRef, InferenceResult, db::HirDatabase, display::{HirDisplay, HirDisplayError, HirFormatter}, infer::BindingMode, @@ -121,7 +121,7 @@ impl<'a, 'db> PatCtxt<'a, 'db> { self.infer.pat_adjustments.get(&pat).map(|it| &**it).unwrap_or_default().iter().rev().fold( unadjusted_pat, |subpattern, ref_ty| Pat { - ty: ref_ty.as_ref(), + ty: ref_ty.source.as_ref(), kind: Box::new(PatKind::Deref { subpattern }), }, ) @@ -158,8 +158,8 @@ impl<'a, 'db> PatCtxt<'a, 'db> { ty = self.infer.binding_ty(id); let name = &self.body[id].name; match (bm, ty.kind()) { - (BindingMode::Ref(_), TyKind::Ref(_, rty, _)) => ty = rty, - (BindingMode::Ref(_), _) => { + (BindingMode(ByRef::Yes(_), _), TyKind::Ref(_, rty, _)) => ty = rty, + (BindingMode(ByRef::Yes(_), _), _) => { never!( "`ref {}` has wrong type {:?}", name.display(self.db, Edition::LATEST), 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 diff --git a/crates/hir-ty/src/infer/cast.rs b/crates/hir-ty/src/infer/cast.rs index e5ee734474..09855766d8 100644 --- a/crates/hir-ty/src/infer/cast.rs +++ b/crates/hir-ty/src/infer/cast.rs @@ -131,7 +131,7 @@ impl<'db> CastCheck<'db> { // This should always come first so that we apply the coercion, which impacts infer vars. if ctx .coerce( - self.source_expr.into(), + self.source_expr, self.expr_ty, self.cast_ty, AllowTwoPhase::No, @@ -167,7 +167,7 @@ impl<'db> CastCheck<'db> { let sig = self.expr_ty.fn_sig(ctx.interner()); let fn_ptr = Ty::new_fn_ptr(ctx.interner(), sig); match ctx.coerce( - self.source_expr.into(), + self.source_expr, self.expr_ty, fn_ptr, AllowTwoPhase::No, @@ -275,7 +275,7 @@ impl<'db> CastCheck<'db> { let array_ptr_type = Ty::new_ptr(ctx.interner(), t_expr, m_expr); if ctx .coerce( - self.source_expr.into(), + self.source_expr, self.expr_ty, array_ptr_type, AllowTwoPhase::No, diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index 2207bc37e8..0d2de9b984 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -20,7 +20,7 @@ use tracing::debug; use crate::{ FnAbi, db::{InternedClosure, InternedClosureId, InternedCoroutineClosureId, InternedCoroutineId}, - infer::{BreakableKind, Diverges, coerce::CoerceMany}, + infer::{BreakableKind, Diverges, coerce::CoerceMany, pat::PatOrigin}, next_solver::{ AliasTy, Binder, ClauseKind, DbInterner, ErrorGuaranteed, FnSig, GenericArgs, PolyFnSig, PolyProjectionPredicate, Predicate, PredicateKind, SolverDefId, Ty, TyKind, @@ -263,7 +263,7 @@ impl<'db> InferenceContext<'_, 'db> { // Now go through the argument patterns for (arg_pat, arg_ty) in args.iter().zip(bound_sig.skip_binder().inputs()) { - self.infer_top_pat(*arg_pat, *arg_ty, None); + self.infer_top_pat(*arg_pat, *arg_ty, PatOrigin::Param); } // FIXME: lift these out into a struct diff --git a/crates/hir-ty/src/infer/closure/analysis.rs b/crates/hir-ty/src/infer/closure/analysis.rs index 668d7496cd..b0f5533b3b 100644 --- a/crates/hir-ty/src/infer/closure/analysis.rs +++ b/crates/hir-ty/src/infer/closure/analysis.rs @@ -506,7 +506,11 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // Build a tuple (U0..Un) of the final upvar types U0..Un // and unify the upvar tuple type in the closure with it: let final_tupled_upvars_type = Ty::new_tup(self.interner(), &final_upvar_tys); - self.demand_suptype(args.tupled_upvars_ty(), final_tupled_upvars_type); + _ = self.demand_suptype( + closure_expr_id.into(), + args.tupled_upvars_ty(), + final_tupled_upvars_type, + ); let fake_reads = delegate.fake_reads; diff --git a/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs b/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs index 45696c402a..bddb01ff99 100644 --- a/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs +++ b/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs @@ -21,11 +21,13 @@ use rustc_type_ir::{ }; use smallvec::{SmallVec, smallvec}; use syntax::ast::{BinaryOp, UnaryOp}; -use tracing::{debug, instrument}; +use tracing::{debug, instrument, trace}; use crate::{ - Adjust, Adjustment, AutoBorrow, BindingMode, - infer::{CaptureSourceStack, InferenceContext, UpvarCapture, closure::analysis::BorrowKind}, + Adjust, Adjustment, AutoBorrow, + infer::{ + ByRef, CaptureSourceStack, InferenceContext, UpvarCapture, closure::analysis::BorrowKind, + }, method_resolution::CandidateId, next_solver::{DbInterner, ErrorGuaranteed, StoredTy, Ty, TyKind}, upvars::UpvarsRef, @@ -947,12 +949,12 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { // In a cases of pattern like `let pat = upvar`, don't use the span // of the pattern, as this just looks confusing, instead use the span // of the discriminant. - match this.cx.result.binding_mode(pat) { - Some(BindingMode::Ref(m)) => { + match this.cx.result.binding_mode(pat).0 { + ByRef::Yes(m) => { let bk = BorrowKind::from_mutbl(m); this.delegate.borrow(place, bk, this.cx); } - None | Some(BindingMode::Move) => { + ByRef::No => { debug!("walk_pat binding consuming pat"); this.consume_or_copy(place); } @@ -1194,18 +1196,48 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { // that these are never attached to binding patterns, so // actually this is somewhat "disjoint" from the code below // that aims to account for `ref x`. - if let Some(vec) = self.cx.result.pat_adjustments.get(&pat) - && let Some(first_adjust) = vec.first() + if let Some(vec) = self.cx.result.pat_adjustment(pat) { + if let Some(first_adjust) = vec.first() { + debug!("pat_ty(pat={:?}) found adjustment `{:?}`", pat, first_adjust); + return Ok(first_adjust.source.as_ref()); + } + } else if let Pat::Ref { pat: subpat, .. } = self.cx.store[pat] + && self.cx.result.is_skipped_ref_pat(pat) { - debug!("pat_ty(pat={:?}) found adjustment `{:?}`", pat, first_adjust); - return Ok(first_adjust.as_ref()); + return self.pat_ty_adjusted(subpat); } + self.pat_ty_unadjusted(pat) } /// Like [`Self::pat_ty_adjusted`], but ignores implicit `&` patterns. fn pat_ty_unadjusted(&mut self, pat: PatId) -> Result<Ty<'db>> { - Ok(self.cx.result.pat_ty(pat)) + let base_ty = self.node_ty(pat.into())?; + trace!(?base_ty); + + // This code detects whether we are looking at a `ref x`, + // and if so, figures out what the type *being borrowed* is. + match self.cx.store[pat] { + Pat::Bind { .. } => { + let bm = self.cx.result.binding_mode(pat); + + if let ByRef::Yes(_) = bm.0 { + // a bind-by-ref means that the base_ty will be the type of the ident itself, + // but what we want here is the type of the underlying value being borrowed. + // So peel off one-level, turning the &T into T. + match self.cx.table.structurally_resolve_type(base_ty).builtin_deref(false) { + Some(ty) => Ok(ty), + None => { + debug!("By-ref binding of non-derefable type: {base_ty:?}"); + Err(ErrorGuaranteed) + } + } + } else { + Ok(base_ty) + } + } + _ => Ok(base_ty), + } } fn cat_expr(&mut self, expr: ExprId) -> Result<PlaceWithOrigin> { @@ -1602,7 +1634,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { .builtin_index() else { debug!("explicit index of non-indexable type {:?}", place_with_id); - panic!("explicit index of non-indexable type"); + return Err(ErrorGuaranteed); }; let elt_place = self.cat_projection( pat.into(), diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs index 732a583047..db912c0eb6 100644 --- a/crates/hir-ty/src/infer/coerce.rs +++ b/crates/hir-ty/src/infer/coerce.rs @@ -38,10 +38,7 @@ use std::ops::ControlFlow; use hir_def::{ - CallableDefId, TraitId, - attrs::AttrFlags, - hir::{ExprId, ExprOrPatId}, - signatures::FunctionSignature, + CallableDefId, TraitId, attrs::AttrFlags, hir::ExprId, signatures::FunctionSignature, }; use rustc_ast_ir::Mutability; use rustc_type_ir::{ @@ -894,7 +891,7 @@ impl<'db> InferenceContext<'_, 'db> { /// The expressions *must not* have any preexisting adjustments. pub(crate) fn coerce( &mut self, - expr: ExprOrPatId, + expr: ExprId, expr_ty: Ty<'db>, mut target: Ty<'db>, allow_two_phase: AllowTwoPhase, @@ -905,13 +902,7 @@ impl<'db> InferenceContext<'_, 'db> { debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target); let cause = ObligationCause::new(); - let coerce_never = match expr { - ExprOrPatId::ExprId(idx) => { - self.expr_guaranteed_to_constitute_read_for_never(idx, expr_is_read) - } - // `PatId` is passed for `PatKind::Path`. - ExprOrPatId::PatId(_) => false, - }; + let coerce_never = self.expr_guaranteed_to_constitute_read_for_never(expr, expr_is_read); let mut coerce = Coerce { delegate: InferenceCoercionDelegate(self), cause, @@ -922,11 +913,7 @@ impl<'db> InferenceContext<'_, 'db> { let ok = coerce.commit_if_ok(|coerce| coerce.coerce(source, target))?; let (adjustments, _) = self.table.register_infer_ok(ok); - match expr { - ExprOrPatId::ExprId(expr) => self.write_expr_adj(expr, adjustments.into_boxed_slice()), - ExprOrPatId::PatId(pat) => self - .write_pat_adj(pat, adjustments.into_iter().map(|adjust| adjust.target).collect()), - } + self.write_expr_adj(expr, adjustments.into_boxed_slice()); Ok(target) } @@ -1335,7 +1322,7 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> { // To be honest, I'm not entirely sure why we do this. // We don't allow two-phase borrows, see comment in try_find_coercion_lub for why icx.coerce( - expression.into(), + expression, expression_ty, self.expected_ty, AllowTwoPhase::No, diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index f26be63806..e84a03a2e7 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -24,12 +24,9 @@ use syntax::ast::RangeOp; use tracing::debug; use crate::{ - Adjust, Adjustment, CallableDefId, DeclContext, DeclOrigin, Rawness, consteval, + Adjust, Adjustment, CallableDefId, Rawness, consteval, generics::generics, - infer::{ - AllowTwoPhase, BreakableKind, coerce::CoerceMany, find_continuable, - pat::contains_explicit_ref_binding, - }, + infer::{AllowTwoPhase, BreakableKind, coerce::CoerceMany, find_continuable, pat::PatOrigin}, lower::{GenericPredicates, lower_mutability}, method_resolution::{self, CandidateId, MethodCallee, MethodError}, next_solver::{ @@ -92,7 +89,7 @@ impl<'db> InferenceContext<'_, 'db> { ) -> Ty<'db> { let ty = self.infer_expr_inner(expr, expected, is_read); if let Some(target) = expected.only_has_type(&mut self.table) { - match self.coerce(expr.into(), ty, target, AllowTwoPhase::No, is_read) { + match self.coerce(expr, ty, target, AllowTwoPhase::No, is_read) { Ok(res) => res, Err(_) => { self.result.type_mismatches.get_or_insert_default().insert( @@ -279,7 +276,7 @@ impl<'db> InferenceContext<'_, 'db> { } if let Some(target) = expected.only_has_type(&mut self.table) { - self.coerce(expr.into(), ty, target, AllowTwoPhase::No, ExprIsRead::Yes) + self.coerce(expr, ty, target, AllowTwoPhase::No, ExprIsRead::Yes) .expect("never-to-any coercion should always succeed") } else { ty @@ -366,11 +363,7 @@ impl<'db> InferenceContext<'_, 'db> { ExprIsRead::No }; let input_ty = self.infer_expr(expr, &Expectation::none(), child_is_read); - self.infer_top_pat( - pat, - input_ty, - Some(DeclContext { origin: DeclOrigin::LetExpr }), - ); + self.infer_top_pat(pat, input_ty, PatOrigin::LetExpr); self.types.types.bool } Expr::Block { statements, tail, label, id: _ } => { @@ -449,7 +442,7 @@ impl<'db> InferenceContext<'_, 'db> { let matchee_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); let mut all_arms_diverge = Diverges::Always; for arm in arms.iter() { - self.infer_top_pat(arm.pat, input_ty, None); + self.infer_top_pat(arm.pat, input_ty, PatOrigin::MatchArm); } let expected = expected.adjust_for_branches(&mut self.table); @@ -566,7 +559,7 @@ impl<'db> InferenceContext<'_, 'db> { } else { let unit = self.types.types.unit; let _ = self.coerce( - tgt_expr.into(), + tgt_expr, unit, yield_ty, AllowTwoPhase::No, @@ -586,7 +579,7 @@ impl<'db> InferenceContext<'_, 'db> { self.types.types.never } Expr::RecordLit { path, fields, spread, .. } => { - let (ty, def_id) = self.resolve_variant(tgt_expr.into(), path.as_deref(), false); + let (ty, def_id) = self.resolve_variant(tgt_expr.into(), path, false); if let Some(t) = expected.only_has_type(&mut self.table) { self.unify(ty, t); @@ -727,7 +720,7 @@ impl<'db> InferenceContext<'_, 'db> { let resolver_guard = self.resolver.update_to_inner_scope(self.db, self.owner, tgt_expr); self.inside_assignment = true; - self.infer_top_pat(target, rhs_ty, None); + self.infer_top_pat(target, rhs_ty, PatOrigin::DestructuringAssignment); self.inside_assignment = false; self.resolver.reset_to_guard(resolver_guard); } @@ -959,7 +952,7 @@ impl<'db> InferenceContext<'_, 'db> { .instantiate(this.interner(), parameters), ); _ = this.coerce( - expr.into(), + expr, ty, fnptr_ty, AllowTwoPhase::No, @@ -969,7 +962,7 @@ impl<'db> InferenceContext<'_, 'db> { TyKind::Ref(_, base_ty, mutbl) => { let ptr_ty = Ty::new_ptr(this.interner(), base_ty, mutbl); _ = this.coerce( - expr.into(), + expr, ty, ptr_ty, AllowTwoPhase::No, @@ -1100,7 +1093,7 @@ impl<'db> InferenceContext<'_, 'db> { fn infer_expr_path(&mut self, path: &Path, id: ExprOrPatId, scope_id: ExprId) -> Ty<'db> { let g = self.resolver.update_to_inner_scope(self.db, self.owner, scope_id); let ty = match self.infer_path(path, id) { - Some(ty) => ty, + Some((_, ty)) => ty, None => { if path.mod_path().is_some_and(|mod_path| mod_path.is_ident() || mod_path.is_self()) { @@ -1288,16 +1281,7 @@ impl<'db> InferenceContext<'_, 'db> { .unwrap_or_else(Expectation::none); let inner_ty = self.infer_expr_inner(inner_expr, &inner_exp, ExprIsRead::Yes); - Ty::new_adt( - self.interner(), - box_id, - GenericArgs::fill_with_defaults( - self.interner(), - box_id.into(), - [inner_ty.into()], - |_, id, _| self.table.next_var_for_param(id), - ), - ) + Ty::new_box(self.interner(), inner_ty) } else { self.err_ty() } @@ -1333,7 +1317,7 @@ impl<'db> InferenceContext<'_, 'db> { } else { ExprIsRead::No }; - let ty = if contains_explicit_ref_binding(this.store, *pat) { + let ty = if this.contains_explicit_ref_binding(*pat) { this.infer_expr( *expr, &Expectation::has_type(decl_ty), @@ -1351,11 +1335,11 @@ impl<'db> InferenceContext<'_, 'db> { decl_ty }; - let decl = DeclContext { - origin: DeclOrigin::LocalDecl { has_else: else_branch.is_some() }, - }; - - this.infer_top_pat(*pat, ty, Some(decl)); + this.infer_top_pat( + *pat, + ty, + PatOrigin::LetStmt { has_else: else_branch.is_some() }, + ); if let Some(expr) = else_branch { let previous_diverges = mem::replace(&mut this.diverges, Diverges::Maybe); @@ -1399,7 +1383,7 @@ impl<'db> InferenceContext<'_, 'db> { } else if let Some(t) = expected.only_has_type(&mut this.table) { if this .coerce( - expr.into(), + expr, this.types.types.unit, t, AllowTwoPhase::No, @@ -1893,13 +1877,7 @@ impl<'db> InferenceContext<'_, 'db> { let coerced_ty = this.table.resolve_vars_with_obligations(coerced_ty); let coerce_error = this - .coerce( - provided_arg.into(), - checked_ty, - coerced_ty, - AllowTwoPhase::Yes, - ExprIsRead::Yes, - ) + .coerce(provided_arg, checked_ty, coerced_ty, AllowTwoPhase::Yes, ExprIsRead::Yes) .err(); if coerce_error.is_some() { return Err((coerce_error, coerced_ty, checked_ty)); diff --git a/crates/hir-ty/src/infer/op.rs b/crates/hir-ty/src/infer/op.rs index 95d63ffb50..2916a46ca3 100644 --- a/crates/hir-ty/src/infer/op.rs +++ b/crates/hir-ty/src/infer/op.rs @@ -38,7 +38,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, category) { - self.enforce_builtin_binop_types(lhs_ty, rhs_ty, category); + self.enforce_builtin_binop_types(expr, lhs_ty, rhs_ty, category); self.types.types.unit } else { return_ty @@ -107,7 +107,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { && is_builtin_binop(lhs_ty, rhs_ty, category) { let builtin_return_ty = - self.enforce_builtin_binop_types(lhs_ty, rhs_ty, category); + self.enforce_builtin_binop_types(expr, lhs_ty, rhs_ty, category); _ = self.demand_eqtype(expr.into(), builtin_return_ty, return_ty); builtin_return_ty } else { @@ -119,6 +119,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { fn enforce_builtin_binop_types( &mut self, + expr: ExprId, lhs_ty: Ty<'db>, rhs_ty: Ty<'db>, category: BinOpCategory, @@ -131,8 +132,8 @@ impl<'a, 'db> InferenceContext<'a, 'db> { match category { BinOpCategory::Shortcircuit => { - self.demand_suptype(self.types.types.bool, lhs_ty); - self.demand_suptype(self.types.types.bool, rhs_ty); + _ = self.demand_suptype(expr.into(), self.types.types.bool, lhs_ty); + _ = self.demand_suptype(expr.into(), self.types.types.bool, rhs_ty); self.types.types.bool } @@ -143,13 +144,13 @@ impl<'a, 'db> InferenceContext<'a, 'db> { BinOpCategory::Math | BinOpCategory::Bitwise => { // both LHS and RHS and result will have the same type - self.demand_suptype(lhs_ty, rhs_ty); + _ = self.demand_suptype(expr.into(), lhs_ty, rhs_ty); lhs_ty } BinOpCategory::Comparison => { // both LHS and RHS and result will have the same type - self.demand_suptype(lhs_ty, rhs_ty); + _ = self.demand_suptype(expr.into(), lhs_ty, rhs_ty); self.types.types.bool } } 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 + } + } + } } diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs index 3cadc8e933..835721942a 100644 --- a/crates/hir-ty/src/infer/path.rs +++ b/crates/hir-ty/src/infer/path.rs @@ -26,29 +26,36 @@ use crate::{ use super::{ExprOrPatId, InferenceContext, InferenceTyDiagnosticSource}; impl<'db> InferenceContext<'_, 'db> { - pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<Ty<'db>> { - let (value_def, generic_def, substs) = match self.resolve_value_path(path, id)? { - ValuePathResolution::GenericDef(value_def, generic_def, substs) => { - (value_def, generic_def, substs) - } - ValuePathResolution::NonGeneric(ty) => return Some(ty), - }; + pub(super) fn infer_path( + &mut self, + path: &Path, + id: ExprOrPatId, + ) -> Option<(ValueNs, Ty<'db>)> { + let (value, self_subst) = self.resolve_value_path_inner(path, id, false)?; + + let (value_def, generic_def, substs) = + match self.resolve_value_path(path, id, value, self_subst)? { + ValuePathResolution::GenericDef(value_def, generic_def, substs) => { + (value_def, generic_def, substs) + } + ValuePathResolution::NonGeneric(ty) => return Some((value, ty)), + }; let args = self.insert_type_vars(substs); self.add_required_obligations_for_value_path(generic_def, args); let ty = self.db.value_ty(value_def)?.instantiate(self.interner(), args); let ty = self.process_remote_user_written_ty(ty); - Some(ty) + Some((value, ty)) } fn resolve_value_path( &mut self, path: &Path, id: ExprOrPatId, + value: ValueNs, + self_subst: Option<GenericArgs<'db>>, ) -> Option<ValuePathResolution<'db>> { - let (value, self_subst) = self.resolve_value_path_inner(path, id, false)?; - let value_def: ValueTyDefId = match value { ValueNs::FunctionId(it) => it.into(), ValueNs::ConstId(it) => it.into(), diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index d004b5e3ef..c6c7856c8c 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs @@ -91,7 +91,7 @@ use crate::{ pub use autoderef::autoderef; pub use infer::{ - Adjust, Adjustment, AutoBorrow, BindingMode, InferenceDiagnostic, InferenceResult, + Adjust, Adjustment, AutoBorrow, BindingMode, ByRef, InferenceDiagnostic, InferenceResult, InferenceTyDiagnosticSource, OverloadedDeref, PointerCast, cast::CastError, could_coerce, could_unify, could_unify_deeply, infer_query_with_inspect, }; @@ -660,23 +660,6 @@ pub fn known_const_to_ast<'db>( Some(make::expr_const_value(konst.display(db, display_target).to_string().as_str())) } -#[derive(Debug, Copy, Clone)] -pub(crate) enum DeclOrigin { - LetExpr, - /// from `let x = ..` - LocalDecl { - has_else: bool, - }, -} - -/// Provides context for checking patterns in declarations. More specifically this -/// allows us to infer array types if the pattern is irrefutable and allows us to infer -/// the size of the array. See issue rust-lang/rust#76342. -#[derive(Debug, Copy, Clone)] -pub(crate) struct DeclContext { - pub(crate) origin: DeclOrigin, -} - pub fn setup_tracing() -> Option<tracing::subscriber::DefaultGuard> { use std::env; use std::sync::LazyLock; diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 0f0ed729c9..b935a6ed32 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -886,13 +886,12 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { RecordSpread::FieldDefaults => not_supported!("empty record spread"), }; let variant_id = - self.infer.variant_resolution_for_expr(expr_id).ok_or_else(|| match path { - Some(p) => MirLowerError::UnresolvedName( - hir_display_with_store(&**p, self.store) + self.infer.variant_resolution_for_expr(expr_id).ok_or_else(|| { + MirLowerError::UnresolvedName( + hir_display_with_store(path, self.store) .display(self.db, self.display_target()) .to_string(), - ), - None => MirLowerError::RecordLiteralWithoutPath, + ) })?; let subst = match self.expr_ty_without_adjust(expr_id).kind() { TyKind::Adt(_, s) => s, diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs index 99c5f0fc65..68b1c8b3b6 100644 --- a/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -4,7 +4,7 @@ use hir_def::{hir::ExprId, signatures::VariantFields}; use rustc_type_ir::inherent::{IntoKind, Ty as _}; use crate::{ - BindingMode, + BindingMode, ByRef, mir::{ LocalId, MutBorrowKind, Operand, OperandKind, lower::{ @@ -104,7 +104,7 @@ impl<'db> MirLowerCtx<'_, 'db> { ) -> Result<'db, (BasicBlockId, Option<BasicBlockId>)> { self.pattern_match_binding( id, - BindingMode::Move, + BindingMode(ByRef::No, rustc_ast_ir::Mutability::Not), local.into(), MirSpan::SelfParam, current, @@ -385,7 +385,7 @@ impl<'db> MirLowerCtx<'_, 'db> { self.push_match_assignment( current, local, - BindingMode::Move, + BindingMode(ByRef::No, rustc_ast_ir::Mutability::Not), cond_place, pattern.into(), ); @@ -535,13 +535,13 @@ impl<'db> MirLowerCtx<'_, 'db> { current, target_place.into(), match mode { - BindingMode::Move => { + BindingMode(ByRef::No, _) => { Operand { kind: OperandKind::Copy(cond_place), span: None }.into() } - BindingMode::Ref(rustc_ast_ir::Mutability::Not) => { + BindingMode(ByRef::Yes(rustc_ast_ir::Mutability::Not), _) => { Rvalue::Ref(BorrowKind::Shared, cond_place) } - BindingMode::Ref(rustc_ast_ir::Mutability::Mut) => { + BindingMode(ByRef::Yes(rustc_ast_ir::Mutability::Mut), _) => { Rvalue::Ref(BorrowKind::Mut { kind: MutBorrowKind::Default }, cond_place) } }, diff --git a/crates/hir-ty/src/next_solver.rs b/crates/hir-ty/src/next_solver.rs index 161a3142df..aa118149e2 100644 --- a/crates/hir-ty/src/next_solver.rs +++ b/crates/hir-ty/src/next_solver.rs @@ -85,6 +85,10 @@ pub struct DefaultTypes<'db> { pub error: Ty<'db>, /// `&'static str` pub static_str_ref: Ty<'db>, + /// `[u8]` + pub u8_slice: Ty<'db>, + /// `&'static [u8]` + pub static_u8_slice: Ty<'db>, /// `*mut ()` pub mut_unit_ptr: Ty<'db>, } @@ -236,6 +240,8 @@ pub fn default_types<'a, 'db>(db: &'db dyn HirDatabase) -> &'a DefaultAny<'db> { let empty_tys = create_tys(&[]); let unit = create_ty(TyKind::Tuple(empty_tys)); let u8 = create_ty(TyKind::Uint(rustc_ast_ir::UintTy::U8)); + let u8_slice = create_ty(TyKind::Slice(u8)); + let static_u8_slice = create_ty(TyKind::Ref(statik, u8_slice, Mutability::Not)); DefaultAny { types: DefaultTypes { usize: create_ty(TyKind::Uint(rustc_ast_ir::UintTy::Usize)), @@ -261,6 +267,8 @@ pub fn default_types<'a, 'db>(db: &'db dyn HirDatabase) -> &'a DefaultAny<'db> { never: create_ty(TyKind::Never), error: create_ty(TyKind::Error(ErrorGuaranteed)), static_str_ref: create_ty(TyKind::Ref(statik, str, rustc_ast_ir::Mutability::Not)), + u8_slice, + static_u8_slice, mut_unit_ptr: create_ty(TyKind::RawPtr(unit, rustc_ast_ir::Mutability::Mut)), }, consts: DefaultConsts { diff --git a/crates/hir-ty/src/next_solver/infer/mod.rs b/crates/hir-ty/src/next_solver/infer/mod.rs index a6352c7899..de21c5442b 100644 --- a/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/crates/hir-ty/src/next_solver/infer/mod.rs @@ -7,12 +7,11 @@ use std::sync::Arc; pub use BoundRegionConversionTime::*; use ena::unify as ut; -use hir_def::GenericParamId; +use hir_def::{GenericParamId, TraitId}; use opaque_types::{OpaqueHiddenType, OpaqueTypeStorage}; use region_constraints::{RegionConstraintCollector, RegionConstraintStorage}; use rustc_next_trait_solver::solve::{GoalEvaluation, SolverDelegateEvalExt}; use rustc_pattern_analysis::Captures; -use rustc_type_ir::solve::{NoSolution, inspect}; use rustc_type_ir::{ ClosureKind, ConstVid, FloatVarValue, FloatVid, GenericArgKind, InferConst, InferTy, IntVarValue, IntVid, OutlivesPredicate, RegionVid, TermKind, TyVid, TypeFoldable, TypeFolder, @@ -22,6 +21,10 @@ use rustc_type_ir::{ Const as _, GenericArg as _, GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _, }, }; +use rustc_type_ir::{ + Upcast, + solve::{NoSolution, inspect}, +}; use snapshot::undo_log::InferCtxtUndoLogs; use tracing::{debug, instrument}; use traits::{ObligationCause, PredicateObligations}; @@ -509,6 +512,52 @@ impl<'db> InferCtxt<'db> { self.evaluate_obligation(obligation).must_apply_modulo_regions() } + /// Check whether a `ty` implements given trait(trait_def_id) without side-effects. + /// + /// The inputs are: + /// + /// - the def-id of the trait + /// - the type parameters of the trait, including the self-type + /// - the parameter environment + /// + /// Invokes `evaluate_obligation`, so in the event that evaluating + /// `Ty: Trait` causes overflow, EvaluatedToAmbigStackDependent will be returned. + /// + /// `type_implements_trait` is a convenience function for simple cases like + /// + /// ```ignore (illustrative) + /// let copy_trait = infcx.tcx.require_lang_item(LangItem::Copy, span); + /// let implements_copy = infcx.type_implements_trait(copy_trait, [ty], param_env) + /// .must_apply_modulo_regions(); + /// ``` + /// + /// In most cases you should instead create an [Obligation] and check whether + /// it holds via [`evaluate_obligation`] or one of its helper functions like + /// [`predicate_must_hold_modulo_regions`], because it properly handles higher ranked traits + /// and it is more convenient and safer when your `params` are inside a [`Binder`]. + /// + /// [Obligation]: traits::Obligation + /// [`evaluate_obligation`]: InferCtxt::evaluate_obligation + /// [`predicate_must_hold_modulo_regions`]: InferCtxt::predicate_must_hold_modulo_regions + /// [`Binder`]: rustc_type_ir::Binder + #[instrument(level = "debug", skip(self, params), ret)] + pub fn type_implements_trait( + &self, + trait_def_id: TraitId, + params: impl IntoIterator<Item: Into<GenericArg<'db>>>, + param_env: ParamEnv<'db>, + ) -> EvaluationResult { + let trait_ref = TraitRef::new(self.interner, trait_def_id.into(), params); + + let obligation = traits::Obligation { + cause: traits::ObligationCause::dummy(), + param_env, + recursion_depth: 0, + predicate: trait_ref.upcast(self.interner), + }; + self.evaluate_obligation(&obligation) + } + /// Evaluate a given predicate, capturing overflow and propagating it back. fn evaluate_obligation(&self, obligation: &PredicateObligation<'db>) -> EvaluationResult { self.probe(|snapshot| { diff --git a/crates/hir-ty/src/next_solver/infer/select.rs b/crates/hir-ty/src/next_solver/infer/select.rs index bd407fd157..bbfc8a4757 100644 --- a/crates/hir-ty/src/next_solver/infer/select.rs +++ b/crates/hir-ty/src/next_solver/infer/select.rs @@ -63,7 +63,7 @@ pub enum NotConstEvaluatable { /// so they are noops when unioned with a definite error, and within /// the categories it's easy to see that the unions are correct. #[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] -pub(crate) enum EvaluationResult { +pub enum EvaluationResult { /// Evaluation successful. EvaluatedToOk, /// Evaluation successful, but there were unevaluated region obligations. diff --git a/crates/hir-ty/src/next_solver/ty.rs b/crates/hir-ty/src/next_solver/ty.rs index 39abdaf079..c953e79602 100644 --- a/crates/hir-ty/src/next_solver/ty.rs +++ b/crates/hir-ty/src/next_solver/ty.rs @@ -183,6 +183,35 @@ impl<'db> Ty<'db> { ) } + /// Note: this needs an interner with crate. + pub fn new_array(interner: DbInterner<'db>, ty: Ty<'db>, n: u128) -> Ty<'db> { + Ty::new( + interner, + TyKind::Array( + ty, + crate::consteval::usize_const(interner.db, Some(n), interner.expect_crate()), + ), + ) + } + + fn new_generic_adt(interner: DbInterner<'db>, adt_id: AdtId, ty_param: Ty<'db>) -> Ty<'db> { + let args = GenericArgs::fill_with_defaults( + interner, + adt_id.into(), + [ty_param.into()], + |_, _, _| panic!("all params except the first should have defaults"), + ); + Ty::new_adt(interner, adt_id, args) + } + + /// Note: Unlike most other constructors, this require the interner to have a crate, because this needs lang items. + pub fn new_box(interner: DbInterner<'db>, ty: Ty<'db>) -> Ty<'db> { + let Some(def_id) = interner.lang_items().OwnedBox else { + return Ty::new_error(interner, ErrorGuaranteed); + }; + Ty::new_generic_adt(interner, def_id.into(), ty) + } + /// Returns the `Size` for primitive types (bool, uint, int, char, float). pub fn primitive_size(self, interner: DbInterner<'db>) -> Size { match self.kind() { diff --git a/crates/hir-ty/src/tests/coercion.rs b/crates/hir-ty/src/tests/coercion.rs index a80ce5002d..cc3464cd7f 100644 --- a/crates/hir-ty/src/tests/coercion.rs +++ b/crates/hir-ty/src/tests/coercion.rs @@ -878,11 +878,11 @@ struct V<T> { t: T } fn main() { let a: V<&dyn Tr>; (a,) = V { t: &S }; - //^^^^expected V<&'? S>, got (V<&'? (dyn Tr + '?)>,) + //^^^^expected V<&'? S>, got ({unknown},) let mut a: V<&dyn Tr> = V { t: &S }; (a,) = V { t: &S }; - //^^^^expected V<&'? S>, got (V<&'? (dyn Tr + '?)>,) + //^^^^expected V<&'? S>, got ({unknown},) } "#, ); diff --git a/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs index d6bc03f57d..9449f531a6 100644 --- a/crates/hir-ty/src/tests/patterns.rs +++ b/crates/hir-ty/src/tests/patterns.rs @@ -133,8 +133,8 @@ fn infer_literal_pattern() { 55..72 'let "f... any()': bool 59..64 '"foo"': &'static str 59..64 '"foo"': &'static str - 67..70 'any': fn any<&'static str>() -> &'static str - 67..72 'any()': &'static str + 67..70 'any': fn any<&'? str>() -> &'? str + 67..72 'any()': &'? str 73..75 '{}': () 80..99 'if let...y() {}': () 83..96 'let 1 = any()': bool @@ -193,31 +193,27 @@ fn infer_literal_pattern() { fn infer_range_pattern() { check_infer_with_mismatches( r#" -//- minicore: range -fn test(x..y: &core::ops::Range<u32>) { +fn test() { if let 1..76 = 2u32 {} if let 1..=76 = 2u32 {} } "#, expect![[r#" - 8..9 'x': Range<u32> - 8..12 'x..y': Range<u32> - 11..12 'y': Range<u32> - 38..96 '{ ...2 {} }': () - 44..66 'if let...u32 {}': () - 47..63 'let 1....= 2u32': bool - 51..52 '1': u32 - 51..56 '1..76': u32 + 10..68 '{ ...2 {} }': () + 16..38 'if let...u32 {}': () + 19..35 'let 1....= 2u32': bool + 23..24 '1': u32 + 23..28 '1..76': u32 + 26..28 '76': u32 + 31..35 '2u32': u32 + 36..38 '{}': () + 43..66 'if let...u32 {}': () + 46..63 'let 1....= 2u32': bool + 50..51 '1': u32 + 50..56 '1..=76': u32 54..56 '76': u32 59..63 '2u32': u32 64..66 '{}': () - 71..94 'if let...u32 {}': () - 74..91 'let 1....= 2u32': bool - 78..79 '1': u32 - 78..84 '1..=76': u32 - 82..84 '76': u32 - 87..91 '2u32': u32 - 92..94 '{}': () "#]], ); check_no_mismatches( @@ -265,7 +261,6 @@ fn infer_pattern_match_ergonomics() { #[test] fn infer_pattern_match_ergonomics_ref() { - cov_mark::check!(match_ergonomics_ref); check_infer( r#" fn test() { @@ -409,7 +404,7 @@ fn infer_pattern_match_byte_string_literal() { 209..233 'if let...[S] {}': () 212..230 'let b"... &v[S]': bool 216..222 'b"foo"': &'static [u8] - 216..222 'b"foo"': &'static [u8] + 216..222 'b"foo"': &'static [u8; 3] 225..230 '&v[S]': &'? [u8] 226..227 'v': [u8; 3] 226..230 'v[S]': [u8] @@ -825,6 +820,8 @@ fn box_pattern() { ); check_infer( r#" + #![feature(lang_items)] + #[lang = "owned_box"] pub struct Box<T>(T); @@ -835,12 +832,13 @@ fn box_pattern() { } "#, expect![[r#" - 52..58 'params': Box<i32> - 70..124 '{ ... } }': () - 76..122 'match ... }': () - 82..88 'params': Box<i32> - 99..110 'box integer': Box<i32> - 114..116 '{}': () + 77..83 'params': Box<i32> + 95..149 '{ ... } }': () + 101..147 'match ... }': () + 107..113 'params': Box<i32> + 124..135 'box integer': Box<i32> + 128..135 'integer': i32 + 139..141 '{}': () "#]], ); } diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs index e30fa779da..1b63a4a2c0 100644 --- a/crates/hir-ty/src/tests/regression.rs +++ b/crates/hir-ty/src/tests/regression.rs @@ -1956,7 +1956,7 @@ fn main() { Alias::Braced; //^^^^^^^^^^^^^ {unknown} let Alias::Braced = loop {}; - //^^^^^^^^^^^^^ ! + //^^^^^^^^^^^^^ {unknown} let Alias::Braced(..) = loop {}; //^^^^^^^^^^^^^^^^^ Enum diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs index c30b24cd27..fb46e4b58b 100644 --- a/crates/hir-ty/src/tests/simple.rs +++ b/crates/hir-ty/src/tests/simple.rs @@ -3460,15 +3460,13 @@ struct TS(usize); fn main() { let x; [x,] = &[1,]; - //^^^^expected &'? [i32; 1], got [{unknown}] let x; [(x,),] = &[(1,),]; - //^^^^^^^expected &'? [(i32,); 1], got [{unknown}] let x; ((x,),) = &((1,),); - //^^^^^^^expected &'? ((i32,),), got (({unknown},),) + //^^^^^^^expected &'? ((i32,),), got ({unknown},) let x; (x,) = &(1,); @@ -3476,7 +3474,7 @@ fn main() { let x; (S { a: x },) = &(S { a: 42 },); - //^^^^^^^^^^^^^expected &'? (S,), got (S,) + //^^^^^^^^^^^^^expected &'? (S,), got ({unknown},) let x; S { a: x } = &S { a: 42 }; diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index 6cfb79d5a1..b14bf64266 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -716,16 +716,10 @@ impl<'db> AnyDiagnostic<'db> { TypedHole { expr, expected: Type::new(db, def, expected.as_ref()) }.into() } &InferenceDiagnostic::MismatchedTupleStructPatArgCount { pat, expected, found } => { - let expr_or_pat = match pat { - ExprOrPatId::ExprId(expr) => expr_syntax(expr)?, - ExprOrPatId::PatId(pat) => { - let InFile { file_id, value } = pat_syntax(pat)?; - - // cast from Either<Pat, SelfParam> -> Either<_, Pat> - let ptr = AstPtr::try_from_raw(value.syntax_node_ptr())?; - InFile { file_id, value: ptr } - } - }; + let InFile { file_id, value } = pat_syntax(pat)?; + // cast from Either<Pat, SelfParam> -> Either<_, Pat> + let ptr = AstPtr::try_from_raw(value.syntax_node_ptr())?; + let expr_or_pat = InFile { file_id, value: ptr }; MismatchedTupleStructPatArgCount { expr_or_pat, expected, found }.into() } InferenceDiagnostic::CastToUnsized { expr, cast_ty } => { diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index bbe1e670de..d2658e06dc 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -409,7 +409,7 @@ impl<'db> SourceAnalyzer<'db> { ExprOrPatId::PatId(idx) => infer .pat_adjustment(idx) .and_then(|adjusts| adjusts.last()) - .map(|adjust| adjust.as_ref()), + .map(|adjust| adjust.source.as_ref()), }; let ty = infer.expr_or_pat_ty(expr_or_pat_id); @@ -449,12 +449,12 @@ impl<'db> SourceAnalyzer<'db> { ) -> Option<BindingMode> { let id = self.pat_id(&pat.clone().into())?; let infer = self.infer()?; - infer.binding_mode(id.as_pat()?).map(|bm| match bm { - hir_ty::BindingMode::Move => BindingMode::Move, - hir_ty::BindingMode::Ref(hir_ty::next_solver::Mutability::Mut) => { + Some(match infer.binding_mode(id.as_pat()?) { + hir_ty::BindingMode(hir_ty::ByRef::No, _) => BindingMode::Move, + hir_ty::BindingMode(hir_ty::ByRef::Yes(hir_ty::next_solver::Mutability::Mut), _) => { BindingMode::Ref(Mutability::Mut) } - hir_ty::BindingMode::Ref(hir_ty::next_solver::Mutability::Not) => { + hir_ty::BindingMode(hir_ty::ByRef::Yes(hir_ty::next_solver::Mutability::Not), _) => { BindingMode::Ref(Mutability::Shared) } }) @@ -470,7 +470,7 @@ impl<'db> SourceAnalyzer<'db> { infer .pat_adjustment(pat_id.as_pat()?)? .iter() - .map(|ty| Type::new_with_resolver(db, &self.resolver, ty.as_ref())) + .map(|adjust| Type::new_with_resolver(db, &self.resolver, adjust.source.as_ref())) .collect(), ) } diff --git a/crates/ide-assists/src/handlers/destructure_tuple_binding.rs b/crates/ide-assists/src/handlers/destructure_tuple_binding.rs index 291605056b..09a554234a 100644 --- a/crates/ide-assists/src/handlers/destructure_tuple_binding.rs +++ b/crates/ide-assists/src/handlers/destructure_tuple_binding.rs @@ -1787,7 +1787,7 @@ fn main() { // * `?` check_in_place_assist( r#" -//- minicore: option +//- minicore: try, option fn f1(v: i32) {} fn f2(v: &i32) {} trait T { diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index 99dd2ea237..c5fd0201e9 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -2499,6 +2499,7 @@ fn $0fun_name() { check_assist( extract_function, r#" +//- minicore: iterator fn foo() { $0for v in &[0, 1] { }$0 } @@ -4746,7 +4747,7 @@ async fn some_function() { check_assist( extract_function, r#" -//- minicore: future, result +//- minicore: future, result, try async fn foo() -> Result<(), ()> { $0async {}.await; Err(())?$0 diff --git a/crates/ide-assists/src/handlers/inline_local_variable.rs b/crates/ide-assists/src/handlers/inline_local_variable.rs index 2af074f1fc..bf9bc394b3 100644 --- a/crates/ide-assists/src/handlers/inline_local_variable.rs +++ b/crates/ide-assists/src/handlers/inline_local_variable.rs @@ -724,6 +724,7 @@ fn foo() { check_assist( inline_local_variable, r" +//- minicore: iterator fn foo() { let a$0 = vec![10, 20]; for i in a {} diff --git a/crates/ide-completion/src/completions/postfix.rs b/crates/ide-completion/src/completions/postfix.rs index 82baf885dd..1e7f989110 100644 --- a/crates/ide-completion/src/completions/postfix.rs +++ b/crates/ide-completion/src/completions/postfix.rs @@ -1186,7 +1186,9 @@ fn main() { ); check_edit( kind, - r#"fn main() { for i in 0..10 {}.$0 }"#, + r#" +//- minicore: iterator +fn main() { for i in 0..10 {}.$0 }"#, &format!("fn main() {{ {kind} {{ for i in 0..10 {{}} }} }}"), ); check_edit( diff --git a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs index d52fc73870..8cd41f7aed 100644 --- a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs +++ b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs @@ -300,7 +300,7 @@ fn main() { } match (true, false) { (true, false, true) => (), - //^^^^^^^^^^^^^^^^^^^ error: expected (bool, bool), found (bool, bool, bool) + //^^^^^^^^^^^^^^^^^^^ error: expected (bool, bool), found (bool, bool, {unknown}) (true) => (), // ^^^^ error: expected (bool, bool), found bool } @@ -1198,4 +1198,20 @@ fn main() { ); } } + + #[test] + fn no_overloaded_deref_is_not_projection() { + check_diagnostics( + r#" +const FOO: &str = ""; + +fn foo() { + match "" { + FOO => {} + _ => {} + } +} + "#, + ); + } } diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs index 98a4474ef1..355617a2b1 100644 --- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -739,7 +739,7 @@ fn foo() -> Result<(), ()> { check_fix( r#" -//- minicore: result +//- minicore: result, iterator fn foo() -> Result<(), ()> { for _ in 0..5 {}$0 } @@ -1190,9 +1190,7 @@ fn f() { let &() = &mut (); //^^^ error: expected &mut (), found &() match &() { - // FIXME: we should only show the deep one. &9 => () - //^^ error: expected &(), found &i32 //^ error: expected (), found i32 } } diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs index fb885c2ad1..6ac4fa1fba 100644 --- a/crates/ide/src/expand_macro.rs +++ b/crates/ide/src/expand_macro.rs @@ -448,6 +448,7 @@ fn main() { fn macro_expand_match_ast_inside_let_statement() { check( r#" +//- minicore: try macro_rules! match_ast { (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) }; (match ($node:expr) {}) => {{}}; diff --git a/crates/ide/src/inlay_hints/binding_mode.rs b/crates/ide/src/inlay_hints/binding_mode.rs index e8d305afb3..63a83ea335 100644 --- a/crates/ide/src/inlay_hints/binding_mode.rs +++ b/crates/ide/src/inlay_hints/binding_mode.rs @@ -169,13 +169,14 @@ fn __( } match &(0,) { (x,) | (x,) => (), - //^^^^^^^^^^^) - //^^^^^^^^^^^&( + //^^^^& //^ ref //^ ref + //^^^^& ((x,) | (x,)) => (), - //^^^^^^^^^^^^^& + //^^^^& //^ ref + //^^^^& //^ ref } match &mut (0,) { @@ -183,7 +184,8 @@ fn __( //^^^^ &mut //^ ref mut } -}"#, +} +"#, ); } @@ -217,8 +219,8 @@ fn main() { expect![[r#" fn main() { match &(0,) { - &(&((ref x,) | (ref x,))) => (), - &((ref x,) | (ref x,)) => (), + &(ref x,) | &(ref x,) => (), + (&(ref x,) | &(ref x,)) => (), } } "#]], diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index d687cb40a9..b1094872ff 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs @@ -1348,7 +1348,7 @@ fn benchmark_syntax_highlighting_parser() { }) .count() }; - assert_eq!(hash, 1631); + assert_eq!(hash, 1644); } #[test] diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs index 614411598b..8dcc73d81f 100644 --- a/crates/intern/src/symbol/symbols.rs +++ b/crates/intern/src/symbol/symbols.rs @@ -194,6 +194,7 @@ define_symbols! { Default, deprecated, deref_mut, + deref_pure, deref_target, deref, derive_const, @@ -578,4 +579,8 @@ define_symbols! { field, field_base, field_type, + ref_pat_eat_one_layer_2024, + ref_pat_eat_one_layer_2024_structural, + deref_patterns, + mut_ref, } |