Skip to content

Commit 24ba1be

Browse files
committed
Set expectation for no-semi expression statements to unit
1 parent 73e2505 commit 24ba1be

File tree

5 files changed

+81
-46
lines changed

5 files changed

+81
-46
lines changed

crates/hir-def/src/resolver.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,8 +294,8 @@ impl Resolver {
294294
}
295295
}
296296

297-
if let res @ Some(_) = self.module_scope.resolve_path_in_value_ns(db, path) {
298-
return res;
297+
if let Some(res) = self.module_scope.resolve_path_in_value_ns(db, path) {
298+
return Some(res);
299299
}
300300

301301
// If a path of the shape `u16::from_le_bytes` failed to resolve at all, then we fall back

crates/hir-ty/src/infer/expr.rs

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ impl<'a> InferenceContext<'a> {
130130
);
131131
let ty = match label {
132132
Some(_) => {
133-
let break_ty = self.table.new_type_var();
133+
let break_ty = expected.coercion_target_type(&mut self.table);
134134
let (breaks, ty) = self.with_breakable_ctx(
135135
BreakableKind::Block,
136136
Some(break_ty.clone()),
@@ -403,37 +403,47 @@ impl<'a> InferenceContext<'a> {
403403
Expr::Match { expr, arms } => {
404404
let input_ty = self.infer_expr(*expr, &Expectation::none());
405405

406-
let expected = expected.adjust_for_branches(&mut self.table);
407-
408-
let result_ty = if arms.is_empty() {
406+
if arms.is_empty() {
407+
self.diverges = Diverges::Always;
409408
self.result.standard_types.never.clone()
410409
} else {
411-
expected.coercion_target_type(&mut self.table)
412-
};
413-
let mut coerce = CoerceMany::new(result_ty);
414-
415-
let matchee_diverges = self.diverges;
416-
let mut all_arms_diverge = Diverges::Always;
417-
418-
for arm in arms.iter() {
419-
self.diverges = Diverges::Maybe;
420-
let input_ty = self.resolve_ty_shallow(&input_ty);
421-
self.infer_top_pat(arm.pat, &input_ty);
422-
if let Some(guard_expr) = arm.guard {
423-
self.infer_expr(
424-
guard_expr,
425-
&Expectation::HasType(self.result.standard_types.bool_.clone()),
426-
);
410+
let matchee_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
411+
let mut all_arms_diverge = Diverges::Always;
412+
for arm in arms.iter() {
413+
let input_ty = self.resolve_ty_shallow(&input_ty);
414+
self.infer_top_pat(arm.pat, &input_ty);
427415
}
428416

429-
let arm_ty = self.infer_expr_inner(arm.expr, &expected);
430-
all_arms_diverge &= self.diverges;
431-
coerce.coerce(self, Some(arm.expr), &arm_ty);
432-
}
417+
let expected = expected.adjust_for_branches(&mut self.table);
418+
let result_ty = match &expected {
419+
// We don't coerce to `()` so that if the match expression is a
420+
// statement it's branches can have any consistent type.
421+
Expectation::HasType(ty) if *ty != self.result.standard_types.unit => {
422+
ty.clone()
423+
}
424+
_ => self.table.new_type_var(),
425+
};
426+
let mut coerce = CoerceMany::new(result_ty);
427+
428+
for arm in arms.iter() {
429+
if let Some(guard_expr) = arm.guard {
430+
self.diverges = Diverges::Maybe;
431+
self.infer_expr(
432+
guard_expr,
433+
&Expectation::HasType(self.result.standard_types.bool_.clone()),
434+
);
435+
}
436+
self.diverges = Diverges::Maybe;
433437

434-
self.diverges = matchee_diverges | all_arms_diverge;
438+
let arm_ty = self.infer_expr_inner(arm.expr, &expected);
439+
all_arms_diverge &= self.diverges;
440+
coerce.coerce(self, Some(arm.expr), &arm_ty);
441+
}
435442

436-
coerce.complete(self)
443+
self.diverges = matchee_diverges | all_arms_diverge;
444+
445+
coerce.complete(self)
446+
}
437447
}
438448
Expr::Path(p) => {
439449
// FIXME this could be more efficient...
@@ -1179,8 +1189,15 @@ impl<'a> InferenceContext<'a> {
11791189
self.diverges = previous_diverges;
11801190
}
11811191
}
1182-
Statement::Expr { expr, .. } => {
1183-
self.infer_expr(*expr, &Expectation::none());
1192+
&Statement::Expr { expr, has_semi } => {
1193+
self.infer_expr(
1194+
expr,
1195+
&if has_semi {
1196+
Expectation::none()
1197+
} else {
1198+
Expectation::HasType(self.result.standard_types.unit.clone())
1199+
},
1200+
);
11841201
}
11851202
}
11861203
}

crates/hir-ty/src/infer/path.rs

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,20 +40,14 @@ impl<'a> InferenceContext<'a> {
4040
id: ExprOrPatId,
4141
) -> Option<Ty> {
4242
let (value, self_subst) = if let Some(type_ref) = path.type_anchor() {
43-
if path.segments().is_empty() {
44-
// This can't actually happen syntax-wise
45-
return None;
46-
}
43+
let Some(last) = path.segments().last() else { return None };
4744
let ty = self.make_ty(type_ref);
4845
let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1);
4946
let ctx = crate::lower::TyLoweringContext::new(self.db, resolver);
5047
let (ty, _) = ctx.lower_ty_relative_path(ty, None, remaining_segments_for_ty);
51-
self.resolve_ty_assoc_item(
52-
ty,
53-
path.segments().last().expect("path had at least one segment").name,
54-
id,
55-
)?
48+
self.resolve_ty_assoc_item(ty, last.name, id)?
5649
} else {
50+
// FIXME: report error, unresolved first path segment
5751
let value_or_partial =
5852
resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path())?;
5953

@@ -66,10 +60,13 @@ impl<'a> InferenceContext<'a> {
6660
};
6761

6862
let typable: ValueTyDefId = match value {
69-
ValueNs::LocalBinding(pat) => {
70-
let ty = self.result.type_of_pat.get(pat)?.clone();
71-
return Some(ty);
72-
}
63+
ValueNs::LocalBinding(pat) => match self.result.type_of_pat.get(pat) {
64+
Some(ty) => return Some(ty.clone()),
65+
None => {
66+
never!("uninferred pattern?");
67+
return None;
68+
}
69+
},
7370
ValueNs::FunctionId(it) => it.into(),
7471
ValueNs::ConstId(it) => it.into(),
7572
ValueNs::StaticId(it) => it.into(),
@@ -91,7 +88,7 @@ impl<'a> InferenceContext<'a> {
9188
let ty = self.db.value_ty(struct_id.into()).substitute(Interner, &substs);
9289
return Some(ty);
9390
} else {
94-
// FIXME: diagnostic, invalid Self reference
91+
// FIXME: report error, invalid Self reference
9592
return None;
9693
}
9794
}

crates/hir-ty/src/tests/diagnostics.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,24 @@ fn test(x: bool) -> &'static str {
7373
"#,
7474
);
7575
}
76+
77+
#[test]
78+
fn non_unit_block_expr_stmt_no_semi() {
79+
check(
80+
r#"
81+
fn test(x: bool) {
82+
if x {
83+
"notok"
84+
//^^^^^^^ expected (), got &str
85+
} else {
86+
"ok"
87+
//^^^^ expected (), got &str
88+
}
89+
match x { true => true, false => 0 }
90+
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected (), got bool
91+
//^ expected bool, got i32
92+
()
93+
}
94+
"#,
95+
);
96+
}

crates/hir-ty/src/tests/regression.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,9 +1015,9 @@ fn cfg_tail() {
10151015
20..31 '{ "first" }': ()
10161016
22..29 '"first"': &str
10171017
72..190 '{ ...] 13 }': ()
1018-
78..88 '{ "fake" }': &str
1018+
78..88 '{ "fake" }': ()
10191019
80..86 '"fake"': &str
1020-
93..103 '{ "fake" }': &str
1020+
93..103 '{ "fake" }': ()
10211021
95..101 '"fake"': &str
10221022
108..120 '{ "second" }': ()
10231023
110..118 '"second"': &str

0 commit comments

Comments
 (0)