Skip to content

typeck: catch continues pointing to blocks #141317

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 26 additions & 8 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -532,14 +532,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ExprKind::Break(destination, ref expr_opt) => {
self.check_expr_break(destination, expr_opt.as_deref(), expr)
}
ExprKind::Continue(destination) => {
if destination.target_id.is_ok() {
tcx.types.never
} else {
// There was an error; make type-check fail.
Ty::new_misc_error(tcx)
}
}
ExprKind::Continue(destination) => self.check_expr_continue(destination, expr),
ExprKind::Ret(ref expr_opt) => self.check_expr_return(expr_opt.as_deref(), expr),
ExprKind::Become(call) => self.check_expr_become(call, expr),
ExprKind::Let(let_expr) => self.check_expr_let(let_expr, expr.hir_id),
Expand Down Expand Up @@ -989,6 +982,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}

fn check_expr_continue(
&self,
destination: hir::Destination,
expr: &'tcx hir::Expr<'tcx>,
) -> Ty<'tcx> {
if let Ok(target_id) = destination.target_id {
if let hir::Node::Expr(hir::Expr { kind: ExprKind::Loop(..), .. }) =
self.tcx.hir_node(target_id)
{
self.tcx.types.never
} else {
// Liveness linting assumes `continue`s all point to loops. We'll report an error
// in `check_mod_loops`, but make sure we don't run liveness (#113379, #121623).
let guar = self.dcx().span_delayed_bug(
expr.span,
"found `continue` not pointing to loop, but no error reported",
);
Ty::new_error(self.tcx, guar)
}
} else {
// There was an error; make type-check fail.
Ty::new_misc_error(self.tcx)
}
}

fn check_expr_return(
&self,
expr_opt: Option<&'tcx hir::Expr<'tcx>>,
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_mir_build/src/builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,7 @@ pub fn build_mir<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> Body<'tcx> {
}
};

// this must run before MIR dump, because
// "not all control paths return a value" is reported here.
// Checking liveness after building the THIR ensures there were no typeck errors.
//
// maybe move the check to a MIR pass?
tcx.ensure_ok().check_liveness(def);
Expand Down
10 changes: 6 additions & 4 deletions compiler/rustc_passes/src/liveness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,6 @@ enum LiveNodeKind {
VarDefNode(Span, HirId),
ClosureNode,
ExitNode,
ErrNode,
}

fn live_node_kind_to_string(lnk: LiveNodeKind, tcx: TyCtxt<'_>) -> String {
Expand All @@ -133,7 +132,6 @@ fn live_node_kind_to_string(lnk: LiveNodeKind, tcx: TyCtxt<'_>) -> String {
VarDefNode(s, _) => format!("Var def node [{}]", sm.span_to_diagnostic_string(s)),
ClosureNode => "Closure node".to_owned(),
ExitNode => "Exit node".to_owned(),
ErrNode => "Error node".to_owned(),
}
}

Expand Down Expand Up @@ -492,6 +490,9 @@ struct Liveness<'a, 'tcx> {
impl<'a, 'tcx> Liveness<'a, 'tcx> {
fn new(ir: &'a mut IrMaps<'tcx>, body_owner: LocalDefId) -> Liveness<'a, 'tcx> {
let typeck_results = ir.tcx.typeck(body_owner);
// Liveness linting runs after building the THIR. We make several assumptions based on
// typeck succeeding, e.g. that breaks and continues are well-formed.
assert!(typeck_results.tainted_by_errors.is_none());
// FIXME(#132279): we're in a body here.
let typing_env = ty::TypingEnv::non_body_analysis(ir.tcx, body_owner);
let closure_min_captures = typeck_results.closure_min_captures.get(&body_owner);
Expand Down Expand Up @@ -976,8 +977,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
// Now that we know the label we're going to,
// look it up in the continue loop nodes table
self.cont_ln.get(&sc).cloned().unwrap_or_else(|| {
self.ir.tcx.dcx().span_delayed_bug(expr.span, "continue to unknown label");
self.ir.add_live_node(ErrNode)
// Liveness linting happens after building the THIR. Bad labels should already
// have been caught.
span_bug!(expr.span, "continue to unknown label");
})
}

Expand Down
7 changes: 0 additions & 7 deletions tests/crashes/113379.rs

This file was deleted.

8 changes: 0 additions & 8 deletions tests/crashes/121623.rs

This file was deleted.

12 changes: 12 additions & 0 deletions tests/ui/label/continue-pointing-to-block-ice-113379.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//! Regression test for ICE #113379. Liveness linting assumes that `continue`s all point to loops.
//! This tests that if a `continue` points to a block, we don't run liveness lints.
async fn f999() -> Vec<usize> {
//~^ ERROR `async fn` is not permitted in Rust 2015
'b: {
//~^ ERROR mismatched types
continue 'b;
//~^ ERROR `continue` pointing to a labeled block
}
}
//~^ ERROR `main` function not found
43 changes: 43 additions & 0 deletions tests/ui/label/continue-pointing-to-block-ice-113379.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
error[E0670]: `async fn` is not permitted in Rust 2015
--> $DIR/continue-pointing-to-block-ice-113379.rs:4:1
|
LL | async fn f999() -> Vec<usize> {
| ^^^^^ to use `async fn`, switch to Rust 2018 or later
|
= help: pass `--edition 2024` to `rustc`
= note: for more on editions, read https://doc.rust-lang.org/edition-guide

error[E0601]: `main` function not found in crate `continue_pointing_to_block_ice_113379`
--> $DIR/continue-pointing-to-block-ice-113379.rs:11:2
|
LL | }
| ^ consider adding a `main` function to `$DIR/continue-pointing-to-block-ice-113379.rs`

error[E0696]: `continue` pointing to a labeled block
--> $DIR/continue-pointing-to-block-ice-113379.rs:8:9
|
LL | / 'b: {
LL | |
LL | | continue 'b;
| | ^^^^^^^^^^^ labeled blocks cannot be `continue`'d
LL | |
LL | | }
| |_____- labeled block the `continue` points to

error[E0308]: mismatched types
--> $DIR/continue-pointing-to-block-ice-113379.rs:6:5
|
LL | / 'b: {
LL | |
LL | | continue 'b;
LL | |
LL | | }
| |_____^ expected `Vec<usize>`, found `()`
|
= note: expected struct `Vec<usize>`
found unit type `()`

error: aborting due to 4 previous errors

Some errors have detailed explanations: E0308, E0601, E0670, E0696.
For more information about an error, try `rustc --explain E0308`.
11 changes: 11 additions & 0 deletions tests/ui/label/continue-pointing-to-block-ice-121623.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//! Regression test for ICE #121623. Liveness linting assumes that `continue`s all point to loops.
//! This tests that if a `continue` points to a block, we don't run liveness lints.
fn main() {
match () {
_ => 'b: {
continue 'b;
//~^ ERROR `continue` pointing to a labeled block
}
}
}
14 changes: 14 additions & 0 deletions tests/ui/label/continue-pointing-to-block-ice-121623.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error[E0696]: `continue` pointing to a labeled block
--> $DIR/continue-pointing-to-block-ice-121623.rs:7:13
|
LL | _ => 'b: {
| ______________-
LL | | continue 'b;
| | ^^^^^^^^^^^ labeled blocks cannot be `continue`'d
LL | |
LL | | }
| |_________- labeled block the `continue` points to

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0696`.
Loading