Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #22134 from ChayimFriedman2/port-pats
fix: Port pattern inference from rustc
Shoyu Vanilla (Flint) 4 weeks ago
parent cc360fc · parent a085e07 · commit 34c231e
-rw-r--r--.typos.toml1
-rw-r--r--bench_data/glorious_old_parser2
-rw-r--r--crates/hir-def/src/expr_store/lower.rs68
-rw-r--r--crates/hir-def/src/expr_store/lower/format_args.rs13
-rw-r--r--crates/hir-def/src/expr_store/pretty.rs15
-rw-r--r--crates/hir-def/src/hir.rs6
-rw-r--r--crates/hir-def/src/lang_item.rs3
-rw-r--r--crates/hir-def/src/unstable_features.rs4
-rw-r--r--crates/hir-ty/src/diagnostics/expr.rs4
-rw-r--r--crates/hir-ty/src/diagnostics/match_check.rs8
-rw-r--r--crates/hir-ty/src/infer.rs132
-rw-r--r--crates/hir-ty/src/infer/cast.rs6
-rw-r--r--crates/hir-ty/src/infer/closure.rs4
-rw-r--r--crates/hir-ty/src/infer/closure/analysis.rs6
-rw-r--r--crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs56
-rw-r--r--crates/hir-ty/src/infer/coerce.rs23
-rw-r--r--crates/hir-ty/src/infer/expr.rs64
-rw-r--r--crates/hir-ty/src/infer/op.rs13
-rw-r--r--crates/hir-ty/src/infer/pat.rs2052
-rw-r--r--crates/hir-ty/src/infer/path.rs27
-rw-r--r--crates/hir-ty/src/lib.rs19
-rw-r--r--crates/hir-ty/src/mir/lower.rs9
-rw-r--r--crates/hir-ty/src/mir/lower/pattern_matching.rs12
-rw-r--r--crates/hir-ty/src/next_solver.rs8
-rw-r--r--crates/hir-ty/src/next_solver/infer/mod.rs53
-rw-r--r--crates/hir-ty/src/next_solver/infer/select.rs2
-rw-r--r--crates/hir-ty/src/next_solver/ty.rs29
-rw-r--r--crates/hir-ty/src/tests/coercion.rs4
-rw-r--r--crates/hir-ty/src/tests/patterns.rs52
-rw-r--r--crates/hir-ty/src/tests/regression.rs2
-rw-r--r--crates/hir-ty/src/tests/simple.rs6
-rw-r--r--crates/hir/src/diagnostics.rs14
-rw-r--r--crates/hir/src/source_analyzer.rs12
-rw-r--r--crates/ide-assists/src/handlers/destructure_tuple_binding.rs2
-rw-r--r--crates/ide-assists/src/handlers/extract_function.rs3
-rw-r--r--crates/ide-assists/src/handlers/inline_local_variable.rs1
-rw-r--r--crates/ide-completion/src/completions/postfix.rs4
-rw-r--r--crates/ide-diagnostics/src/handlers/missing_match_arms.rs18
-rw-r--r--crates/ide-diagnostics/src/handlers/type_mismatch.rs4
-rw-r--r--crates/ide/src/expand_macro.rs1
-rw-r--r--crates/ide/src/inlay_hints/binding_mode.rs14
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs2
-rw-r--r--crates/intern/src/symbol/symbols.rs5
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,
}