diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index 04b1c4f01e2..3164a5f4c29 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) -> 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 4b4664a1cf4..622756ee8a9 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,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>, + ) { + 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]; diff --git a/crates/hir-def/src/body/scope.rs b/crates/hir-def/src/body/scope.rs index 2617d4288a3..e7078b7953b 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 8d6f0be2648..78a2f861233 100644 --- a/crates/hir-def/src/expr.rs +++ b/crates/hir-def/src/expr.rs @@ -109,6 +109,26 @@ pub enum Expr { tail: Option, label: Option, }, + TryBlock { + id: BlockId, + statements: Box<[Statement]>, + tail: Option, + }, + Async { + id: BlockId, + statements: Box<[Statement]>, + tail: Option, + }, + Const { + id: BlockId, + statements: Box<[Statement]>, + tail: Option, + }, + Unsafe { + id: BlockId, + statements: Box<[Statement]>, + tail: Option, + }, Loop { body: ExprId, label: Option, @@ -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, @@ -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 0b9c136c7eb..664db292a7f 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 431ab949b46..d25c0ccf00d 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 02024e1ea78..8895dc095f9 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 { 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: Future - 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, + label: Option, 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( diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs index 0a8527afbd0..b3867623f37 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 { 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 1fa21e230c4..936b56a0217 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, + statements: &[hir_def::expr::Statement], + mut current: BasicBlockId, + tail: Option, + place: Place, + ) -> Result { + 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 { 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 ab848a18eb3..759878b10bb 100644 --- a/crates/hir-ty/src/tests.rs +++ b/crates/hir-ty/src/tests.rs @@ -61,22 +61,27 @@ fn setup_tracing() -> Option { 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 f00fa972948..1876be303ad 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 41c53701df6..4b671449e15 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 be67329fee4..74bcab6caa9 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 de6ae7fff8f..2fa6234da1e 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 2e5787b701c..1a07a2c51d8 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 - 57..85 'async ...wait }': () 57..85 'async ...wait }': impl Future - 65..77 'async { () }': () 65..77 'async { () }': impl Future 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> 125..139 'return Err(())': ! 132..135 'Err': Err<(), ()>(()) -> Result<(), ()> @@ -2134,7 +2126,6 @@ fn main() { 167..171 'test': fn test<(), (), || -> impl Future>, impl Future>>(|| -> impl Future>) 167..228 'test(|... })': () 172..227 '|| asy... }': || -> impl Future> - 175..227 'async ... }': Result<(), ()> 175..227 'async ... }': impl Future> 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 a738deffb95..16356141288 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"