mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 05:23:24 +00:00
Auto merge of #12428 - lowr:experimental/destructuring-assignment, r=flodiebold
feat: implement destructuring assignment This is an attempt to implement destructuring assignments, or more specifically, type inference for [assignee expressions](https://doc.rust-lang.org/reference/expressions.html#place-expressions-and-value-expressions). I'm not sure if this is the right approach, so I don't even expect this to be merged (hence the branch name 😉) but rather want to propose one direction we could choose. I don't mind getting merged if this is good enough though! Some notes on the implementation choices: - Assignee expressions are **not** desugared on HIR level unlike rustc, but are inferred directly along with other expressions. This matches the processing of other syntaxes that are desugared in rustc but not in r-a. I find this reasonable because r-a only needs to infer types and it's easier to relate AST nodes and HIR nodes, so I followed it. - Assignee expressions obviously resemble patterns, so type inference for each kind of pattern and its corresponding assignee expressions share a significant amount of logic. I tried to reuse the type inference functions for patterns by introducing `PatLike` trait which generalizes assignee expressions and patterns. - This is not the most elegant solution I suspect (and I really don't like the name of the trait!), but it's cleaner and the change is smaller than other ways I experimented, like making the functions generic without such trait, or making them take `Either<ExprId, PatId>` in place of `PatId`. in case this is merged: Closes #11532 Closes #11839 Closes #12322
This commit is contained in:
commit
2ff505ab48
7 changed files with 532 additions and 68 deletions
|
@ -536,7 +536,7 @@ impl ExprCollector<'_> {
|
|||
|
||||
self.alloc_expr(Expr::MacroStmts { tail }, syntax_ptr)
|
||||
}
|
||||
ast::Expr::UnderscoreExpr(_) => return None,
|
||||
ast::Expr::UnderscoreExpr(_) => self.alloc_expr(Expr::Underscore, syntax_ptr),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -203,6 +203,7 @@ pub enum Expr {
|
|||
},
|
||||
Array(Array),
|
||||
Literal(Literal),
|
||||
Underscore,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
|
@ -345,6 +346,7 @@ impl Expr {
|
|||
},
|
||||
Expr::MacroStmts { tail } => f(*tail),
|
||||
Expr::Literal(_) => {}
|
||||
Expr::Underscore => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -125,6 +125,39 @@ impl Default for BindingMode {
|
|||
}
|
||||
}
|
||||
|
||||
/// Used to generalize patterns and assignee expressions.
|
||||
trait PatLike: Into<ExprOrPatId> + Copy {
|
||||
type BindingMode: Copy;
|
||||
|
||||
fn infer(
|
||||
this: &mut InferenceContext,
|
||||
id: Self,
|
||||
expected_ty: &Ty,
|
||||
default_bm: Self::BindingMode,
|
||||
) -> Ty;
|
||||
}
|
||||
|
||||
impl PatLike for ExprId {
|
||||
type BindingMode = ();
|
||||
|
||||
fn infer(this: &mut InferenceContext, id: Self, expected_ty: &Ty, _: Self::BindingMode) -> Ty {
|
||||
this.infer_assignee_expr(id, expected_ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl PatLike for PatId {
|
||||
type BindingMode = BindingMode;
|
||||
|
||||
fn infer(
|
||||
this: &mut InferenceContext,
|
||||
id: Self,
|
||||
expected_ty: &Ty,
|
||||
default_bm: Self::BindingMode,
|
||||
) -> Ty {
|
||||
this.infer_pat(id, expected_ty, default_bm)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct InferOk<T> {
|
||||
value: T,
|
||||
|
|
|
@ -593,8 +593,8 @@ impl<'a> InferenceContext<'a> {
|
|||
}
|
||||
Expr::BinaryOp { lhs, rhs, op } => match op {
|
||||
Some(BinaryOp::Assignment { op: None }) => {
|
||||
let lhs_ty = self.infer_expr(*lhs, &Expectation::none());
|
||||
self.infer_expr_coerce(*rhs, &Expectation::has_type(lhs_ty));
|
||||
let rhs_ty = self.infer_expr(*rhs, &Expectation::none());
|
||||
self.infer_assignee_expr(*lhs, &rhs_ty);
|
||||
self.result.standard_types.unit.clone()
|
||||
}
|
||||
Some(BinaryOp::LogicOp(_)) => {
|
||||
|
@ -775,6 +775,12 @@ impl<'a> InferenceContext<'a> {
|
|||
},
|
||||
},
|
||||
Expr::MacroStmts { tail } => self.infer_expr_inner(*tail, expected),
|
||||
Expr::Underscore => {
|
||||
// Underscore expressions may only appear in assignee expressions,
|
||||
// which are handled by `infer_assignee_expr()`, so any underscore
|
||||
// expression reaching this branch is an error.
|
||||
self.err_ty()
|
||||
}
|
||||
};
|
||||
// use a new type variable if we got unknown here
|
||||
let ty = self.insert_type_vars_shallow(ty);
|
||||
|
@ -811,6 +817,95 @@ impl<'a> InferenceContext<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn infer_assignee_expr(&mut self, lhs: ExprId, rhs_ty: &Ty) -> Ty {
|
||||
let is_rest_expr = |expr| {
|
||||
matches!(
|
||||
&self.body[expr],
|
||||
Expr::Range { lhs: None, rhs: None, range_type: RangeOp::Exclusive },
|
||||
)
|
||||
};
|
||||
|
||||
let rhs_ty = self.resolve_ty_shallow(rhs_ty);
|
||||
|
||||
let ty = match &self.body[lhs] {
|
||||
Expr::Tuple { exprs } => {
|
||||
// We don't consider multiple ellipses. This is analogous to
|
||||
// `hir_def::body::lower::ExprCollector::collect_tuple_pat()`.
|
||||
let ellipsis = exprs.iter().position(|e| is_rest_expr(*e));
|
||||
let exprs: Vec<_> = exprs.iter().filter(|e| !is_rest_expr(**e)).copied().collect();
|
||||
|
||||
self.infer_tuple_pat_like(&rhs_ty, (), ellipsis, &exprs)
|
||||
}
|
||||
Expr::Call { callee, args } => {
|
||||
// Tuple structs
|
||||
let path = match &self.body[*callee] {
|
||||
Expr::Path(path) => Some(path),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// We don't consider multiple ellipses. This is analogous to
|
||||
// `hir_def::body::lower::ExprCollector::collect_tuple_pat()`.
|
||||
let ellipsis = args.iter().position(|e| is_rest_expr(*e));
|
||||
let args: Vec<_> = args.iter().filter(|e| !is_rest_expr(**e)).copied().collect();
|
||||
|
||||
self.infer_tuple_struct_pat_like(path, &rhs_ty, (), lhs, ellipsis, &args)
|
||||
}
|
||||
Expr::Array(Array::ElementList(elements)) => {
|
||||
let elem_ty = match rhs_ty.kind(Interner) {
|
||||
TyKind::Array(st, _) => st.clone(),
|
||||
_ => self.err_ty(),
|
||||
};
|
||||
|
||||
// There's no need to handle `..` as it cannot be bound.
|
||||
let sub_exprs = elements.iter().filter(|e| !is_rest_expr(**e));
|
||||
|
||||
for e in sub_exprs {
|
||||
self.infer_assignee_expr(*e, &elem_ty);
|
||||
}
|
||||
|
||||
match rhs_ty.kind(Interner) {
|
||||
TyKind::Array(_, _) => rhs_ty.clone(),
|
||||
// Even when `rhs_ty` is not an array type, this assignee
|
||||
// expression is infered to be an array (of unknown element
|
||||
// type and length). This should not be just an error type,
|
||||
// because we are to compute the unifiability of this type and
|
||||
// `rhs_ty` in the end of this function to issue type mismatches.
|
||||
_ => TyKind::Array(self.err_ty(), crate::consteval::usize_const(None))
|
||||
.intern(Interner),
|
||||
}
|
||||
}
|
||||
Expr::RecordLit { path, fields, .. } => {
|
||||
let subs = fields.iter().map(|f| (f.name.clone(), f.expr));
|
||||
|
||||
self.infer_record_pat_like(path.as_deref(), &rhs_ty, (), lhs.into(), subs)
|
||||
}
|
||||
Expr::Underscore => rhs_ty.clone(),
|
||||
_ => {
|
||||
// `lhs` is a place expression, a unit struct, or an enum variant.
|
||||
let lhs_ty = self.infer_expr(lhs, &Expectation::none());
|
||||
|
||||
// This is the only branch where this function may coerce any type.
|
||||
// We are returning early to avoid the unifiability check below.
|
||||
let lhs_ty = self.insert_type_vars_shallow(lhs_ty);
|
||||
let ty = match self.coerce(None, &rhs_ty, &lhs_ty) {
|
||||
Ok(ty) => ty,
|
||||
Err(_) => self.err_ty(),
|
||||
};
|
||||
self.write_expr_ty(lhs, ty.clone());
|
||||
return ty;
|
||||
}
|
||||
};
|
||||
|
||||
let ty = self.insert_type_vars_shallow(ty);
|
||||
if !self.unify(&ty, &rhs_ty) {
|
||||
self.result
|
||||
.type_mismatches
|
||||
.insert(lhs.into(), TypeMismatch { expected: rhs_ty.clone(), actual: ty.clone() });
|
||||
}
|
||||
self.write_expr_ty(lhs, ty.clone());
|
||||
ty
|
||||
}
|
||||
|
||||
fn infer_overloadable_binop(
|
||||
&mut self,
|
||||
lhs: ExprId,
|
||||
|
|
|
@ -4,7 +4,7 @@ use std::iter::repeat_with;
|
|||
|
||||
use chalk_ir::Mutability;
|
||||
use hir_def::{
|
||||
expr::{BindingAnnotation, Expr, Literal, Pat, PatId, RecordFieldPat},
|
||||
expr::{BindingAnnotation, Expr, Literal, Pat, PatId},
|
||||
path::Path,
|
||||
type_ref::ConstScalar,
|
||||
};
|
||||
|
@ -17,15 +17,20 @@ use crate::{
|
|||
TyKind,
|
||||
};
|
||||
|
||||
use super::PatLike;
|
||||
|
||||
impl<'a> InferenceContext<'a> {
|
||||
fn infer_tuple_struct_pat(
|
||||
/// 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<T: PatLike>(
|
||||
&mut self,
|
||||
path: Option<&Path>,
|
||||
subpats: &[PatId],
|
||||
expected: &Ty,
|
||||
default_bm: BindingMode,
|
||||
id: PatId,
|
||||
default_bm: T::BindingMode,
|
||||
id: T,
|
||||
ellipsis: Option<usize>,
|
||||
subs: &[T],
|
||||
) -> Ty {
|
||||
let (ty, def) = self.resolve_variant(path, true);
|
||||
let var_data = def.map(|it| it.variant_data(self.db.upcast()));
|
||||
|
@ -39,8 +44,8 @@ impl<'a> InferenceContext<'a> {
|
|||
|
||||
let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default();
|
||||
let (pre, post) = match ellipsis {
|
||||
Some(idx) => subpats.split_at(idx),
|
||||
None => (subpats, &[][..]),
|
||||
Some(idx) => subs.split_at(idx),
|
||||
None => (subs, &[][..]),
|
||||
};
|
||||
let post_idx_offset = field_tys.iter().count().saturating_sub(post.len());
|
||||
|
||||
|
@ -54,22 +59,22 @@ impl<'a> InferenceContext<'a> {
|
|||
field_tys[field].clone().substitute(Interner, &substs)
|
||||
});
|
||||
let expected_ty = self.normalize_associated_types_in(expected_ty);
|
||||
self.infer_pat(subpat, &expected_ty, default_bm);
|
||||
T::infer(self, subpat, &expected_ty, default_bm);
|
||||
}
|
||||
|
||||
ty
|
||||
}
|
||||
|
||||
fn infer_record_pat(
|
||||
/// Infers type for record pattern or its corresponding assignee expression.
|
||||
pub(super) fn infer_record_pat_like<T: PatLike>(
|
||||
&mut self,
|
||||
path: Option<&Path>,
|
||||
subpats: &[RecordFieldPat],
|
||||
expected: &Ty,
|
||||
default_bm: BindingMode,
|
||||
id: PatId,
|
||||
default_bm: T::BindingMode,
|
||||
id: T,
|
||||
subs: impl Iterator<Item = (Name, T)>,
|
||||
) -> Ty {
|
||||
let (ty, def) = self.resolve_variant(path, false);
|
||||
let var_data = def.map(|it| it.variant_data(self.db.upcast()));
|
||||
if let Some(variant) = def {
|
||||
self.write_variant_resolution(id.into(), variant);
|
||||
}
|
||||
|
@ -80,18 +85,64 @@ impl<'a> InferenceContext<'a> {
|
|||
ty.as_adt().map(|(_, s)| s.clone()).unwrap_or_else(|| Substitution::empty(Interner));
|
||||
|
||||
let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default();
|
||||
for subpat in subpats {
|
||||
let matching_field = var_data.as_ref().and_then(|it| it.field(&subpat.name));
|
||||
let expected_ty = matching_field.map_or(self.err_ty(), |field| {
|
||||
field_tys[field].clone().substitute(Interner, &substs)
|
||||
});
|
||||
let var_data = def.map(|it| it.variant_data(self.db.upcast()));
|
||||
|
||||
for (name, inner) in subs {
|
||||
let expected_ty = var_data
|
||||
.as_ref()
|
||||
.and_then(|it| it.field(&name))
|
||||
.map_or(self.err_ty(), |f| field_tys[f].clone().substitute(Interner, &substs));
|
||||
let expected_ty = self.normalize_associated_types_in(expected_ty);
|
||||
self.infer_pat(subpat.pat, &expected_ty, default_bm);
|
||||
|
||||
T::infer(self, inner, &expected_ty, default_bm);
|
||||
}
|
||||
|
||||
ty
|
||||
}
|
||||
|
||||
/// Infers type for tuple pattern or its corresponding assignee expression.
|
||||
///
|
||||
/// Ellipses found in the original pattern or expression must be filtered out.
|
||||
pub(super) fn infer_tuple_pat_like<T: PatLike>(
|
||||
&mut self,
|
||||
expected: &Ty,
|
||||
default_bm: T::BindingMode,
|
||||
ellipsis: Option<usize>,
|
||||
subs: &[T],
|
||||
) -> Ty {
|
||||
let expectations = match expected.as_tuple() {
|
||||
Some(parameters) => &*parameters.as_slice(Interner),
|
||||
_ => &[],
|
||||
};
|
||||
|
||||
let ((pre, post), n_uncovered_patterns) = match ellipsis {
|
||||
Some(idx) => (subs.split_at(idx), expectations.len().saturating_sub(subs.len())),
|
||||
None => ((&subs[..], &[][..]), 0),
|
||||
};
|
||||
let mut expectations_iter = expectations
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|a| a.assert_ty_ref(Interner).clone())
|
||||
.chain(repeat_with(|| self.table.new_type_var()));
|
||||
|
||||
let mut inner_tys = Vec::with_capacity(n_uncovered_patterns + subs.len());
|
||||
|
||||
inner_tys.extend(expectations_iter.by_ref().take(n_uncovered_patterns + subs.len()));
|
||||
|
||||
// Process pre
|
||||
for (ty, pat) in inner_tys.iter_mut().zip(pre) {
|
||||
*ty = T::infer(self, *pat, ty, default_bm);
|
||||
}
|
||||
|
||||
// Process post
|
||||
for (ty, pat) in inner_tys.iter_mut().skip(pre.len() + n_uncovered_patterns).zip(post) {
|
||||
*ty = T::infer(self, *pat, ty, default_bm);
|
||||
}
|
||||
|
||||
TyKind::Tuple(inner_tys.len(), Substitution::from_iter(Interner, inner_tys))
|
||||
.intern(Interner)
|
||||
}
|
||||
|
||||
pub(super) fn infer_pat(
|
||||
&mut self,
|
||||
pat: PatId,
|
||||
|
@ -129,42 +180,7 @@ impl<'a> InferenceContext<'a> {
|
|||
|
||||
let ty = match &self.body[pat] {
|
||||
Pat::Tuple { args, ellipsis } => {
|
||||
let expectations = match expected.as_tuple() {
|
||||
Some(parameters) => &*parameters.as_slice(Interner),
|
||||
_ => &[],
|
||||
};
|
||||
|
||||
let ((pre, post), n_uncovered_patterns) = match ellipsis {
|
||||
Some(idx) => {
|
||||
(args.split_at(*idx), expectations.len().saturating_sub(args.len()))
|
||||
}
|
||||
None => ((&args[..], &[][..]), 0),
|
||||
};
|
||||
let mut expectations_iter = expectations
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|a| a.assert_ty_ref(Interner).clone())
|
||||
.chain(repeat_with(|| self.table.new_type_var()));
|
||||
|
||||
let mut inner_tys = Vec::with_capacity(n_uncovered_patterns + args.len());
|
||||
|
||||
inner_tys
|
||||
.extend(expectations_iter.by_ref().take(n_uncovered_patterns + args.len()));
|
||||
|
||||
// Process pre
|
||||
for (ty, pat) in inner_tys.iter_mut().zip(pre) {
|
||||
*ty = self.infer_pat(*pat, ty, default_bm);
|
||||
}
|
||||
|
||||
// Process 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);
|
||||
}
|
||||
|
||||
TyKind::Tuple(inner_tys.len(), Substitution::from_iter(Interner, inner_tys))
|
||||
.intern(Interner)
|
||||
self.infer_tuple_pat_like(&expected, default_bm, *ellipsis, args)
|
||||
}
|
||||
Pat::Or(pats) => {
|
||||
if let Some((first_pat, rest)) = pats.split_first() {
|
||||
|
@ -191,16 +207,18 @@ impl<'a> InferenceContext<'a> {
|
|||
let subty = self.infer_pat(*pat, &expectation, default_bm);
|
||||
TyKind::Ref(mutability, static_lifetime(), subty).intern(Interner)
|
||||
}
|
||||
Pat::TupleStruct { path: p, args: subpats, ellipsis } => self.infer_tuple_struct_pat(
|
||||
p.as_deref(),
|
||||
subpats,
|
||||
&expected,
|
||||
default_bm,
|
||||
pat,
|
||||
*ellipsis,
|
||||
),
|
||||
Pat::TupleStruct { path: p, args: subpats, ellipsis } => self
|
||||
.infer_tuple_struct_pat_like(
|
||||
p.as_deref(),
|
||||
&expected,
|
||||
default_bm,
|
||||
pat,
|
||||
*ellipsis,
|
||||
subpats,
|
||||
),
|
||||
Pat::Record { path: p, args: fields, ellipsis: _ } => {
|
||||
self.infer_record_pat(p.as_deref(), fields, &expected, default_bm, pat)
|
||||
let subs = fields.iter().map(|f| (f.name.clone(), f.pat));
|
||||
self.infer_record_pat_like(p.as_deref(), &expected, default_bm, pat.into(), subs)
|
||||
}
|
||||
Pat::Path(path) => {
|
||||
// FIXME use correct resolver for the surrounding expression
|
||||
|
|
|
@ -312,6 +312,24 @@ fn f(text: &str) {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn destructuring_assign_coerce() {
|
||||
check_no_mismatches(
|
||||
r"
|
||||
//- minicore: deref
|
||||
struct String;
|
||||
impl core::ops::Deref for String { type Target = str; }
|
||||
fn g(_text: &str) {}
|
||||
fn f(text: &str) {
|
||||
let mut text = text;
|
||||
let tmp = String;
|
||||
[text, _] = [&tmp, &tmp];
|
||||
g(text);
|
||||
}
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coerce_fn_item_to_fn_ptr() {
|
||||
check_no_mismatches(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use expect_test::expect;
|
||||
|
||||
use super::{check_infer, check_no_mismatches, check_types};
|
||||
use super::{check, check_infer, check_no_mismatches, check_types};
|
||||
|
||||
#[test]
|
||||
fn infer_box() {
|
||||
|
@ -2745,3 +2745,301 @@ fn f() {
|
|||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn destructuring_assignment_slice() {
|
||||
check_types(
|
||||
r#"
|
||||
fn main() {
|
||||
let a;
|
||||
//^usize
|
||||
[a,] = [0usize];
|
||||
|
||||
let a;
|
||||
//^usize
|
||||
[a, ..] = [0usize; 5];
|
||||
|
||||
let a;
|
||||
//^usize
|
||||
[.., a] = [0usize; 5];
|
||||
|
||||
let a;
|
||||
//^usize
|
||||
[.., a, _] = [0usize; 5];
|
||||
|
||||
let a;
|
||||
//^usize
|
||||
[_, a, ..] = [0usize; 5];
|
||||
|
||||
let a: &mut i64 = &mut 0;
|
||||
[*a, ..] = [1, 2, 3];
|
||||
|
||||
let a: usize;
|
||||
let b;
|
||||
//^usize
|
||||
[a, _, b] = [3, 4, 5];
|
||||
//^usize
|
||||
|
||||
let a;
|
||||
//^i64
|
||||
let b;
|
||||
//^i64
|
||||
[[a, ..], .., [.., b]] = [[1, 2], [3i64, 4], [5, 6], [7, 8]];
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn destructuring_assignment_tuple() {
|
||||
check_types(
|
||||
r#"
|
||||
fn main() {
|
||||
let a;
|
||||
//^char
|
||||
let b;
|
||||
//^i64
|
||||
(a, b) = ('c', 0i64);
|
||||
|
||||
let a;
|
||||
//^char
|
||||
(a, ..) = ('c', 0i64);
|
||||
|
||||
let a;
|
||||
//^i64
|
||||
(.., a) = ('c', 0i64);
|
||||
|
||||
let a;
|
||||
//^char
|
||||
let b;
|
||||
//^i64
|
||||
(a, .., b) = ('c', 0i64);
|
||||
|
||||
let a;
|
||||
//^char
|
||||
let b;
|
||||
//^bool
|
||||
(a, .., b) = ('c', 0i64, true);
|
||||
|
||||
let a;
|
||||
//^i64
|
||||
let b;
|
||||
//^bool
|
||||
(_, a, .., b) = ('c', 0i64, true);
|
||||
|
||||
let a;
|
||||
//^i64
|
||||
let b;
|
||||
//^usize
|
||||
(_, a, .., b) = ('c', 0i64, true, 0usize);
|
||||
|
||||
let mut a = 1;
|
||||
//^^^^^i64
|
||||
let mut b: i64 = 0;
|
||||
(a, b) = (b, a);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn destructuring_assignment_tuple_struct() {
|
||||
check_types(
|
||||
r#"
|
||||
struct S2(char, i64);
|
||||
struct S3(char, i64, bool);
|
||||
struct S4(char, i64, bool usize);
|
||||
fn main() {
|
||||
let a;
|
||||
//^char
|
||||
let b;
|
||||
//^i64
|
||||
S2(a, b) = S2('c', 0i64);
|
||||
|
||||
let a;
|
||||
//^char
|
||||
let b;
|
||||
//^i64
|
||||
S2(a, .., b) = S2('c', 0i64);
|
||||
|
||||
let a;
|
||||
//^char
|
||||
let b;
|
||||
//^bool
|
||||
S3(a, .., b) = S3('c', 0i64, true);
|
||||
|
||||
let a;
|
||||
//^i64
|
||||
let b;
|
||||
//^bool
|
||||
S3(_, a, .., b) = S3('c', 0i64, true);
|
||||
|
||||
let a;
|
||||
//^i64
|
||||
let b;
|
||||
//^usize
|
||||
S4(_, a, .., b) = S4('c', 0i64, true, 0usize);
|
||||
|
||||
struct Swap(i64, i64);
|
||||
|
||||
let mut a = 1;
|
||||
//^^^^^i64
|
||||
let mut b = 0;
|
||||
//^^^^^i64
|
||||
Swap(a, b) = Swap(b, a);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn destructuring_assignment_struct() {
|
||||
check_types(
|
||||
r#"
|
||||
struct S {
|
||||
a: usize,
|
||||
b: char,
|
||||
}
|
||||
struct T {
|
||||
s: S,
|
||||
t: i64,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a;
|
||||
//^usize
|
||||
let c;
|
||||
//^char
|
||||
S { a, b: c } = S { a: 3, b: 'b' };
|
||||
|
||||
let a;
|
||||
//^char
|
||||
S { b: a, .. } = S { a: 3, b: 'b' };
|
||||
|
||||
let a;
|
||||
//^char
|
||||
S { b: a, _ } = S { a: 3, b: 'b' };
|
||||
|
||||
let a;
|
||||
//^usize
|
||||
let c;
|
||||
//^char
|
||||
let t;
|
||||
//^i64
|
||||
T { s: S { a, b: c }, t } = T { s: S { a: 3, b: 'b' }, t: 0 };
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn destructuring_assignment_nested() {
|
||||
check_types(
|
||||
r#"
|
||||
struct S {
|
||||
a: TS,
|
||||
b: [char; 3],
|
||||
}
|
||||
struct TS(usize, i64);
|
||||
|
||||
fn main() {
|
||||
let a;
|
||||
//^i32
|
||||
let b;
|
||||
//^bool
|
||||
([.., a], .., b, _) = ([0, 1, 2], true, 'c');
|
||||
|
||||
let a;
|
||||
//^i32
|
||||
let b;
|
||||
//^i32
|
||||
[(.., a, _), .., (b, ..)] = [(1, 2); 5];
|
||||
|
||||
let a;
|
||||
//^usize
|
||||
let b;
|
||||
//^char
|
||||
S { a: TS(a, ..), b: [_, b, ..] } = S { a: TS(0, 0), b: ['a'; 3] };
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn destructuring_assignment_unit_struct() {
|
||||
// taken from rustc; see https://github.com/rust-lang/rust/pull/95380
|
||||
check_no_mismatches(
|
||||
r#"
|
||||
struct S;
|
||||
enum E { V, }
|
||||
type A = E;
|
||||
|
||||
fn main() {
|
||||
let mut a;
|
||||
|
||||
(S, a) = (S, ());
|
||||
|
||||
(E::V, a) = (E::V, ());
|
||||
|
||||
(<E>::V, a) = (E::V, ());
|
||||
(A::V, a) = (E::V, ());
|
||||
}
|
||||
|
||||
impl S {
|
||||
fn check() {
|
||||
let a;
|
||||
(Self, a) = (S, ());
|
||||
}
|
||||
}
|
||||
|
||||
impl E {
|
||||
fn check() {
|
||||
let a;
|
||||
(Self::V, a) = (E::V, ());
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn destructuring_assignment_no_default_binding_mode() {
|
||||
check(
|
||||
r#"
|
||||
struct S { a: usize }
|
||||
struct TS(usize);
|
||||
fn main() {
|
||||
let x;
|
||||
[x,] = &[1,];
|
||||
//^^^^expected &[i32; 1], got [{unknown}; _]
|
||||
|
||||
// FIXME we only want the outermost error, but this matches the current
|
||||
// behavior of slice patterns
|
||||
let x;
|
||||
[(x,),] = &[(1,),];
|
||||
// ^^^^expected {unknown}, got ({unknown},)
|
||||
//^^^^^^^expected &[(i32,); 1], got [{unknown}; _]
|
||||
|
||||
let x;
|
||||
((x,),) = &((1,),);
|
||||
//^^^^^^^expected &((i32,),), got (({unknown},),)
|
||||
|
||||
let x;
|
||||
(x,) = &(1,);
|
||||
//^^^^expected &(i32,), got ({unknown},)
|
||||
|
||||
let x;
|
||||
(S { a: x },) = &(S { a: 42 },);
|
||||
//^^^^^^^^^^^^^expected &(S,), got (S,)
|
||||
|
||||
let x;
|
||||
S { a: x } = &S { a: 42 };
|
||||
//^^^^^^^^^^expected &S, got S
|
||||
|
||||
let x;
|
||||
TS(x) = &TS(42);
|
||||
//^^^^^expected &TS, got TS
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue