@@ -1214,19 +1214,117 @@ pub fn arrayalloca(cx: &Block, ty: Type, v: ValueRef) -> ValueRef {
12141214pub fn make_return_slot_pointer ( fcx : & FunctionContext , output_type : ty:: t ) -> ValueRef {
12151215 let lloutputtype = type_of:: type_of ( fcx. ccx , output_type) ;
12161216
1217- // Let's create the stack slot
1218- let slot = AllocaFcx ( fcx, lloutputtype. ptr_to ( ) , "llretslotptr" ) ;
1217+ // We create an alloca to hold a pointer of type `output_type`
1218+ // which will hold the pointer to the right alloca which has the
1219+ // final ret value
1220+ if fcx. needs_ret_allocas {
1221+ // Let's create the stack slot
1222+ let slot = AllocaFcx ( fcx, lloutputtype. ptr_to ( ) , "llretslotptr" ) ;
12191223
1220- // and if we're using an out pointer, then store that in our newly made slot
1221- if type_of:: return_uses_outptr ( fcx. ccx , output_type) {
1222- let outptr = get_param ( fcx. llfn , 0 ) ;
1224+ // and if we're using an out pointer, then store that in our newly made slot
1225+ if type_of:: return_uses_outptr ( fcx. ccx , output_type) {
1226+ let outptr = get_param ( fcx. llfn , 0 ) ;
12231227
1224- let b = fcx. ccx . builder ( ) ;
1225- b. position_before ( fcx. alloca_insert_pt . get ( ) . unwrap ( ) ) ;
1226- b. store ( outptr, slot) ;
1228+ let b = fcx. ccx . builder ( ) ;
1229+ b. position_before ( fcx. alloca_insert_pt . get ( ) . unwrap ( ) ) ;
1230+ b. store ( outptr, slot) ;
1231+ }
1232+
1233+ slot
1234+
1235+ // But if there are no nested returns, we skip the indirection and have a single
1236+ // retslot
1237+ } else {
1238+ if type_of:: return_uses_outptr ( fcx. ccx , output_type) {
1239+ get_param ( fcx. llfn , 0 )
1240+ } else {
1241+ AllocaFcx ( fcx, lloutputtype, "sret_slot" )
1242+ }
12271243 }
1244+ }
12281245
1229- slot
1246+ struct CheckForNestedReturnsVisitor {
1247+ found : bool
1248+ }
1249+
1250+ impl Visitor < bool > for CheckForNestedReturnsVisitor {
1251+ fn visit_expr ( & mut self , e : & ast:: Expr , in_return : bool ) {
1252+ match e. node {
1253+ ast:: ExprRet ( ..) if in_return => {
1254+ self . found = true ;
1255+ return ;
1256+ }
1257+ ast:: ExprRet ( ..) => visit:: walk_expr ( self , e, true ) ,
1258+ _ => visit:: walk_expr ( self , e, in_return)
1259+ }
1260+ }
1261+ }
1262+
1263+ fn has_nested_returns ( tcx : & ty:: ctxt , id : ast:: NodeId ) -> bool {
1264+ match tcx. map . find ( id) {
1265+ Some ( ast_map:: NodeItem ( i) ) => {
1266+ match i. node {
1267+ ast:: ItemFn ( _, _, _, _, blk) => {
1268+ let mut explicit = CheckForNestedReturnsVisitor { found : false } ;
1269+ let mut implicit = CheckForNestedReturnsVisitor { found : false } ;
1270+ visit:: walk_item ( & mut explicit, & * i, false ) ;
1271+ visit:: walk_expr_opt ( & mut implicit, blk. expr , true ) ;
1272+ explicit. found || implicit. found
1273+ }
1274+ _ => tcx. sess . bug ( "unexpected item variant in has_nested_returns" )
1275+ }
1276+ }
1277+ Some ( ast_map:: NodeTraitMethod ( trait_method) ) => {
1278+ match * trait_method {
1279+ ast:: Provided ( m) => {
1280+ match m. node {
1281+ ast:: MethDecl ( _, _, _, _, _, _, blk, _) => {
1282+ let mut explicit = CheckForNestedReturnsVisitor { found : false } ;
1283+ let mut implicit = CheckForNestedReturnsVisitor { found : false } ;
1284+ visit:: walk_method_helper ( & mut explicit, & * m, false ) ;
1285+ visit:: walk_expr_opt ( & mut implicit, blk. expr , true ) ;
1286+ explicit. found || implicit. found
1287+ }
1288+ ast:: MethMac ( _) => tcx. sess . bug ( "unexpanded macro" )
1289+ }
1290+ }
1291+ ast:: Required ( _) => tcx. sess . bug ( "unexpected variant: required trait method in \
1292+ has_nested_returns")
1293+ }
1294+ }
1295+ Some ( ast_map:: NodeMethod ( m) ) => {
1296+ match m. node {
1297+ ast:: MethDecl ( _, _, _, _, _, _, blk, _) => {
1298+ let mut explicit = CheckForNestedReturnsVisitor { found : false } ;
1299+ let mut implicit = CheckForNestedReturnsVisitor { found : false } ;
1300+ visit:: walk_method_helper ( & mut explicit, & * m, false ) ;
1301+ visit:: walk_expr_opt ( & mut implicit, blk. expr , true ) ;
1302+ explicit. found || implicit. found
1303+ }
1304+ ast:: MethMac ( _) => tcx. sess . bug ( "unexpanded macro" )
1305+ }
1306+ }
1307+ Some ( ast_map:: NodeExpr ( e) ) => {
1308+ match e. node {
1309+ ast:: ExprFnBlock ( _, blk) | ast:: ExprProc ( _, blk) | ast:: ExprUnboxedFn ( _, blk) => {
1310+ let mut explicit = CheckForNestedReturnsVisitor { found : false } ;
1311+ let mut implicit = CheckForNestedReturnsVisitor { found : false } ;
1312+ visit:: walk_expr ( & mut explicit, & * e, false ) ;
1313+ visit:: walk_expr_opt ( & mut implicit, blk. expr , true ) ;
1314+ explicit. found || implicit. found
1315+ }
1316+ _ => tcx. sess . bug ( "unexpected expr variant in has_nested_returns" )
1317+ }
1318+ }
1319+
1320+ Some ( ast_map:: NodeVariant ( ..) ) | Some ( ast_map:: NodeStructCtor ( ..) ) => false ,
1321+
1322+ // glue, shims, etc
1323+ None if id == ast:: DUMMY_NODE_ID => false ,
1324+
1325+ _ => tcx. sess . bug ( format ! ( "unexpected variant in has_nested_returns: {}" ,
1326+ tcx. map. path_to_string( id) ) . as_slice ( ) )
1327+ }
12301328}
12311329
12321330// NB: must keep 4 fns in sync:
@@ -1261,13 +1359,15 @@ pub fn new_fn_ctxt<'a>(ccx: &'a CrateContext,
12611359 let substd_output_type = output_type. substp ( ccx. tcx ( ) , param_substs) ;
12621360 let uses_outptr = type_of:: return_uses_outptr ( ccx, substd_output_type) ;
12631361 let debug_context = debuginfo:: create_function_debug_context ( ccx, id, param_substs, llfndecl) ;
1362+ let nested_returns = has_nested_returns ( ccx. tcx ( ) , id) ;
12641363
12651364 let mut fcx = FunctionContext {
12661365 llfn : llfndecl,
12671366 llenv : None ,
12681367 llretslotptr : Cell :: new ( None ) ,
12691368 alloca_insert_pt : Cell :: new ( None ) ,
12701369 llreturn : Cell :: new ( None ) ,
1370+ needs_ret_allocas : nested_returns,
12711371 personality : Cell :: new ( None ) ,
12721372 caller_expects_out_pointer : uses_outptr,
12731373 llargs : RefCell :: new ( NodeMap :: new ( ) ) ,
@@ -1540,11 +1640,16 @@ pub fn finish_fn<'a>(fcx: &'a FunctionContext<'a>,
15401640
15411641// Builds the return block for a function.
15421642pub fn build_return_block ( fcx : & FunctionContext , ret_cx : & Block , retty : ty:: t ) {
1543- if fcx. llretslotptr . get ( ) . is_none ( ) {
1643+ if fcx. llretslotptr . get ( ) . is_none ( ) ||
1644+ ( !fcx. needs_ret_allocas && fcx. caller_expects_out_pointer ) {
15441645 return RetVoid ( ret_cx) ;
15451646 }
15461647
1547- let retslot = Load ( ret_cx, fcx. llretslotptr . get ( ) . unwrap ( ) ) ;
1648+ let retslot = if fcx. needs_ret_allocas {
1649+ Load ( ret_cx, fcx. llretslotptr . get ( ) . unwrap ( ) )
1650+ } else {
1651+ fcx. llretslotptr . get ( ) . unwrap ( )
1652+ } ;
15481653 let retptr = Value ( retslot) ;
15491654 match retptr. get_dominating_store ( ret_cx) {
15501655 // If there's only a single store to the ret slot, we can directly return
@@ -1678,7 +1783,7 @@ pub fn trans_closure(ccx: &CrateContext,
16781783 debuginfo:: start_emitting_source_locations( & fcx) ;
16791784
16801785 let dest = match fcx. llretslotptr. get( ) {
1681- Some ( _) => expr:: SaveIn ( alloca ( bcx, type_of :: type_of ( bcx . ccx ( ) , block_ty) , "iret_slot" ) ) ,
1786+ Some ( _) => expr:: SaveIn ( fcx . get_ret_slot ( bcx, block_ty, "iret_slot" ) ) ,
16821787 None => {
16831788 assert ! ( type_is_zero_size( bcx. ccx( ) , block_ty) ) ;
16841789 expr:: Ignore
@@ -1692,7 +1797,7 @@ pub fn trans_closure(ccx: &CrateContext,
16921797 bcx = controlflow:: trans_block( bcx, body, dest) ;
16931798
16941799 match dest {
1695- expr : : SaveIn ( slot) => {
1800+ expr : : SaveIn ( slot) if fcx . needs_ret_allocas => {
16961801 Store ( bcx, slot, fcx. llretslotptr. get( ) . unwrap( ) ) ;
16971802 }
16981803 _ => { }
@@ -1862,12 +1967,14 @@ fn trans_enum_variant_or_tuple_like_struct(ccx: &CrateContext,
18621967 param_substs, None , & arena, TranslateItems ) ;
18631968 let bcx = init_function( & fcx, false, result_ty) ;
18641969
1970+ assert!( !fcx. needs_ret_allocas) ;
1971+
18651972 let arg_tys = ty:: ty_fn_args( ctor_ty) ;
18661973
18671974 let arg_datums = create_datums_for_fn_args( & fcx, arg_tys. as_slice( ) ) ;
18681975
18691976 if !type_is_zero_size( fcx. ccx, result_ty) {
1870- let dest = alloca ( bcx, type_of :: type_of ( bcx . ccx ( ) , result_ty) , "eret_slot") ;
1977+ let dest = fcx . get_ret_slot ( bcx, result_ty, "eret_slot") ;
18711978 let repr = adt:: represent_type( ccx, result_ty) ;
18721979 for ( i, arg_datum) in arg_datums. move_iter( ) . enumerate( ) {
18731980 let lldestptr = adt:: trans_field_ptr( bcx,
@@ -1878,7 +1985,6 @@ fn trans_enum_variant_or_tuple_like_struct(ccx: &CrateContext,
18781985 arg_datum. store_to( bcx, lldestptr) ;
18791986 }
18801987 adt:: trans_set_discr( bcx, & * repr, dest, disr) ;
1881- Store ( bcx, dest, fcx. llretslotptr. get( ) . unwrap( ) ) ;
18821988 }
18831989
18841990 finish_fn( & fcx, bcx, result_ty) ;
0 commit comments