@@ -50,6 +50,7 @@ use util::nodemap::{DefIdMap, NodeMap, FxHashMap};
50
50
51
51
use std:: collections:: BTreeMap ;
52
52
use std:: iter;
53
+ use std:: mem;
53
54
54
55
use syntax:: attr;
55
56
use syntax:: ast:: * ;
@@ -79,6 +80,9 @@ pub struct LoweringContext<'a> {
79
80
impl_items : BTreeMap < hir:: ImplItemId , hir:: ImplItem > ,
80
81
bodies : FxHashMap < hir:: BodyId , hir:: Body > ,
81
82
83
+ loop_scopes : Vec < NodeId > ,
84
+ is_in_loop_condition : bool ,
85
+
82
86
type_def_lifetime_params : DefIdMap < usize > ,
83
87
}
84
88
@@ -112,6 +116,8 @@ pub fn lower_crate(sess: &Session,
112
116
trait_items : BTreeMap :: new ( ) ,
113
117
impl_items : BTreeMap :: new ( ) ,
114
118
bodies : FxHashMap ( ) ,
119
+ loop_scopes : Vec :: new ( ) ,
120
+ is_in_loop_condition : false ,
115
121
type_def_lifetime_params : DefIdMap ( ) ,
116
122
} . lower_crate ( krate)
117
123
}
@@ -244,6 +250,55 @@ impl<'a> LoweringContext<'a> {
244
250
span
245
251
}
246
252
253
+ fn with_loop_scope < T , F > ( & mut self , loop_id : NodeId , f : F ) -> T
254
+ where F : FnOnce ( & mut LoweringContext ) -> T
255
+ {
256
+ // We're no longer in the base loop's condition; we're in another loop.
257
+ let was_in_loop_condition = self . is_in_loop_condition ;
258
+ self . is_in_loop_condition = false ;
259
+
260
+ let len = self . loop_scopes . len ( ) ;
261
+ self . loop_scopes . push ( loop_id) ;
262
+
263
+ let result = f ( self ) ;
264
+ assert_eq ! ( len + 1 , self . loop_scopes. len( ) ,
265
+ "Loop scopes should be added and removed in stack order" ) ;
266
+
267
+ self . loop_scopes . pop ( ) . unwrap ( ) ;
268
+
269
+ self . is_in_loop_condition = was_in_loop_condition;
270
+
271
+ result
272
+ }
273
+
274
+ fn with_loop_condition_scope < T , F > ( & mut self , f : F ) -> T
275
+ where F : FnOnce ( & mut LoweringContext ) -> T
276
+ {
277
+ let was_in_loop_condition = self . is_in_loop_condition ;
278
+ self . is_in_loop_condition = true ;
279
+
280
+ let result = f ( self ) ;
281
+
282
+ self . is_in_loop_condition = was_in_loop_condition;
283
+
284
+ result
285
+ }
286
+
287
+ fn with_new_loop_scopes < T , F > ( & mut self , f : F ) -> T
288
+ where F : FnOnce ( & mut LoweringContext ) -> T
289
+ {
290
+ let was_in_loop_condition = self . is_in_loop_condition ;
291
+ self . is_in_loop_condition = false ;
292
+
293
+ let loop_scopes = mem:: replace ( & mut self . loop_scopes , Vec :: new ( ) ) ;
294
+ let result = f ( self ) ;
295
+ mem:: replace ( & mut self . loop_scopes , loop_scopes) ;
296
+
297
+ self . is_in_loop_condition = was_in_loop_condition;
298
+
299
+ result
300
+ }
301
+
247
302
fn with_parent_def < T , F > ( & mut self , parent_id : NodeId , f : F ) -> T
248
303
where F : FnOnce ( & mut LoweringContext ) -> T
249
304
{
@@ -271,17 +326,24 @@ impl<'a> LoweringContext<'a> {
271
326
o_id. map ( |sp_ident| respan ( sp_ident. span , sp_ident. node . name ) )
272
327
}
273
328
274
- fn lower_label ( & mut self , id : NodeId , label : Option < Spanned < Ident > > ) -> Option < hir:: Label > {
275
- label. map ( |sp_ident| {
276
- hir:: Label {
277
- span : sp_ident. span ,
278
- name : sp_ident. node . name ,
279
- loop_id : match self . expect_full_def ( id) {
280
- Def :: Label ( loop_id) => loop_id,
281
- _ => DUMMY_NODE_ID
329
+ fn lower_destination ( & mut self , destination : Option < ( NodeId , Spanned < Ident > ) > )
330
+ -> hir:: Destination
331
+ {
332
+ match destination {
333
+ Some ( ( id, label_ident) ) => hir:: Destination {
334
+ ident : Some ( label_ident) ,
335
+ loop_id : if let Def :: Label ( loop_id) = self . expect_full_def ( id) {
336
+ hir:: LoopIdResult :: Ok ( loop_id)
337
+ } else {
338
+ hir:: LoopIdResult :: Err ( hir:: LoopIdError :: UnresolvedLabel )
282
339
}
340
+ } ,
341
+ None => hir:: Destination {
342
+ ident : None ,
343
+ loop_id : self . loop_scopes . last ( ) . map ( |innermost_loop_id| Ok ( * innermost_loop_id) )
344
+ . unwrap_or ( Err ( hir:: LoopIdError :: OutsideLoopScope ) ) . into ( )
283
345
}
284
- } )
346
+ }
285
347
}
286
348
287
349
fn lower_attrs ( & mut self , attrs : & Vec < Attribute > ) -> hir:: HirVec < Attribute > {
@@ -992,15 +1054,17 @@ impl<'a> LoweringContext<'a> {
992
1054
self . record_body ( value, None ) )
993
1055
}
994
1056
ItemKind :: Fn ( ref decl, unsafety, constness, abi, ref generics, ref body) => {
995
- let body = self . lower_block ( body) ;
996
- let body = self . expr_block ( body, ThinVec :: new ( ) ) ;
997
- let body_id = self . record_body ( body, Some ( decl) ) ;
998
- hir:: ItemFn ( self . lower_fn_decl ( decl) ,
999
- self . lower_unsafety ( unsafety) ,
1000
- self . lower_constness ( constness) ,
1001
- abi,
1002
- self . lower_generics ( generics) ,
1003
- body_id)
1057
+ self . with_new_loop_scopes ( |this| {
1058
+ let body = this. lower_block ( body) ;
1059
+ let body = this. expr_block ( body, ThinVec :: new ( ) ) ;
1060
+ let body_id = this. record_body ( body, Some ( decl) ) ;
1061
+ hir:: ItemFn ( this. lower_fn_decl ( decl) ,
1062
+ this. lower_unsafety ( unsafety) ,
1063
+ this. lower_constness ( constness) ,
1064
+ abi,
1065
+ this. lower_generics ( generics) ,
1066
+ body_id)
1067
+ } )
1004
1068
}
1005
1069
ItemKind :: Mod ( ref m) => hir:: ItemMod ( self . lower_mod ( m) ) ,
1006
1070
ItemKind :: ForeignMod ( ref nm) => hir:: ItemForeignMod ( self . lower_foreign_mod ( nm) ) ,
@@ -1562,26 +1626,32 @@ impl<'a> LoweringContext<'a> {
1562
1626
hir:: ExprIf ( P ( self . lower_expr ( cond) ) , self . lower_block ( blk) , else_opt)
1563
1627
}
1564
1628
ExprKind :: While ( ref cond, ref body, opt_ident) => {
1565
- hir:: ExprWhile ( P ( self . lower_expr ( cond) ) , self . lower_block ( body) ,
1566
- self . lower_opt_sp_ident ( opt_ident) )
1629
+ self . with_loop_scope ( e. id , |this|
1630
+ hir:: ExprWhile (
1631
+ this. with_loop_condition_scope ( |this| P ( this. lower_expr ( cond) ) ) ,
1632
+ this. lower_block ( body) ,
1633
+ this. lower_opt_sp_ident ( opt_ident) ) )
1567
1634
}
1568
1635
ExprKind :: Loop ( ref body, opt_ident) => {
1569
- hir:: ExprLoop ( self . lower_block ( body) ,
1570
- self . lower_opt_sp_ident ( opt_ident) ,
1571
- hir:: LoopSource :: Loop )
1636
+ self . with_loop_scope ( e. id , |this|
1637
+ hir:: ExprLoop ( this. lower_block ( body) ,
1638
+ this. lower_opt_sp_ident ( opt_ident) ,
1639
+ hir:: LoopSource :: Loop ) )
1572
1640
}
1573
1641
ExprKind :: Match ( ref expr, ref arms) => {
1574
1642
hir:: ExprMatch ( P ( self . lower_expr ( expr) ) ,
1575
1643
arms. iter ( ) . map ( |x| self . lower_arm ( x) ) . collect ( ) ,
1576
1644
hir:: MatchSource :: Normal )
1577
1645
}
1578
1646
ExprKind :: Closure ( capture_clause, ref decl, ref body, fn_decl_span) => {
1579
- self . with_parent_def ( e. id , |this| {
1580
- let expr = this. lower_expr ( body) ;
1581
- hir:: ExprClosure ( this. lower_capture_clause ( capture_clause) ,
1582
- this. lower_fn_decl ( decl) ,
1583
- this. record_body ( expr, Some ( decl) ) ,
1584
- fn_decl_span)
1647
+ self . with_new_loop_scopes ( |this| {
1648
+ this. with_parent_def ( e. id , |this| {
1649
+ let expr = this. lower_expr ( body) ;
1650
+ hir:: ExprClosure ( this. lower_capture_clause ( capture_clause) ,
1651
+ this. lower_fn_decl ( decl) ,
1652
+ this. record_body ( expr, Some ( decl) ) ,
1653
+ fn_decl_span)
1654
+ } )
1585
1655
} )
1586
1656
}
1587
1657
ExprKind :: Block ( ref blk) => hir:: ExprBlock ( self . lower_block ( blk) ) ,
@@ -1660,10 +1730,29 @@ impl<'a> LoweringContext<'a> {
1660
1730
hir:: ExprPath ( self . lower_qpath ( e. id , qself, path, ParamMode :: Optional ) )
1661
1731
}
1662
1732
ExprKind :: Break ( opt_ident, ref opt_expr) => {
1663
- hir:: ExprBreak ( self . lower_label ( e. id , opt_ident) ,
1664
- opt_expr. as_ref ( ) . map ( |x| P ( self . lower_expr ( x) ) ) )
1733
+ let label_result = if self . is_in_loop_condition && opt_ident. is_none ( ) {
1734
+ hir:: Destination {
1735
+ ident : opt_ident,
1736
+ loop_id : Err ( hir:: LoopIdError :: UnlabeledCfInWhileCondition ) . into ( ) ,
1737
+ }
1738
+ } else {
1739
+ self . lower_destination ( opt_ident. map ( |ident| ( e. id , ident) ) )
1740
+ } ;
1741
+ hir:: ExprBreak (
1742
+ label_result,
1743
+ opt_expr. as_ref ( ) . map ( |x| P ( self . lower_expr ( x) ) ) )
1665
1744
}
1666
- ExprKind :: Continue ( opt_ident) => hir:: ExprAgain ( self . lower_label ( e. id , opt_ident) ) ,
1745
+ ExprKind :: Continue ( opt_ident) =>
1746
+ hir:: ExprAgain (
1747
+ if self . is_in_loop_condition && opt_ident. is_none ( ) {
1748
+ hir:: Destination {
1749
+ ident : opt_ident,
1750
+ loop_id : Err (
1751
+ hir:: LoopIdError :: UnlabeledCfInWhileCondition ) . into ( ) ,
1752
+ }
1753
+ } else {
1754
+ self . lower_destination ( opt_ident. map ( |ident| ( e. id , ident) ) )
1755
+ } ) ,
1667
1756
ExprKind :: Ret ( ref e) => hir:: ExprRet ( e. as_ref ( ) . map ( |x| P ( self . lower_expr ( x) ) ) ) ,
1668
1757
ExprKind :: InlineAsm ( ref asm) => {
1669
1758
let hir_asm = hir:: InlineAsm {
@@ -1804,9 +1893,16 @@ impl<'a> LoweringContext<'a> {
1804
1893
// }
1805
1894
// }
1806
1895
1896
+ // Note that the block AND the condition are evaluated in the loop scope.
1897
+ // This is done to allow `break` from inside the condition of the loop.
1898
+ let ( body, break_expr, sub_expr) = self . with_loop_scope ( e. id , |this| (
1899
+ this. lower_block ( body) ,
1900
+ this. expr_break ( e. span , ThinVec :: new ( ) ) ,
1901
+ this. with_loop_condition_scope ( |this| P ( this. lower_expr ( sub_expr) ) ) ,
1902
+ ) ) ;
1903
+
1807
1904
// `<pat> => <body>`
1808
1905
let pat_arm = {
1809
- let body = self . lower_block ( body) ;
1810
1906
let body_expr = P ( self . expr_block ( body, ThinVec :: new ( ) ) ) ;
1811
1907
let pat = self . lower_pat ( pat) ;
1812
1908
self . arm ( hir_vec ! [ pat] , body_expr)
@@ -1815,13 +1911,11 @@ impl<'a> LoweringContext<'a> {
1815
1911
// `_ => break`
1816
1912
let break_arm = {
1817
1913
let pat_under = self . pat_wild ( e. span ) ;
1818
- let break_expr = self . expr_break ( e. span , ThinVec :: new ( ) ) ;
1819
1914
self . arm ( hir_vec ! [ pat_under] , break_expr)
1820
1915
} ;
1821
1916
1822
1917
// `match <sub_expr> { ... }`
1823
1918
let arms = hir_vec ! [ pat_arm, break_arm] ;
1824
- let sub_expr = P ( self . lower_expr ( sub_expr) ) ;
1825
1919
let match_expr = self . expr ( e. span ,
1826
1920
hir:: ExprMatch ( sub_expr,
1827
1921
arms,
@@ -1863,7 +1957,7 @@ impl<'a> LoweringContext<'a> {
1863
1957
1864
1958
// `::std::option::Option::Some(<pat>) => <body>`
1865
1959
let pat_arm = {
1866
- let body_block = self . lower_block ( body) ;
1960
+ let body_block = self . with_loop_scope ( e . id , |this| this . lower_block ( body) ) ;
1867
1961
let body_expr = P ( self . expr_block ( body_block, ThinVec :: new ( ) ) ) ;
1868
1962
let pat = self . lower_pat ( pat) ;
1869
1963
let some_pat = self . pat_some ( e. span , pat) ;
@@ -1873,7 +1967,8 @@ impl<'a> LoweringContext<'a> {
1873
1967
1874
1968
// `::std::option::Option::None => break`
1875
1969
let break_arm = {
1876
- let break_expr = self . expr_break ( e. span , ThinVec :: new ( ) ) ;
1970
+ let break_expr = self . with_loop_scope ( e. id , |this|
1971
+ this. expr_break ( e. span , ThinVec :: new ( ) ) ) ;
1877
1972
let pat = self . pat_none ( e. span ) ;
1878
1973
self . arm ( hir_vec ! [ pat] , break_expr)
1879
1974
} ;
@@ -2151,7 +2246,8 @@ impl<'a> LoweringContext<'a> {
2151
2246
}
2152
2247
2153
2248
fn expr_break ( & mut self , span : Span , attrs : ThinVec < Attribute > ) -> P < hir:: Expr > {
2154
- P ( self . expr ( span, hir:: ExprBreak ( None , None ) , attrs) )
2249
+ let expr_break = hir:: ExprBreak ( self . lower_destination ( None ) , None ) ;
2250
+ P ( self . expr ( span, expr_break, attrs) )
2155
2251
}
2156
2252
2157
2253
fn expr_call ( & mut self , span : Span , e : P < hir:: Expr > , args : hir:: HirVec < hir:: Expr > )
0 commit comments