Skip to content

Commit c614c17

Browse files
committed
Auto merge of rust-lang#116080 - estebank:issue-115905-2, r=compiler-errors
Point at more causes of expectation of break value when possible Follow up to rust-lang#116071. r? `@compiler-errors` Disregard the first commit, which is in the other PR.
2 parents 0288f2e + ddb3b7e commit c614c17

10 files changed

+322
-55
lines changed

compiler/rustc_hir_typeck/src/demand.rs

+77-9
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
530530

531531
// When encountering a type error on the value of a `break`, try to point at the reason for the
532532
// expected type.
533-
fn annotate_loop_expected_due_to_inference(
533+
pub fn annotate_loop_expected_due_to_inference(
534534
&self,
535535
err: &mut Diagnostic,
536536
expr: &hir::Expr<'_>,
@@ -540,16 +540,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
540540
return;
541541
};
542542
let mut parent_id = self.tcx.hir().parent_id(expr.hir_id);
543-
loop {
543+
let mut parent;
544+
'outer: loop {
544545
// Climb the HIR tree to see if the current `Expr` is part of a `break;` statement.
545-
let Some(hir::Node::Expr(parent)) = self.tcx.hir().find(parent_id) else {
546+
let Some(
547+
hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Semi(&ref p), .. })
548+
| hir::Node::Expr(&ref p),
549+
) = self.tcx.hir().find(parent_id)
550+
else {
546551
break;
547552
};
548-
parent_id = self.tcx.hir().parent_id(parent.hir_id);
553+
parent = p;
554+
parent_id = self.tcx.hir().parent_id(parent_id);
549555
let hir::ExprKind::Break(destination, _) = parent.kind else {
550556
continue;
551557
};
552-
let mut parent_id = parent.hir_id;
558+
let mut parent_id = parent_id;
559+
let mut direct = false;
553560
loop {
554561
// Climb the HIR tree to find the (desugared) `loop` this `break` corresponds to.
555562
let parent = match self.tcx.hir().find(parent_id) {
@@ -565,14 +572,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
565572
parent_id = self.tcx.hir().parent_id(*hir_id);
566573
parent
567574
}
568-
Some(hir::Node::Block(hir::Block { .. })) => {
575+
Some(hir::Node::Block(_)) => {
569576
parent_id = self.tcx.hir().parent_id(parent_id);
570577
parent
571578
}
572579
_ => break,
573580
};
574-
if let hir::ExprKind::Loop(_, label, _, span) = parent.kind
575-
&& destination.label == label
581+
if let hir::ExprKind::Loop(..) = parent.kind {
582+
// When you have `'a: loop { break; }`, the `break` corresponds to the labeled
583+
// loop, so we need to account for that.
584+
direct = !direct;
585+
}
586+
if let hir::ExprKind::Loop(block, label, _, span) = parent.kind
587+
&& (destination.label == label || direct)
576588
{
577589
if let Some((reason_span, message)) =
578590
self.maybe_get_coercion_reason(parent_id, parent.span)
@@ -582,8 +594,64 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
582594
span,
583595
format!("this loop is expected to be of type `{expected}`"),
584596
);
597+
break 'outer;
598+
} else {
599+
// Locate all other `break` statements within the same `loop` that might
600+
// have affected inference.
601+
struct FindBreaks<'tcx> {
602+
label: Option<rustc_ast::Label>,
603+
uses: Vec<&'tcx hir::Expr<'tcx>>,
604+
nest_depth: usize,
605+
}
606+
impl<'tcx> Visitor<'tcx> for FindBreaks<'tcx> {
607+
fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
608+
let nest_depth = self.nest_depth;
609+
if let hir::ExprKind::Loop(_, label, _, _) = ex.kind {
610+
if label == self.label {
611+
// Account for `'a: loop { 'a: loop {...} }`.
612+
return;
613+
}
614+
self.nest_depth += 1;
615+
}
616+
if let hir::ExprKind::Break(destination, _) = ex.kind
617+
&& (self.label == destination.label
618+
// Account for `loop { 'a: loop { loop { break; } } }`.
619+
|| destination.label.is_none() && self.nest_depth == 0)
620+
{
621+
self.uses.push(ex);
622+
}
623+
hir::intravisit::walk_expr(self, ex);
624+
self.nest_depth = nest_depth;
625+
}
626+
}
627+
let mut expr_finder = FindBreaks { label, uses: vec![], nest_depth: 0 };
628+
expr_finder.visit_block(block);
629+
let mut exit = false;
630+
for ex in expr_finder.uses {
631+
let hir::ExprKind::Break(_, val) = ex.kind else {
632+
continue;
633+
};
634+
let ty = match val {
635+
Some(val) => {
636+
match self.typeck_results.borrow().expr_ty_adjusted_opt(val) {
637+
None => continue,
638+
Some(ty) => ty,
639+
}
640+
}
641+
None => self.tcx.types.unit,
642+
};
643+
if self.can_eq(self.param_env, ty, expected) {
644+
err.span_label(
645+
ex.span,
646+
format!("expected because of this `break`"),
647+
);
648+
exit = true;
649+
}
650+
}
651+
if exit {
652+
break 'outer;
653+
}
585654
}
586-
break;
587655
}
588656
}
589657
}

