mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-15 17:28:09 +00:00
Auto merge of #14251 - Veykril:ty-expr-stmt, r=Veykril
internal: Set expectation for no-semi expression statements to unit
This commit is contained in:
commit
94dc7a3eb0
16 changed files with 395 additions and 289 deletions
|
@ -37,7 +37,7 @@ use crate::{
|
|||
item_scope::BuiltinShadowMode,
|
||||
path::{GenericArgs, Path},
|
||||
type_ref::{Mutability, Rawness, TypeRef},
|
||||
AdtId, BlockLoc, ModuleDefId, UnresolvedMacro,
|
||||
AdtId, BlockId, BlockLoc, ModuleDefId, UnresolvedMacro,
|
||||
};
|
||||
|
||||
pub struct LowerCtx<'a> {
|
||||
|
@ -238,33 +238,32 @@ impl ExprCollector<'_> {
|
|||
}
|
||||
ast::Expr::BlockExpr(e) => match e.modifier() {
|
||||
Some(ast::BlockModifier::Try(_)) => {
|
||||
let body = self.collect_block(e);
|
||||
self.alloc_expr(Expr::TryBlock { body }, syntax_ptr)
|
||||
self.collect_block_(e, |id, statements, tail| Expr::TryBlock {
|
||||
id,
|
||||
statements,
|
||||
tail,
|
||||
})
|
||||
}
|
||||
Some(ast::BlockModifier::Unsafe(_)) => {
|
||||
let body = self.collect_block(e);
|
||||
self.alloc_expr(Expr::Unsafe { body }, syntax_ptr)
|
||||
self.collect_block_(e, |id, statements, tail| Expr::Unsafe {
|
||||
id,
|
||||
statements,
|
||||
tail,
|
||||
})
|
||||
}
|
||||
// FIXME: we need to record these effects somewhere...
|
||||
Some(ast::BlockModifier::Label(label)) => {
|
||||
let label = self.collect_label(label);
|
||||
let res = self.collect_block(e);
|
||||
match &mut self.body.exprs[res] {
|
||||
Expr::Block { label: block_label, .. } => {
|
||||
*block_label = Some(label);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
res
|
||||
}
|
||||
Some(ast::BlockModifier::Async(_)) => {
|
||||
let body = self.collect_block(e);
|
||||
self.alloc_expr(Expr::Async { body }, syntax_ptr)
|
||||
}
|
||||
Some(ast::BlockModifier::Const(_)) => {
|
||||
let body = self.collect_block(e);
|
||||
self.alloc_expr(Expr::Const { body }, syntax_ptr)
|
||||
self.collect_block_(e, |id, statements, tail| Expr::Block {
|
||||
id,
|
||||
statements,
|
||||
tail,
|
||||
label: Some(label),
|
||||
})
|
||||
}
|
||||
Some(ast::BlockModifier::Async(_)) => self
|
||||
.collect_block_(e, |id, statements, tail| Expr::Async { id, statements, tail }),
|
||||
Some(ast::BlockModifier::Const(_)) => self
|
||||
.collect_block_(e, |id, statements, tail| Expr::Const { id, statements, tail }),
|
||||
None => self.collect_block(e),
|
||||
},
|
||||
ast::Expr::LoopExpr(e) => {
|
||||
|
@ -737,6 +736,19 @@ impl ExprCollector<'_> {
|
|||
}
|
||||
|
||||
fn collect_block(&mut self, block: ast::BlockExpr) -> ExprId {
|
||||
self.collect_block_(block, |id, statements, tail| Expr::Block {
|
||||
id,
|
||||
statements,
|
||||
tail,
|
||||
label: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn collect_block_(
|
||||
&mut self,
|
||||
block: ast::BlockExpr,
|
||||
mk_block: impl FnOnce(BlockId, Box<[Statement]>, Option<ExprId>) -> Expr,
|
||||
) -> ExprId {
|
||||
let file_local_id = self.ast_id_map.ast_id(&block);
|
||||
let ast_id = AstId::new(self.expander.current_file_id, file_local_id);
|
||||
let block_loc =
|
||||
|
@ -769,15 +781,8 @@ impl ExprCollector<'_> {
|
|||
});
|
||||
|
||||
let syntax_node_ptr = AstPtr::new(&block.into());
|
||||
let expr_id = self.alloc_expr(
|
||||
Expr::Block {
|
||||
id: block_id,
|
||||
statements: statements.into_boxed_slice(),
|
||||
tail,
|
||||
label: None,
|
||||
},
|
||||
syntax_node_ptr,
|
||||
);
|
||||
let expr_id = self
|
||||
.alloc_expr(mk_block(block_id, statements.into_boxed_slice(), tail), syntax_node_ptr);
|
||||
|
||||
self.expander.def_map = prev_def_map;
|
||||
self.expander.module = prev_local_module;
|
||||
|
|
|
@ -292,18 +292,6 @@ impl<'a> Printer<'a> {
|
|||
self.print_expr(*expr);
|
||||
w!(self, "?");
|
||||
}
|
||||
Expr::TryBlock { body } => {
|
||||
w!(self, "try ");
|
||||
self.print_expr(*body);
|
||||
}
|
||||
Expr::Async { body } => {
|
||||
w!(self, "async ");
|
||||
self.print_expr(*body);
|
||||
}
|
||||
Expr::Const { body } => {
|
||||
w!(self, "const ");
|
||||
self.print_expr(*body);
|
||||
}
|
||||
Expr::Cast { expr, type_ref } => {
|
||||
self.print_expr(*expr);
|
||||
w!(self, " as ");
|
||||
|
@ -402,10 +390,6 @@ impl<'a> Printer<'a> {
|
|||
}
|
||||
w!(self, ")");
|
||||
}
|
||||
Expr::Unsafe { body } => {
|
||||
w!(self, "unsafe ");
|
||||
self.print_expr(*body);
|
||||
}
|
||||
Expr::Array(arr) => {
|
||||
w!(self, "[");
|
||||
if !matches!(arr, Array::ElementList { elements, .. } if elements.is_empty()) {
|
||||
|
@ -428,27 +412,49 @@ impl<'a> Printer<'a> {
|
|||
}
|
||||
Expr::Literal(lit) => self.print_literal(lit),
|
||||
Expr::Block { id: _, statements, tail, label } => {
|
||||
self.whitespace();
|
||||
if let Some(lbl) = label {
|
||||
w!(self, "{}: ", self.body[*lbl].name);
|
||||
}
|
||||
w!(self, "{{");
|
||||
if !statements.is_empty() || tail.is_some() {
|
||||
self.indented(|p| {
|
||||
for stmt in &**statements {
|
||||
p.print_stmt(stmt);
|
||||
}
|
||||
if let Some(tail) = tail {
|
||||
p.print_expr(*tail);
|
||||
}
|
||||
p.newline();
|
||||
});
|
||||
}
|
||||
w!(self, "}}");
|
||||
let label = label.map(|lbl| format!("{}: ", self.body[lbl].name));
|
||||
self.print_block(label.as_deref(), statements, tail);
|
||||
}
|
||||
Expr::Unsafe { id: _, statements, tail } => {
|
||||
self.print_block(Some("unsafe "), statements, tail);
|
||||
}
|
||||
Expr::TryBlock { id: _, statements, tail } => {
|
||||
self.print_block(Some("try "), statements, tail);
|
||||
}
|
||||
Expr::Async { id: _, statements, tail } => {
|
||||
self.print_block(Some("async "), statements, tail);
|
||||
}
|
||||
Expr::Const { id: _, statements, tail } => {
|
||||
self.print_block(Some("const "), statements, tail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_block(
|
||||
&mut self,
|
||||
label: Option<&str>,
|
||||
statements: &Box<[Statement]>,
|
||||
tail: &Option<la_arena::Idx<Expr>>,
|
||||
) {
|
||||
self.whitespace();
|
||||
if let Some(lbl) = label {
|
||||
w!(self, "{}", lbl);
|
||||
}
|
||||
w!(self, "{{");
|
||||
if !statements.is_empty() || tail.is_some() {
|
||||
self.indented(|p| {
|
||||
for stmt in &**statements {
|
||||
p.print_stmt(stmt);
|
||||
}
|
||||
if let Some(tail) = tail {
|
||||
p.print_expr(*tail);
|
||||
}
|
||||
p.newline();
|
||||
});
|
||||
}
|
||||
w!(self, "}}");
|
||||
}
|
||||
|
||||
fn print_pat(&mut self, pat: PatId) {
|
||||
let pat = &self.body[pat];
|
||||
|
||||
|
|
|
@ -194,6 +194,16 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
|
|||
scopes.set_scope(expr, scope);
|
||||
compute_block_scopes(statements, *tail, body, scopes, &mut scope);
|
||||
}
|
||||
Expr::Unsafe { id, statements, tail }
|
||||
| Expr::Async { id, statements, tail }
|
||||
| Expr::Const { id, statements, tail }
|
||||
| Expr::TryBlock { id, statements, tail } => {
|
||||
let mut scope = scopes.new_block_scope(*scope, *id, None);
|
||||
// Overwrite the old scope for the block expr, so that every block scope can be found
|
||||
// via the block itself (important for blocks that only contain items, no expressions).
|
||||
scopes.set_scope(expr, scope);
|
||||
compute_block_scopes(statements, *tail, body, scopes, &mut scope);
|
||||
}
|
||||
Expr::For { iterable, pat, body: body_expr, label } => {
|
||||
compute_expr_scopes(*iterable, body, scopes, scope);
|
||||
let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
|
||||
|
|
|
@ -109,6 +109,26 @@ pub enum Expr {
|
|||
tail: Option<ExprId>,
|
||||
label: Option<LabelId>,
|
||||
},
|
||||
TryBlock {
|
||||
id: BlockId,
|
||||
statements: Box<[Statement]>,
|
||||
tail: Option<ExprId>,
|
||||
},
|
||||
Async {
|
||||
id: BlockId,
|
||||
statements: Box<[Statement]>,
|
||||
tail: Option<ExprId>,
|
||||
},
|
||||
Const {
|
||||
id: BlockId,
|
||||
statements: Box<[Statement]>,
|
||||
tail: Option<ExprId>,
|
||||
},
|
||||
Unsafe {
|
||||
id: BlockId,
|
||||
statements: Box<[Statement]>,
|
||||
tail: Option<ExprId>,
|
||||
},
|
||||
Loop {
|
||||
body: ExprId,
|
||||
label: Option<LabelId>,
|
||||
|
@ -172,15 +192,6 @@ pub enum Expr {
|
|||
Try {
|
||||
expr: ExprId,
|
||||
},
|
||||
TryBlock {
|
||||
body: ExprId,
|
||||
},
|
||||
Async {
|
||||
body: ExprId,
|
||||
},
|
||||
Const {
|
||||
body: ExprId,
|
||||
},
|
||||
Cast {
|
||||
expr: ExprId,
|
||||
type_ref: Interned<TypeRef>,
|
||||
|
@ -222,9 +233,6 @@ pub enum Expr {
|
|||
exprs: Box<[ExprId]>,
|
||||
is_assignee_expr: bool,
|
||||
},
|
||||
Unsafe {
|
||||
body: ExprId,
|
||||
},
|
||||
Array(Array),
|
||||
Literal(Literal),
|
||||
Underscore,
|
||||
|
@ -290,13 +298,20 @@ impl Expr {
|
|||
Expr::Let { expr, .. } => {
|
||||
f(*expr);
|
||||
}
|
||||
Expr::Block { statements, tail, .. } => {
|
||||
Expr::Block { statements, tail, .. }
|
||||
| Expr::TryBlock { statements, tail, .. }
|
||||
| Expr::Unsafe { statements, tail, .. }
|
||||
| Expr::Async { statements, tail, .. }
|
||||
| Expr::Const { statements, tail, .. } => {
|
||||
for stmt in statements.iter() {
|
||||
match stmt {
|
||||
Statement::Let { initializer, .. } => {
|
||||
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),
|
||||
}
|
||||
|
@ -305,10 +320,6 @@ impl Expr {
|
|||
f(expr);
|
||||
}
|
||||
}
|
||||
Expr::TryBlock { body }
|
||||
| Expr::Unsafe { body }
|
||||
| Expr::Async { body }
|
||||
| Expr::Const { body } => f(*body),
|
||||
Expr::Loop { body, .. } => f(*body),
|
||||
Expr::While { condition, body, .. } => {
|
||||
f(*condition);
|
||||
|
|
|
@ -294,8 +294,8 @@ impl Resolver {
|
|||
}
|
||||
}
|
||||
|
||||
if let res @ Some(_) = self.module_scope.resolve_path_in_value_ns(db, path) {
|
||||
return res;
|
||||
if let Some(res) = self.module_scope.resolve_path_in_value_ns(db, path) {
|
||||
return Some(res);
|
||||
}
|
||||
|
||||
// If a path of the shape `u16::from_le_bytes` failed to resolve at all, then we fall back
|
||||
|
|
|
@ -94,8 +94,10 @@ fn walk_unsafe(
|
|||
unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
|
||||
}
|
||||
}
|
||||
Expr::Unsafe { body: child } => {
|
||||
return walk_unsafe(db, infer, def, body, *child, true, unsafe_expr_cb);
|
||||
Expr::Unsafe { .. } => {
|
||||
return expr.walk_child_exprs(|child| {
|
||||
walk_unsafe(db, infer, def, body, child, true, unsafe_expr_cb);
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
|
@ -84,6 +84,30 @@ impl<'a> InferenceContext<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn infer_expr_coerce_never(&mut self, expr: ExprId, expected: &Expectation) -> Ty {
|
||||
let ty = self.infer_expr_inner(expr, expected);
|
||||
// While we don't allow *arbitrary* coercions here, we *do* allow
|
||||
// coercions from ! to `expected`.
|
||||
if ty.is_never() {
|
||||
if let Some(adjustments) = self.result.expr_adjustments.get(&expr) {
|
||||
return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &**adjustments {
|
||||
target.clone()
|
||||
} else {
|
||||
self.err_ty()
|
||||
};
|
||||
}
|
||||
|
||||
let adj_ty = self.table.new_type_var();
|
||||
self.write_expr_adj(
|
||||
expr,
|
||||
vec![Adjustment { kind: Adjust::NeverToAny, target: adj_ty.clone() }],
|
||||
);
|
||||
adj_ty
|
||||
} else {
|
||||
ty
|
||||
}
|
||||
}
|
||||
|
||||
fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
|
||||
self.db.unwind_if_cancelled();
|
||||
|
||||
|
@ -91,7 +115,7 @@ impl<'a> InferenceContext<'a> {
|
|||
Expr::Missing => self.err_ty(),
|
||||
&Expr::If { condition, then_branch, else_branch } => {
|
||||
let expected = &expected.adjust_for_branches(&mut self.table);
|
||||
self.infer_expr(
|
||||
self.infer_expr_coerce_never(
|
||||
condition,
|
||||
&Expectation::HasType(self.result.standard_types.bool_.clone()),
|
||||
);
|
||||
|
@ -124,41 +148,18 @@ impl<'a> InferenceContext<'a> {
|
|||
self.result.standard_types.bool_.clone()
|
||||
}
|
||||
Expr::Block { statements, tail, label, id: _ } => {
|
||||
let old_resolver = mem::replace(
|
||||
&mut self.resolver,
|
||||
resolver_for_expr(self.db.upcast(), self.owner, tgt_expr),
|
||||
);
|
||||
let ty = match label {
|
||||
Some(_) => {
|
||||
let break_ty = self.table.new_type_var();
|
||||
let (breaks, ty) = self.with_breakable_ctx(
|
||||
BreakableKind::Block,
|
||||
Some(break_ty.clone()),
|
||||
*label,
|
||||
|this| {
|
||||
this.infer_block(
|
||||
tgt_expr,
|
||||
statements,
|
||||
*tail,
|
||||
&Expectation::has_type(break_ty),
|
||||
)
|
||||
},
|
||||
);
|
||||
breaks.unwrap_or(ty)
|
||||
}
|
||||
None => self.infer_block(tgt_expr, statements, *tail, expected),
|
||||
};
|
||||
self.resolver = old_resolver;
|
||||
ty
|
||||
self.infer_block(tgt_expr, statements, *tail, *label, expected)
|
||||
}
|
||||
Expr::Unsafe { body } => self.infer_expr(*body, expected),
|
||||
Expr::Const { body } => {
|
||||
Expr::Unsafe { id: _, statements, tail } => {
|
||||
self.infer_block(tgt_expr, statements, *tail, None, expected)
|
||||
}
|
||||
Expr::Const { id: _, statements, tail } => {
|
||||
self.with_breakable_ctx(BreakableKind::Border, None, None, |this| {
|
||||
this.infer_expr(*body, expected)
|
||||
this.infer_block(tgt_expr, statements, *tail, None, expected)
|
||||
})
|
||||
.1
|
||||
}
|
||||
Expr::TryBlock { body } => {
|
||||
Expr::TryBlock { id: _, statements, tail } => {
|
||||
// The type that is returned from the try block
|
||||
let try_ty = self.table.new_type_var();
|
||||
if let Some(ty) = expected.only_has_type(&mut self.table) {
|
||||
|
@ -169,13 +170,16 @@ impl<'a> InferenceContext<'a> {
|
|||
let ok_ty =
|
||||
self.resolve_associated_type(try_ty.clone(), self.resolve_ops_try_output());
|
||||
|
||||
self.with_breakable_ctx(BreakableKind::Block, Some(ok_ty.clone()), None, |this| {
|
||||
this.infer_expr(*body, &Expectation::has_type(ok_ty));
|
||||
});
|
||||
|
||||
self.infer_block(
|
||||
tgt_expr,
|
||||
statements,
|
||||
*tail,
|
||||
None,
|
||||
&Expectation::has_type(ok_ty.clone()),
|
||||
);
|
||||
try_ty
|
||||
}
|
||||
Expr::Async { body } => {
|
||||
Expr::Async { id: _, statements, tail } => {
|
||||
let ret_ty = self.table.new_type_var();
|
||||
let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
|
||||
let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone());
|
||||
|
@ -184,7 +188,13 @@ impl<'a> InferenceContext<'a> {
|
|||
|
||||
let (_, inner_ty) =
|
||||
self.with_breakable_ctx(BreakableKind::Border, None, None, |this| {
|
||||
this.infer_expr_coerce(*body, &Expectation::has_type(ret_ty))
|
||||
this.infer_block(
|
||||
tgt_expr,
|
||||
statements,
|
||||
*tail,
|
||||
None,
|
||||
&Expectation::has_type(ret_ty),
|
||||
)
|
||||
});
|
||||
|
||||
self.diverges = prev_diverges;
|
||||
|
@ -193,7 +203,8 @@ impl<'a> InferenceContext<'a> {
|
|||
|
||||
// Use the first type parameter as the output type of future.
|
||||
// existential type AsyncBlockImplTrait<InnerType>: Future<Output = InnerType>
|
||||
let impl_trait_id = crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, *body);
|
||||
let impl_trait_id =
|
||||
crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, tgt_expr);
|
||||
let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into();
|
||||
TyKind::OpaqueType(opaque_ty_id, Substitution::from1(Interner, inner_ty))
|
||||
.intern(Interner)
|
||||
|
@ -403,37 +414,47 @@ impl<'a> InferenceContext<'a> {
|
|||
Expr::Match { expr, arms } => {
|
||||
let input_ty = self.infer_expr(*expr, &Expectation::none());
|
||||
|
||||
let expected = expected.adjust_for_branches(&mut self.table);
|
||||
|
||||
let result_ty = if arms.is_empty() {
|
||||
if arms.is_empty() {
|
||||
self.diverges = Diverges::Always;
|
||||
self.result.standard_types.never.clone()
|
||||
} else {
|
||||
expected.coercion_target_type(&mut self.table)
|
||||
};
|
||||
let mut coerce = CoerceMany::new(result_ty);
|
||||
|
||||
let matchee_diverges = self.diverges;
|
||||
let mut all_arms_diverge = Diverges::Always;
|
||||
|
||||
for arm in arms.iter() {
|
||||
self.diverges = Diverges::Maybe;
|
||||
let input_ty = self.resolve_ty_shallow(&input_ty);
|
||||
self.infer_top_pat(arm.pat, &input_ty);
|
||||
if let Some(guard_expr) = arm.guard {
|
||||
self.infer_expr(
|
||||
guard_expr,
|
||||
&Expectation::HasType(self.result.standard_types.bool_.clone()),
|
||||
);
|
||||
let matchee_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
|
||||
let mut all_arms_diverge = Diverges::Always;
|
||||
for arm in arms.iter() {
|
||||
let input_ty = self.resolve_ty_shallow(&input_ty);
|
||||
self.infer_top_pat(arm.pat, &input_ty);
|
||||
}
|
||||
|
||||
let arm_ty = self.infer_expr_inner(arm.expr, &expected);
|
||||
all_arms_diverge &= self.diverges;
|
||||
coerce.coerce(self, Some(arm.expr), &arm_ty);
|
||||
let expected = expected.adjust_for_branches(&mut self.table);
|
||||
let result_ty = match &expected {
|
||||
// We don't coerce to `()` so that if the match expression is a
|
||||
// statement it's branches can have any consistent type.
|
||||
Expectation::HasType(ty) if *ty != self.result.standard_types.unit => {
|
||||
ty.clone()
|
||||
}
|
||||
_ => self.table.new_type_var(),
|
||||
};
|
||||
let mut coerce = CoerceMany::new(result_ty);
|
||||
|
||||
for arm in arms.iter() {
|
||||
if let Some(guard_expr) = arm.guard {
|
||||
self.diverges = Diverges::Maybe;
|
||||
self.infer_expr_coerce_never(
|
||||
guard_expr,
|
||||
&Expectation::HasType(self.result.standard_types.bool_.clone()),
|
||||
);
|
||||
}
|
||||
self.diverges = Diverges::Maybe;
|
||||
|
||||
let arm_ty = self.infer_expr_inner(arm.expr, &expected);
|
||||
all_arms_diverge &= self.diverges;
|
||||
coerce.coerce(self, Some(arm.expr), &arm_ty);
|
||||
}
|
||||
|
||||
self.diverges = matchee_diverges | all_arms_diverge;
|
||||
|
||||
coerce.complete(self)
|
||||
}
|
||||
|
||||
self.diverges = matchee_diverges | all_arms_diverge;
|
||||
|
||||
coerce.complete(self)
|
||||
}
|
||||
Expr::Path(p) => {
|
||||
// FIXME this could be more efficient...
|
||||
|
@ -1143,73 +1164,101 @@ impl<'a> InferenceContext<'a> {
|
|||
expr: ExprId,
|
||||
statements: &[Statement],
|
||||
tail: Option<ExprId>,
|
||||
label: Option<LabelId>,
|
||||
expected: &Expectation,
|
||||
) -> Ty {
|
||||
for stmt in statements {
|
||||
match stmt {
|
||||
Statement::Let { pat, type_ref, initializer, else_branch } => {
|
||||
let decl_ty = type_ref
|
||||
.as_ref()
|
||||
.map(|tr| self.make_ty(tr))
|
||||
.unwrap_or_else(|| self.table.new_type_var());
|
||||
let coerce_ty = expected.coercion_target_type(&mut self.table);
|
||||
let old_resolver =
|
||||
mem::replace(&mut self.resolver, resolver_for_expr(self.db.upcast(), self.owner, expr));
|
||||
let (break_ty, ty) =
|
||||
self.with_breakable_ctx(BreakableKind::Block, Some(coerce_ty.clone()), label, |this| {
|
||||
for stmt in statements {
|
||||
match stmt {
|
||||
Statement::Let { pat, type_ref, initializer, else_branch } => {
|
||||
let decl_ty = type_ref
|
||||
.as_ref()
|
||||
.map(|tr| this.make_ty(tr))
|
||||
.unwrap_or_else(|| this.table.new_type_var());
|
||||
|
||||
let ty = if let Some(expr) = initializer {
|
||||
let ty = if contains_explicit_ref_binding(&self.body, *pat) {
|
||||
self.infer_expr(*expr, &Expectation::has_type(decl_ty.clone()))
|
||||
} else {
|
||||
self.infer_expr_coerce(*expr, &Expectation::has_type(decl_ty.clone()))
|
||||
};
|
||||
if type_ref.is_some() {
|
||||
decl_ty
|
||||
} else {
|
||||
ty
|
||||
let ty = if let Some(expr) = initializer {
|
||||
let ty = if contains_explicit_ref_binding(&this.body, *pat) {
|
||||
this.infer_expr(*expr, &Expectation::has_type(decl_ty.clone()))
|
||||
} else {
|
||||
this.infer_expr_coerce(
|
||||
*expr,
|
||||
&Expectation::has_type(decl_ty.clone()),
|
||||
)
|
||||
};
|
||||
if type_ref.is_some() {
|
||||
decl_ty
|
||||
} else {
|
||||
ty
|
||||
}
|
||||
} else {
|
||||
decl_ty
|
||||
};
|
||||
|
||||
this.infer_top_pat(*pat, &ty);
|
||||
|
||||
if let Some(expr) = else_branch {
|
||||
let previous_diverges =
|
||||
mem::replace(&mut this.diverges, Diverges::Maybe);
|
||||
this.infer_expr_coerce(
|
||||
*expr,
|
||||
&Expectation::HasType(this.result.standard_types.never.clone()),
|
||||
);
|
||||
this.diverges = previous_diverges;
|
||||
}
|
||||
}
|
||||
&Statement::Expr { expr, has_semi } => {
|
||||
if has_semi {
|
||||
this.infer_expr(expr, &Expectation::none());
|
||||
} else {
|
||||
this.infer_expr_coerce(
|
||||
expr,
|
||||
&Expectation::HasType(this.result.standard_types.unit.clone()),
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
decl_ty
|
||||
};
|
||||
|
||||
self.infer_top_pat(*pat, &ty);
|
||||
|
||||
if let Some(expr) = else_branch {
|
||||
let previous_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
|
||||
self.infer_expr_coerce(
|
||||
*expr,
|
||||
&Expectation::HasType(self.result.standard_types.never.clone()),
|
||||
);
|
||||
self.diverges = previous_diverges;
|
||||
}
|
||||
}
|
||||
Statement::Expr { expr, .. } => {
|
||||
self.infer_expr(*expr, &Expectation::none());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(expr) = tail {
|
||||
self.infer_expr_coerce(expr, expected)
|
||||
} else {
|
||||
// Citing rustc: if there is no explicit tail expression,
|
||||
// that is typically equivalent to a tail expression
|
||||
// of `()` -- except if the block diverges. In that
|
||||
// case, there is no value supplied from the tail
|
||||
// expression (assuming there are no other breaks,
|
||||
// this implies that the type of the block will be
|
||||
// `!`).
|
||||
if self.diverges.is_always() {
|
||||
// we don't even make an attempt at coercion
|
||||
self.table.new_maybe_never_var()
|
||||
} else if let Some(t) = expected.only_has_type(&mut self.table) {
|
||||
if self.coerce(Some(expr), &TyBuilder::unit(), &t).is_err() {
|
||||
self.result.type_mismatches.insert(
|
||||
expr.into(),
|
||||
TypeMismatch { expected: t.clone(), actual: TyBuilder::unit() },
|
||||
);
|
||||
// FIXME: This should make use of the breakable CoerceMany
|
||||
if let Some(expr) = tail {
|
||||
this.infer_expr_coerce(expr, expected)
|
||||
} else {
|
||||
// Citing rustc: if there is no explicit tail expression,
|
||||
// that is typically equivalent to a tail expression
|
||||
// of `()` -- except if the block diverges. In that
|
||||
// case, there is no value supplied from the tail
|
||||
// expression (assuming there are no other breaks,
|
||||
// this implies that the type of the block will be
|
||||
// `!`).
|
||||
if this.diverges.is_always() {
|
||||
// we don't even make an attempt at coercion
|
||||
this.table.new_maybe_never_var()
|
||||
} else if let Some(t) = expected.only_has_type(&mut this.table) {
|
||||
if this
|
||||
.coerce(Some(expr), &this.result.standard_types.unit.clone(), &t)
|
||||
.is_err()
|
||||
{
|
||||
this.result.type_mismatches.insert(
|
||||
expr.into(),
|
||||
TypeMismatch {
|
||||
expected: t.clone(),
|
||||
actual: this.result.standard_types.unit.clone(),
|
||||
},
|
||||
);
|
||||
}
|
||||
t
|
||||
} else {
|
||||
this.result.standard_types.unit.clone()
|
||||
}
|
||||
}
|
||||
t
|
||||
} else {
|
||||
TyBuilder::unit()
|
||||
}
|
||||
}
|
||||
});
|
||||
self.resolver = old_resolver;
|
||||
|
||||
break_ty.unwrap_or(ty)
|
||||
}
|
||||
|
||||
fn lookup_field(
|
||||
|
|
|
@ -40,20 +40,14 @@ impl<'a> InferenceContext<'a> {
|
|||
id: ExprOrPatId,
|
||||
) -> Option<Ty> {
|
||||
let (value, self_subst) = if let Some(type_ref) = path.type_anchor() {
|
||||
if path.segments().is_empty() {
|
||||
// This can't actually happen syntax-wise
|
||||
return None;
|
||||
}
|
||||
let Some(last) = path.segments().last() else { return None };
|
||||
let ty = self.make_ty(type_ref);
|
||||
let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1);
|
||||
let ctx = crate::lower::TyLoweringContext::new(self.db, resolver);
|
||||
let (ty, _) = ctx.lower_ty_relative_path(ty, None, remaining_segments_for_ty);
|
||||
self.resolve_ty_assoc_item(
|
||||
ty,
|
||||
path.segments().last().expect("path had at least one segment").name,
|
||||
id,
|
||||
)?
|
||||
self.resolve_ty_assoc_item(ty, last.name, id)?
|
||||
} else {
|
||||
// FIXME: report error, unresolved first path segment
|
||||
let value_or_partial =
|
||||
resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path())?;
|
||||
|
||||
|
@ -66,10 +60,13 @@ impl<'a> InferenceContext<'a> {
|
|||
};
|
||||
|
||||
let typable: ValueTyDefId = match value {
|
||||
ValueNs::LocalBinding(pat) => {
|
||||
let ty = self.result.type_of_pat.get(pat)?.clone();
|
||||
return Some(ty);
|
||||
}
|
||||
ValueNs::LocalBinding(pat) => match self.result.type_of_pat.get(pat) {
|
||||
Some(ty) => return Some(ty.clone()),
|
||||
None => {
|
||||
never!("uninferred pattern?");
|
||||
return None;
|
||||
}
|
||||
},
|
||||
ValueNs::FunctionId(it) => it.into(),
|
||||
ValueNs::ConstId(it) => it.into(),
|
||||
ValueNs::StaticId(it) => it.into(),
|
||||
|
@ -91,7 +88,7 @@ impl<'a> InferenceContext<'a> {
|
|||
let ty = self.db.value_ty(struct_id.into()).substitute(Interner, &substs);
|
||||
return Some(ty);
|
||||
} else {
|
||||
// FIXME: diagnostic, invalid Self reference
|
||||
// FIXME: report error, invalid Self reference
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -331,56 +331,11 @@ impl MirLowerCtx<'_> {
|
|||
}
|
||||
Ok(result)
|
||||
}
|
||||
Expr::Unsafe { id: _, statements, tail } => {
|
||||
self.lower_block_to_place(None, statements, current, *tail, place)
|
||||
}
|
||||
Expr::Block { id: _, statements, tail, label } => {
|
||||
if label.is_some() {
|
||||
not_supported!("block with label");
|
||||
}
|
||||
for statement in statements.iter() {
|
||||
match statement {
|
||||
hir_def::expr::Statement::Let {
|
||||
pat,
|
||||
initializer,
|
||||
else_branch,
|
||||
type_ref: _,
|
||||
} => match initializer {
|
||||
Some(expr_id) => {
|
||||
let else_block;
|
||||
let init_place;
|
||||
(init_place, current) =
|
||||
self.lower_expr_to_some_place(*expr_id, current)?;
|
||||
(current, else_block) = self.pattern_match(
|
||||
current,
|
||||
None,
|
||||
init_place,
|
||||
self.expr_ty(*expr_id),
|
||||
*pat,
|
||||
BindingAnnotation::Unannotated,
|
||||
)?;
|
||||
match (else_block, else_branch) {
|
||||
(None, _) => (),
|
||||
(Some(else_block), None) => {
|
||||
self.set_terminator(else_block, Terminator::Unreachable);
|
||||
}
|
||||
(Some(else_block), Some(else_branch)) => {
|
||||
let (_, b) = self
|
||||
.lower_expr_to_some_place(*else_branch, else_block)?;
|
||||
self.set_terminator(b, Terminator::Unreachable);
|
||||
}
|
||||
}
|
||||
}
|
||||
None => continue,
|
||||
},
|
||||
hir_def::expr::Statement::Expr { expr, has_semi: _ } => {
|
||||
let ty = self.expr_ty(*expr);
|
||||
let temp = self.temp(ty)?;
|
||||
current = self.lower_expr_to_place(*expr, temp.into(), current)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
match tail {
|
||||
Some(tail) => self.lower_expr_to_place(*tail, place, current),
|
||||
None => Ok(current),
|
||||
}
|
||||
self.lower_block_to_place(*label, statements, current, *tail, place)
|
||||
}
|
||||
Expr::Loop { body, label } => self.lower_loop(current, *label, |this, begin, _| {
|
||||
let (_, block) = this.lower_expr_to_some_place(*body, begin)?;
|
||||
|
@ -686,7 +641,6 @@ impl MirLowerCtx<'_> {
|
|||
self.push_assignment(current, place, r);
|
||||
Ok(current)
|
||||
}
|
||||
Expr::Unsafe { body } => self.lower_expr_to_place(*body, place, current),
|
||||
Expr::Array(l) => match l {
|
||||
Array::ElementList { elements, .. } => {
|
||||
let elem_ty = match &self.expr_ty(expr_id).data(Interner).kind {
|
||||
|
@ -723,6 +677,62 @@ impl MirLowerCtx<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
fn lower_block_to_place(
|
||||
&mut self,
|
||||
label: Option<LabelId>,
|
||||
statements: &[hir_def::expr::Statement],
|
||||
mut current: BasicBlockId,
|
||||
tail: Option<ExprId>,
|
||||
place: Place,
|
||||
) -> Result<BasicBlockId> {
|
||||
if label.is_some() {
|
||||
not_supported!("block with label");
|
||||
}
|
||||
for statement in statements.iter() {
|
||||
match statement {
|
||||
hir_def::expr::Statement::Let { pat, initializer, else_branch, type_ref: _ } => {
|
||||
match initializer {
|
||||
Some(expr_id) => {
|
||||
let else_block;
|
||||
let init_place;
|
||||
(init_place, current) =
|
||||
self.lower_expr_to_some_place(*expr_id, current)?;
|
||||
(current, else_block) = self.pattern_match(
|
||||
current,
|
||||
None,
|
||||
init_place,
|
||||
self.expr_ty(*expr_id),
|
||||
*pat,
|
||||
BindingAnnotation::Unannotated,
|
||||
)?;
|
||||
match (else_block, else_branch) {
|
||||
(None, _) => (),
|
||||
(Some(else_block), None) => {
|
||||
self.set_terminator(else_block, Terminator::Unreachable);
|
||||
}
|
||||
(Some(else_block), Some(else_branch)) => {
|
||||
let (_, b) =
|
||||
self.lower_expr_to_some_place(*else_branch, else_block)?;
|
||||
self.set_terminator(b, Terminator::Unreachable);
|
||||
}
|
||||
}
|
||||
}
|
||||
None => continue,
|
||||
}
|
||||
}
|
||||
hir_def::expr::Statement::Expr { expr, has_semi: _ } => {
|
||||
let ty = self.expr_ty(*expr);
|
||||
let temp = self.temp(ty)?;
|
||||
current = self.lower_expr_to_place(*expr, temp.into(), current)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
match tail {
|
||||
Some(tail) => self.lower_expr_to_place(tail, place, current),
|
||||
None => Ok(current),
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_literal_to_operand(&mut self, ty: Ty, l: &Literal) -> Result<Operand> {
|
||||
let size = layout_of_ty(self.db, &ty, self.owner.module(self.db.upcast()).krate())?
|
||||
.size
|
||||
|
|
|
@ -61,22 +61,27 @@ fn setup_tracing() -> Option<tracing::subscriber::DefaultGuard> {
|
|||
Some(tracing::subscriber::set_default(subscriber))
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn check_types(ra_fixture: &str) {
|
||||
check_impl(ra_fixture, false, true, false)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn check_types_source_code(ra_fixture: &str) {
|
||||
check_impl(ra_fixture, false, true, true)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn check_no_mismatches(ra_fixture: &str) {
|
||||
check_impl(ra_fixture, true, false, false)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn check(ra_fixture: &str) {
|
||||
check_impl(ra_fixture, false, false, false)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_source: bool) {
|
||||
let _tracing = setup_tracing();
|
||||
let (db, files) = TestDB::with_many_files(ra_fixture);
|
||||
|
|
|
@ -73,3 +73,24 @@ fn test(x: bool) -> &'static str {
|
|||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_unit_block_expr_stmt_no_semi() {
|
||||
check(
|
||||
r#"
|
||||
fn test(x: bool) {
|
||||
if x {
|
||||
"notok"
|
||||
//^^^^^^^ expected (), got &str
|
||||
} else {
|
||||
"ok"
|
||||
//^^^^ expected (), got &str
|
||||
}
|
||||
match x { true => true, false => 0 }
|
||||
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected (), got bool
|
||||
//^ expected bool, got i32
|
||||
()
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1167,7 +1167,6 @@ fn test() {
|
|||
123..167 '{ ...o(); }': ()
|
||||
133..134 's': &S
|
||||
137..151 'unsafe { f() }': &S
|
||||
137..151 'unsafe { f() }': &S
|
||||
146..147 'f': fn f() -> &S
|
||||
146..149 'f()': &S
|
||||
157..158 's': &S
|
||||
|
|
|
@ -476,7 +476,7 @@ fn infer_adt_pattern() {
|
|||
183..184 'x': usize
|
||||
190..191 'x': usize
|
||||
201..205 'E::B': E
|
||||
209..212 'foo': bool
|
||||
209..212 'foo': {unknown}
|
||||
216..217 '1': usize
|
||||
227..231 'E::B': E
|
||||
235..237 '10': usize
|
||||
|
|
|
@ -270,7 +270,7 @@ fn infer_std_crash_5() {
|
|||
61..320 '{ ... }': ()
|
||||
75..79 'name': &{unknown}
|
||||
82..166 'if doe... }': &{unknown}
|
||||
85..98 'doesnt_matter': bool
|
||||
85..98 'doesnt_matter': {unknown}
|
||||
99..128 '{ ... }': &{unknown}
|
||||
113..118 'first': &{unknown}
|
||||
134..166 '{ ... }': &{unknown}
|
||||
|
@ -279,7 +279,7 @@ fn infer_std_crash_5() {
|
|||
181..188 'content': &{unknown}
|
||||
191..313 'if ICE... }': &{unknown}
|
||||
194..231 'ICE_RE..._VALUE': {unknown}
|
||||
194..247 'ICE_RE...&name)': bool
|
||||
194..247 'ICE_RE...&name)': {unknown}
|
||||
241..246 '&name': &&{unknown}
|
||||
242..246 'name': &{unknown}
|
||||
248..276 '{ ... }': &{unknown}
|
||||
|
@ -1015,9 +1015,9 @@ fn cfg_tail() {
|
|||
20..31 '{ "first" }': ()
|
||||
22..29 '"first"': &str
|
||||
72..190 '{ ...] 13 }': ()
|
||||
78..88 '{ "fake" }': &str
|
||||
78..88 '{ "fake" }': ()
|
||||
80..86 '"fake"': &str
|
||||
93..103 '{ "fake" }': &str
|
||||
93..103 '{ "fake" }': ()
|
||||
95..101 '"fake"': &str
|
||||
108..120 '{ "second" }': ()
|
||||
110..118 '"second"': &str
|
||||
|
|
|
@ -352,7 +352,6 @@ unsafe fn baz(u: MyUnion) {
|
|||
71..89 'MyUnio...o: 0 }': MyUnion
|
||||
86..87 '0': u32
|
||||
95..113 'unsafe...(u); }': ()
|
||||
95..113 'unsafe...(u); }': ()
|
||||
104..107 'baz': fn baz(MyUnion)
|
||||
104..110 'baz(u)': ()
|
||||
108..109 'u': MyUnion
|
||||
|
@ -360,7 +359,6 @@ unsafe fn baz(u: MyUnion) {
|
|||
126..146 'MyUnio... 0.0 }': MyUnion
|
||||
141..144 '0.0': f32
|
||||
152..170 'unsafe...(u); }': ()
|
||||
152..170 'unsafe...(u); }': ()
|
||||
161..164 'baz': fn baz(MyUnion)
|
||||
161..167 'baz(u)': ()
|
||||
165..166 'u': MyUnion
|
||||
|
@ -2077,22 +2075,17 @@ async fn main() {
|
|||
16..193 '{ ...2 }; }': ()
|
||||
26..27 'x': i32
|
||||
30..43 'unsafe { 92 }': i32
|
||||
30..43 'unsafe { 92 }': i32
|
||||
39..41 '92': i32
|
||||
53..54 'y': impl Future<Output = ()>
|
||||
57..85 'async ...wait }': ()
|
||||
57..85 'async ...wait }': impl Future<Output = ()>
|
||||
65..77 'async { () }': ()
|
||||
65..77 'async { () }': impl Future<Output = ()>
|
||||
65..83 'async ....await': ()
|
||||
73..75 '()': ()
|
||||
95..96 'z': ControlFlow<(), ()>
|
||||
130..140 'try { () }': ()
|
||||
130..140 'try { () }': ControlFlow<(), ()>
|
||||
136..138 '()': ()
|
||||
150..151 'w': i32
|
||||
154..166 'const { 92 }': i32
|
||||
154..166 'const { 92 }': i32
|
||||
162..164 '92': i32
|
||||
176..177 't': i32
|
||||
180..190 ''a: { 92 }': i32
|
||||
|
@ -2122,7 +2115,6 @@ fn main() {
|
|||
83..84 'f': F
|
||||
89..91 '{}': ()
|
||||
103..231 '{ ... }); }': ()
|
||||
109..161 'async ... }': Result<(), ()>
|
||||
109..161 'async ... }': impl Future<Output = Result<(), ()>>
|
||||
125..139 'return Err(())': !
|
||||
132..135 'Err': Err<(), ()>(()) -> Result<(), ()>
|
||||
|
@ -2134,7 +2126,6 @@ fn main() {
|
|||
167..171 'test': fn test<(), (), || -> impl Future<Output = Result<(), ()>>, impl Future<Output = Result<(), ()>>>(|| -> impl Future<Output = Result<(), ()>>)
|
||||
167..228 'test(|... })': ()
|
||||
172..227 '|| asy... }': || -> impl Future<Output = Result<(), ()>>
|
||||
175..227 'async ... }': Result<(), ()>
|
||||
175..227 'async ... }': impl Future<Output = Result<(), ()>>
|
||||
191..205 'return Err(())': !
|
||||
198..201 'Err': Err<(), ()>(()) -> Result<(), ()>
|
||||
|
|
|
@ -287,7 +287,7 @@ fn foo() {
|
|||
extract_variable,
|
||||
r"
|
||||
fn foo() {
|
||||
$0{ let x = 0; x }$0
|
||||
$0{ let x = 0; x }$0;
|
||||
something_else();
|
||||
}",
|
||||
r"
|
||||
|
|
Loading…
Reference in a new issue