Unnamed repository; edit this file 'description' to name the repository.
Auto merge of #14251 - Veykril:ty-expr-stmt, r=Veykril
internal: Set expectation for no-semi expression statements to unit
bors 2023-03-05
parent 5efcfe5 · parent b85e2af · commit 94dc7a3
-rw-r--r--crates/hir-def/src/body/lower.rs67
-rw-r--r--crates/hir-def/src/body/pretty.rs70
-rw-r--r--crates/hir-def/src/body/scope.rs10
-rw-r--r--crates/hir-def/src/expr.rs47
-rw-r--r--crates/hir-def/src/resolver.rs4
-rw-r--r--crates/hir-ty/src/diagnostics/unsafe_check.rs6
-rw-r--r--crates/hir-ty/src/infer/expr.rs295
-rw-r--r--crates/hir-ty/src/infer/path.rs25
-rw-r--r--crates/hir-ty/src/mir/lower.rs110
-rw-r--r--crates/hir-ty/src/tests.rs5
-rw-r--r--crates/hir-ty/src/tests/diagnostics.rs21
-rw-r--r--crates/hir-ty/src/tests/method_resolution.rs1
-rw-r--r--crates/hir-ty/src/tests/patterns.rs2
-rw-r--r--crates/hir-ty/src/tests/regression.rs8
-rw-r--r--crates/hir-ty/src/tests/simple.rs9
-rw-r--r--crates/ide-assists/src/handlers/extract_variable.rs2
16 files changed, 394 insertions, 288 deletions
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index 04b1c4f01e..3164a5f4c2 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -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;
diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs
index 4b4664a1cf..622756ee8a 100644
--- a/crates/hir-def/src/body/pretty.rs
+++ b/crates/hir-def/src/body/pretty.rs
@@ -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,25 +412,47 @@ 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);
+ 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);
}
- 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();
- });
+ if let Some(tail) = tail {
+ p.print_expr(*tail);
}
- w!(self, "}}");
- }
+ p.newline();
+ });
}
+ w!(self, "}}");
}
fn print_pat(&mut self, pat: PatId) {
diff --git a/crates/hir-def/src/body/scope.rs b/crates/hir-def/src/body/scope.rs
index 2617d4288a..e7078b7953 100644
--- a/crates/hir-def/src/body/scope.rs
+++ b/crates/hir-def/src/body/scope.rs
@@ -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));
diff --git a/crates/hir-def/src/expr.rs b/crates/hir-def/src/expr.rs
index 8d6f0be264..78a2f86123 100644
--- a/crates/hir-def/src/expr.rs
+++ b/crates/hir-def/src/expr.rs
@@ -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);
diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs
index 0b9c136c7e..664db292a7 100644
--- a/crates/hir-def/src/resolver.rs
+++ b/crates/hir-def/src/resolver.rs
@@ -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
diff --git a/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs
index 431ab949b4..d25c0ccf00 100644
--- a/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -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);
+ });
}
_ => {}
}
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index 02024e1ea7..8895dc095f 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -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 { id: _, statements, tail } => {
+ self.infer_block(tgt_expr, statements, *tail, None, expected)
}
- Expr::Unsafe { body } => self.infer_expr(*body, expected),
- Expr::Const { body } => {
+ 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;
- self.diverges = matchee_diverges | all_arms_diverge;
+ let arm_ty = self.infer_expr_inner(arm.expr, &expected);
+ all_arms_diverge &= self.diverges;
+ coerce.coerce(self, Some(arm.expr), &arm_ty);
+ }
- 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 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 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(&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(
diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs
index 0a8527afbd..b3867623f3 100644
--- a/crates/hir-ty/src/infer/path.rs
+++ b/crates/hir-ty/src/infer/path.rs
@@ -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;
}
}
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index 1fa21e230c..936b56a021 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -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
diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs
index ab848a18eb..759878b10b 100644
--- a/crates/hir-ty/src/tests.rs
+++ b/crates/hir-ty/src/tests.rs
@@ -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);
diff --git a/crates/hir-ty/src/tests/diagnostics.rs b/crates/hir-ty/src/tests/diagnostics.rs
index f00fa97294..1876be303a 100644
--- a/crates/hir-ty/src/tests/diagnostics.rs
+++ b/crates/hir-ty/src/tests/diagnostics.rs
@@ -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
+ ()
+}
+"#,
+ );
+}
diff --git a/crates/hir-ty/src/tests/method_resolution.rs b/crates/hir-ty/src/tests/method_resolution.rs
index 41c53701df..4b671449e1 100644
--- a/crates/hir-ty/src/tests/method_resolution.rs
+++ b/crates/hir-ty/src/tests/method_resolution.rs
@@ -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
diff --git a/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs
index be67329fee..74bcab6caa 100644
--- a/crates/hir-ty/src/tests/patterns.rs
+++ b/crates/hir-ty/src/tests/patterns.rs
@@ -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
diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs
index de6ae7fff8..2fa6234da1 100644
--- a/crates/hir-ty/src/tests/regression.rs
+++ b/crates/hir-ty/src/tests/regression.rs
@@ -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
diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs
index 2e5787b701..1a07a2c51d 100644
--- a/crates/hir-ty/src/tests/simple.rs
+++ b/crates/hir-ty/src/tests/simple.rs
@@ -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<(), ()>
diff --git a/crates/ide-assists/src/handlers/extract_variable.rs b/crates/ide-assists/src/handlers/extract_variable.rs
index a738deffb9..1635614128 100644
--- a/crates/ide-assists/src/handlers/extract_variable.rs
+++ b/crates/ide-assists/src/handlers/extract_variable.rs
@@ -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"