mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-13 13:48:50 +00:00
Handle destructuring assignments uniformly
Instead of lowering them to `<expr> = <expr>`, then hacking on-demand to resolve them, we lower them to `<pat> = <expr>`, and use the pattern infrastructure to handle them. It turns out, destructuring assignments are surprisingly similar to pattern bindings, and so only minor modifications are needed. This fixes few bugs that arose because of the non-uniform handling (for example, MIR lowering not handling slice and record patterns, and closure capture calculation not handling destructuring assignments at all), and furthermore, guarantees we won't have such bugs in the future, since the programmer will always have to explicitly handle `Expr::Assignment`. Tests don't pass yet; that's because the generated patterns do not exist in the source map. The next commit will fix that.
This commit is contained in:
parent
4379153e59
commit
61f162a43d
20 changed files with 886 additions and 707 deletions
|
@ -22,7 +22,8 @@ use crate::{
|
|||
db::DefDatabase,
|
||||
expander::Expander,
|
||||
hir::{
|
||||
dummy_expr_id, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat, PatId, RecordFieldPat,
|
||||
dummy_expr_id, Array, AsmOperand, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat,
|
||||
PatId, RecordFieldPat, Statement,
|
||||
},
|
||||
item_tree::AttrOwner,
|
||||
nameres::DefMap,
|
||||
|
@ -286,7 +287,8 @@ impl Body {
|
|||
| Pat::Path(..)
|
||||
| Pat::ConstBlock(..)
|
||||
| Pat::Wild
|
||||
| Pat::Missing => {}
|
||||
| Pat::Missing
|
||||
| Pat::Expr(_) => {}
|
||||
&Pat::Bind { subpat, .. } => {
|
||||
if let Some(subpat) = subpat {
|
||||
f(subpat);
|
||||
|
@ -322,6 +324,143 @@ impl Body {
|
|||
None => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn walk_child_exprs(&self, expr_id: ExprId, mut f: impl FnMut(ExprId)) {
|
||||
let expr = &self[expr_id];
|
||||
match expr {
|
||||
Expr::Continue { .. }
|
||||
| Expr::Const(_)
|
||||
| Expr::Missing
|
||||
| Expr::Path(_)
|
||||
| Expr::OffsetOf(_)
|
||||
| Expr::Literal(_)
|
||||
| Expr::Underscore => {}
|
||||
Expr::InlineAsm(it) => it.operands.iter().for_each(|(_, op)| match op {
|
||||
AsmOperand::In { expr, .. }
|
||||
| AsmOperand::Out { expr: Some(expr), .. }
|
||||
| AsmOperand::InOut { expr, .. } => f(*expr),
|
||||
AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
|
||||
f(*in_expr);
|
||||
if let Some(out_expr) = out_expr {
|
||||
f(*out_expr);
|
||||
}
|
||||
}
|
||||
AsmOperand::Out { expr: None, .. }
|
||||
| AsmOperand::Const(_)
|
||||
| AsmOperand::Label(_)
|
||||
| AsmOperand::Sym(_) => (),
|
||||
}),
|
||||
Expr::If { condition, then_branch, else_branch } => {
|
||||
f(*condition);
|
||||
f(*then_branch);
|
||||
if let &Some(else_branch) = else_branch {
|
||||
f(else_branch);
|
||||
}
|
||||
}
|
||||
Expr::Let { expr, .. } => {
|
||||
f(*expr);
|
||||
}
|
||||
Expr::Block { statements, tail, .. }
|
||||
| Expr::Unsafe { statements, tail, .. }
|
||||
| Expr::Async { statements, tail, .. } => {
|
||||
for stmt in statements.iter() {
|
||||
match stmt {
|
||||
Statement::Let { initializer, else_branch, pat, .. } => {
|
||||
if let &Some(expr) = initializer {
|
||||
f(expr);
|
||||
}
|
||||
if let &Some(expr) = else_branch {
|
||||
f(expr);
|
||||
}
|
||||
walk_exprs_in_pat(self, *pat, &mut f);
|
||||
}
|
||||
Statement::Expr { expr: expression, .. } => f(*expression),
|
||||
Statement::Item => (),
|
||||
}
|
||||
}
|
||||
if let &Some(expr) = tail {
|
||||
f(expr);
|
||||
}
|
||||
}
|
||||
Expr::Loop { body, .. } => f(*body),
|
||||
Expr::Call { callee, args, .. } => {
|
||||
f(*callee);
|
||||
args.iter().copied().for_each(f);
|
||||
}
|
||||
Expr::MethodCall { receiver, args, .. } => {
|
||||
f(*receiver);
|
||||
args.iter().copied().for_each(f);
|
||||
}
|
||||
Expr::Match { expr, arms } => {
|
||||
f(*expr);
|
||||
arms.iter().map(|arm| arm.expr).for_each(f);
|
||||
}
|
||||
Expr::Break { expr, .. }
|
||||
| Expr::Return { expr }
|
||||
| Expr::Yield { expr }
|
||||
| Expr::Yeet { expr } => {
|
||||
if let &Some(expr) = expr {
|
||||
f(expr);
|
||||
}
|
||||
}
|
||||
Expr::Become { expr } => f(*expr),
|
||||
Expr::RecordLit { fields, spread, .. } => {
|
||||
for field in fields.iter() {
|
||||
f(field.expr);
|
||||
}
|
||||
if let &Some(expr) = spread {
|
||||
f(expr);
|
||||
}
|
||||
}
|
||||
Expr::Closure { body, .. } => {
|
||||
f(*body);
|
||||
}
|
||||
Expr::BinaryOp { lhs, rhs, .. } => {
|
||||
f(*lhs);
|
||||
f(*rhs);
|
||||
}
|
||||
Expr::Range { lhs, rhs, .. } => {
|
||||
if let &Some(lhs) = rhs {
|
||||
f(lhs);
|
||||
}
|
||||
if let &Some(rhs) = lhs {
|
||||
f(rhs);
|
||||
}
|
||||
}
|
||||
Expr::Index { base, index, .. } => {
|
||||
f(*base);
|
||||
f(*index);
|
||||
}
|
||||
Expr::Field { expr, .. }
|
||||
| Expr::Await { expr }
|
||||
| Expr::Cast { expr, .. }
|
||||
| Expr::Ref { expr, .. }
|
||||
| Expr::UnaryOp { expr, .. }
|
||||
| Expr::Box { expr } => {
|
||||
f(*expr);
|
||||
}
|
||||
Expr::Tuple { exprs, .. } => exprs.iter().copied().for_each(f),
|
||||
Expr::Array(a) => match a {
|
||||
Array::ElementList { elements, .. } => elements.iter().copied().for_each(f),
|
||||
Array::Repeat { initializer, repeat } => {
|
||||
f(*initializer);
|
||||
f(*repeat)
|
||||
}
|
||||
},
|
||||
&Expr::Assignment { target, value } => {
|
||||
walk_exprs_in_pat(self, target, &mut f);
|
||||
f(value);
|
||||
}
|
||||
}
|
||||
|
||||
fn walk_exprs_in_pat(this: &Body, pat_id: PatId, f: &mut impl FnMut(ExprId)) {
|
||||
this.walk_pats(pat_id, &mut |pat| {
|
||||
if let Pat::Expr(expr) | Pat::ConstBlock(expr) = this[pat] {
|
||||
f(expr);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Body {
|
||||
|
|
|
@ -70,7 +70,6 @@ pub(super) fn lower(
|
|||
body: Body::default(),
|
||||
expander,
|
||||
current_try_block_label: None,
|
||||
is_lowering_assignee_expr: false,
|
||||
is_lowering_coroutine: false,
|
||||
label_ribs: Vec::new(),
|
||||
current_binding_owner: None,
|
||||
|
@ -89,7 +88,6 @@ struct ExprCollector<'a> {
|
|||
body: Body,
|
||||
source_map: BodySourceMap,
|
||||
|
||||
is_lowering_assignee_expr: bool,
|
||||
is_lowering_coroutine: bool,
|
||||
|
||||
current_try_block_label: Option<LabelId>,
|
||||
|
@ -359,14 +357,7 @@ impl ExprCollector<'_> {
|
|||
} else {
|
||||
Box::default()
|
||||
};
|
||||
self.alloc_expr(
|
||||
Expr::Call {
|
||||
callee,
|
||||
args,
|
||||
is_assignee_expr: self.is_lowering_assignee_expr,
|
||||
},
|
||||
syntax_ptr,
|
||||
)
|
||||
self.alloc_expr(Expr::Call { callee, args }, syntax_ptr)
|
||||
}
|
||||
}
|
||||
ast::Expr::MethodCallExpr(e) => {
|
||||
|
@ -457,7 +448,6 @@ impl ExprCollector<'_> {
|
|||
ast::Expr::RecordExpr(e) => {
|
||||
let path =
|
||||
e.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
|
||||
let is_assignee_expr = self.is_lowering_assignee_expr;
|
||||
let record_lit = if let Some(nfl) = e.record_expr_field_list() {
|
||||
let fields = nfl
|
||||
.fields()
|
||||
|
@ -476,16 +466,9 @@ impl ExprCollector<'_> {
|
|||
})
|
||||
.collect();
|
||||
let spread = nfl.spread().map(|s| self.collect_expr(s));
|
||||
let ellipsis = nfl.dotdot_token().is_some();
|
||||
Expr::RecordLit { path, fields, spread, ellipsis, is_assignee_expr }
|
||||
Expr::RecordLit { path, fields, spread }
|
||||
} else {
|
||||
Expr::RecordLit {
|
||||
path,
|
||||
fields: Box::default(),
|
||||
spread: None,
|
||||
ellipsis: false,
|
||||
is_assignee_expr,
|
||||
}
|
||||
Expr::RecordLit { path, fields: Box::default(), spread: None }
|
||||
};
|
||||
|
||||
self.alloc_expr(record_lit, syntax_ptr)
|
||||
|
@ -602,12 +585,14 @@ impl ExprCollector<'_> {
|
|||
ast::Expr::BinExpr(e) => {
|
||||
let op = e.op_kind();
|
||||
if let Some(ast::BinaryOp::Assignment { op: None }) = op {
|
||||
self.is_lowering_assignee_expr = true;
|
||||
let target = self.collect_expr_as_pat_opt(e.lhs());
|
||||
let value = self.collect_expr_opt(e.rhs());
|
||||
self.alloc_expr(Expr::Assignment { target, value }, syntax_ptr)
|
||||
} else {
|
||||
let lhs = self.collect_expr_opt(e.lhs());
|
||||
let rhs = self.collect_expr_opt(e.rhs());
|
||||
self.alloc_expr(Expr::BinaryOp { lhs, rhs, op }, syntax_ptr)
|
||||
}
|
||||
let lhs = self.collect_expr_opt(e.lhs());
|
||||
self.is_lowering_assignee_expr = false;
|
||||
let rhs = self.collect_expr_opt(e.rhs());
|
||||
self.alloc_expr(Expr::BinaryOp { lhs, rhs, op }, syntax_ptr)
|
||||
}
|
||||
ast::Expr::TupleExpr(e) => {
|
||||
let mut exprs: Vec<_> = e.fields().map(|expr| self.collect_expr(expr)).collect();
|
||||
|
@ -617,13 +602,7 @@ impl ExprCollector<'_> {
|
|||
exprs.insert(0, self.missing_expr());
|
||||
}
|
||||
|
||||
self.alloc_expr(
|
||||
Expr::Tuple {
|
||||
exprs: exprs.into_boxed_slice(),
|
||||
is_assignee_expr: self.is_lowering_assignee_expr,
|
||||
},
|
||||
syntax_ptr,
|
||||
)
|
||||
self.alloc_expr(Expr::Tuple { exprs: exprs.into_boxed_slice() }, syntax_ptr)
|
||||
}
|
||||
ast::Expr::ArrayExpr(e) => {
|
||||
let kind = e.kind();
|
||||
|
@ -631,13 +610,7 @@ impl ExprCollector<'_> {
|
|||
match kind {
|
||||
ArrayExprKind::ElementList(e) => {
|
||||
let elements = e.map(|expr| self.collect_expr(expr)).collect();
|
||||
self.alloc_expr(
|
||||
Expr::Array(Array::ElementList {
|
||||
elements,
|
||||
is_assignee_expr: self.is_lowering_assignee_expr,
|
||||
}),
|
||||
syntax_ptr,
|
||||
)
|
||||
self.alloc_expr(Expr::Array(Array::ElementList { elements }), syntax_ptr)
|
||||
}
|
||||
ArrayExprKind::Repeat { initializer, repeat } => {
|
||||
let initializer = self.collect_expr_opt(initializer);
|
||||
|
@ -664,8 +637,7 @@ impl ExprCollector<'_> {
|
|||
ast::Expr::IndexExpr(e) => {
|
||||
let base = self.collect_expr_opt(e.base());
|
||||
let index = self.collect_expr_opt(e.index());
|
||||
let is_assignee_expr = self.is_lowering_assignee_expr;
|
||||
self.alloc_expr(Expr::Index { base, index, is_assignee_expr }, syntax_ptr)
|
||||
self.alloc_expr(Expr::Index { base, index }, syntax_ptr)
|
||||
}
|
||||
ast::Expr::RangeExpr(e) => {
|
||||
let lhs = e.start().map(|lhs| self.collect_expr(lhs));
|
||||
|
@ -705,6 +677,178 @@ impl ExprCollector<'_> {
|
|||
})
|
||||
}
|
||||
|
||||
fn collect_expr_as_pat_opt(&mut self, expr: Option<ast::Expr>) -> PatId {
|
||||
match expr {
|
||||
Some(expr) => self.collect_expr_as_pat(expr),
|
||||
_ => self.missing_pat(),
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_expr_as_pat(&mut self, expr: ast::Expr) -> PatId {
|
||||
self.maybe_collect_expr_as_pat(&expr).unwrap_or_else(|| {
|
||||
let syntax_ptr = AstPtr::new(&expr);
|
||||
let expr = self.collect_expr(expr);
|
||||
self.alloc_pat_from_expr(Pat::Expr(expr), syntax_ptr)
|
||||
})
|
||||
}
|
||||
|
||||
fn maybe_collect_expr_as_pat(&mut self, expr: &ast::Expr) -> Option<PatId> {
|
||||
self.check_cfg(expr)?;
|
||||
let syntax_ptr = AstPtr::new(expr);
|
||||
|
||||
let result = match expr {
|
||||
ast::Expr::UnderscoreExpr(_) => self.alloc_pat_from_expr(Pat::Wild, syntax_ptr),
|
||||
ast::Expr::ParenExpr(e) => {
|
||||
// We special-case `(..)` for consistency with patterns.
|
||||
if let Some(ast::Expr::RangeExpr(range)) = e.expr() {
|
||||
if range.is_range_full() {
|
||||
return Some(self.alloc_pat_from_expr(
|
||||
Pat::Tuple { args: Box::default(), ellipsis: Some(0) },
|
||||
syntax_ptr,
|
||||
));
|
||||
}
|
||||
}
|
||||
return e.expr().and_then(|expr| self.maybe_collect_expr_as_pat(&expr));
|
||||
}
|
||||
ast::Expr::TupleExpr(e) => {
|
||||
let (ellipsis, args) = collect_tuple(self, e.fields());
|
||||
self.alloc_pat_from_expr(Pat::Tuple { args, ellipsis }, syntax_ptr)
|
||||
}
|
||||
ast::Expr::ArrayExpr(e) => {
|
||||
if e.semicolon_token().is_some() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut elements = e.exprs();
|
||||
let prefix = elements
|
||||
.by_ref()
|
||||
.map_while(|elem| collect_possibly_rest(self, elem).left())
|
||||
.collect();
|
||||
let suffix = elements.map(|elem| self.collect_expr_as_pat(elem)).collect();
|
||||
self.alloc_pat_from_expr(Pat::Slice { prefix, slice: None, suffix }, syntax_ptr)
|
||||
}
|
||||
ast::Expr::CallExpr(e) => {
|
||||
let path = collect_path(self, e.expr()?)?;
|
||||
let path = path
|
||||
.path()
|
||||
.and_then(|path| self.expander.parse_path(self.db, path))
|
||||
.map(Box::new);
|
||||
let (ellipsis, args) = collect_tuple(self, e.arg_list()?.args());
|
||||
self.alloc_pat_from_expr(Pat::TupleStruct { path, args, ellipsis }, syntax_ptr)
|
||||
}
|
||||
ast::Expr::PathExpr(e) => {
|
||||
let path = Box::new(self.expander.parse_path(self.db, e.path()?)?);
|
||||
self.alloc_pat_from_expr(Pat::Path(path), syntax_ptr)
|
||||
}
|
||||
ast::Expr::MacroExpr(e) => {
|
||||
let e = e.macro_call()?;
|
||||
let macro_ptr = AstPtr::new(&e);
|
||||
let id = self.collect_macro_call(e, macro_ptr, true, |this, expansion| {
|
||||
expansion.map(|it| this.collect_expr_as_pat(it))
|
||||
});
|
||||
match id {
|
||||
Some(id) => {
|
||||
// FIXME: Insert pat into source map.
|
||||
id
|
||||
}
|
||||
None => self.alloc_pat_from_expr(Pat::Missing, syntax_ptr),
|
||||
}
|
||||
}
|
||||
ast::Expr::RecordExpr(e) => {
|
||||
let path =
|
||||
e.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
|
||||
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()`.
|
||||
let args = record_field_list
|
||||
.fields()
|
||||
.filter_map(|f| {
|
||||
self.check_cfg(&f)?;
|
||||
let field_expr = f.expr()?;
|
||||
let pat = self.collect_expr_as_pat(field_expr);
|
||||
let name = f.field_name()?.as_name();
|
||||
// FIXME: Enable this.
|
||||
// let src = self.expander.in_file(AstPtr::new(&f));
|
||||
// self.source_map.pat_field_map_back.insert(pat, src);
|
||||
Some(RecordFieldPat { name, pat })
|
||||
})
|
||||
.collect();
|
||||
self.alloc_pat_from_expr(Pat::Record { path, args, ellipsis }, syntax_ptr)
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
return Some(result);
|
||||
|
||||
fn collect_path(this: &mut ExprCollector<'_>, expr: ast::Expr) -> Option<ast::PathExpr> {
|
||||
match expr {
|
||||
ast::Expr::PathExpr(e) => Some(e),
|
||||
ast::Expr::MacroExpr(mac) => {
|
||||
let call = mac.macro_call()?;
|
||||
{
|
||||
let macro_ptr = AstPtr::new(&call);
|
||||
this.collect_macro_call(call, macro_ptr, true, |this, expanded_path| {
|
||||
collect_path(this, expanded_path?)
|
||||
})
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_possibly_rest(
|
||||
this: &mut ExprCollector<'_>,
|
||||
expr: ast::Expr,
|
||||
) -> Either<PatId, ()> {
|
||||
match &expr {
|
||||
ast::Expr::RangeExpr(e) if e.is_range_full() => Either::Right(()),
|
||||
ast::Expr::MacroExpr(mac) => match mac.macro_call() {
|
||||
Some(call) => {
|
||||
let macro_ptr = AstPtr::new(&call);
|
||||
let pat = this.collect_macro_call(
|
||||
call,
|
||||
macro_ptr,
|
||||
true,
|
||||
|this, expanded_expr| match expanded_expr {
|
||||
Some(expanded_pat) => collect_possibly_rest(this, expanded_pat),
|
||||
None => Either::Left(this.missing_pat()),
|
||||
},
|
||||
);
|
||||
// FIXME: Insert pat into source map.
|
||||
pat
|
||||
}
|
||||
None => {
|
||||
let ptr = AstPtr::new(&expr);
|
||||
Either::Left(this.alloc_pat_from_expr(Pat::Missing, ptr))
|
||||
}
|
||||
},
|
||||
_ => Either::Left(this.collect_expr_as_pat(expr)),
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_tuple(
|
||||
this: &mut ExprCollector<'_>,
|
||||
fields: ast::AstChildren<ast::Expr>,
|
||||
) -> (Option<u32>, Box<[la_arena::Idx<Pat>]>) {
|
||||
let mut ellipsis = None;
|
||||
let args = fields
|
||||
.enumerate()
|
||||
.filter_map(|(idx, elem)| {
|
||||
match collect_possibly_rest(this, elem) {
|
||||
Either::Left(pat) => Some(pat),
|
||||
Either::Right(()) => {
|
||||
if ellipsis.is_none() {
|
||||
ellipsis = Some(idx as u32);
|
||||
}
|
||||
// FIXME: Report an error here otherwise.
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
(ellipsis, args)
|
||||
}
|
||||
}
|
||||
|
||||
fn initialize_binding_owner(
|
||||
&mut self,
|
||||
syntax_ptr: AstPtr<ast::Expr>,
|
||||
|
@ -755,17 +899,13 @@ impl ExprCollector<'_> {
|
|||
|
||||
let callee = self.alloc_expr_desugared_with_ptr(Expr::Path(try_from_output), ptr);
|
||||
let next_tail = match btail {
|
||||
Some(tail) => self.alloc_expr_desugared_with_ptr(
|
||||
Expr::Call { callee, args: Box::new([tail]), is_assignee_expr: false },
|
||||
ptr,
|
||||
),
|
||||
Some(tail) => self
|
||||
.alloc_expr_desugared_with_ptr(Expr::Call { callee, args: Box::new([tail]) }, ptr),
|
||||
None => {
|
||||
let unit = self.alloc_expr_desugared_with_ptr(
|
||||
Expr::Tuple { exprs: Box::new([]), is_assignee_expr: false },
|
||||
ptr,
|
||||
);
|
||||
let unit =
|
||||
self.alloc_expr_desugared_with_ptr(Expr::Tuple { exprs: Box::new([]) }, ptr);
|
||||
self.alloc_expr_desugared_with_ptr(
|
||||
Expr::Call { callee, args: Box::new([unit]), is_assignee_expr: false },
|
||||
Expr::Call { callee, args: Box::new([unit]) },
|
||||
ptr,
|
||||
)
|
||||
}
|
||||
|
@ -851,11 +991,7 @@ impl ExprCollector<'_> {
|
|||
let head = self.collect_expr_opt(e.iterable());
|
||||
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]),
|
||||
is_assignee_expr: false,
|
||||
},
|
||||
Expr::Call { callee: into_iter_fn_expr, args: Box::new([head]) },
|
||||
syntax_ptr,
|
||||
);
|
||||
let none_arm = MatchArm {
|
||||
|
@ -884,11 +1020,7 @@ impl ExprCollector<'_> {
|
|||
);
|
||||
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]),
|
||||
is_assignee_expr: false,
|
||||
},
|
||||
Expr::Call { callee: iter_next_fn_expr, args: Box::new([iter_expr_mut]) },
|
||||
syntax_ptr,
|
||||
);
|
||||
let loop_inner = self.alloc_expr(
|
||||
|
@ -942,10 +1074,8 @@ impl ExprCollector<'_> {
|
|||
};
|
||||
let operand = self.collect_expr_opt(e.expr());
|
||||
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]), is_assignee_expr: false },
|
||||
syntax_ptr,
|
||||
);
|
||||
let expr = self
|
||||
.alloc_expr(Expr::Call { callee: try_branch, args: Box::new([operand]) }, syntax_ptr);
|
||||
let continue_name = Name::generate_new_name(self.body.bindings.len());
|
||||
let continue_binding =
|
||||
self.alloc_binding(continue_name.clone(), BindingAnnotation::Unannotated);
|
||||
|
@ -975,10 +1105,8 @@ impl ExprCollector<'_> {
|
|||
expr: {
|
||||
let it = self.alloc_expr(Expr::Path(Path::from(break_name)), syntax_ptr);
|
||||
let callee = self.alloc_expr(Expr::Path(try_from_residual), syntax_ptr);
|
||||
let result = self.alloc_expr(
|
||||
Expr::Call { callee, args: Box::new([it]), is_assignee_expr: false },
|
||||
syntax_ptr,
|
||||
);
|
||||
let result =
|
||||
self.alloc_expr(Expr::Call { callee, args: Box::new([it]) }, syntax_ptr);
|
||||
self.alloc_expr(
|
||||
match self.current_try_block_label {
|
||||
Some(label) => Expr::Break { expr: Some(result), label: Some(label) },
|
||||
|
@ -1723,10 +1851,8 @@ impl ExprCollector<'_> {
|
|||
}
|
||||
})
|
||||
.collect();
|
||||
let lit_pieces = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
|
||||
elements: lit_pieces,
|
||||
is_assignee_expr: false,
|
||||
}));
|
||||
let lit_pieces =
|
||||
self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: lit_pieces }));
|
||||
let lit_pieces = self.alloc_expr_desugared(Expr::Ref {
|
||||
expr: lit_pieces,
|
||||
rawness: Rawness::Ref,
|
||||
|
@ -1743,10 +1869,7 @@ impl ExprCollector<'_> {
|
|||
Some(self.make_format_spec(placeholder, &mut argmap))
|
||||
})
|
||||
.collect();
|
||||
let array = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
|
||||
elements,
|
||||
is_assignee_expr: false,
|
||||
}));
|
||||
let array = self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements }));
|
||||
self.alloc_expr_desugared(Expr::Ref {
|
||||
expr: array,
|
||||
rawness: Rawness::Ref,
|
||||
|
@ -1756,10 +1879,8 @@ impl ExprCollector<'_> {
|
|||
let arguments = &*fmt.arguments.arguments;
|
||||
|
||||
let args = if arguments.is_empty() {
|
||||
let expr = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
|
||||
elements: Box::default(),
|
||||
is_assignee_expr: false,
|
||||
}));
|
||||
let expr = self
|
||||
.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: Box::default() }));
|
||||
self.alloc_expr_desugared(Expr::Ref {
|
||||
expr,
|
||||
rawness: Rawness::Ref,
|
||||
|
@ -1786,10 +1907,8 @@ impl ExprCollector<'_> {
|
|||
self.make_argument(arg, ty)
|
||||
})
|
||||
.collect();
|
||||
let array = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
|
||||
elements: args,
|
||||
is_assignee_expr: false,
|
||||
}));
|
||||
let array =
|
||||
self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: args }));
|
||||
self.alloc_expr_desugared(Expr::Ref {
|
||||
expr: array,
|
||||
rawness: Rawness::Ref,
|
||||
|
@ -1822,11 +1941,8 @@ impl ExprCollector<'_> {
|
|||
let new_v1_formatted = self.alloc_expr_desugared(Expr::Path(new_v1_formatted));
|
||||
|
||||
let unsafe_arg_new = self.alloc_expr_desugared(Expr::Path(unsafe_arg_new));
|
||||
let unsafe_arg_new = self.alloc_expr_desugared(Expr::Call {
|
||||
callee: unsafe_arg_new,
|
||||
args: Box::default(),
|
||||
is_assignee_expr: false,
|
||||
});
|
||||
let unsafe_arg_new =
|
||||
self.alloc_expr_desugared(Expr::Call { callee: unsafe_arg_new, args: Box::default() });
|
||||
let unsafe_arg_new = self.alloc_expr_desugared(Expr::Unsafe {
|
||||
id: None,
|
||||
// We collect the unused expressions here so that we still infer them instead of
|
||||
|
@ -1843,7 +1959,6 @@ impl ExprCollector<'_> {
|
|||
Expr::Call {
|
||||
callee: new_v1_formatted,
|
||||
args: Box::new([lit_pieces, args, format_options, unsafe_arg_new]),
|
||||
is_assignee_expr: false,
|
||||
},
|
||||
syntax_ptr,
|
||||
);
|
||||
|
@ -1938,7 +2053,6 @@ impl ExprCollector<'_> {
|
|||
self.alloc_expr_desugared(Expr::Call {
|
||||
callee: format_placeholder_new,
|
||||
args: Box::new([position, fill, align, flags, precision, width]),
|
||||
is_assignee_expr: false,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1980,11 +2094,7 @@ impl ExprCollector<'_> {
|
|||
Some(count_is) => self.alloc_expr_desugared(Expr::Path(count_is)),
|
||||
None => self.missing_expr(),
|
||||
};
|
||||
self.alloc_expr_desugared(Expr::Call {
|
||||
callee: count_is,
|
||||
args: Box::new([args]),
|
||||
is_assignee_expr: false,
|
||||
})
|
||||
self.alloc_expr_desugared(Expr::Call { callee: count_is, args: Box::new([args]) })
|
||||
}
|
||||
Some(FormatCount::Argument(arg)) => {
|
||||
if let Ok(arg_index) = arg.index {
|
||||
|
@ -2005,7 +2115,6 @@ impl ExprCollector<'_> {
|
|||
self.alloc_expr_desugared(Expr::Call {
|
||||
callee: count_param,
|
||||
args: Box::new([args]),
|
||||
is_assignee_expr: false,
|
||||
})
|
||||
} else {
|
||||
// FIXME: This drops arg causing it to potentially not be resolved/type checked
|
||||
|
@ -2054,11 +2163,7 @@ impl ExprCollector<'_> {
|
|||
Some(new_fn) => self.alloc_expr_desugared(Expr::Path(new_fn)),
|
||||
None => self.missing_expr(),
|
||||
};
|
||||
self.alloc_expr_desugared(Expr::Call {
|
||||
callee: new_fn,
|
||||
args: Box::new([arg]),
|
||||
is_assignee_expr: false,
|
||||
})
|
||||
self.alloc_expr_desugared(Expr::Call { callee: new_fn, args: Box::new([arg]) })
|
||||
}
|
||||
|
||||
// endregion: format
|
||||
|
@ -2110,6 +2215,10 @@ impl ExprCollector<'_> {
|
|||
binding
|
||||
}
|
||||
|
||||
fn alloc_pat_from_expr(&mut self, pat: Pat, _ptr: ExprPtr) -> PatId {
|
||||
// FIXME: Insert into source map.
|
||||
self.body.pats.alloc(pat)
|
||||
}
|
||||
fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
|
||||
let src = self.expander.in_file(ptr);
|
||||
let id = self.body.pats.alloc(pat);
|
||||
|
|
|
@ -277,7 +277,7 @@ impl Printer<'_> {
|
|||
w!(self, "loop ");
|
||||
self.print_expr(*body);
|
||||
}
|
||||
Expr::Call { callee, args, is_assignee_expr: _ } => {
|
||||
Expr::Call { callee, args } => {
|
||||
self.print_expr(*callee);
|
||||
w!(self, "(");
|
||||
if !args.is_empty() {
|
||||
|
@ -372,7 +372,7 @@ impl Printer<'_> {
|
|||
self.print_expr(*expr);
|
||||
}
|
||||
}
|
||||
Expr::RecordLit { path, fields, spread, ellipsis, is_assignee_expr: _ } => {
|
||||
Expr::RecordLit { path, fields, spread } => {
|
||||
match path {
|
||||
Some(path) => self.print_path(path),
|
||||
None => w!(self, "<EFBFBD>"),
|
||||
|
@ -391,9 +391,6 @@ impl Printer<'_> {
|
|||
p.print_expr(*spread);
|
||||
wln!(p);
|
||||
}
|
||||
if *ellipsis {
|
||||
wln!(p, "..");
|
||||
}
|
||||
});
|
||||
w!(self, "}}");
|
||||
}
|
||||
|
@ -466,7 +463,7 @@ impl Printer<'_> {
|
|||
w!(self, ") ");
|
||||
}
|
||||
}
|
||||
Expr::Index { base, index, is_assignee_expr: _ } => {
|
||||
Expr::Index { base, index } => {
|
||||
self.print_expr(*base);
|
||||
w!(self, "[");
|
||||
self.print_expr(*index);
|
||||
|
@ -507,7 +504,7 @@ impl Printer<'_> {
|
|||
self.whitespace();
|
||||
self.print_expr(*body);
|
||||
}
|
||||
Expr::Tuple { exprs, is_assignee_expr: _ } => {
|
||||
Expr::Tuple { exprs } => {
|
||||
w!(self, "(");
|
||||
for expr in exprs.iter() {
|
||||
self.print_expr(*expr);
|
||||
|
@ -519,7 +516,7 @@ impl Printer<'_> {
|
|||
w!(self, "[");
|
||||
if !matches!(arr, Array::ElementList { elements, .. } if elements.is_empty()) {
|
||||
self.indented(|p| match arr {
|
||||
Array::ElementList { elements, is_assignee_expr: _ } => {
|
||||
Array::ElementList { elements } => {
|
||||
for elem in elements.iter() {
|
||||
p.print_expr(*elem);
|
||||
w!(p, ", ");
|
||||
|
@ -551,6 +548,11 @@ impl Printer<'_> {
|
|||
Expr::Const(id) => {
|
||||
w!(self, "const {{ /* {id:?} */ }}");
|
||||
}
|
||||
&Expr::Assignment { target, value } => {
|
||||
self.print_pat(target);
|
||||
w!(self, " = ");
|
||||
self.print_expr(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -719,6 +721,9 @@ impl Printer<'_> {
|
|||
w!(self, "const ");
|
||||
self.print_expr(*c);
|
||||
}
|
||||
Pat::Expr(expr) => {
|
||||
self.print_expr(*expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -282,7 +282,7 @@ fn compute_expr_scopes(
|
|||
*scope = scopes.new_scope(*scope);
|
||||
scopes.add_pat_bindings(body, *scope, pat);
|
||||
}
|
||||
e => e.walk_child_exprs(|e| compute_expr_scopes(scopes, e, scope)),
|
||||
_ => body.walk_child_exprs(expr, |e| compute_expr_scopes(scopes, e, scope)),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -204,7 +204,6 @@ pub enum Expr {
|
|||
Call {
|
||||
callee: ExprId,
|
||||
args: Box<[ExprId]>,
|
||||
is_assignee_expr: bool,
|
||||
},
|
||||
MethodCall {
|
||||
receiver: ExprId,
|
||||
|
@ -239,8 +238,6 @@ pub enum Expr {
|
|||
path: Option<Box<Path>>,
|
||||
fields: Box<[RecordLitField]>,
|
||||
spread: Option<ExprId>,
|
||||
ellipsis: bool,
|
||||
is_assignee_expr: bool,
|
||||
},
|
||||
Field {
|
||||
expr: ExprId,
|
||||
|
@ -265,11 +262,17 @@ pub enum Expr {
|
|||
expr: ExprId,
|
||||
op: UnaryOp,
|
||||
},
|
||||
/// `op` cannot be bare `=` (but can be `op=`), these are lowered to `Assignment` instead.
|
||||
BinaryOp {
|
||||
lhs: ExprId,
|
||||
rhs: ExprId,
|
||||
op: Option<BinaryOp>,
|
||||
},
|
||||
// Assignments need a special treatment because of destructuring assignment.
|
||||
Assignment {
|
||||
target: PatId,
|
||||
value: ExprId,
|
||||
},
|
||||
Range {
|
||||
lhs: Option<ExprId>,
|
||||
rhs: Option<ExprId>,
|
||||
|
@ -278,7 +281,6 @@ pub enum Expr {
|
|||
Index {
|
||||
base: ExprId,
|
||||
index: ExprId,
|
||||
is_assignee_expr: bool,
|
||||
},
|
||||
Closure {
|
||||
args: Box<[PatId]>,
|
||||
|
@ -290,7 +292,6 @@ pub enum Expr {
|
|||
},
|
||||
Tuple {
|
||||
exprs: Box<[ExprId]>,
|
||||
is_assignee_expr: bool,
|
||||
},
|
||||
Array(Array),
|
||||
Literal(Literal),
|
||||
|
@ -446,7 +447,7 @@ pub enum Movability {
|
|||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum Array {
|
||||
ElementList { elements: Box<[ExprId]>, is_assignee_expr: bool },
|
||||
ElementList { elements: Box<[ExprId]> },
|
||||
Repeat { initializer: ExprId, repeat: ExprId },
|
||||
}
|
||||
|
||||
|
@ -480,130 +481,6 @@ pub enum Statement {
|
|||
Item,
|
||||
}
|
||||
|
||||
impl Expr {
|
||||
pub fn walk_child_exprs(&self, mut f: impl FnMut(ExprId)) {
|
||||
match self {
|
||||
Expr::Missing => {}
|
||||
Expr::Path(_) | Expr::OffsetOf(_) => {}
|
||||
Expr::InlineAsm(it) => it.operands.iter().for_each(|(_, op)| match op {
|
||||
AsmOperand::In { expr, .. }
|
||||
| AsmOperand::Out { expr: Some(expr), .. }
|
||||
| AsmOperand::InOut { expr, .. } => f(*expr),
|
||||
AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
|
||||
f(*in_expr);
|
||||
if let Some(out_expr) = out_expr {
|
||||
f(*out_expr);
|
||||
}
|
||||
}
|
||||
AsmOperand::Out { expr: None, .. }
|
||||
| AsmOperand::Const(_)
|
||||
| AsmOperand::Label(_)
|
||||
| AsmOperand::Sym(_) => (),
|
||||
}),
|
||||
Expr::If { condition, then_branch, else_branch } => {
|
||||
f(*condition);
|
||||
f(*then_branch);
|
||||
if let &Some(else_branch) = else_branch {
|
||||
f(else_branch);
|
||||
}
|
||||
}
|
||||
Expr::Let { expr, .. } => {
|
||||
f(*expr);
|
||||
}
|
||||
Expr::Const(_) => (),
|
||||
Expr::Block { statements, tail, .. }
|
||||
| Expr::Unsafe { statements, tail, .. }
|
||||
| Expr::Async { statements, tail, .. } => {
|
||||
for stmt in statements.iter() {
|
||||
match stmt {
|
||||
Statement::Let { initializer, else_branch, .. } => {
|
||||
if let &Some(expr) = initializer {
|
||||
f(expr);
|
||||
}
|
||||
if let &Some(expr) = else_branch {
|
||||
f(expr);
|
||||
}
|
||||
}
|
||||
Statement::Expr { expr: expression, .. } => f(*expression),
|
||||
Statement::Item => (),
|
||||
}
|
||||
}
|
||||
if let &Some(expr) = tail {
|
||||
f(expr);
|
||||
}
|
||||
}
|
||||
Expr::Loop { body, .. } => f(*body),
|
||||
Expr::Call { callee, args, .. } => {
|
||||
f(*callee);
|
||||
args.iter().copied().for_each(f);
|
||||
}
|
||||
Expr::MethodCall { receiver, args, .. } => {
|
||||
f(*receiver);
|
||||
args.iter().copied().for_each(f);
|
||||
}
|
||||
Expr::Match { expr, arms } => {
|
||||
f(*expr);
|
||||
arms.iter().map(|arm| arm.expr).for_each(f);
|
||||
}
|
||||
Expr::Continue { .. } => {}
|
||||
Expr::Break { expr, .. }
|
||||
| Expr::Return { expr }
|
||||
| Expr::Yield { expr }
|
||||
| Expr::Yeet { expr } => {
|
||||
if let &Some(expr) = expr {
|
||||
f(expr);
|
||||
}
|
||||
}
|
||||
Expr::Become { expr } => f(*expr),
|
||||
Expr::RecordLit { fields, spread, .. } => {
|
||||
for field in fields.iter() {
|
||||
f(field.expr);
|
||||
}
|
||||
if let &Some(expr) = spread {
|
||||
f(expr);
|
||||
}
|
||||
}
|
||||
Expr::Closure { body, .. } => {
|
||||
f(*body);
|
||||
}
|
||||
Expr::BinaryOp { lhs, rhs, .. } => {
|
||||
f(*lhs);
|
||||
f(*rhs);
|
||||
}
|
||||
Expr::Range { lhs, rhs, .. } => {
|
||||
if let &Some(lhs) = rhs {
|
||||
f(lhs);
|
||||
}
|
||||
if let &Some(rhs) = lhs {
|
||||
f(rhs);
|
||||
}
|
||||
}
|
||||
Expr::Index { base, index, .. } => {
|
||||
f(*base);
|
||||
f(*index);
|
||||
}
|
||||
Expr::Field { expr, .. }
|
||||
| Expr::Await { expr }
|
||||
| Expr::Cast { expr, .. }
|
||||
| Expr::Ref { expr, .. }
|
||||
| Expr::UnaryOp { expr, .. }
|
||||
| Expr::Box { expr } => {
|
||||
f(*expr);
|
||||
}
|
||||
Expr::Tuple { exprs, .. } => exprs.iter().copied().for_each(f),
|
||||
Expr::Array(a) => match a {
|
||||
Array::ElementList { elements, .. } => elements.iter().copied().for_each(f),
|
||||
Array::Repeat { initializer, repeat } => {
|
||||
f(*initializer);
|
||||
f(*repeat)
|
||||
}
|
||||
},
|
||||
Expr::Literal(_) => {}
|
||||
Expr::Underscore => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Explicit binding annotations given in the HIR for a binding. Note
|
||||
/// that this is not the final binding *mode* that we infer after type
|
||||
/// inference.
|
||||
|
@ -665,18 +542,49 @@ pub struct RecordFieldPat {
|
|||
pub enum Pat {
|
||||
Missing,
|
||||
Wild,
|
||||
Tuple { args: Box<[PatId]>, ellipsis: Option<u32> },
|
||||
Tuple {
|
||||
args: Box<[PatId]>,
|
||||
ellipsis: Option<u32>,
|
||||
},
|
||||
Or(Box<[PatId]>),
|
||||
Record { path: Option<Box<Path>>, args: Box<[RecordFieldPat]>, ellipsis: bool },
|
||||
Range { start: Option<Box<LiteralOrConst>>, end: Option<Box<LiteralOrConst>> },
|
||||
Slice { prefix: Box<[PatId]>, slice: Option<PatId>, suffix: Box<[PatId]> },
|
||||
Record {
|
||||
path: Option<Box<Path>>,
|
||||
args: Box<[RecordFieldPat]>,
|
||||
ellipsis: bool,
|
||||
},
|
||||
Range {
|
||||
start: Option<Box<LiteralOrConst>>,
|
||||
end: Option<Box<LiteralOrConst>>,
|
||||
},
|
||||
Slice {
|
||||
prefix: Box<[PatId]>,
|
||||
slice: Option<PatId>,
|
||||
suffix: Box<[PatId]>,
|
||||
},
|
||||
/// This might refer to a variable if a single segment path (specifically, on destructuring assignment).
|
||||
Path(Box<Path>),
|
||||
Lit(ExprId),
|
||||
Bind { id: BindingId, subpat: Option<PatId> },
|
||||
TupleStruct { path: Option<Box<Path>>, args: Box<[PatId]>, ellipsis: Option<u32> },
|
||||
Ref { pat: PatId, mutability: Mutability },
|
||||
Box { inner: PatId },
|
||||
Bind {
|
||||
id: BindingId,
|
||||
subpat: Option<PatId>,
|
||||
},
|
||||
TupleStruct {
|
||||
path: Option<Box<Path>>,
|
||||
args: Box<[PatId]>,
|
||||
ellipsis: Option<u32>,
|
||||
},
|
||||
Ref {
|
||||
pat: PatId,
|
||||
mutability: Mutability,
|
||||
},
|
||||
Box {
|
||||
inner: PatId,
|
||||
},
|
||||
ConstBlock(ExprId),
|
||||
/// An expression inside a pattern. That can only occur inside assignments.
|
||||
///
|
||||
/// E.g. in `(a, *b) = (1, &mut 2)`, `*b` is an expression.
|
||||
Expr(ExprId),
|
||||
}
|
||||
|
||||
impl Pat {
|
||||
|
@ -687,7 +595,8 @@ impl Pat {
|
|||
| Pat::Path(..)
|
||||
| Pat::ConstBlock(..)
|
||||
| Pat::Wild
|
||||
| Pat::Missing => {}
|
||||
| Pat::Missing
|
||||
| Pat::Expr(_) => {}
|
||||
Pat::Bind { subpat, .. } => {
|
||||
subpat.iter().copied().for_each(f);
|
||||
}
|
||||
|
|
|
@ -319,7 +319,7 @@ pub(crate) fn eval_to_const(
|
|||
return true;
|
||||
}
|
||||
let mut r = false;
|
||||
body[expr].walk_child_exprs(|idx| r |= has_closure(body, idx));
|
||||
body.walk_child_exprs(expr, |idx| r |= has_closure(body, idx));
|
||||
r
|
||||
}
|
||||
if has_closure(ctx.body, expr) {
|
||||
|
|
|
@ -546,10 +546,7 @@ pub fn record_literal_missing_fields(
|
|||
expr: &Expr,
|
||||
) -> Option<(VariantId, Vec<LocalFieldId>, /*exhaustive*/ bool)> {
|
||||
let (fields, exhaustive) = match expr {
|
||||
Expr::RecordLit { fields, spread, ellipsis, is_assignee_expr, .. } => {
|
||||
let exhaustive = if *is_assignee_expr { !*ellipsis } else { spread.is_none() };
|
||||
(fields, exhaustive)
|
||||
}
|
||||
Expr::RecordLit { fields, spread, .. } => (fields, spread.is_none()),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
|
|
|
@ -117,14 +117,14 @@ fn walk_unsafe(
|
|||
}
|
||||
}
|
||||
Expr::Unsafe { .. } => {
|
||||
return expr.walk_child_exprs(|child| {
|
||||
return body.walk_child_exprs(current, |child| {
|
||||
walk_unsafe(db, infer, body, resolver, def, child, true, unsafe_expr_cb);
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
expr.walk_child_exprs(|child| {
|
||||
body.walk_child_exprs(current, |child| {
|
||||
walk_unsafe(db, infer, body, resolver, def, child, inside_unsafe_block, unsafe_expr_cb);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -228,7 +228,7 @@ pub enum InferenceDiagnostic {
|
|||
id: ExprOrPatId,
|
||||
},
|
||||
UnresolvedIdent {
|
||||
expr: ExprId,
|
||||
id: ExprOrPatId,
|
||||
},
|
||||
// FIXME: This should be emitted in body lowering
|
||||
BreakOutsideOfLoop {
|
||||
|
@ -561,6 +561,9 @@ pub(crate) struct InferenceContext<'a> {
|
|||
diverges: Diverges,
|
||||
breakables: Vec<BreakableContext>,
|
||||
|
||||
/// Whether we are inside the pattern of a destructuring assignment.
|
||||
inside_assignment: bool,
|
||||
|
||||
deferred_cast_checks: Vec<CastCheck>,
|
||||
|
||||
// fields related to closure capture
|
||||
|
@ -656,6 +659,7 @@ impl<'a> InferenceContext<'a> {
|
|||
current_closure: None,
|
||||
deferred_closures: FxHashMap::default(),
|
||||
closure_dependencies: FxHashMap::default(),
|
||||
inside_assignment: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,11 +11,12 @@ use either::Either;
|
|||
use hir_def::{
|
||||
data::adt::VariantData,
|
||||
hir::{
|
||||
Array, AsmOperand, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat, PatId, Statement,
|
||||
UnaryOp,
|
||||
Array, AsmOperand, BinaryOp, BindingId, CaptureBy, Expr, ExprId, ExprOrPatId, Pat, PatId,
|
||||
Statement, UnaryOp,
|
||||
},
|
||||
lang_item::LangItem,
|
||||
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
|
||||
path::Path,
|
||||
resolver::ValueNs,
|
||||
DefWithBodyId, FieldId, HasModule, TupleFieldId, TupleId, VariantId,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
|
@ -508,18 +509,37 @@ impl InferenceContext<'_> {
|
|||
apply_adjusts_to_place(&mut self.current_capture_span_stack, r, adjustments)
|
||||
}
|
||||
|
||||
/// Pushes the span into `current_capture_span_stack`, *without clearing it first*.
|
||||
fn path_place(&mut self, path: &Path, id: ExprOrPatId) -> Option<HirPlace> {
|
||||
if path.type_anchor().is_some() {
|
||||
return None;
|
||||
}
|
||||
let result = self.resolver.resolve_path_in_value_ns_fully(self.db.upcast(), path).and_then(
|
||||
|result| match result {
|
||||
ValueNs::LocalBinding(binding) => {
|
||||
let mir_span = match id {
|
||||
ExprOrPatId::ExprId(id) => MirSpan::ExprId(id),
|
||||
ExprOrPatId::PatId(id) => MirSpan::PatId(id),
|
||||
};
|
||||
self.current_capture_span_stack.push(mir_span);
|
||||
Some(HirPlace { local: binding, projections: Vec::new() })
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
);
|
||||
result
|
||||
}
|
||||
|
||||
/// Changes `current_capture_span_stack` to contain the stack of spans for this expr.
|
||||
fn place_of_expr_without_adjust(&mut self, tgt_expr: ExprId) -> Option<HirPlace> {
|
||||
self.current_capture_span_stack.clear();
|
||||
match &self.body[tgt_expr] {
|
||||
Expr::Path(p) => {
|
||||
let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr);
|
||||
if let Some(ResolveValueResult::ValueNs(ValueNs::LocalBinding(b), _)) =
|
||||
resolver.resolve_path_in_value_ns(self.db.upcast(), p)
|
||||
{
|
||||
self.current_capture_span_stack.push(MirSpan::ExprId(tgt_expr));
|
||||
return Some(HirPlace { local: b, projections: vec![] });
|
||||
}
|
||||
let resolver_guard =
|
||||
self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, tgt_expr);
|
||||
let result = self.path_place(p, tgt_expr.into());
|
||||
self.resolver.reset_to_guard(resolver_guard);
|
||||
return result;
|
||||
}
|
||||
Expr::Field { expr, name: _ } => {
|
||||
let mut place = self.place_of_expr(*expr)?;
|
||||
|
@ -590,6 +610,16 @@ impl InferenceContext<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
fn mutate_path_pat(&mut self, path: &Path, id: PatId) {
|
||||
if let Some(place) = self.path_place(path, id.into()) {
|
||||
self.add_capture(
|
||||
place,
|
||||
CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::Default }),
|
||||
);
|
||||
self.current_capture_span_stack.pop(); // Remove the pattern span.
|
||||
}
|
||||
}
|
||||
|
||||
fn mutate_expr(&mut self, expr: ExprId, place: Option<HirPlace>) {
|
||||
if let Some(place) = place {
|
||||
self.add_capture(
|
||||
|
@ -722,7 +752,7 @@ impl InferenceContext<'_> {
|
|||
self.consume_expr(*tail);
|
||||
}
|
||||
}
|
||||
Expr::Call { callee, args, is_assignee_expr: _ } => {
|
||||
Expr::Call { callee, args } => {
|
||||
self.consume_expr(*callee);
|
||||
self.consume_exprs(args.iter().copied());
|
||||
}
|
||||
|
@ -838,7 +868,7 @@ impl InferenceContext<'_> {
|
|||
self.consume_expr(expr);
|
||||
}
|
||||
}
|
||||
Expr::Index { base, index, is_assignee_expr: _ } => {
|
||||
Expr::Index { base, index } => {
|
||||
self.select_from_expr(*base);
|
||||
self.consume_expr(*index);
|
||||
}
|
||||
|
@ -862,10 +892,30 @@ impl InferenceContext<'_> {
|
|||
}));
|
||||
self.current_captures = cc;
|
||||
}
|
||||
Expr::Array(Array::ElementList { elements: exprs, is_assignee_expr: _ })
|
||||
| Expr::Tuple { exprs, is_assignee_expr: _ } => {
|
||||
Expr::Array(Array::ElementList { elements: exprs }) | Expr::Tuple { exprs } => {
|
||||
self.consume_exprs(exprs.iter().copied())
|
||||
}
|
||||
&Expr::Assignment { target, value } => {
|
||||
self.walk_expr(value);
|
||||
let resolver_guard =
|
||||
self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, tgt_expr);
|
||||
match self.place_of_expr(value) {
|
||||
Some(rhs_place) => {
|
||||
self.inside_assignment = true;
|
||||
self.consume_with_pat(rhs_place, target);
|
||||
self.inside_assignment = false;
|
||||
}
|
||||
None => self.body.walk_pats(target, &mut |pat| match &self.body[pat] {
|
||||
Pat::Path(path) => self.mutate_path_pat(path, pat),
|
||||
&Pat::Expr(expr) => {
|
||||
let place = self.place_of_expr(expr);
|
||||
self.mutate_expr(expr, place);
|
||||
}
|
||||
_ => {}
|
||||
}),
|
||||
}
|
||||
self.resolver.reset_to_guard(resolver_guard);
|
||||
}
|
||||
|
||||
Expr::Missing
|
||||
| Expr::Continue { .. }
|
||||
|
@ -903,6 +953,7 @@ impl InferenceContext<'_> {
|
|||
| Pat::Missing
|
||||
| Pat::Wild
|
||||
| Pat::Tuple { .. }
|
||||
| Pat::Expr(_)
|
||||
| Pat::Or(_) => (),
|
||||
Pat::TupleStruct { .. } | Pat::Record { .. } => {
|
||||
if let Some(variant) = self.result.variant_resolution_for_pat(p) {
|
||||
|
@ -1122,11 +1173,15 @@ impl InferenceContext<'_> {
|
|||
}
|
||||
}
|
||||
}
|
||||
Pat::Range { .. }
|
||||
| Pat::Slice { .. }
|
||||
| Pat::ConstBlock(_)
|
||||
| Pat::Path(_)
|
||||
| Pat::Lit(_) => self.consume_place(place),
|
||||
Pat::Range { .. } | Pat::Slice { .. } | Pat::ConstBlock(_) | Pat::Lit(_) => {
|
||||
self.consume_place(place)
|
||||
}
|
||||
Pat::Path(path) => {
|
||||
if self.inside_assignment {
|
||||
self.mutate_path_pat(path, tgt_pat);
|
||||
}
|
||||
self.consume_place(place);
|
||||
}
|
||||
&Pat::Bind { id, subpat: _ } => {
|
||||
let mode = self.result.binding_modes[tgt_pat];
|
||||
let capture_kind = match mode {
|
||||
|
@ -1180,6 +1235,15 @@ impl InferenceContext<'_> {
|
|||
self.current_capture_span_stack.pop();
|
||||
}
|
||||
Pat::Box { .. } => (), // not supported
|
||||
&Pat::Expr(expr) => {
|
||||
self.consume_place(place);
|
||||
let pat_capture_span_stack = mem::take(&mut self.current_capture_span_stack);
|
||||
let old_inside_assignment = mem::replace(&mut self.inside_assignment, false);
|
||||
let lhs_place = self.place_of_expr(expr);
|
||||
self.mutate_expr(expr, lhs_place);
|
||||
self.inside_assignment = old_inside_assignment;
|
||||
self.current_capture_span_stack = pat_capture_span_stack;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.current_capture_span_stack
|
||||
|
|
|
@ -9,8 +9,8 @@ use chalk_ir::{cast::Cast, fold::Shift, DebruijnIndex, Mutability, TyVariableKin
|
|||
use either::Either;
|
||||
use hir_def::{
|
||||
hir::{
|
||||
ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, ClosureKind, Expr, ExprId, LabelId,
|
||||
Literal, Pat, PatId, Statement, UnaryOp,
|
||||
ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, ClosureKind, Expr, ExprId, ExprOrPatId,
|
||||
LabelId, Literal, Pat, PatId, Statement, UnaryOp,
|
||||
},
|
||||
lang_item::{LangItem, LangItemTarget},
|
||||
path::{GenericArg, GenericArgs, Path},
|
||||
|
@ -188,6 +188,9 @@ impl InferenceContext<'_> {
|
|||
| Pat::ConstBlock(_)
|
||||
| Pat::Record { .. }
|
||||
| Pat::Missing => true,
|
||||
Pat::Expr(_) => unreachable!(
|
||||
"we don't call pat_guaranteed_to_constitute_read_for_never() with assignments"
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -223,6 +226,7 @@ impl InferenceContext<'_> {
|
|||
| Expr::Const(..)
|
||||
| Expr::UnaryOp { .. }
|
||||
| Expr::BinaryOp { .. }
|
||||
| Expr::Assignment { .. }
|
||||
| Expr::Yield { .. }
|
||||
| Expr::Cast { .. }
|
||||
| Expr::Async { .. }
|
||||
|
@ -609,23 +613,7 @@ impl InferenceContext<'_> {
|
|||
coerce.complete(self)
|
||||
}
|
||||
}
|
||||
Expr::Path(p) => {
|
||||
let g = self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, tgt_expr);
|
||||
let ty = match self.infer_path(p, tgt_expr.into()) {
|
||||
Some(ty) => ty,
|
||||
None => {
|
||||
if matches!(p, Path::Normal { mod_path, .. } if mod_path.is_ident() || mod_path.is_self())
|
||||
{
|
||||
self.push_diagnostic(InferenceDiagnostic::UnresolvedIdent {
|
||||
expr: tgt_expr,
|
||||
});
|
||||
}
|
||||
self.err_ty()
|
||||
}
|
||||
};
|
||||
self.resolver.reset_to_guard(g);
|
||||
ty
|
||||
}
|
||||
Expr::Path(p) => self.infer_expr_path(p, tgt_expr.into(), tgt_expr),
|
||||
&Expr::Continue { label } => {
|
||||
if find_continuable(&mut self.breakables, label).is_none() {
|
||||
self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
|
||||
|
@ -892,36 +880,6 @@ impl InferenceContext<'_> {
|
|||
}
|
||||
}
|
||||
Expr::BinaryOp { lhs, rhs, op } => match op {
|
||||
Some(BinaryOp::Assignment { op: None }) => {
|
||||
let lhs = *lhs;
|
||||
let is_ordinary = match &self.body[lhs] {
|
||||
Expr::Array(_)
|
||||
| Expr::RecordLit { .. }
|
||||
| Expr::Tuple { .. }
|
||||
| Expr::Underscore => false,
|
||||
Expr::Call { callee, .. } => !matches!(&self.body[*callee], Expr::Path(_)),
|
||||
_ => true,
|
||||
};
|
||||
|
||||
// In ordinary (non-destructuring) assignments, the type of
|
||||
// `lhs` must be inferred first so that the ADT fields
|
||||
// instantiations in RHS can be coerced to it. Note that this
|
||||
// cannot happen in destructuring assignments because of how
|
||||
// they are desugared.
|
||||
if is_ordinary {
|
||||
// LHS of assignment doesn't constitute reads.
|
||||
let lhs_ty = self.infer_expr(lhs, &Expectation::none(), ExprIsRead::No);
|
||||
self.infer_expr_coerce(
|
||||
*rhs,
|
||||
&Expectation::has_type(lhs_ty),
|
||||
ExprIsRead::No,
|
||||
);
|
||||
} else {
|
||||
let rhs_ty = self.infer_expr(*rhs, &Expectation::none(), ExprIsRead::Yes);
|
||||
self.infer_assignee_expr(lhs, &rhs_ty);
|
||||
}
|
||||
self.result.standard_types.unit.clone()
|
||||
}
|
||||
Some(BinaryOp::LogicOp(_)) => {
|
||||
let bool_ty = self.result.standard_types.bool_.clone();
|
||||
self.infer_expr_coerce(
|
||||
|
@ -942,6 +900,35 @@ impl InferenceContext<'_> {
|
|||
Some(op) => self.infer_overloadable_binop(*lhs, *op, *rhs, tgt_expr),
|
||||
_ => self.err_ty(),
|
||||
},
|
||||
&Expr::Assignment { target, value } => {
|
||||
// In ordinary (non-destructuring) assignments, the type of
|
||||
// `lhs` must be inferred first so that the ADT fields
|
||||
// instantiations in RHS can be coerced to it. Note that this
|
||||
// cannot happen in destructuring assignments because of how
|
||||
// they are desugared.
|
||||
let lhs_ty = match &self.body[target] {
|
||||
// LHS of assignment doesn't constitute reads.
|
||||
&Pat::Expr(expr) => {
|
||||
Some(self.infer_expr(expr, &Expectation::none(), ExprIsRead::No))
|
||||
}
|
||||
Pat::Path(path) => Some(self.infer_expr_path(path, target.into(), tgt_expr)),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(lhs_ty) = lhs_ty {
|
||||
self.write_pat_ty(target, lhs_ty.clone());
|
||||
self.infer_expr_coerce(value, &Expectation::has_type(lhs_ty), ExprIsRead::No);
|
||||
} else {
|
||||
let rhs_ty = self.infer_expr(value, &Expectation::none(), ExprIsRead::Yes);
|
||||
let resolver_guard =
|
||||
self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, tgt_expr);
|
||||
self.inside_assignment = true;
|
||||
self.infer_top_pat(target, &rhs_ty);
|
||||
self.inside_assignment = false;
|
||||
self.resolver.reset_to_guard(resolver_guard);
|
||||
}
|
||||
self.result.standard_types.unit.clone()
|
||||
}
|
||||
Expr::Range { lhs, rhs, range_type } => {
|
||||
let lhs_ty =
|
||||
lhs.map(|e| self.infer_expr_inner(e, &Expectation::none(), ExprIsRead::Yes));
|
||||
|
@ -981,7 +968,7 @@ impl InferenceContext<'_> {
|
|||
(RangeOp::Inclusive, _, None) => self.err_ty(),
|
||||
}
|
||||
}
|
||||
Expr::Index { base, index, is_assignee_expr } => {
|
||||
Expr::Index { base, index } => {
|
||||
let base_ty = self.infer_expr_inner(*base, &Expectation::none(), ExprIsRead::Yes);
|
||||
let index_ty = self.infer_expr(*index, &Expectation::none(), ExprIsRead::Yes);
|
||||
|
||||
|
@ -1017,23 +1004,11 @@ impl InferenceContext<'_> {
|
|||
self.write_method_resolution(tgt_expr, func, subst);
|
||||
}
|
||||
let assoc = self.resolve_ops_index_output();
|
||||
let res = self.resolve_associated_type_with_params(
|
||||
self.resolve_associated_type_with_params(
|
||||
self_ty.clone(),
|
||||
assoc,
|
||||
&[index_ty.clone().cast(Interner)],
|
||||
);
|
||||
|
||||
if *is_assignee_expr {
|
||||
if let Some(index_trait) = self.resolve_lang_trait(LangItem::IndexMut) {
|
||||
let trait_ref = TyBuilder::trait_ref(self.db, index_trait)
|
||||
.push(self_ty)
|
||||
.fill(|_| index_ty.clone().cast(Interner))
|
||||
.build();
|
||||
self.push_obligation(trait_ref.cast(Interner));
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
)
|
||||
} else {
|
||||
self.err_ty()
|
||||
}
|
||||
|
@ -1151,9 +1126,7 @@ impl InferenceContext<'_> {
|
|||
},
|
||||
},
|
||||
Expr::Underscore => {
|
||||
// Underscore expressions may only appear in assignee expressions,
|
||||
// which are handled by `infer_assignee_expr()`.
|
||||
// Any other underscore expression is an error, we render a specialized diagnostic
|
||||
// Underscore expression is an error, we render a specialized diagnostic
|
||||
// to let the user know what type is expected though.
|
||||
let expected = expected.to_option(&mut self.table).unwrap_or_else(|| self.err_ty());
|
||||
self.push_diagnostic(InferenceDiagnostic::TypedHole {
|
||||
|
@ -1232,6 +1205,22 @@ impl InferenceContext<'_> {
|
|||
ty
|
||||
}
|
||||
|
||||
fn infer_expr_path(&mut self, path: &Path, id: ExprOrPatId, scope_id: ExprId) -> Ty {
|
||||
let g = self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, scope_id);
|
||||
let ty = match self.infer_path(path, id) {
|
||||
Some(ty) => ty,
|
||||
None => {
|
||||
if matches!(path, Path::Normal { mod_path, .. } if mod_path.is_ident() || mod_path.is_self())
|
||||
{
|
||||
self.push_diagnostic(InferenceDiagnostic::UnresolvedIdent { id });
|
||||
}
|
||||
self.err_ty()
|
||||
}
|
||||
};
|
||||
self.resolver.reset_to_guard(g);
|
||||
ty
|
||||
}
|
||||
|
||||
fn infer_async_block(
|
||||
&mut self,
|
||||
tgt_expr: ExprId,
|
||||
|
@ -1482,107 +1471,6 @@ impl InferenceContext<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
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)).map(|it| it as u32);
|
||||
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)).map(|it| it as u32);
|
||||
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 inferred 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(self.db, None, self.resolver.krate()),
|
||||
)
|
||||
.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, subs)
|
||||
}
|
||||
Expr::Underscore => rhs_ty.clone(),
|
||||
_ => {
|
||||
// `lhs` is a place expression, a unit struct, or an enum variant.
|
||||
// LHS of assignment doesn't constitute reads.
|
||||
let lhs_ty = self.infer_expr_inner(lhs, &Expectation::none(), ExprIsRead::No);
|
||||
|
||||
// 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, CoerceNever::Yes) {
|
||||
Ok(ty) => ty,
|
||||
Err(_) => {
|
||||
self.result.type_mismatches.insert(
|
||||
lhs.into(),
|
||||
TypeMismatch { expected: rhs_ty.clone(), actual: lhs_ty.clone() },
|
||||
);
|
||||
// `rhs_ty` is returned so no further type mismatches are
|
||||
// reported because of this mismatch.
|
||||
rhs_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,8 @@
|
|||
use chalk_ir::{cast::Cast, Mutability};
|
||||
use hir_def::{
|
||||
hir::{
|
||||
Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp,
|
||||
Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, Pat, PatId, Statement,
|
||||
UnaryOp,
|
||||
},
|
||||
lang_item::LangItem,
|
||||
};
|
||||
|
@ -96,7 +97,7 @@ impl InferenceContext<'_> {
|
|||
}
|
||||
}
|
||||
Expr::MethodCall { receiver: it, method_name: _, args, generic_args: _ }
|
||||
| Expr::Call { callee: it, args, is_assignee_expr: _ } => {
|
||||
| Expr::Call { callee: it, args } => {
|
||||
self.infer_mut_not_expr_iter(args.iter().copied().chain(Some(*it)));
|
||||
}
|
||||
Expr::Match { expr, arms } => {
|
||||
|
@ -120,10 +121,10 @@ impl InferenceContext<'_> {
|
|||
Expr::Become { expr } => {
|
||||
self.infer_mut_expr(*expr, Mutability::Not);
|
||||
}
|
||||
Expr::RecordLit { path: _, fields, spread, ellipsis: _, is_assignee_expr: _ } => {
|
||||
Expr::RecordLit { path: _, fields, spread } => {
|
||||
self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr).chain(*spread))
|
||||
}
|
||||
&Expr::Index { base, index, is_assignee_expr } => {
|
||||
&Expr::Index { base, index } => {
|
||||
if mutability == Mutability::Mut {
|
||||
if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) {
|
||||
if let Some(index_trait) = self
|
||||
|
@ -148,11 +149,8 @@ impl InferenceContext<'_> {
|
|||
target,
|
||||
}) = base_adjustments
|
||||
{
|
||||
// For assignee exprs `IndexMut` obligations are already applied
|
||||
if !is_assignee_expr {
|
||||
if let TyKind::Ref(_, _, ty) = target.kind(Interner) {
|
||||
base_ty = Some(ty.clone());
|
||||
}
|
||||
if let TyKind::Ref(_, _, ty) = target.kind(Interner) {
|
||||
base_ty = Some(ty.clone());
|
||||
}
|
||||
*mutability = Mutability::Mut;
|
||||
}
|
||||
|
@ -233,6 +231,14 @@ impl InferenceContext<'_> {
|
|||
self.infer_mut_expr(*lhs, Mutability::Mut);
|
||||
self.infer_mut_expr(*rhs, Mutability::Not);
|
||||
}
|
||||
&Expr::Assignment { target, value } => {
|
||||
self.body.walk_pats(target, &mut |pat| match self.body[pat] {
|
||||
Pat::Expr(expr) => self.infer_mut_expr(expr, Mutability::Mut),
|
||||
Pat::ConstBlock(block) => self.infer_mut_expr(block, Mutability::Not),
|
||||
_ => {}
|
||||
});
|
||||
self.infer_mut_expr(value, Mutability::Not);
|
||||
}
|
||||
Expr::Array(Array::Repeat { initializer: lhs, repeat: rhs })
|
||||
| Expr::BinaryOp { lhs, rhs, op: _ }
|
||||
| Expr::Range { lhs: Some(lhs), rhs: Some(rhs), range_type: _ } => {
|
||||
|
@ -242,8 +248,7 @@ impl InferenceContext<'_> {
|
|||
Expr::Closure { body, .. } => {
|
||||
self.infer_mut_expr(*body, Mutability::Not);
|
||||
}
|
||||
Expr::Tuple { exprs, is_assignee_expr: _ }
|
||||
| Expr::Array(Array::ElementList { elements: exprs, is_assignee_expr: _ }) => {
|
||||
Expr::Tuple { exprs } | Expr::Array(Array::ElementList { elements: exprs }) => {
|
||||
self.infer_mut_not_expr_iter(exprs.iter().copied());
|
||||
}
|
||||
// These don't need any action, as they don't have sub expressions
|
||||
|
|
|
@ -4,7 +4,7 @@ use std::iter::repeat_with;
|
|||
|
||||
use hir_def::{
|
||||
body::Body,
|
||||
hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId},
|
||||
hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, Literal, Pat, PatId},
|
||||
path::Path,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
|
@ -12,63 +12,28 @@ use stdx::TupleExt;
|
|||
|
||||
use crate::{
|
||||
consteval::{try_const_usize, usize_const},
|
||||
infer::{expr::ExprIsRead, BindingMode, Expectation, InferenceContext, TypeMismatch},
|
||||
infer::{
|
||||
coerce::CoerceNever, expr::ExprIsRead, BindingMode, Expectation, InferenceContext,
|
||||
TypeMismatch,
|
||||
},
|
||||
lower::lower_to_chalk_mutability,
|
||||
primitive::UintTy,
|
||||
static_lifetime, InferenceDiagnostic, Interner, Mutability, Scalar, Substitution, Ty,
|
||||
TyBuilder, TyExt, TyKind,
|
||||
};
|
||||
|
||||
/// Used to generalize patterns and assignee expressions.
|
||||
pub(super) 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)
|
||||
}
|
||||
}
|
||||
|
||||
impl InferenceContext<'_> {
|
||||
/// 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>(
|
||||
pub(super) fn infer_tuple_struct_pat_like(
|
||||
&mut self,
|
||||
path: Option<&Path>,
|
||||
expected: &Ty,
|
||||
default_bm: T::BindingMode,
|
||||
id: T,
|
||||
default_bm: BindingMode,
|
||||
id: PatId,
|
||||
ellipsis: Option<u32>,
|
||||
subs: &[T],
|
||||
subs: &[PatId],
|
||||
) -> Ty {
|
||||
let (ty, def) = self.resolve_variant(path, true);
|
||||
let var_data = def.map(|it| it.variant_data(self.db.upcast()));
|
||||
|
@ -127,13 +92,13 @@ impl InferenceContext<'_> {
|
|||
}
|
||||
};
|
||||
|
||||
T::infer(self, subpat, &expected_ty, default_bm);
|
||||
self.infer_pat(subpat, &expected_ty, default_bm);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let err_ty = self.err_ty();
|
||||
for &inner in subs {
|
||||
T::infer(self, inner, &err_ty, default_bm);
|
||||
self.infer_pat(inner, &err_ty, default_bm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -142,13 +107,13 @@ impl InferenceContext<'_> {
|
|||
}
|
||||
|
||||
/// Infers type for record pattern or its corresponding assignee expression.
|
||||
pub(super) fn infer_record_pat_like<T: PatLike>(
|
||||
pub(super) fn infer_record_pat_like(
|
||||
&mut self,
|
||||
path: Option<&Path>,
|
||||
expected: &Ty,
|
||||
default_bm: T::BindingMode,
|
||||
id: T,
|
||||
subs: impl ExactSizeIterator<Item = (Name, T)>,
|
||||
default_bm: BindingMode,
|
||||
id: PatId,
|
||||
subs: impl ExactSizeIterator<Item = (Name, PatId)>,
|
||||
) -> Ty {
|
||||
let (ty, def) = self.resolve_variant(path, false);
|
||||
if let Some(variant) = def {
|
||||
|
@ -197,13 +162,13 @@ impl InferenceContext<'_> {
|
|||
}
|
||||
};
|
||||
|
||||
T::infer(self, inner, &expected_ty, default_bm);
|
||||
self.infer_pat(inner, &expected_ty, default_bm);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let err_ty = self.err_ty();
|
||||
for (_, inner) in subs {
|
||||
T::infer(self, inner, &err_ty, default_bm);
|
||||
self.infer_pat(inner, &err_ty, default_bm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -214,12 +179,12 @@ impl InferenceContext<'_> {
|
|||
/// 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>(
|
||||
pub(super) fn infer_tuple_pat_like(
|
||||
&mut self,
|
||||
expected: &Ty,
|
||||
default_bm: T::BindingMode,
|
||||
default_bm: BindingMode,
|
||||
ellipsis: Option<u32>,
|
||||
subs: &[T],
|
||||
subs: &[PatId],
|
||||
) -> Ty {
|
||||
let expected = self.resolve_ty_shallow(expected);
|
||||
let expectations = match expected.as_tuple() {
|
||||
|
@ -244,18 +209,20 @@ impl InferenceContext<'_> {
|
|||
|
||||
// Process pre
|
||||
for (ty, pat) in inner_tys.iter_mut().zip(pre) {
|
||||
*ty = T::infer(self, *pat, ty, default_bm);
|
||||
*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 = T::infer(self, *pat, ty, default_bm);
|
||||
*ty = self.infer_pat(*pat, ty, default_bm);
|
||||
}
|
||||
|
||||
TyKind::Tuple(inner_tys.len(), Substitution::from_iter(Interner, inner_tys))
|
||||
.intern(Interner)
|
||||
}
|
||||
|
||||
/// The resolver needs to be updated to the surrounding expression when inside assignment
|
||||
/// (because there, `Pat::Path` can refer to a variable).
|
||||
pub(super) fn infer_top_pat(&mut self, pat: PatId, expected: &Ty) {
|
||||
self.infer_pat(pat, expected, BindingMode::default());
|
||||
}
|
||||
|
@ -263,7 +230,14 @@ impl InferenceContext<'_> {
|
|||
fn infer_pat(&mut self, pat: PatId, expected: &Ty, mut default_bm: BindingMode) -> Ty {
|
||||
let mut expected = self.resolve_ty_shallow(expected);
|
||||
|
||||
if self.is_non_ref_pat(self.body, pat) {
|
||||
if matches!(&self.body[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.body, pat) {
|
||||
let mut pat_adjustments = Vec::new();
|
||||
while let Some((inner, _lifetime, mutability)) = expected.as_reference() {
|
||||
pat_adjustments.push(expected.clone());
|
||||
|
@ -279,11 +253,6 @@ impl InferenceContext<'_> {
|
|||
pat_adjustments.shrink_to_fit();
|
||||
self.result.pat_adjustments.insert(pat, pat_adjustments);
|
||||
}
|
||||
} else if let Pat::Ref { .. } = &self.body[pat] {
|
||||
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);`
|
||||
default_bm = BindingMode::Move;
|
||||
}
|
||||
|
||||
// Lose mutability.
|
||||
|
@ -320,8 +289,34 @@ impl InferenceContext<'_> {
|
|||
self.infer_record_pat_like(p.as_deref(), &expected, default_bm, pat, subs)
|
||||
}
|
||||
Pat::Path(path) => {
|
||||
// FIXME update resolver for the surrounding expression
|
||||
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());
|
||||
let ty_inserted_vars = self.insert_type_vars_shallow(ty.clone());
|
||||
match self.table.coerce(&expected, &ty_inserted_vars, CoerceNever::Yes) {
|
||||
Ok((adjustments, coerced_ty)) => {
|
||||
if !adjustments.is_empty() {
|
||||
self.result
|
||||
.pat_adjustments
|
||||
.entry(pat)
|
||||
.or_default()
|
||||
.extend(adjustments.into_iter().map(|adjust| adjust.target));
|
||||
}
|
||||
self.write_pat_ty(pat, coerced_ty);
|
||||
return self.pat_ty_after_adjustment(pat);
|
||||
}
|
||||
Err(_) => {
|
||||
self.result.type_mismatches.insert(
|
||||
pat.into(),
|
||||
TypeMismatch {
|
||||
expected: expected.clone(),
|
||||
actual: ty_inserted_vars.clone(),
|
||||
},
|
||||
);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Pat::Bind { id, subpat } => {
|
||||
return self.infer_bind_pat(pat, *id, default_bm, *subpat, &expected);
|
||||
|
@ -361,7 +356,40 @@ impl InferenceContext<'_> {
|
|||
None => self.err_ty(),
|
||||
},
|
||||
Pat::ConstBlock(expr) => {
|
||||
self.infer_expr(*expr, &Expectation::has_type(expected.clone()), ExprIsRead::Yes)
|
||||
let old_inside_assign = std::mem::replace(&mut self.inside_assignment, false);
|
||||
let result = self.infer_expr(
|
||||
*expr,
|
||||
&Expectation::has_type(expected.clone()),
|
||||
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 result = self.infer_expr_coerce(
|
||||
*expr,
|
||||
&Expectation::has_type(expected.clone()),
|
||||
ExprIsRead::No,
|
||||
);
|
||||
// We are returning early to avoid the unifiability check below.
|
||||
let lhs_ty = self.insert_type_vars_shallow(result);
|
||||
let ty = match self.coerce(None, &expected, &lhs_ty, CoerceNever::Yes) {
|
||||
Ok(ty) => ty,
|
||||
Err(_) => {
|
||||
self.result.type_mismatches.insert(
|
||||
pat.into(),
|
||||
TypeMismatch { expected: expected.clone(), actual: lhs_ty.clone() },
|
||||
);
|
||||
// `rhs_ty` is returned so no further type mismatches are
|
||||
// reported because of this mismatch.
|
||||
expected
|
||||
}
|
||||
};
|
||||
self.write_pat_ty(pat, ty.clone());
|
||||
self.inside_assignment = old_inside_assign;
|
||||
return ty;
|
||||
}
|
||||
Pat::Missing => self.err_ty(),
|
||||
};
|
||||
|
@ -517,9 +545,12 @@ impl InferenceContext<'_> {
|
|||
body[*expr],
|
||||
Expr::Literal(Literal::String(..) | Literal::CString(..) | Literal::ByteString(..))
|
||||
),
|
||||
Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Box { .. } | Pat::Missing => {
|
||||
false
|
||||
}
|
||||
Pat::Wild
|
||||
| Pat::Bind { .. }
|
||||
| Pat::Ref { .. }
|
||||
| Pat::Box { .. }
|
||||
| Pat::Missing
|
||||
| Pat::Expr(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ use hir_def::{
|
|||
},
|
||||
lang_item::{LangItem, LangItemTarget},
|
||||
path::Path,
|
||||
resolver::{resolver_for_expr, HasResolver, ResolveValueResult, ValueNs},
|
||||
resolver::{HasResolver, ResolveValueResult, Resolver, ValueNs},
|
||||
AdtId, DefWithBodyId, EnumVariantId, GeneralConstId, HasModule, ItemContainerId, LocalFieldId,
|
||||
Lookup, TraitId, TupleId, TypeOrConstParamId,
|
||||
};
|
||||
|
@ -76,6 +76,7 @@ struct MirLowerCtx<'a> {
|
|||
db: &'a dyn HirDatabase,
|
||||
body: &'a Body,
|
||||
infer: &'a InferenceResult,
|
||||
resolver: Resolver,
|
||||
drop_scopes: Vec<DropScope>,
|
||||
}
|
||||
|
||||
|
@ -278,6 +279,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
owner,
|
||||
closures: vec![],
|
||||
};
|
||||
let resolver = owner.resolver(db.upcast());
|
||||
|
||||
MirLowerCtx {
|
||||
result: mir,
|
||||
|
@ -285,6 +287,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
infer,
|
||||
body,
|
||||
owner,
|
||||
resolver,
|
||||
current_loop_blocks: None,
|
||||
labeled_loop_blocks: Default::default(),
|
||||
discr_temp: None,
|
||||
|
@ -410,43 +413,48 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
Err(MirLowerError::IncompleteExpr)
|
||||
}
|
||||
Expr::Path(p) => {
|
||||
let pr =
|
||||
if let Some((assoc, subst)) = self.infer.assoc_resolutions_for_expr(expr_id) {
|
||||
match assoc {
|
||||
hir_def::AssocItemId::ConstId(c) => {
|
||||
self.lower_const(
|
||||
c.into(),
|
||||
current,
|
||||
place,
|
||||
subst,
|
||||
expr_id.into(),
|
||||
self.expr_ty_without_adjust(expr_id),
|
||||
)?;
|
||||
return Ok(Some(current));
|
||||
}
|
||||
hir_def::AssocItemId::FunctionId(_) => {
|
||||
// FnDefs are zero sized, no action is needed.
|
||||
return Ok(Some(current));
|
||||
}
|
||||
hir_def::AssocItemId::TypeAliasId(_) => {
|
||||
// FIXME: If it is unreachable, use proper error instead of `not_supported`.
|
||||
not_supported!("associated functions and types")
|
||||
}
|
||||
let pr = if let Some((assoc, subst)) =
|
||||
self.infer.assoc_resolutions_for_expr(expr_id)
|
||||
{
|
||||
match assoc {
|
||||
hir_def::AssocItemId::ConstId(c) => {
|
||||
self.lower_const(
|
||||
c.into(),
|
||||
current,
|
||||
place,
|
||||
subst,
|
||||
expr_id.into(),
|
||||
self.expr_ty_without_adjust(expr_id),
|
||||
)?;
|
||||
return Ok(Some(current));
|
||||
}
|
||||
} else if let Some(variant) = self.infer.variant_resolution_for_expr(expr_id) {
|
||||
match variant {
|
||||
VariantId::EnumVariantId(e) => ValueNs::EnumVariantId(e),
|
||||
VariantId::StructId(s) => ValueNs::StructId(s),
|
||||
VariantId::UnionId(_) => implementation_error!("Union variant as path"),
|
||||
hir_def::AssocItemId::FunctionId(_) => {
|
||||
// FnDefs are zero sized, no action is needed.
|
||||
return Ok(Some(current));
|
||||
}
|
||||
} else {
|
||||
let unresolved_name =
|
||||
|| MirLowerError::unresolved_path(self.db, p, self.edition());
|
||||
let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
|
||||
resolver
|
||||
.resolve_path_in_value_ns_fully(self.db.upcast(), p)
|
||||
.ok_or_else(unresolved_name)?
|
||||
};
|
||||
hir_def::AssocItemId::TypeAliasId(_) => {
|
||||
// FIXME: If it is unreachable, use proper error instead of `not_supported`.
|
||||
not_supported!("associated functions and types")
|
||||
}
|
||||
}
|
||||
} else if let Some(variant) = self.infer.variant_resolution_for_expr(expr_id) {
|
||||
match variant {
|
||||
VariantId::EnumVariantId(e) => ValueNs::EnumVariantId(e),
|
||||
VariantId::StructId(s) => ValueNs::StructId(s),
|
||||
VariantId::UnionId(_) => implementation_error!("Union variant as path"),
|
||||
}
|
||||
} else {
|
||||
let resolver_guard =
|
||||
self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr_id);
|
||||
let result = self
|
||||
.resolver
|
||||
.resolve_path_in_value_ns_fully(self.db.upcast(), p)
|
||||
.ok_or_else(|| {
|
||||
MirLowerError::unresolved_path(self.db, p, self.edition())
|
||||
})?;
|
||||
self.resolver.reset_to_guard(resolver_guard);
|
||||
result
|
||||
};
|
||||
match pr {
|
||||
ValueNs::LocalBinding(_) | ValueNs::StaticId(_) => {
|
||||
let Some((temp, current)) =
|
||||
|
@ -553,8 +561,11 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
return Ok(None);
|
||||
};
|
||||
self.push_fake_read(current, cond_place, expr_id.into());
|
||||
let resolver_guard =
|
||||
self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr_id);
|
||||
let (then_target, else_target) =
|
||||
self.pattern_match(current, None, cond_place, *pat)?;
|
||||
self.resolver.reset_to_guard(resolver_guard);
|
||||
self.write_bytes_to_place(
|
||||
then_target,
|
||||
place,
|
||||
|
@ -688,6 +699,8 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
};
|
||||
self.push_fake_read(current, cond_place, expr_id.into());
|
||||
let mut end = None;
|
||||
let resolver_guard =
|
||||
self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr_id);
|
||||
for MatchArm { pat, guard, expr } in arms.iter() {
|
||||
let (then, mut otherwise) =
|
||||
self.pattern_match(current, None, cond_place, *pat)?;
|
||||
|
@ -721,6 +734,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
self.resolver.reset_to_guard(resolver_guard);
|
||||
if self.is_unterminated(current) {
|
||||
self.set_terminator(current, TerminatorKind::Unreachable, expr_id.into());
|
||||
}
|
||||
|
@ -795,7 +809,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
}
|
||||
Expr::Become { .. } => not_supported!("tail-calls"),
|
||||
Expr::Yield { .. } => not_supported!("yield"),
|
||||
Expr::RecordLit { fields, path, spread, ellipsis: _, is_assignee_expr: _ } => {
|
||||
Expr::RecordLit { fields, path, spread } => {
|
||||
let spread_place = match spread {
|
||||
&Some(it) => {
|
||||
let Some((p, c)) = self.lower_expr_as_place(current, it, true)? else {
|
||||
|
@ -1010,35 +1024,28 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
);
|
||||
}
|
||||
}
|
||||
if let hir_def::hir::BinaryOp::Assignment { op } = op {
|
||||
if let Some(op) = op {
|
||||
// last adjustment is `&mut` which we don't want it.
|
||||
let adjusts = self
|
||||
.infer
|
||||
.expr_adjustments
|
||||
.get(lhs)
|
||||
.and_then(|it| it.split_last())
|
||||
.map(|it| it.1)
|
||||
.ok_or(MirLowerError::TypeError(
|
||||
"adjustment of binary op was missing",
|
||||
))?;
|
||||
let Some((lhs_place, current)) =
|
||||
self.lower_expr_as_place_with_adjust(current, *lhs, false, adjusts)?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
let Some((rhs_op, current)) =
|
||||
self.lower_expr_to_some_operand(*rhs, current)?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
let r_value =
|
||||
Rvalue::CheckedBinaryOp(op.into(), Operand::Copy(lhs_place), rhs_op);
|
||||
self.push_assignment(current, lhs_place, r_value, expr_id.into());
|
||||
return Ok(Some(current));
|
||||
} else {
|
||||
return self.lower_assignment(current, *lhs, *rhs, expr_id.into());
|
||||
}
|
||||
if let hir_def::hir::BinaryOp::Assignment { op: Some(op) } = op {
|
||||
// last adjustment is `&mut` which we don't want it.
|
||||
let adjusts = self
|
||||
.infer
|
||||
.expr_adjustments
|
||||
.get(lhs)
|
||||
.and_then(|it| it.split_last())
|
||||
.map(|it| it.1)
|
||||
.ok_or(MirLowerError::TypeError("adjustment of binary op was missing"))?;
|
||||
let Some((lhs_place, current)) =
|
||||
self.lower_expr_as_place_with_adjust(current, *lhs, false, adjusts)?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
let r_value =
|
||||
Rvalue::CheckedBinaryOp(op.into(), Operand::Copy(lhs_place), rhs_op);
|
||||
self.push_assignment(current, lhs_place, r_value, expr_id.into());
|
||||
return Ok(Some(current));
|
||||
}
|
||||
let Some((lhs_op, current)) = self.lower_expr_to_some_operand(*lhs, current)?
|
||||
else {
|
||||
|
@ -1097,6 +1104,18 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
);
|
||||
Ok(Some(current))
|
||||
}
|
||||
&Expr::Assignment { target, value } => {
|
||||
let Some((value, mut current)) = self.lower_expr_as_place(current, value, true)?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
self.push_fake_read(current, value, expr_id.into());
|
||||
let resolver_guard =
|
||||
self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr_id);
|
||||
current = self.pattern_match_assignment(current, value, target)?;
|
||||
self.resolver.reset_to_guard(resolver_guard);
|
||||
Ok(Some(current))
|
||||
}
|
||||
&Expr::Range { lhs, rhs, range_type: _ } => {
|
||||
let ty = self.expr_ty_without_adjust(expr_id);
|
||||
let Some((adt, subst)) = ty.as_adt() else {
|
||||
|
@ -1213,7 +1232,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
);
|
||||
Ok(Some(current))
|
||||
}
|
||||
Expr::Tuple { exprs, is_assignee_expr: _ } => {
|
||||
Expr::Tuple { exprs } => {
|
||||
let Some(values) = exprs
|
||||
.iter()
|
||||
.map(|it| {
|
||||
|
@ -1291,73 +1310,6 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn lower_destructing_assignment(
|
||||
&mut self,
|
||||
mut current: BasicBlockId,
|
||||
lhs: ExprId,
|
||||
rhs: Place,
|
||||
span: MirSpan,
|
||||
) -> Result<Option<BasicBlockId>> {
|
||||
match &self.body.exprs[lhs] {
|
||||
Expr::Tuple { exprs, is_assignee_expr: _ } => {
|
||||
for (i, expr) in exprs.iter().enumerate() {
|
||||
let rhs = rhs.project(
|
||||
ProjectionElem::Field(Either::Right(TupleFieldId {
|
||||
tuple: TupleId(!0), // Dummy this as its unused
|
||||
index: i as u32,
|
||||
})),
|
||||
&mut self.result.projection_store,
|
||||
);
|
||||
let Some(c) = self.lower_destructing_assignment(current, *expr, rhs, span)?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
current = c;
|
||||
}
|
||||
Ok(Some(current))
|
||||
}
|
||||
Expr::Underscore => Ok(Some(current)),
|
||||
_ => {
|
||||
let Some((lhs_place, current)) = self.lower_expr_as_place(current, lhs, false)?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
self.push_assignment(current, lhs_place, Operand::Copy(rhs).into(), span);
|
||||
Ok(Some(current))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_assignment(
|
||||
&mut self,
|
||||
current: BasicBlockId,
|
||||
lhs: ExprId,
|
||||
rhs: ExprId,
|
||||
span: MirSpan,
|
||||
) -> Result<Option<BasicBlockId>> {
|
||||
let Some((rhs_op, current)) = self.lower_expr_to_some_operand(rhs, current)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
if matches!(&self.body.exprs[lhs], Expr::Underscore) {
|
||||
self.push_fake_read_for_operand(current, rhs_op, span);
|
||||
return Ok(Some(current));
|
||||
}
|
||||
if matches!(
|
||||
&self.body.exprs[lhs],
|
||||
Expr::Tuple { .. } | Expr::RecordLit { .. } | Expr::Call { .. }
|
||||
) {
|
||||
let temp = self.temp(self.expr_ty_after_adjustments(rhs), current, rhs.into())?;
|
||||
let temp = Place::from(temp);
|
||||
self.push_assignment(current, temp, rhs_op.into(), span);
|
||||
return self.lower_destructing_assignment(current, lhs, temp, span);
|
||||
}
|
||||
let Some((lhs_place, current)) = self.lower_expr_as_place(current, lhs, false)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
self.push_assignment(current, lhs_place, rhs_op.into(), span);
|
||||
Ok(Some(current))
|
||||
}
|
||||
|
||||
fn placeholder_subst(&mut self) -> Substitution {
|
||||
match self.owner.as_generic_def_id(self.db.upcast()) {
|
||||
Some(it) => TyBuilder::placeholder_subst(self.db, it),
|
||||
|
@ -1407,8 +1359,8 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
let edition = self.edition();
|
||||
let unresolved_name =
|
||||
|| MirLowerError::unresolved_path(self.db, c.as_ref(), edition);
|
||||
let resolver = self.owner.resolver(self.db.upcast());
|
||||
let pr = resolver
|
||||
let pr = self
|
||||
.resolver
|
||||
.resolve_path_in_value_ns(self.db.upcast(), c.as_ref())
|
||||
.ok_or_else(unresolved_name)?;
|
||||
match pr {
|
||||
|
@ -1632,12 +1584,6 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
self.push_statement(block, StatementKind::FakeRead(p).with_span(span));
|
||||
}
|
||||
|
||||
fn push_fake_read_for_operand(&mut self, block: BasicBlockId, operand: Operand, span: MirSpan) {
|
||||
if let Operand::Move(p) | Operand::Copy(p) = operand {
|
||||
self.push_fake_read(block, p, span);
|
||||
}
|
||||
}
|
||||
|
||||
fn push_assignment(
|
||||
&mut self,
|
||||
block: BasicBlockId,
|
||||
|
@ -1791,8 +1737,16 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
};
|
||||
current = c;
|
||||
self.push_fake_read(current, init_place, span);
|
||||
// Using the initializer for the resolver scope is good enough for us, as it cannot create new declarations
|
||||
// and has all declarations of the `let`.
|
||||
let resolver_guard = self.resolver.update_to_inner_scope(
|
||||
self.db.upcast(),
|
||||
self.owner,
|
||||
*expr_id,
|
||||
);
|
||||
(current, else_block) =
|
||||
self.pattern_match(current, None, init_place, *pat)?;
|
||||
self.resolver.reset_to_guard(resolver_guard);
|
||||
match (else_block, else_branch) {
|
||||
(None, _) => (),
|
||||
(Some(else_block), None) => {
|
||||
|
@ -2066,11 +2020,13 @@ pub fn mir_body_for_closure_query(
|
|||
let Some(sig) = ClosureSubst(substs).sig_ty().callable_sig(db) else {
|
||||
implementation_error!("closure has not callable sig");
|
||||
};
|
||||
let resolver_guard = ctx.resolver.update_to_inner_scope(db.upcast(), owner, expr);
|
||||
let current = ctx.lower_params_and_bindings(
|
||||
args.iter().zip(sig.params().iter()).map(|(it, y)| (*it, y.clone())),
|
||||
None,
|
||||
|_| true,
|
||||
)?;
|
||||
ctx.resolver.reset_to_guard(resolver_guard);
|
||||
if let Some(current) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? {
|
||||
let current = ctx.pop_drop_scope_assert_finished(current, root.into())?;
|
||||
ctx.set_terminator(current, TerminatorKind::Return, (*root).into());
|
||||
|
|
|
@ -135,8 +135,11 @@ impl MirLowerCtx<'_> {
|
|||
};
|
||||
match &self.body.exprs[expr_id] {
|
||||
Expr::Path(p) => {
|
||||
let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
|
||||
let Some(pr) = resolver.resolve_path_in_value_ns_fully(self.db.upcast(), p) else {
|
||||
let resolver_guard =
|
||||
self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr_id);
|
||||
let resolved = self.resolver.resolve_path_in_value_ns_fully(self.db.upcast(), p);
|
||||
self.resolver.reset_to_guard(resolver_guard);
|
||||
let Some(pr) = resolved else {
|
||||
return try_rvalue(self);
|
||||
};
|
||||
match pr {
|
||||
|
@ -216,7 +219,7 @@ impl MirLowerCtx<'_> {
|
|||
self.push_field_projection(&mut r, expr_id)?;
|
||||
Ok(Some((r, current)))
|
||||
}
|
||||
Expr::Index { base, index, is_assignee_expr: _ } => {
|
||||
Expr::Index { base, index } => {
|
||||
let base_ty = self.expr_ty_after_adjustments(*base);
|
||||
let index_ty = self.expr_ty_after_adjustments(*index);
|
||||
if index_ty != TyBuilder::usize()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! MIR lowering for patterns
|
||||
|
||||
use hir_def::{hir::LiteralOrConst, resolver::HasResolver, AssocItemId};
|
||||
use hir_def::{hir::LiteralOrConst, AssocItemId};
|
||||
|
||||
use crate::{
|
||||
mir::{
|
||||
|
@ -46,6 +46,8 @@ enum MatchingMode {
|
|||
Check,
|
||||
/// Assume that this pattern matches, fill bindings
|
||||
Bind,
|
||||
/// Assume that this pattern matches, assign to existing variables.
|
||||
Assign,
|
||||
}
|
||||
|
||||
impl MirLowerCtx<'_> {
|
||||
|
@ -82,6 +84,17 @@ impl MirLowerCtx<'_> {
|
|||
Ok((current, current_else))
|
||||
}
|
||||
|
||||
pub(super) fn pattern_match_assignment(
|
||||
&mut self,
|
||||
current: BasicBlockId,
|
||||
value: Place,
|
||||
pattern: PatId,
|
||||
) -> Result<BasicBlockId> {
|
||||
let (current, _) =
|
||||
self.pattern_match_inner(current, None, value, pattern, MatchingMode::Assign)?;
|
||||
Ok(current)
|
||||
}
|
||||
|
||||
pub(super) fn match_self_param(
|
||||
&mut self,
|
||||
id: BindingId,
|
||||
|
@ -155,14 +168,8 @@ impl MirLowerCtx<'_> {
|
|||
*pat,
|
||||
MatchingMode::Check,
|
||||
)?;
|
||||
if mode == MatchingMode::Bind {
|
||||
(next, _) = self.pattern_match_inner(
|
||||
next,
|
||||
None,
|
||||
cond_place,
|
||||
*pat,
|
||||
MatchingMode::Bind,
|
||||
)?;
|
||||
if mode != MatchingMode::Check {
|
||||
(next, _) = self.pattern_match_inner(next, None, cond_place, *pat, mode)?;
|
||||
}
|
||||
self.set_goto(next, then_target, pattern.into());
|
||||
match next_else {
|
||||
|
@ -176,11 +183,11 @@ impl MirLowerCtx<'_> {
|
|||
}
|
||||
}
|
||||
if !finished {
|
||||
if mode == MatchingMode::Bind {
|
||||
self.set_terminator(current, TerminatorKind::Unreachable, pattern.into());
|
||||
} else {
|
||||
if mode == MatchingMode::Check {
|
||||
let ce = *current_else.get_or_insert_with(|| self.new_basic_block());
|
||||
self.set_goto(current, ce, pattern.into());
|
||||
} else {
|
||||
self.set_terminator(current, TerminatorKind::Unreachable, pattern.into());
|
||||
}
|
||||
}
|
||||
(then_target, current_else)
|
||||
|
@ -300,7 +307,7 @@ impl MirLowerCtx<'_> {
|
|||
self.pattern_match_inner(current, current_else, next_place, pat, mode)?;
|
||||
}
|
||||
if let &Some(slice) = slice {
|
||||
if mode == MatchingMode::Bind {
|
||||
if mode != MatchingMode::Check {
|
||||
if let Pat::Bind { id, subpat: _ } = self.body[slice] {
|
||||
let next_place = cond_place.project(
|
||||
ProjectionElem::Subslice {
|
||||
|
@ -342,17 +349,34 @@ impl MirLowerCtx<'_> {
|
|||
mode,
|
||||
)?,
|
||||
None => {
|
||||
// The path is not a variant, so it is a const
|
||||
let unresolved_name =
|
||||
|| MirLowerError::unresolved_path(self.db, p, self.edition());
|
||||
let pr = self
|
||||
.resolver
|
||||
.resolve_path_in_value_ns(self.db.upcast(), p)
|
||||
.ok_or_else(unresolved_name)?;
|
||||
|
||||
if let (
|
||||
MatchingMode::Assign,
|
||||
ResolveValueResult::ValueNs(ValueNs::LocalBinding(binding), _),
|
||||
) = (mode, &pr)
|
||||
{
|
||||
let local = self.binding_local(*binding)?;
|
||||
self.push_match_assignment(
|
||||
current,
|
||||
local,
|
||||
BindingMode::Move,
|
||||
cond_place,
|
||||
pattern.into(),
|
||||
);
|
||||
return Ok((current, current_else));
|
||||
}
|
||||
|
||||
// The path is not a variant or a local, so it is a const
|
||||
if mode != MatchingMode::Check {
|
||||
// A const don't bind anything. Only needs check.
|
||||
return Ok((current, current_else));
|
||||
}
|
||||
let unresolved_name =
|
||||
|| MirLowerError::unresolved_path(self.db, p, self.edition());
|
||||
let resolver = self.owner.resolver(self.db.upcast());
|
||||
let pr = resolver
|
||||
.resolve_path_in_value_ns(self.db.upcast(), p)
|
||||
.ok_or_else(unresolved_name)?;
|
||||
let (c, subst) = 'b: {
|
||||
if let Some(x) = self.infer.assoc_resolutions_for_pat(pattern) {
|
||||
if let AssocItemId::ConstId(c) = x.0 {
|
||||
|
@ -415,7 +439,7 @@ impl MirLowerCtx<'_> {
|
|||
(current, current_else) =
|
||||
self.pattern_match_inner(current, current_else, cond_place, *subpat, mode)?
|
||||
}
|
||||
if mode == MatchingMode::Bind {
|
||||
if mode != MatchingMode::Check {
|
||||
let mode = self.infer.binding_modes[pattern];
|
||||
self.pattern_match_binding(
|
||||
*id,
|
||||
|
@ -448,6 +472,23 @@ impl MirLowerCtx<'_> {
|
|||
cond_place.project(ProjectionElem::Deref, &mut self.result.projection_store);
|
||||
self.pattern_match_inner(current, current_else, cond_place, *pat, mode)?
|
||||
}
|
||||
&Pat::Expr(expr) => {
|
||||
stdx::always!(
|
||||
mode == MatchingMode::Assign,
|
||||
"Pat::Expr can only come in destructuring assignments"
|
||||
);
|
||||
let Some((lhs_place, current)) = self.lower_expr_as_place(current, expr, false)?
|
||||
else {
|
||||
return Ok((current, current_else));
|
||||
};
|
||||
self.push_assignment(
|
||||
current,
|
||||
lhs_place,
|
||||
Operand::Copy(cond_place).into(),
|
||||
expr.into(),
|
||||
);
|
||||
(current, current_else)
|
||||
}
|
||||
Pat::Box { .. } => not_supported!("box pattern"),
|
||||
Pat::ConstBlock(_) => not_supported!("const block pattern"),
|
||||
})
|
||||
|
@ -464,6 +505,18 @@ impl MirLowerCtx<'_> {
|
|||
) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
|
||||
let target_place = self.binding_local(id)?;
|
||||
self.push_storage_live(id, current)?;
|
||||
self.push_match_assignment(current, target_place, mode, cond_place, span);
|
||||
Ok((current, current_else))
|
||||
}
|
||||
|
||||
fn push_match_assignment(
|
||||
&mut self,
|
||||
current: BasicBlockId,
|
||||
target_place: LocalId,
|
||||
mode: BindingMode,
|
||||
cond_place: Place,
|
||||
span: MirSpan,
|
||||
) {
|
||||
self.push_assignment(
|
||||
current,
|
||||
target_place.into(),
|
||||
|
@ -476,7 +529,6 @@ impl MirLowerCtx<'_> {
|
|||
},
|
||||
span,
|
||||
);
|
||||
Ok((current, current_else))
|
||||
}
|
||||
|
||||
fn pattern_match_const(
|
||||
|
|
|
@ -246,7 +246,7 @@ pub struct UnresolvedAssocItem {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct UnresolvedIdent {
|
||||
pub expr: InFile<AstPtr<ast::Expr>>,
|
||||
pub expr_or_pat: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -541,6 +541,10 @@ impl AnyDiagnostic {
|
|||
let pat_syntax = |pat| {
|
||||
source_map.pat_syntax(pat).inspect_err(|_| tracing::error!("synthetic syntax")).ok()
|
||||
};
|
||||
let expr_or_pat_syntax = |id| match id {
|
||||
ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(|it| it.map(AstPtr::wrap_left)),
|
||||
ExprOrPatId::PatId(pat) => pat_syntax(pat).map(|it| it.map(AstPtr::wrap_right)),
|
||||
};
|
||||
Some(match d {
|
||||
&InferenceDiagnostic::NoSuchField { field: expr, private, variant } => {
|
||||
let expr_or_pat = match expr {
|
||||
|
@ -562,10 +566,7 @@ impl AnyDiagnostic {
|
|||
PrivateField { expr, field }.into()
|
||||
}
|
||||
&InferenceDiagnostic::PrivateAssocItem { id, item } => {
|
||||
let expr_or_pat = match id {
|
||||
ExprOrPatId::ExprId(expr) => expr_syntax(expr)?.map(AstPtr::wrap_left),
|
||||
ExprOrPatId::PatId(pat) => pat_syntax(pat)?.map(AstPtr::wrap_right),
|
||||
};
|
||||
let expr_or_pat = expr_or_pat_syntax(id)?;
|
||||
let item = item.into();
|
||||
PrivateAssocItem { expr_or_pat, item }.into()
|
||||
}
|
||||
|
@ -609,15 +610,12 @@ impl AnyDiagnostic {
|
|||
.into()
|
||||
}
|
||||
&InferenceDiagnostic::UnresolvedAssocItem { id } => {
|
||||
let expr_or_pat = match id {
|
||||
ExprOrPatId::ExprId(expr) => expr_syntax(expr)?.map(AstPtr::wrap_left),
|
||||
ExprOrPatId::PatId(pat) => pat_syntax(pat)?.map(AstPtr::wrap_right),
|
||||
};
|
||||
let expr_or_pat = expr_or_pat_syntax(id)?;
|
||||
UnresolvedAssocItem { expr_or_pat }.into()
|
||||
}
|
||||
&InferenceDiagnostic::UnresolvedIdent { expr } => {
|
||||
let expr = expr_syntax(expr)?;
|
||||
UnresolvedIdent { expr }.into()
|
||||
&InferenceDiagnostic::UnresolvedIdent { id } => {
|
||||
let expr_or_pat = expr_or_pat_syntax(id)?;
|
||||
UnresolvedIdent { expr_or_pat }.into()
|
||||
}
|
||||
&InferenceDiagnostic::BreakOutsideOfLoop { expr, is_break, bad_value_break } => {
|
||||
let expr = expr_syntax(expr)?;
|
||||
|
|
|
@ -1275,4 +1275,19 @@ fn main() {
|
|||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn destructuring_assignment_needs_mut() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
//- minicore: fn
|
||||
|
||||
fn main() {
|
||||
let mut var = 1;
|
||||
let mut func = || (var,) = (2,);
|
||||
func();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ pub(crate) fn unresolved_ident(
|
|||
ctx,
|
||||
DiagnosticCode::RustcHardError("E0425"),
|
||||
"no such value in this scope",
|
||||
d.expr.map(Into::into),
|
||||
d.expr_or_pat.map(Into::into),
|
||||
)
|
||||
.experimental()
|
||||
}
|
||||
|
|
|
@ -232,6 +232,10 @@ impl ast::RangeExpr {
|
|||
Some((ix, token, bin_op))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_range_full(&self) -> bool {
|
||||
support::children::<Expr>(&self.syntax).next().is_none()
|
||||
}
|
||||
}
|
||||
|
||||
impl RangeItem for ast::RangeExpr {
|
||||
|
|
Loading…
Reference in a new issue