@@ -38,6 +38,7 @@ use llvm::{BasicBlockRef, Linkage, ValueRef, Vector, get_param};
38
38
use llvm;
39
39
use metadata:: { csearch, encoder, loader} ;
40
40
use middle:: astencode;
41
+ use middle:: cfg;
41
42
use middle:: lang_items:: { LangItem , ExchangeMallocFnLangItem , StartFnLangItem } ;
42
43
use middle:: subst;
43
44
use middle:: weak_lang_items;
@@ -1306,47 +1307,33 @@ pub fn make_return_slot_pointer<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
1306
1307
}
1307
1308
}
1308
1309
1309
- struct CheckForNestedReturnsVisitor {
1310
+ struct FindNestedReturn {
1310
1311
found : bool ,
1311
- in_return : bool
1312
1312
}
1313
1313
1314
- impl CheckForNestedReturnsVisitor {
1315
- fn explicit ( ) -> CheckForNestedReturnsVisitor {
1316
- CheckForNestedReturnsVisitor { found : false , in_return : false }
1317
- }
1318
- fn implicit ( ) -> CheckForNestedReturnsVisitor {
1319
- CheckForNestedReturnsVisitor { found : false , in_return : true }
1314
+ impl FindNestedReturn {
1315
+ fn new ( ) -> FindNestedReturn {
1316
+ FindNestedReturn { found : false }
1320
1317
}
1321
1318
}
1322
1319
1323
- impl < ' v > Visitor < ' v > for CheckForNestedReturnsVisitor {
1320
+ impl < ' v > Visitor < ' v > for FindNestedReturn {
1324
1321
fn visit_expr ( & mut self , e : & ast:: Expr ) {
1325
1322
match e. node {
1326
1323
ast:: ExprRet ( ..) => {
1327
- if self . in_return {
1328
- self . found = true ;
1329
- } else {
1330
- self . in_return = true ;
1331
- visit:: walk_expr ( self , e) ;
1332
- self . in_return = false ;
1333
- }
1324
+ self . found = true ;
1334
1325
}
1335
1326
_ => visit:: walk_expr ( self , e)
1336
1327
}
1337
1328
}
1338
1329
}
1339
1330
1340
- fn has_nested_returns ( tcx : & ty:: ctxt , id : ast:: NodeId ) -> bool {
1341
- match tcx. map . find ( id) {
1331
+ fn build_cfg ( tcx : & ty:: ctxt , id : ast:: NodeId ) -> ( ast :: NodeId , Option < cfg :: CFG > ) {
1332
+ let blk = match tcx. map . find ( id) {
1342
1333
Some ( ast_map:: NodeItem ( i) ) => {
1343
1334
match i. node {
1344
1335
ast:: ItemFn ( _, _, _, _, ref blk) => {
1345
- let mut explicit = CheckForNestedReturnsVisitor :: explicit ( ) ;
1346
- let mut implicit = CheckForNestedReturnsVisitor :: implicit ( ) ;
1347
- visit:: walk_item ( & mut explicit, & * i) ;
1348
- visit:: walk_expr_opt ( & mut implicit, & blk. expr ) ;
1349
- explicit. found || implicit. found
1336
+ blk
1350
1337
}
1351
1338
_ => tcx. sess . bug ( "unexpected item variant in has_nested_returns" )
1352
1339
}
@@ -1356,11 +1343,7 @@ fn has_nested_returns(tcx: &ty::ctxt, id: ast::NodeId) -> bool {
1356
1343
ast:: ProvidedMethod ( ref m) => {
1357
1344
match m. node {
1358
1345
ast:: MethDecl ( _, _, _, _, _, _, ref blk, _) => {
1359
- let mut explicit = CheckForNestedReturnsVisitor :: explicit ( ) ;
1360
- let mut implicit = CheckForNestedReturnsVisitor :: implicit ( ) ;
1361
- visit:: walk_method_helper ( & mut explicit, & * * m) ;
1362
- visit:: walk_expr_opt ( & mut implicit, & blk. expr ) ;
1363
- explicit. found || implicit. found
1346
+ blk
1364
1347
}
1365
1348
ast:: MethMac ( _) => tcx. sess . bug ( "unexpanded macro" )
1366
1349
}
@@ -1380,11 +1363,7 @@ fn has_nested_returns(tcx: &ty::ctxt, id: ast::NodeId) -> bool {
1380
1363
ast:: MethodImplItem ( ref m) => {
1381
1364
match m. node {
1382
1365
ast:: MethDecl ( _, _, _, _, _, _, ref blk, _) => {
1383
- let mut explicit = CheckForNestedReturnsVisitor :: explicit ( ) ;
1384
- let mut implicit = CheckForNestedReturnsVisitor :: implicit ( ) ;
1385
- visit:: walk_method_helper ( & mut explicit, & * * m) ;
1386
- visit:: walk_expr_opt ( & mut implicit, & blk. expr ) ;
1387
- explicit. found || implicit. found
1366
+ blk
1388
1367
}
1389
1368
ast:: MethMac ( _) => tcx. sess . bug ( "unexpanded macro" )
1390
1369
}
@@ -1398,24 +1377,58 @@ fn has_nested_returns(tcx: &ty::ctxt, id: ast::NodeId) -> bool {
1398
1377
Some ( ast_map:: NodeExpr ( e) ) => {
1399
1378
match e. node {
1400
1379
ast:: ExprClosure ( _, _, _, ref blk) => {
1401
- let mut explicit = CheckForNestedReturnsVisitor :: explicit ( ) ;
1402
- let mut implicit = CheckForNestedReturnsVisitor :: implicit ( ) ;
1403
- visit:: walk_expr ( & mut explicit, e) ;
1404
- visit:: walk_expr_opt ( & mut implicit, & blk. expr ) ;
1405
- explicit. found || implicit. found
1380
+ blk
1406
1381
}
1407
1382
_ => tcx. sess . bug ( "unexpected expr variant in has_nested_returns" )
1408
1383
}
1409
1384
}
1410
-
1411
- Some ( ast_map:: NodeVariant ( ..) ) | Some ( ast_map :: NodeStructCtor ( .. ) ) => false ,
1385
+ Some ( ast_map :: NodeVariant ( .. ) ) |
1386
+ Some ( ast_map:: NodeStructCtor ( ..) ) => return ( ast :: DUMMY_NODE_ID , None ) ,
1412
1387
1413
1388
// glue, shims, etc
1414
- None if id == ast:: DUMMY_NODE_ID => false ,
1389
+ None if id == ast:: DUMMY_NODE_ID => return ( ast :: DUMMY_NODE_ID , None ) ,
1415
1390
1416
1391
_ => tcx. sess . bug ( format ! ( "unexpected variant in has_nested_returns: {}" ,
1417
1392
tcx. map. path_to_string( id) ) . as_slice ( ) )
1393
+ } ;
1394
+
1395
+ ( blk. id , Some ( cfg:: CFG :: new ( tcx, & * * blk) ) )
1396
+ }
1397
+
1398
+ // Checks for the presence of "nested returns" in a function.
1399
+ // Nested returns are when the inner expression of a return expression
1400
+ // (the 'expr' in 'return expr') contains a return expression. Only cases
1401
+ // where the outer return is actually reachable are considered. Implicit
1402
+ // returns from the end of blocks are considered as well.
1403
+ //
1404
+ // This check is needed to handle the case where the inner expression is
1405
+ // part of a larger expression that may have already partially-filled the
1406
+ // return slot alloca. This can cause errors related to clean-up due to
1407
+ // the clobbering of the existing value in the return slot.
1408
+ fn has_nested_returns ( tcx : & ty:: ctxt , cfg : & cfg:: CFG , blk_id : ast:: NodeId ) -> bool {
1409
+ for n in cfg. graph . depth_traverse ( cfg. entry ) {
1410
+ match tcx. map . find ( n. id ) {
1411
+ Some ( ast_map:: NodeExpr ( ex) ) => {
1412
+ if let ast:: ExprRet ( Some ( ref ret_expr) ) = ex. node {
1413
+ let mut visitor = FindNestedReturn :: new ( ) ;
1414
+ visit:: walk_expr ( & mut visitor, & * * ret_expr) ;
1415
+ if visitor. found {
1416
+ return true ;
1417
+ }
1418
+ }
1419
+ }
1420
+ Some ( ast_map:: NodeBlock ( blk) ) if blk. id == blk_id => {
1421
+ let mut visitor = FindNestedReturn :: new ( ) ;
1422
+ visit:: walk_expr_opt ( & mut visitor, & blk. expr ) ;
1423
+ if visitor. found {
1424
+ return true ;
1425
+ }
1426
+ }
1427
+ _ => { }
1428
+ }
1418
1429
}
1430
+
1431
+ return false ;
1419
1432
}
1420
1433
1421
1434
// NB: must keep 4 fns in sync:
@@ -1454,7 +1467,12 @@ pub fn new_fn_ctxt<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
1454
1467
ty:: FnDiverging => false
1455
1468
} ;
1456
1469
let debug_context = debuginfo:: create_function_debug_context ( ccx, id, param_substs, llfndecl) ;
1457
- let nested_returns = has_nested_returns ( ccx. tcx ( ) , id) ;
1470
+ let ( blk_id, cfg) = build_cfg ( ccx. tcx ( ) , id) ;
1471
+ let nested_returns = if let Some ( ref cfg) = cfg {
1472
+ has_nested_returns ( ccx. tcx ( ) , cfg, blk_id)
1473
+ } else {
1474
+ false
1475
+ } ;
1458
1476
1459
1477
let mut fcx = FunctionContext {
1460
1478
llfn : llfndecl,
@@ -1473,7 +1491,8 @@ pub fn new_fn_ctxt<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
1473
1491
block_arena : block_arena,
1474
1492
ccx : ccx,
1475
1493
debug_context : debug_context,
1476
- scopes : RefCell :: new ( Vec :: new ( ) )
1494
+ scopes : RefCell :: new ( Vec :: new ( ) ) ,
1495
+ cfg : cfg
1477
1496
} ;
1478
1497
1479
1498
if has_env {
0 commit comments