mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-28 10:39:45 +00:00
Equip infer_pat_* with declaration origin
This commit is contained in:
parent
5a3488a43e
commit
7befcb32e4
4 changed files with 101 additions and 26 deletions
|
|
@ -943,7 +943,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
let ty = self.insert_type_vars(ty);
|
let ty = self.insert_type_vars(ty);
|
||||||
let ty = self.normalize_associated_types_in(ty);
|
let ty = self.normalize_associated_types_in(ty);
|
||||||
|
|
||||||
self.infer_top_pat(*pat, &ty);
|
self.infer_top_pat(*pat, &ty, None);
|
||||||
if ty
|
if ty
|
||||||
.data(Interner)
|
.data(Interner)
|
||||||
.flags
|
.flags
|
||||||
|
|
|
||||||
|
|
@ -43,9 +43,9 @@ use crate::{
|
||||||
primitive::{self, UintTy},
|
primitive::{self, UintTy},
|
||||||
static_lifetime, to_chalk_trait_id,
|
static_lifetime, to_chalk_trait_id,
|
||||||
traits::FnTrait,
|
traits::FnTrait,
|
||||||
Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, FnAbi, FnPointer,
|
Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, DeclContext,
|
||||||
FnSig, FnSubst, Interner, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty,
|
DeclOrigin, FnAbi, FnPointer, FnSig, FnSubst, Interner, Rawness, Scalar, Substitution,
|
||||||
TyBuilder, TyExt, TyKind,
|
TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
|
@ -1633,7 +1633,12 @@ impl InferenceContext<'_> {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.infer_top_pat(*pat, &ty);
|
this.infer_top_pat(*pat, &ty);
|
||||||
|
let decl = DeclContext {
|
||||||
|
has_else: else_branch.is_some(),
|
||||||
|
origin: DeclOrigin::LocalDecl,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.infer_top_pat(*pat, &ty, Some(decl));
|
||||||
if let Some(expr) = else_branch {
|
if let Some(expr) = else_branch {
|
||||||
let previous_diverges =
|
let previous_diverges =
|
||||||
mem::replace(&mut this.diverges, Diverges::Maybe);
|
mem::replace(&mut this.diverges, Diverges::Maybe);
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,8 @@ use crate::{
|
||||||
},
|
},
|
||||||
lower::lower_to_chalk_mutability,
|
lower::lower_to_chalk_mutability,
|
||||||
primitive::UintTy,
|
primitive::UintTy,
|
||||||
static_lifetime, InferenceDiagnostic, Interner, Mutability, Scalar, Substitution, Ty,
|
static_lifetime, DeclContext, DeclOrigin, InferenceDiagnostic, Interner, Mutability, Scalar,
|
||||||
TyBuilder, TyExt, TyKind,
|
Substitution, Ty, TyBuilder, TyExt, TyKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl InferenceContext<'_> {
|
impl InferenceContext<'_> {
|
||||||
|
|
@ -35,6 +35,7 @@ impl InferenceContext<'_> {
|
||||||
id: PatId,
|
id: PatId,
|
||||||
ellipsis: Option<u32>,
|
ellipsis: Option<u32>,
|
||||||
subs: &[PatId],
|
subs: &[PatId],
|
||||||
|
decl: Option<DeclContext>,
|
||||||
) -> Ty {
|
) -> Ty {
|
||||||
let (ty, def) = self.resolve_variant(id.into(), path, true);
|
let (ty, def) = self.resolve_variant(id.into(), path, true);
|
||||||
let var_data = def.map(|it| it.variant_data(self.db.upcast()));
|
let var_data = def.map(|it| it.variant_data(self.db.upcast()));
|
||||||
|
|
@ -93,13 +94,13 @@ impl InferenceContext<'_> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.infer_pat(subpat, &expected_ty, default_bm);
|
self.infer_pat(subpat, &expected_ty, default_bm, decl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let err_ty = self.err_ty();
|
let err_ty = self.err_ty();
|
||||||
for &inner in subs {
|
for &inner in subs {
|
||||||
self.infer_pat(inner, &err_ty, default_bm);
|
self.infer_pat(inner, &err_ty, default_bm, decl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -115,6 +116,7 @@ impl InferenceContext<'_> {
|
||||||
default_bm: BindingMode,
|
default_bm: BindingMode,
|
||||||
id: PatId,
|
id: PatId,
|
||||||
subs: impl ExactSizeIterator<Item = (Name, PatId)>,
|
subs: impl ExactSizeIterator<Item = (Name, PatId)>,
|
||||||
|
decl: Option<DeclContext>,
|
||||||
) -> Ty {
|
) -> Ty {
|
||||||
let (ty, def) = self.resolve_variant(id.into(), path, false);
|
let (ty, def) = self.resolve_variant(id.into(), path, false);
|
||||||
if let Some(variant) = def {
|
if let Some(variant) = def {
|
||||||
|
|
@ -163,13 +165,13 @@ impl InferenceContext<'_> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.infer_pat(inner, &expected_ty, default_bm);
|
self.infer_pat(inner, &expected_ty, default_bm, decl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let err_ty = self.err_ty();
|
let err_ty = self.err_ty();
|
||||||
for (_, inner) in subs {
|
for (_, inner) in subs {
|
||||||
self.infer_pat(inner, &err_ty, default_bm);
|
self.infer_pat(inner, &err_ty, default_bm, decl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -186,6 +188,7 @@ impl InferenceContext<'_> {
|
||||||
default_bm: BindingMode,
|
default_bm: BindingMode,
|
||||||
ellipsis: Option<u32>,
|
ellipsis: Option<u32>,
|
||||||
subs: &[PatId],
|
subs: &[PatId],
|
||||||
|
decl: Option<DeclContext>,
|
||||||
) -> Ty {
|
) -> Ty {
|
||||||
let expected = self.resolve_ty_shallow(expected);
|
let expected = self.resolve_ty_shallow(expected);
|
||||||
let expectations = match expected.as_tuple() {
|
let expectations = match expected.as_tuple() {
|
||||||
|
|
@ -210,12 +213,12 @@ impl InferenceContext<'_> {
|
||||||
|
|
||||||
// Process pre
|
// Process pre
|
||||||
for (ty, pat) in inner_tys.iter_mut().zip(pre) {
|
for (ty, pat) in inner_tys.iter_mut().zip(pre) {
|
||||||
*ty = self.infer_pat(*pat, ty, default_bm);
|
*ty = self.infer_pat(*pat, ty, default_bm, decl);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process post
|
// Process post
|
||||||
for (ty, pat) in inner_tys.iter_mut().skip(pre.len() + n_uncovered_patterns).zip(post) {
|
for (ty, pat) in inner_tys.iter_mut().skip(pre.len() + n_uncovered_patterns).zip(post) {
|
||||||
*ty = self.infer_pat(*pat, ty, default_bm);
|
*ty = self.infer_pat(*pat, ty, default_bm, decl);
|
||||||
}
|
}
|
||||||
|
|
||||||
TyKind::Tuple(inner_tys.len(), Substitution::from_iter(Interner, inner_tys))
|
TyKind::Tuple(inner_tys.len(), Substitution::from_iter(Interner, inner_tys))
|
||||||
|
|
@ -224,11 +227,17 @@ impl InferenceContext<'_> {
|
||||||
|
|
||||||
/// The resolver needs to be updated to the surrounding expression when inside assignment
|
/// The resolver needs to be updated to the surrounding expression when inside assignment
|
||||||
/// (because there, `Pat::Path` can refer to a variable).
|
/// (because there, `Pat::Path` can refer to a variable).
|
||||||
pub(super) fn infer_top_pat(&mut self, pat: PatId, expected: &Ty) {
|
pub(super) fn infer_top_pat(&mut self, pat: PatId, expected: &Ty, decl: Option<DeclContext>) {
|
||||||
self.infer_pat(pat, expected, BindingMode::default());
|
self.infer_pat(pat, expected, BindingMode::default(), decl);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_pat(&mut self, pat: PatId, expected: &Ty, mut default_bm: BindingMode) -> Ty {
|
fn infer_pat(
|
||||||
|
&mut self,
|
||||||
|
pat: PatId,
|
||||||
|
expected: &Ty,
|
||||||
|
mut default_bm: BindingMode,
|
||||||
|
decl: Option<DeclContext>,
|
||||||
|
) -> Ty {
|
||||||
let mut expected = self.resolve_ty_shallow(expected);
|
let mut expected = self.resolve_ty_shallow(expected);
|
||||||
|
|
||||||
if matches!(&self.body[pat], Pat::Ref { .. }) || self.inside_assignment {
|
if matches!(&self.body[pat], Pat::Ref { .. }) || self.inside_assignment {
|
||||||
|
|
@ -262,11 +271,11 @@ impl InferenceContext<'_> {
|
||||||
|
|
||||||
let ty = match &self.body[pat] {
|
let ty = match &self.body[pat] {
|
||||||
Pat::Tuple { args, ellipsis } => {
|
Pat::Tuple { args, ellipsis } => {
|
||||||
self.infer_tuple_pat_like(&expected, default_bm, *ellipsis, args)
|
self.infer_tuple_pat_like(&expected, default_bm, *ellipsis, args, decl)
|
||||||
}
|
}
|
||||||
Pat::Or(pats) => {
|
Pat::Or(pats) => {
|
||||||
for pat in pats.iter() {
|
for pat in pats.iter() {
|
||||||
self.infer_pat(*pat, &expected, default_bm);
|
self.infer_pat(*pat, &expected, default_bm, decl);
|
||||||
}
|
}
|
||||||
expected.clone()
|
expected.clone()
|
||||||
}
|
}
|
||||||
|
|
@ -275,6 +284,7 @@ impl InferenceContext<'_> {
|
||||||
lower_to_chalk_mutability(mutability),
|
lower_to_chalk_mutability(mutability),
|
||||||
&expected,
|
&expected,
|
||||||
default_bm,
|
default_bm,
|
||||||
|
decl,
|
||||||
),
|
),
|
||||||
Pat::TupleStruct { path: p, args: subpats, ellipsis } => self
|
Pat::TupleStruct { path: p, args: subpats, ellipsis } => self
|
||||||
.infer_tuple_struct_pat_like(
|
.infer_tuple_struct_pat_like(
|
||||||
|
|
@ -284,10 +294,11 @@ impl InferenceContext<'_> {
|
||||||
pat,
|
pat,
|
||||||
*ellipsis,
|
*ellipsis,
|
||||||
subpats,
|
subpats,
|
||||||
|
decl,
|
||||||
),
|
),
|
||||||
Pat::Record { path: p, args: fields, ellipsis: _ } => {
|
Pat::Record { path: p, args: fields, ellipsis: _ } => {
|
||||||
let subs = fields.iter().map(|f| (f.name.clone(), f.pat));
|
let subs = fields.iter().map(|f| (f.name.clone(), f.pat));
|
||||||
self.infer_record_pat_like(p.as_deref(), &expected, default_bm, pat, subs)
|
self.infer_record_pat_like(p.as_deref(), &expected, default_bm, pat, subs, decl)
|
||||||
}
|
}
|
||||||
Pat::Path(path) => {
|
Pat::Path(path) => {
|
||||||
let ty = self.infer_path(path, pat.into()).unwrap_or_else(|| self.err_ty());
|
let ty = self.infer_path(path, pat.into()).unwrap_or_else(|| self.err_ty());
|
||||||
|
|
@ -320,10 +331,10 @@ impl InferenceContext<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Pat::Bind { id, subpat } => {
|
Pat::Bind { id, subpat } => {
|
||||||
return self.infer_bind_pat(pat, *id, default_bm, *subpat, &expected);
|
return self.infer_bind_pat(pat, *id, default_bm, *subpat, &expected, decl);
|
||||||
}
|
}
|
||||||
Pat::Slice { prefix, slice, suffix } => {
|
Pat::Slice { prefix, slice, suffix } => {
|
||||||
self.infer_slice_pat(&expected, prefix, slice, suffix, default_bm)
|
self.infer_slice_pat(&expected, prefix, slice, suffix, default_bm, decl)
|
||||||
}
|
}
|
||||||
Pat::Wild => expected.clone(),
|
Pat::Wild => expected.clone(),
|
||||||
Pat::Range { .. } => {
|
Pat::Range { .. } => {
|
||||||
|
|
@ -346,7 +357,7 @@ impl InferenceContext<'_> {
|
||||||
_ => (self.result.standard_types.unknown.clone(), None),
|
_ => (self.result.standard_types.unknown.clone(), None),
|
||||||
};
|
};
|
||||||
|
|
||||||
let inner_ty = self.infer_pat(*inner, &inner_ty, default_bm);
|
let inner_ty = self.infer_pat(*inner, &inner_ty, default_bm, decl);
|
||||||
let mut b = TyBuilder::adt(self.db, box_adt).push(inner_ty);
|
let mut b = TyBuilder::adt(self.db, box_adt).push(inner_ty);
|
||||||
|
|
||||||
if let Some(alloc_ty) = alloc_ty {
|
if let Some(alloc_ty) = alloc_ty {
|
||||||
|
|
@ -421,6 +432,7 @@ impl InferenceContext<'_> {
|
||||||
mutability: Mutability,
|
mutability: Mutability,
|
||||||
expected: &Ty,
|
expected: &Ty,
|
||||||
default_bm: BindingMode,
|
default_bm: BindingMode,
|
||||||
|
decl: Option<DeclContext>,
|
||||||
) -> Ty {
|
) -> Ty {
|
||||||
let (expectation_type, expectation_lt) = match expected.as_reference() {
|
let (expectation_type, expectation_lt) = match expected.as_reference() {
|
||||||
Some((inner_ty, lifetime, _exp_mut)) => (inner_ty.clone(), lifetime.clone()),
|
Some((inner_ty, lifetime, _exp_mut)) => (inner_ty.clone(), lifetime.clone()),
|
||||||
|
|
@ -434,7 +446,7 @@ impl InferenceContext<'_> {
|
||||||
(inner_ty, inner_lt)
|
(inner_ty, inner_lt)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let subty = self.infer_pat(inner_pat, &expectation_type, default_bm);
|
let subty = self.infer_pat(inner_pat, &expectation_type, default_bm, decl);
|
||||||
TyKind::Ref(mutability, expectation_lt, subty).intern(Interner)
|
TyKind::Ref(mutability, expectation_lt, subty).intern(Interner)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -445,6 +457,7 @@ impl InferenceContext<'_> {
|
||||||
default_bm: BindingMode,
|
default_bm: BindingMode,
|
||||||
subpat: Option<PatId>,
|
subpat: Option<PatId>,
|
||||||
expected: &Ty,
|
expected: &Ty,
|
||||||
|
decl: Option<DeclContext>,
|
||||||
) -> Ty {
|
) -> Ty {
|
||||||
let Binding { mode, .. } = self.body.bindings[binding];
|
let Binding { mode, .. } = self.body.bindings[binding];
|
||||||
let mode = if mode == BindingAnnotation::Unannotated {
|
let mode = if mode == BindingAnnotation::Unannotated {
|
||||||
|
|
@ -455,7 +468,7 @@ impl InferenceContext<'_> {
|
||||||
self.result.binding_modes.insert(pat, mode);
|
self.result.binding_modes.insert(pat, mode);
|
||||||
|
|
||||||
let inner_ty = match subpat {
|
let inner_ty = match subpat {
|
||||||
Some(subpat) => self.infer_pat(subpat, expected, default_bm),
|
Some(subpat) => self.infer_pat(subpat, expected, default_bm, decl),
|
||||||
None => expected.clone(),
|
None => expected.clone(),
|
||||||
};
|
};
|
||||||
let inner_ty = self.insert_type_vars_shallow(inner_ty);
|
let inner_ty = self.insert_type_vars_shallow(inner_ty);
|
||||||
|
|
@ -479,12 +492,13 @@ impl InferenceContext<'_> {
|
||||||
slice: &Option<PatId>,
|
slice: &Option<PatId>,
|
||||||
suffix: &[PatId],
|
suffix: &[PatId],
|
||||||
default_bm: BindingMode,
|
default_bm: BindingMode,
|
||||||
|
decl: Option<DeclContext>,
|
||||||
) -> Ty {
|
) -> Ty {
|
||||||
let expected = self.resolve_ty_shallow(expected);
|
let expected = self.resolve_ty_shallow(expected);
|
||||||
|
|
||||||
// If `expected` is an infer ty, we try to equate it to an array if the given pattern
|
// If `expected` is an infer ty, we try to equate it to an array if the given pattern
|
||||||
// allows it. See issue #16609
|
// allows it. See issue #16609
|
||||||
if expected.is_ty_var() {
|
if self.decl_allows_array_type_infer(decl) && expected.is_ty_var() {
|
||||||
if let Some(resolved_array_ty) =
|
if let Some(resolved_array_ty) =
|
||||||
self.try_resolve_slice_ty_to_array_ty(prefix, suffix, slice)
|
self.try_resolve_slice_ty_to_array_ty(prefix, suffix, slice)
|
||||||
{
|
{
|
||||||
|
|
@ -499,7 +513,7 @@ impl InferenceContext<'_> {
|
||||||
};
|
};
|
||||||
|
|
||||||
for &pat_id in prefix.iter().chain(suffix.iter()) {
|
for &pat_id in prefix.iter().chain(suffix.iter()) {
|
||||||
self.infer_pat(pat_id, &elem_ty, default_bm);
|
self.infer_pat(pat_id, &elem_ty, default_bm, decl);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let &Some(slice_pat_id) = slice {
|
if let &Some(slice_pat_id) = slice {
|
||||||
|
|
@ -513,7 +527,7 @@ impl InferenceContext<'_> {
|
||||||
_ => TyKind::Slice(elem_ty.clone()),
|
_ => TyKind::Slice(elem_ty.clone()),
|
||||||
}
|
}
|
||||||
.intern(Interner);
|
.intern(Interner);
|
||||||
self.infer_pat(slice_pat_id, &rest_pat_ty, default_bm);
|
self.infer_pat(slice_pat_id, &rest_pat_ty, default_bm, decl);
|
||||||
}
|
}
|
||||||
|
|
||||||
match expected.kind(Interner) {
|
match expected.kind(Interner) {
|
||||||
|
|
@ -586,6 +600,44 @@ impl InferenceContext<'_> {
|
||||||
let array_ty = TyKind::Array(elem_ty.clone(), size).intern(Interner);
|
let array_ty = TyKind::Array(elem_ty.clone(), size).intern(Interner);
|
||||||
Some(array_ty)
|
Some(array_ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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;
|
||||||
|
/// const ARR: [usize; 2] = [0; 2];
|
||||||
|
/// const ARR2: [usize; 2] = [2; 2];
|
||||||
|
///
|
||||||
|
/// impl Into<&'static [usize; 2]> for Zeroes {
|
||||||
|
/// fn into(self) -> &'static [usize; 2] {
|
||||||
|
/// &ARR
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// impl Into<&'static [usize]> for Zeroes {
|
||||||
|
/// fn into(self) -> &'static [usize] {
|
||||||
|
/// &ARR2
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// let &[a, b]: &[usize] = Zeroes.into() else {
|
||||||
|
/// ..
|
||||||
|
/// };
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// If we're in an irrefutable pattern we prefer the array impl candidate given that
|
||||||
|
/// the slice impl candidate would be be rejected anyway (if no ambiguity existed).
|
||||||
|
fn decl_allows_array_type_infer(&self, decl_ctxt: Option<DeclContext>) -> bool {
|
||||||
|
if let Some(decl_ctxt) = decl_ctxt {
|
||||||
|
!decl_ctxt.has_else && matches!(decl_ctxt.origin, DeclOrigin::LocalDecl)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn contains_explicit_ref_binding(body: &Body, pat_id: PatId) -> bool {
|
pub(super) fn contains_explicit_ref_binding(body: &Body, pat_id: PatId) -> bool {
|
||||||
|
|
|
||||||
|
|
@ -1049,3 +1049,21 @@ pub fn known_const_to_ast(
|
||||||
}
|
}
|
||||||
Some(make::expr_const_value(konst.display(db, edition).to_string().as_str()))
|
Some(make::expr_const_value(konst.display(db, edition).to_string().as_str()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub(crate) enum DeclOrigin {
|
||||||
|
// from an `if let` expression
|
||||||
|
LetExpr,
|
||||||
|
// from `let x = ..`
|
||||||
|
LocalDecl,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 #76342.
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub(crate) struct DeclContext {
|
||||||
|
// whether we're in a let-else context
|
||||||
|
pub(crate) has_else: bool,
|
||||||
|
pub(crate) origin: DeclOrigin,
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue