Skip to content

Commit

Permalink
Continue inside a labeled block now lowers to a error when to lowerin…
Browse files Browse the repository at this point in the history
…g the ast
  • Loading branch information
SarthakSingh31 committed Jul 22, 2024
1 parent ee0fd6c commit 60ed3d1
Show file tree
Hide file tree
Showing 13 changed files with 156 additions and 29 deletions.
5 changes: 5 additions & 0 deletions compiler/rustc_ast_lowering/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ ast_lowering_clobber_abi_not_supported =
ast_lowering_closure_cannot_be_static = closures cannot be static
ast_lowering_continue_labeled_block =
`continue` pointing to a labeled block
.label = labeled blocks cannot be `continue`'d
.block_label = labeled block the `continue` points to
ast_lowering_coroutine_too_many_parameters =
too many parameters for a coroutine (expected 0 or 1 parameters)
Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_ast_lowering/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -451,3 +451,13 @@ pub(crate) struct YieldInClosure {
#[suggestion(code = "#[coroutine] ", applicability = "maybe-incorrect", style = "verbose")]
pub suggestion: Option<Span>,
}

#[derive(Diagnostic)]
#[diag(ast_lowering_continue_labeled_block, code = E0696)]
pub struct ContinueLabeledBlock {
#[primary_span]
#[label]
pub span: Span,
#[label(ast_lowering_block_label)]
pub block_span: Span,
}
20 changes: 14 additions & 6 deletions compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ use std::assert_matches::assert_matches;