compiler/rustc_hir_typeck/src/expr.rs

+10-9
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,10 @@ use rustc_infer::traits::query::NoSolution;
4343
use rustc_infer::traits::ObligationCause;
4444
use rustc_middle::middle::stability;
4545
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
46-
use rustc_middle::ty::error::TypeError::FieldMisMatch;
46+
use rustc_middle::ty::error::{
47+
ExpectedFound,
48+
TypeError::{FieldMisMatch, Sorts},
49+
};
4750
use rustc_middle::ty::GenericArgsRef;
4851
use rustc_middle::ty::{self, AdtKind, Ty, TypeVisitableExt};
4952
use rustc_session::errors::ExprParenthesesNeeded;
@@ -664,15 +667,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
664667
self.suggest_mismatched_types_on_tail(
665668
&mut err, expr, ty, e_ty, target_id,
666669
);
670+
let error = Some(Sorts(ExpectedFound { expected: ty, found: e_ty }));
671+
self.annotate_loop_expected_due_to_inference(&mut err, expr, error);
667672
if let Some(val) = ty_kind_suggestion(ty) {
668-
let label = destination
669-
.label
670-
.map(|l| format!(" {}", l.ident))
671-
.unwrap_or_else(String::new);
672-
err.span_suggestion(
673-
expr.span,
673+
err.span_suggestion_verbose(
674+
expr.span.shrink_to_hi(),
674675
"give it a value of the expected type",
675-
format!("break{label} {val}"),
676+
format!(" {val}"),
676677
Applicability::HasPlaceholders,
677678
);
678679
}
@@ -717,7 +718,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
717718
// ... except when we try to 'break rust;'.
718719
// ICE this expression in particular (see #43162).
719720
if let ExprKind::Path(QPath::Resolved(_, path)) = e.kind {
720-
if path.segments.len() == 1 && path.segments[0].ident.name == sym::rust {
721+
if let [segment] = path.segments && segment.ident.name == sym::rust {
721722
fatally_break_rust(self.tcx);
722723
}
723724
}

compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs

+4
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
6565
let expr = expr.peel_drop_temps();
6666
self.suggest_missing_semicolon(err, expr, expected, false);
6767
let mut pointing_at_return_type = false;
68+
if let hir::ExprKind::Break(..) = expr.kind {
69+
// `break` type mismatches provide better context for tail `loop` expressions.
70+
return false;
71+
}
6872
if let Some((fn_id, fn_decl, can_suggest)) = self.get_fn_decl(blk_id) {
6973
pointing_at_return_type = self.suggest_missing_return_type(
7074
err,

tests/ui/issues/issue-27042.stderr

+6-4
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@ error[E0308]: mismatched types
1212
--> $DIR/issue-27042.rs:6:16
1313
|
1414
LL | loop { break };
15-
| ^^^^^
16-
| |
17-
| expected `i32`, found `()`
18-
| help: give it a value of the expected type: `break 42`
15+
| ^^^^^ expected `i32`, found `()`
16+
|
17+
help: give it a value of the expected type
18+
|
19+
LL | loop { break 42 };
20+
| ++
1921

2022
error[E0308]: mismatched types
2123
--> $DIR/issue-27042.rs:8:9

tests/ui/loops/loop-break-value.rs

+60
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,66 @@ fn main() {
9595
break LOOP;
9696
//~^ ERROR cannot find value `LOOP` in this scope
9797
}
98+
99+
let _ = 'a: loop {
100+
loop {
101+
break; // This doesn't affect the expected break type of the 'a loop
102+
loop {
103+
loop {
104+
break 'a 1;
105+
}
106+
}
107+
}
108+
break; //~ ERROR mismatched types
109+
};
110+
111+
let _ = 'a: loop {
112+
loop {
113+
break; // This doesn't affect the expected break type of the 'a loop
114+
loop {
115+
loop {
116+
break 'a 1;
117+
}
118+
}
119+
}
120+
break 'a; //~ ERROR mismatched types
121+
};
122+
123+
loop {
124+
break;
125+
let _ = loop {
126+
break 2;
127+
loop {
128+
break;
129+
}
130+
};
131+
break 2; //~ ERROR mismatched types
132+
}
133+
134+
'a: loop {
135+
break;
136+
let _ = 'a: loop {
137+
//~^ WARNING label name `'a` shadows a label name that is already in scope
138+
break 2;
139+
loop {
140+
break 'a; //~ ERROR mismatched types
141+
}
142+
};
143+
break 2; //~ ERROR mismatched types
144+
}
145+
146+
'a: loop {
147+
break;
148+
let _ = 'a: loop {
149+
//~^ WARNING label name `'a` shadows a label name that is already in scope
150+
break 'a 2;
151+
loop {
152+
break 'a; //~ ERROR mismatched types
153+
}
154+
};
155+
break 2; //~ ERROR mismatched types
156+
};
157+
98158
loop { // point at the return type
99159
break 2; //~ ERROR mismatched types
100160
}

0 commit comments

Comments
 (0)