Skip to content

Commit 36905cf

Browse files
committed
Support labelled continue in typle_index for loops
The expansion creates a labelled loop with the same label as its enclosing block. This causes a warning during compilation that cannot be suppressed. Case 2 from rust-lang/rust#96296
1 parent a52351e commit 36905cf

File tree

6 files changed

+385
-28
lines changed

6 files changed

+385
-28
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "typle"
3-
version = "0.8.0"
3+
version = "0.8.1"
44
edition = "2021"
55
description = "Generic tuple bounds and transformations"
66
license = "MIT"

src/lib.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -321,15 +321,16 @@
321321
//! ```
322322
//! `func` will be called with 2 and 3, never with 1. The same is true for other places where typle
323323
//! indexes are introduced. For example in a `typle_for!` macro.
324-
//! - for loops using `typle_index!` do not support labelled continue.
324+
//! - A `continue` referencing a label on a `for` loop using `typle_index!` works but displays an
325+
//! [unsuppressible warning](https://github.com/rust-lang/rust/issues/31745) during compilation.
325326
//! ```rust ignore
326-
//! 'label: for typle_index!(i) in 2..=3 {
327+
//! // warning: label name `'cont` shadows a label name that is already in scope
328+
//! 'cont: for typle_index!(i) in 2..=3 {
327329
//! loop {
328330
//! if typle_const!(i == 2) {
329-
//! continue 'label; // compile error
330-
//! } else {
331-
//! break 'label; // works
331+
//! continue 'cont;
332332
//! }
333+
//! break;
333334
//! }
334335
//! }
335336
//! ```

src/specific.rs

Lines changed: 86 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::collections::HashMap;
1+
use std::collections::{HashMap, HashSet};
22
use std::ops::Range;
33

44
use proc_macro2::{Group, Ident, Span, TokenStream, TokenTree};
@@ -9,9 +9,10 @@ use syn::spanned::Spanned as _;
99
use syn::{
1010
parse2, parse_quote, token, AttrStyle, Attribute, Block, Expr, ExprArray, ExprBlock, ExprField,
1111
ExprLit, ExprRange, ExprTuple, Fields, FieldsNamed, FieldsUnnamed, GenericArgument,
12-
GenericParam, Generics, ImplItem, Index, Item, Lit, LitInt, Macro, MacroDelimiter, Member,
13-
Meta, Pat, Path, PathArguments, PathSegment, PredicateType, QSelf, RangeLimits, ReturnType,
14-
Stmt, Token, Type, TypeMacro, TypeParamBound, TypePath, TypeTuple, Variant, WherePredicate,
12+
GenericParam, Generics, ImplItem, Index, Item, Label, Lit, LitInt, Macro, MacroDelimiter,
13+
Member, Meta, Pat, Path, PathArguments, PathSegment, PredicateType, QSelf, RangeLimits,
14+
ReturnType, Stmt, Token, Type, TypeMacro, TypeParamBound, TypePath, TypeTuple, Variant,
15+
WherePredicate,
1516
};
1617

1718
use crate::constant::{evaluate_bool, evaluate_range, evaluate_usize};
@@ -42,6 +43,33 @@ impl Typle {
4243
#[derive(Default)]
4344
pub struct BlockState {
4445
unlabelled_control_flow: bool,
46+
labelled_continue: HashSet<Ident>,
47+
}
48+
49+
impl BlockState {
50+
// Propagate the labels from an inner loop into the state for this loop.
51+
// We exclude any unlabelled_control_flow as it is contained by the inner
52+
// loop. We also exclude any label attached to the inner loop.
53+
fn propagate(&mut self, inner: Self, label: Option<&Label>) {
54+
for ident in inner.labelled_continue {
55+
if let Some(label) = label {
56+
if label.name.ident == ident {
57+
continue;
58+
}
59+
}
60+
self.labelled_continue.insert(ident);
61+
}
62+
}
63+
64+
// Return whether this loop has any labelled continue that refers to this loop's label.
65+
fn has_labelled_continue<'a>(&self, label: Option<&'a Label>) -> Option<&'a Label> {
66+
if let Some(label) = label {
67+
if self.labelled_continue.contains(&label.name.ident) {
68+
return Some(label);
69+
}
70+
}
71+
None
72+
}
4573
}
4674