use super::errors::{
AsyncCoroutinesNotSupported, AwaitOnlyInAsyncFnAndBlocks, BaseExpressionDoubleDot,
ClosureCannotBeStatic, CoroutineTooManyParameters,
ClosureCannotBeStatic, ContinueLabeledBlock, CoroutineTooManyParameters,
FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd, MatchArmWithNoBody,
NeverPatternWithBody, NeverPatternWithGuard, UnderscoreExprLhsAssign,
NeverPatternWithBody, NeverPatternWithGuard, UnderscoreExprLhsAssign, YieldInClosure,
};
use super::ResolverAstLoweringExt;
use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs};
use crate::errors::YieldInClosure;
use crate::{FnDeclKind, ImplTraitPosition};
use rustc_ast::ptr::P as AstP;
use rustc_ast::*;
Expand Down Expand Up @@ -290,7 +289,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::ExprKind::Break(self.lower_jump_destination(e.id, *opt_label), opt_expr)
}
ExprKind::Continue(opt_label) => {
hir::ExprKind::Continue(self.lower_jump_destination(e.id, *opt_label))
if opt_label.is_some()
&& let Some((_, is_loop, block_span)) = self.resolver.get_label_res(e.id)
&& !is_loop
{
hir::ExprKind::Err(
self.dcx().emit_err(ContinueLabeledBlock { span: e.span, block_span }),
)
} else {
hir::ExprKind::Continue(self.lower_jump_destination(e.id, *opt_label))
}
}
ExprKind::Ret(e) => {
let e = e.as_ref().map(|x| self.lower_expr(x));
Expand Down Expand Up @@ -1469,8 +1477,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn lower_loop_destination(&mut self, destination: Option<(NodeId, Label)>) -> hir::Destination {
let target_id = match destination {
Some((id, _)) => {
if let Some(loop_id) = self.resolver.get_label_res(id) {
Ok(self.lower_node_id(loop_id))
if let Some((id, _is_loop, _)) = self.resolver.get_label_res(id) {
Ok(self.lower_node_id(id))
} else {
Err(hir::LoopIdError::UnresolvedLabel)
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ impl ResolverAstLowering {
}

/// Obtains resolution for a label with the given `NodeId`.
fn get_label_res(&self, id: NodeId) -> Option<NodeId> {
fn get_label_res(&self, id: NodeId) -> Option<(NodeId, bool, Span)> {
self.label_res_map.get(&id).copied()
}

Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,8 @@ pub struct ResolverAstLowering {
/// Resolutions for import nodes, which have multiple resolutions in different namespaces.
pub import_res_map: NodeMap<hir::def::PerNS<Option<Res<ast::NodeId>>>>,
/// Resolutions for labels (node IDs of their corresponding blocks or loops).
pub label_res_map: NodeMap<ast::NodeId>,
/// The boolean stores if the node is loop. The span is the span of the node.
pub label_res_map: NodeMap<(ast::NodeId, bool, Span)>,
/// Resolutions for lifetimes.
pub lifetimes_res_map: NodeMap<LifetimeRes>,
/// Lifetime parameters that lowering will have to introduce.
Expand Down
44 changes: 31 additions & 13 deletions compiler/rustc_resolve/src/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,7 @@ struct LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
last_block_rib: Option<Rib<'a>>,

/// The current set of local scopes, for labels.
label_ribs: Vec<Rib<'a, NodeId>>,
label_ribs: Vec<Rib<'a, (NodeId, bool, Span)>>,

/// The current set of local scopes for lifetimes.
lifetime_ribs: Vec<LifetimeRib>,
Expand Down Expand Up @@ -2313,7 +2313,10 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {

/// Searches the current set of local scopes for labels. Returns the `NodeId` of the resolved
/// label and reports an error if the label is not found or is unreachable.
fn resolve_label(&mut self, mut label: Ident) -> Result<(NodeId, Span), ResolutionError<'a>> {
fn resolve_label(
&mut self,
mut label: Ident,
) -> Result<((NodeId, bool, Span), Span), ResolutionError<'a>> {
let mut suggestion = None;

for i in (0..self.label_ribs.len()).rev() {
Expand Down Expand Up @@ -4330,7 +4333,14 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
Ok(Some(result))
}

fn with_resolved_label(&mut self, label: Option<Label>, id: NodeId, f: impl FnOnce(&mut Self)) {
fn with_resolved_label(
&mut self,
label: Option<Label>,
id: NodeId,
is_loop: bool,
span: Span,
f: impl FnOnce(&mut Self),
) {
if let Some(label) = label {
if label.ident.as_str().as_bytes()[1] != b'_' {
self.diag_metadata.unused_labels.insert(id, label.ident.span);
Expand All @@ -4342,16 +4352,22 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {

self.with_label_rib(RibKind::Normal, |this| {
let ident = label.ident.normalize_to_macro_rules();
this.label_ribs.last_mut().unwrap().bindings.insert(ident, id);
this.label_ribs.last_mut().unwrap().bindings.insert(ident, (id, is_loop, span));
f(this);
});
} else {
f(self);
}
}

fn resolve_labeled_block(&mut self, label: Option<Label>, id: NodeId, block: &'ast Block) {
self.with_resolved_label(label, id, |this| this.visit_block(block));
fn resolve_labeled_block(
&mut self,
label: Option<Label>,
id: NodeId,
block: &'ast Block,
is_loop: bool,
) {
self.with_resolved_label(label, id, is_loop, block.span, |this| this.visit_block(block));
}

fn resolve_block(&mut self, block: &'ast Block) {
Expand Down Expand Up @@ -4494,10 +4510,10 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {

ExprKind::Break(Some(label), _) | ExprKind::Continue(Some(label)) => {
match self.resolve_label(label.ident) {
Ok((node_id, _)) => {
Ok((node, _)) => {
// Since this res is a label, it is never read.
self.r.label_res_map.insert(expr.id, node_id);
self.diag_metadata.unused_labels.remove(&node_id);
self.r.label_res_map.insert(expr.id, node);
self.diag_metadata.unused_labels.remove(&node.0);
}
Err(error) => {
self.report_error(label.ident.span, error);
Expand Down Expand Up @@ -4532,11 +4548,11 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
}

ExprKind::Loop(ref block, label, _) => {
self.resolve_labeled_block(label, expr.id, block)
self.resolve_labeled_block(label, expr.id, block, true)
}

ExprKind::While(ref cond, ref block, label) => {
self.with_resolved_label(label, expr.id, |this| {
self.with_resolved_label(label, expr.id, true, block.span, |this| {
this.with_rib(ValueNS, RibKind::Normal, |this| {
let old = this.diag_metadata.in_if_condition.replace(cond);
this.visit_expr(cond);
Expand All @@ -4550,11 +4566,13 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
self.visit_expr(iter);
self.with_rib(ValueNS, RibKind::Normal, |this| {
this.resolve_pattern_top(pat, PatternSource::For);
this.resolve_labeled_block(label, expr.id, body);
this.resolve_labeled_block(label, expr.id, body, true);
});
}

ExprKind::Block(ref block, label) => self.resolve_labeled_block(label, block.id, block),
ExprKind::Block(ref block, label) => {
self.resolve_labeled_block(label, block.id, block, false)
}

// Equivalent to `visit::walk_expr` + passing some context to children.
ExprKind::Field(ref subexpression, _) => {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_resolve/src/late/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -936,7 +936,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
if let Some(err_code) = err.code {
if err_code == E0425 {
for label_rib in &self.label_ribs {
for (label_ident, node_id) in &label_rib.bindings {
for (label_ident, (node_id, _, _)) in &label_rib.bindings {
let ident = path.last().unwrap().ident;
if format!("'{ident}") == label_ident.to_string() {
err.span_label(label_ident.span, "a label with a similar name exists");
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_resolve/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1013,7 +1013,8 @@ pub struct Resolver<'a, 'tcx> {
/// Resolutions for import nodes, which have multiple resolutions in different namespaces.
import_res_map: NodeMap<PerNS<Option<Res>>>,
/// Resolutions for labels (node IDs of their corresponding blocks or loops).
label_res_map: NodeMap<NodeId>,
/// The boolean stores if the node is loop. The span is the span of the node.
label_res_map: NodeMap<(NodeId, bool, Span)>,
/// Resolutions for lifetimes.
lifetimes_res_map: NodeMap<LifetimeRes>,
/// Lifetime parameters that lowering will have to introduce.
Expand Down
12 changes: 6 additions & 6 deletions tests/ui/label/label_break_value_continue.stderr
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
error[E0695]: unlabeled `continue` inside of a labeled block
--> $DIR/label_break_value_continue.rs:6:9
|
LL | continue;
| ^^^^^^^^ `continue` statements that would diverge to or through a labeled block need to bear a label

error[E0696]: `continue` pointing to a labeled block
--> $DIR/label_break_value_continue.rs:13:9
|
Expand All @@ -13,6 +7,12 @@ LL | | continue 'b;
LL | | }
| |_____- labeled block the `continue` points to

error[E0695]: unlabeled `continue` inside of a labeled block
--> $DIR/label_break_value_continue.rs:6:9
|
LL | continue;
| ^^^^^^^^ `continue` statements that would diverge to or through a labeled block need to bear a label

error[E0695]: unlabeled `continue` inside of a labeled block
--> $DIR/label_break_value_continue.rs:21:13
|
Expand Down
29 changes: 29 additions & 0 deletions tests/ui/lowering/cont-in-block/cont-in-fn-issue-113379.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#![allow(incomplete_features)]
#![feature(adt_const_params)]

trait Trait<const S: usize> {}

struct Bug<T>
where
T: Trait<
{
'b: {
//~^ ERROR [E0308]
continue 'b; //~ ERROR [E0696]
}
},
>,
{
t: T,
}

fn f() -> impl Sized {
'b: {
continue 'b;
//~^ ERROR [E0696]
}
}

fn main() {
f();
}
33 changes: 33 additions & 0 deletions tests/ui/lowering/cont-in-block/cont-in-fn-issue-113379.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
error[E0696]: `continue` pointing to a labeled block
--> $DIR/cont-in-fn-issue-113379.rs:12:17
|
LL | / 'b: {
LL | |
LL | | continue 'b;
| | ^^^^^^^^^^^ labeled blocks cannot be `continue`'d
LL | | }
| |_____________- labeled block the `continue` points to

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

error[E0308]: mismatched types
--> $DIR/cont-in-fn-issue-113379.rs:10:13
|
LL | / 'b: {
LL | |
LL | | continue 'b;
LL | | }
| |_____________^ expected `usize`, found `()`

error: aborting due to 3 previous errors

Some errors have detailed explanations: E0308, E0696.
For more information about an error, try `rustc --explain E0308`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
fn main() {
match () {
_ => 'b: {
continue 'b;
//~^ ERROR [E0696]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error[E0696]: `continue` pointing to a labeled block
--> $DIR/cont-in-match-arm-issue-121623.rs:4: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`.

0 comments on commit 60ed3d1

Please sign in to comment.