Skip to content

Commit a20a08f

Browse files
committed
Auto merge of rust-lang#14824 - Veykril:ty-diag-unit, r=Veykril
fix: Diagnose non-value return and break type mismatches Could definitely deserve more polished diagnostics, but this at least brings the message across for now.
2 parents c4026bf + 478705b commit a20a08f

File tree

3 files changed

+62
-23
lines changed

3 files changed

+62
-23
lines changed

Diff for: crates/hir-ty/src/infer/coerce.rs

+21-7
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ fn success(
5050
Ok(InferOk { goals, value: (adj, target) })
5151
}
5252

53+
pub(super) enum CoercionCause {
54+
// FIXME: Make better use of this. Right now things like return and break without a value
55+
// use it to point to themselves, causing us to report a mismatch on those expressions even
56+
// though technically they themselves are `!`
57+
Expr(ExprId),
58+
}
59+
5360
#[derive(Clone, Debug)]
5461
pub(super) struct CoerceMany {
5562
expected_ty: Ty,
@@ -90,8 +97,12 @@ impl CoerceMany {
9097
}
9198
}
9299

93-
pub(super) fn coerce_forced_unit(&mut self, ctx: &mut InferenceContext<'_>) {
94-
self.coerce(ctx, None, &ctx.result.standard_types.unit.clone())
100+
pub(super) fn coerce_forced_unit(
101+
&mut self,
102+
ctx: &mut InferenceContext<'_>,
103+
cause: CoercionCause,
104+
) {
105+
self.coerce(ctx, None, &ctx.result.standard_types.unit.clone(), cause)
95106
}
96107

97108
/// Merge two types from different branches, with possible coercion.
@@ -106,6 +117,7 @@ impl CoerceMany {
106117
ctx: &mut InferenceContext<'_>,
107118
expr: Option<ExprId>,
108119
expr_ty: &Ty,
120+
cause: CoercionCause,
109121
) {
110122
let expr_ty = ctx.resolve_ty_shallow(expr_ty);
111123
self.expected_ty = ctx.resolve_ty_shallow(&self.expected_ty);
@@ -153,11 +165,13 @@ impl CoerceMany {
153165
} else if let Ok(res) = ctx.coerce(expr, &self.merged_ty(), &expr_ty) {
154166
self.final_ty = Some(res);
155167
} else {
156-
if let Some(id) = expr {
157-
ctx.result.type_mismatches.insert(
158-
id.into(),
159-
TypeMismatch { expected: self.merged_ty(), actual: expr_ty.clone() },
160-
);
168+
match cause {
169+
CoercionCause::Expr(id) => {
170+
ctx.result.type_mismatches.insert(
171+
id.into(),
172+
TypeMismatch { expected: self.merged_ty(), actual: expr_ty.clone() },
173+
);
174+
}
161175
}
162176
cov_mark::hit!(coerce_merge_fail_fallback);
163177
}

Diff for: crates/hir-ty/src/infer/expr.rs

+27-16
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ use crate::{
2626
autoderef::{builtin_deref, deref_by_trait, Autoderef},
2727
consteval,
2828
infer::{
29-
coerce::CoerceMany, find_continuable, pat::contains_explicit_ref_binding, BreakableKind,
29+
coerce::{CoerceMany, CoercionCause},
30+
find_continuable,
31+
pat::contains_explicit_ref_binding,
32+
BreakableKind,
3033
},
3134
lang_items::lang_items_for_bin_op,
3235
lower::{
@@ -132,24 +135,28 @@ impl<'a> InferenceContext<'a> {
132135
);
133136

134137
let condition_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
135-
let mut both_arms_diverge = Diverges::Always;
136138

137139
let then_ty = self.infer_expr_inner(then_branch, expected);
138-
both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe);
140+
let then_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
139141
let mut coerce = CoerceMany::new(expected.coercion_target_type(&mut self.table));
140-
coerce.coerce(self, Some(then_branch), &then_ty);
142+
coerce.coerce(self, Some(then_branch), &then_ty, CoercionCause::Expr(then_branch));
141143
match else_branch {
142144
Some(else_branch) => {
143145
let else_ty = self.infer_expr_inner(else_branch, expected);
144-
coerce.coerce(self, Some(else_branch), &else_ty);
146+
let else_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
147+
coerce.coerce(
148+
self,
149+
Some(else_branch),
150+
&else_ty,
151+
CoercionCause::Expr(else_branch),
152+
);
153+
self.diverges = condition_diverges | then_diverges & else_diverges;
145154
}
146155
None => {
147-
coerce.coerce_forced_unit(self);
156+
coerce.coerce_forced_unit(self, CoercionCause::Expr(tgt_expr));
157+
self.diverges = condition_diverges;
148158
}
149159
}
150-
both_arms_diverge &= self.diverges;
151-
152-
self.diverges = condition_diverges | both_arms_diverge;
153160

154161
coerce.complete(self)
155162
}
@@ -444,7 +451,7 @@ impl<'a> InferenceContext<'a> {
444451

445452
let arm_ty = self.infer_expr_inner(arm.expr, &expected);
446453
all_arms_diverge &= self.diverges;
447-
coerce.coerce(self, Some(arm.expr), &arm_ty);
454+
coerce.coerce(self, Some(arm.expr), &arm_ty, CoercionCause::Expr(arm.expr));
448455
}
449456

450457
self.diverges = matchee_diverges | all_arms_diverge;
@@ -492,7 +499,11 @@ impl<'a> InferenceContext<'a> {
492499
match find_breakable(&mut self.breakables, label) {
493500
Some(ctxt) => match ctxt.coerce.take() {
494501
Some(mut coerce) => {
495-
coerce.coerce(self, expr, &val_ty);
502+
let cause = match expr {
503+
Some(expr) => CoercionCause::Expr(expr),
504+
None => CoercionCause::Expr(tgt_expr),
505+
};
506+
coerce.coerce(self, expr, &val_ty, cause);
496507

497508
// Avoiding borrowck
498509
let ctxt = find_breakable(&mut self.breakables, label)
@@ -512,7 +523,7 @@ impl<'a> InferenceContext<'a> {
512523
}
513524
self.result.standard_types.never.clone()
514525
}
515-
&Expr::Return { expr } => self.infer_expr_return(expr),
526+
&Expr::Return { expr } => self.infer_expr_return(tgt_expr, expr),
516527
Expr::Yield { expr } => {
517528
if let Some((resume_ty, yield_ty)) = self.resume_yield_tys.clone() {
518529
if let Some(expr) = expr {
@@ -952,7 +963,7 @@ impl<'a> InferenceContext<'a> {
952963
let mut coerce = CoerceMany::new(elem_ty);
953964
for &expr in elements.iter() {
954965
let cur_elem_ty = self.infer_expr_inner(expr, &expected);
955-
coerce.coerce(self, Some(expr), &cur_elem_ty);
966+
coerce.coerce(self, Some(expr), &cur_elem_ty, CoercionCause::Expr(expr));
956967
}
957968
(
958969
coerce.complete(self),
@@ -997,18 +1008,18 @@ impl<'a> InferenceContext<'a> {
9971008
.expected_ty();
9981009
let return_expr_ty = self.infer_expr_inner(expr, &Expectation::HasType(ret_ty));
9991010
let mut coerce_many = self.return_coercion.take().unwrap();
1000-
coerce_many.coerce(self, Some(expr), &return_expr_ty);
1011+
coerce_many.coerce(self, Some(expr), &return_expr_ty, CoercionCause::Expr(expr));
10011012
self.return_coercion = Some(coerce_many);
10021013
}
10031014

1004-
fn infer_expr_return(&mut self, expr: Option<ExprId>) -> Ty {
1015+
fn infer_expr_return(&mut self, ret: ExprId, expr: Option<ExprId>) -> Ty {
10051016
match self.return_coercion {
10061017
Some(_) => {
10071018
if let Some(expr) = expr {
10081019
self.infer_return(expr);
10091020
} else {
10101021
let mut coerce = self.return_coercion.take().unwrap();
1011-
coerce.coerce_forced_unit(self);
1022+
coerce.coerce_forced_unit(self, CoercionCause::Expr(ret));
10121023
self.return_coercion = Some(coerce);
10131024
}
10141025
}

Diff for: crates/ide-diagnostics/src/handlers/type_mismatch.rs

+14
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,20 @@ struct Bar {
678678
f2: &[u16],
679679
f3: dyn Debug,
680680
}
681+
"#,
682+
);
683+
}
684+
685+
#[test]
686+
fn return_no_value() {
687+
check_diagnostics(
688+
r#"
689+
fn f() -> i32 {
690+
return;
691+
// ^^^^^^ error: expected i32, found ()
692+
0
693+
}
694+
fn g() { return; }
681695
"#,
682696
);
683697
}

0 commit comments

Comments
 (0)