4775
#[derive(Clone)]
@@ -307,7 +335,15 @@ impl<'a> SpecificContext<'a> {
307335
}
308336
Expr::Continue(cont) => {
309337
self.replace_attrs(&mut cont.attrs);
310-
if cont.label.is_none() {
338+
match &cont.label {
339+
Some(lt) => {
340+
state.labelled_continue.insert(lt.ident.clone());
341+
}
342+
None => {
343+
state.unlabelled_control_flow = true;
344+
}
345+
}
346+
{
311347
state.unlabelled_control_flow = true;
312348
}
313349
}
@@ -359,18 +395,46 @@ impl<'a> SpecificContext<'a> {
359395
let mut context = self.clone();
360396
let mut stmts = Vec::new();
361397
context.constants.insert(pat_ident.clone(), 0);
362-
let mut unlabelled_control_flow = false;
398+
let mut check_for_break = false;
363399
for index in start..end {
364400
context.constants.get_mut(&pat_ident).map(|v| *v = index);
365401
let mut block = for_loop.body.clone();
366-
let mut state = BlockState::default();
367-
context.replace_block(&mut block, &mut state);
402+
let mut inner_state = BlockState::default();
403+
context.replace_block(&mut block, &mut inner_state);
368404
if block.stmts.is_empty() {
369405
continue;
370406
}
371-
if state.unlabelled_control_flow {
372-
if !unlabelled_control_flow {
373-
unlabelled_control_flow = true;
407+
if let Some(label) =
408+
inner_state.has_labelled_continue(for_loop.label.as_ref())
409+
{
410+
if !check_for_break {
411+
check_for_break = true;
412+
stmts.push(parse_quote! {
413+
let mut _typle_break = false;
414+
});
415+
}
416+
// If a labelled `continue` occurs inside the block then control
417+
// goes to the start of the loop with `_typle_break = true`
418+
// which causes the loop to be exited with `_typle_break = false`.
419+
// This label shadows the label on the outer block. If there is
420+
// a labelled break as well, then it will break from this loop
421+
// without setting _typle_break back to false.
422+
let stmt = parse_quote! {
423+
if ! _typle_break {
424+
#label loop {
425+
if _typle_break {
426+
_typle_break = false;
427+
break;
428+
}
429+
_typle_break = true;
430+
#block
431+
}
432+
}
433+
};
434+
stmts.push(stmt);
435+
} else if inner_state.unlabelled_control_flow {
436+
if !check_for_break {
437+
check_for_break = true;
374438
stmts.push(parse_quote! {
375439
let mut _typle_break = false;
376440
});
@@ -395,7 +459,7 @@ impl<'a> SpecificContext<'a> {
395459
};
396460
stmts.push(stmt);
397461
} else {
398-
if unlabelled_control_flow {
462+
if check_for_break {
399463
// If this block does not contain a break or continue, but
400464
// previous blocks did we need to check for the `break`
401465
// condition before running this block.
@@ -414,6 +478,7 @@ impl<'a> SpecificContext<'a> {
414478
));
415479
}
416480
}
481+
state.propagate(inner_state, for_loop.label.as_ref());
417482
}
418483
stmts.push(Stmt::Expr(
419484
Expr::Tuple(ExprTuple {
@@ -437,8 +502,9 @@ impl<'a> SpecificContext<'a> {
437502
}
438503
// Otherwise it is a standard for loop
439504
self.replace_pat(&mut for_loop.pat);
440-
let mut state = BlockState::default();
441-
self.replace_block(&mut for_loop.body, &mut state);
505+
let mut inner_state = BlockState::default();
506+
self.replace_block(&mut for_loop.body, &mut inner_state);
507+
state.propagate(inner_state, for_loop.label.as_ref());
442508
}
443509
Expr::Group(group) => {
444510
self.replace_attrs(&mut group.attrs);
@@ -525,8 +591,9 @@ impl<'a> SpecificContext<'a> {
525591
}
526592
Expr::Loop(r#loop) => {
527593
self.replace_attrs(&mut r#loop.attrs);
528-
let mut state = BlockState::default();
529-
self.replace_block(&mut r#loop.body, &mut state);
594+
let mut inner_state = BlockState::default();
595+
self.replace_block(&mut r#loop.body, &mut inner_state);
596+
state.propagate(inner_state, r#loop.label.as_ref());
530597
}
531598
Expr::Macro(r#macro) => {
532599
self.replace_attrs(&mut r#macro.attrs);
@@ -774,8 +841,9 @@ impl<'a> SpecificContext<'a> {
774841
Expr::While(r#while) => {
775842
self.replace_attrs(&mut r#while.attrs);
776843
self.replace_expr(&mut r#while.cond, state);
777-
let mut state = BlockState::default();
778-
self.replace_block(&mut r#while.body, &mut state);
844+
let mut inner_state = BlockState::default();
845+
self.replace_block(&mut r#while.body, &mut inner_state);
846+
state.propagate(inner_state, r#while.label.as_ref());
779847
}
780848
Expr::Yield(r#yield) => {
781849
self.replace_attrs(&mut r#yield.attrs);

tests/compile/for_loop.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,57 @@ impl<T: Tuple> Looper<T> {
4646
}
4747
}
4848
}
49+
50+
#[typle(Tuple for 4..=4)]
51+
pub fn do_continue<T: Tuple>(t: T) -> Vec<usize> {
52+
let mut output = Vec::new();
53+
for typle_index!(i) in 0..T::LEN {
54+
if typle_const!(i == 2) {
55+
continue;
56+
}
57+
output.push(i);
58+
}
59+
output
60+
}
61+
62+
#[typle(Tuple for 4..=4)]
63+
pub fn do_continue_labelled<T: Tuple>(t: T) -> Vec<usize> {
64+
let mut output = Vec::new();
65+
'label: for typle_index!(i) in 0..T::LEN {
66+
loop {
67+
if typle_const!(i == 2) {
68+
continue 'label;
69+
}
70+
output.push(i);
71+
break;
72+
}
73+
}
74+
output
75+
}
76+
77+
#[typle(Tuple for 4..=4)]
78+
pub fn do_break<T: Tuple>(t: T) -> Vec<usize> {
79+
let mut output = Vec::new();
80+
for typle_index!(i) in 0..T::LEN {
81+
if typle_const!(i == 2) {
82+
break;
83+
}
84+
output.push(i);
85+
}
86+
output
87+
}
88+
89+
#[typle(Tuple for 4..=4)]
90+
pub fn do_break_labelled<T: Tuple>(t: T) -> Vec<usize> {
91+
let mut output = Vec::new();
92+
'label: for typle_index!(i) in 0..T::LEN {
93+
loop {
94+
if typle_const!(i == 2) {
95+
break 'label;
96+
}
97+
output.push(i);
98+
break;
99+
}
100+
}
101+
output
102+
}

0 commit comments

Comments
 (0)