Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix nested returns. #16081

Merged
merged 6 commits into from
Aug 12, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/libcore/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,13 @@ extern "rust-intrinsic" {
/// ```
pub fn transmute<T,U>(e: T) -> U;

/// Gives the address for the return value of the enclosing function.
///
/// Using this instrinsic in a function that does not use an out pointer
/// will trigger a compiler error.
#[cfg(not(stage0))]
pub fn return_address() -> *const u8;

/// Returns `true` if a type requires drop glue.
pub fn needs_drop<T>() -> bool;

Expand Down
190 changes: 162 additions & 28 deletions src/librustc/middle/trans/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1122,7 +1122,7 @@ pub fn memcpy_ty(bcx: &Block, dst: ValueRef, src: ValueRef, t: ty::t) {
let llalign = llalign_of_min(ccx, llty);
call_memcpy(bcx, dst, src, llsz, llalign as u32);
} else {
Store(bcx, Load(bcx, src), dst);
store_ty(bcx, Load(bcx, src), dst, t);
}
}

Expand Down Expand Up @@ -1210,15 +1210,120 @@ pub fn arrayalloca(cx: &Block, ty: Type, v: ValueRef) -> ValueRef {
p
}

// Creates and returns space for, or returns the argument representing, the
// slot where the return value of the function must go.
pub fn make_return_pointer(fcx: &FunctionContext, output_type: ty::t)
-> ValueRef {
if type_of::return_uses_outptr(fcx.ccx, output_type) {
get_param(fcx.llfn, 0)
// Creates the alloca slot which holds the pointer to the slot for the final return value
pub fn make_return_slot_pointer(fcx: &FunctionContext, output_type: ty::t) -> ValueRef {
let lloutputtype = type_of::type_of(fcx.ccx, output_type);

// We create an alloca to hold a pointer of type `output_type`
// which will hold the pointer to the right alloca which has the
// final ret value
if fcx.needs_ret_allocas {
// Let's create the stack slot
let slot = AllocaFcx(fcx, lloutputtype.ptr_to(), "llretslotptr");

// and if we're using an out pointer, then store that in our newly made slot
if type_of::return_uses_outptr(fcx.ccx, output_type) {
let outptr = get_param(fcx.llfn, 0);

let b = fcx.ccx.builder();
b.position_before(fcx.alloca_insert_pt.get().unwrap());
b.store(outptr, slot);
}

slot

// But if there are no nested returns, we skip the indirection and have a single
// retslot
} else {
let lloutputtype = type_of::type_of(fcx.ccx, output_type);
AllocaFcx(fcx, lloutputtype, "__make_return_pointer")
if type_of::return_uses_outptr(fcx.ccx, output_type) {
get_param(fcx.llfn, 0)
} else {
AllocaFcx(fcx, lloutputtype, "sret_slot")
}
}
}

struct CheckForNestedReturnsVisitor {
found: bool
}

impl Visitor<bool> for CheckForNestedReturnsVisitor {
fn visit_expr(&mut self, e: &ast::Expr, in_return: bool) {
match e.node {
ast::ExprRet(..) if in_return => {
self.found = true;
return;
}
ast::ExprRet(..) => visit::walk_expr(self, e, true),
_ => visit::walk_expr(self, e, in_return)
}
}
}

fn has_nested_returns(tcx: &ty::ctxt, id: ast::NodeId) -> bool {
match tcx.map.find(id) {
Some(ast_map::NodeItem(i)) => {
match i.node {
ast::ItemFn(_, _, _, _, blk) => {
let mut explicit = CheckForNestedReturnsVisitor { found: false };
let mut implicit = CheckForNestedReturnsVisitor { found: false };
visit::walk_item(&mut explicit, &*i, false);
visit::walk_expr_opt(&mut implicit, blk.expr, true);
explicit.found || implicit.found
}
_ => tcx.sess.bug("unexpected item variant in has_nested_returns")
}
}
Some(ast_map::NodeTraitMethod(trait_method)) => {
match *trait_method {
ast::Provided(m) => {
match m.node {
ast::MethDecl(_, _, _, _, _, _, blk, _) => {
let mut explicit = CheckForNestedReturnsVisitor { found: false };
let mut implicit = CheckForNestedReturnsVisitor { found: false };
visit::walk_method_helper(&mut explicit, &*m, false);
visit::walk_expr_opt(&mut implicit, blk.expr, true);
explicit.found || implicit.found
}
ast::MethMac(_) => tcx.sess.bug("unexpanded macro")
}
}
ast::Required(_) => tcx.sess.bug("unexpected variant: required trait method in \
has_nested_returns")
}
}
Some(ast_map::NodeMethod(m)) => {
match m.node {
ast::MethDecl(_, _, _, _, _, _, blk, _) => {
let mut explicit = CheckForNestedReturnsVisitor { found: false };
let mut implicit = CheckForNestedReturnsVisitor { found: false };
visit::walk_method_helper(&mut explicit, &*m, false);
visit::walk_expr_opt(&mut implicit, blk.expr, true);
explicit.found || implicit.found
}
ast::MethMac(_) => tcx.sess.bug("unexpanded macro")
}
}
Some(ast_map::NodeExpr(e)) => {
match e.node {
ast::ExprFnBlock(_, blk) | ast::ExprProc(_, blk) | ast::ExprUnboxedFn(_, blk) => {
let mut explicit = CheckForNestedReturnsVisitor { found: false };
let mut implicit = CheckForNestedReturnsVisitor { found: false };
visit::walk_expr(&mut explicit, &*e, false);
visit::walk_expr_opt(&mut implicit, blk.expr, true);
explicit.found || implicit.found
}
_ => tcx.sess.bug("unexpected expr variant in has_nested_returns")
}
}

Some(ast_map::NodeVariant(..)) | Some(ast_map::NodeStructCtor(..)) => false,

// glue, shims, etc
None if id == ast::DUMMY_NODE_ID => false,

_ => tcx.sess.bug(format!("unexpected variant in has_nested_returns: {}",
tcx.map.path_to_string(id)).as_slice())
}
}

Expand Down Expand Up @@ -1254,13 +1359,15 @@ pub fn new_fn_ctxt<'a>(ccx: &'a CrateContext,
let substd_output_type = output_type.substp(ccx.tcx(), param_substs);
let uses_outptr = type_of::return_uses_outptr(ccx, substd_output_type);
let debug_context = debuginfo::create_function_debug_context(ccx, id, param_substs, llfndecl);
let nested_returns = has_nested_returns(ccx.tcx(), id);

let mut fcx = FunctionContext {
llfn: llfndecl,
llenv: None,
llretptr: Cell::new(None),
llretslotptr: Cell::new(None),
alloca_insert_pt: Cell::new(None),
llreturn: Cell::new(None),
needs_ret_allocas: nested_returns,
personality: Cell::new(None),
caller_expects_out_pointer: uses_outptr,
llargs: RefCell::new(NodeMap::new()),
Expand Down Expand Up @@ -1303,12 +1410,12 @@ pub fn init_function<'a>(fcx: &'a FunctionContext<'a>,

if !return_type_is_void(fcx.ccx, substd_output_type) {
// If the function returns nil/bot, there is no real return
// value, so do not set `llretptr`.
// value, so do not set `llretslotptr`.
if !skip_retptr || fcx.caller_expects_out_pointer {
// Otherwise, we normally allocate the llretptr, unless we
// Otherwise, we normally allocate the llretslotptr, unless we
// have been instructed to skip it for immediate return
// values.
fcx.llretptr.set(Some(make_return_pointer(fcx, substd_output_type)));
fcx.llretslotptr.set(Some(make_return_slot_pointer(fcx, substd_output_type)));
}
}

Expand Down Expand Up @@ -1533,13 +1640,18 @@ pub fn finish_fn<'a>(fcx: &'a FunctionContext<'a>,

// Builds the return block for a function.
pub fn build_return_block(fcx: &FunctionContext, ret_cx: &Block, retty: ty::t) {
// Return the value if this function immediate; otherwise, return void.
if fcx.llretptr.get().is_none() || fcx.caller_expects_out_pointer {
if fcx.llretslotptr.get().is_none() ||
(!fcx.needs_ret_allocas && fcx.caller_expects_out_pointer) {
return RetVoid(ret_cx);
}

let retptr = Value(fcx.llretptr.get().unwrap());
let retval = match retptr.get_dominating_store(ret_cx) {
let retslot = if fcx.needs_ret_allocas {
Load(ret_cx, fcx.llretslotptr.get().unwrap())
} else {
fcx.llretslotptr.get().unwrap()
};
let retptr = Value(retslot);
match retptr.get_dominating_store(ret_cx) {
// If there's only a single store to the ret slot, we can directly return
// the value that was stored and omit the store and the alloca
Some(s) => {
Expand All @@ -1550,17 +1662,29 @@ pub fn build_return_block(fcx: &FunctionContext, ret_cx: &Block, retty: ty::t) {
retptr.erase_from_parent();
}

if ty::type_is_bool(retty) {
let retval = if ty::type_is_bool(retty) {
Trunc(ret_cx, retval, Type::i1(fcx.ccx))
} else {
retval
};

if fcx.caller_expects_out_pointer {
store_ty(ret_cx, retval, get_param(fcx.llfn, 0), retty);
return RetVoid(ret_cx);
} else {
return Ret(ret_cx, retval);
}
}
// Otherwise, load the return value from the ret slot
None => load_ty(ret_cx, fcx.llretptr.get().unwrap(), retty)
};

Ret(ret_cx, retval);
// Otherwise, copy the return value to the ret slot
None => {
if fcx.caller_expects_out_pointer {
memcpy_ty(ret_cx, get_param(fcx.llfn, 0), retslot, retty);
return RetVoid(ret_cx);
} else {
return Ret(ret_cx, load_ty(ret_cx, retslot, retty));
}
}
}
}

#[deriving(Clone, Eq, PartialEq)]
Expand Down Expand Up @@ -1658,10 +1782,10 @@ pub fn trans_closure(ccx: &CrateContext,
// emitting should be enabled.
debuginfo::start_emitting_source_locations(&fcx);

let dest = match fcx.llretptr.get() {
Some(e) => {expr::SaveIn(e)}
let dest = match fcx.llretslotptr.get() {
Some(_) => expr::SaveIn(fcx.get_ret_slot(bcx, block_ty, "iret_slot")),
None => {
assert!(type_is_zero_size(bcx.ccx(), block_ty))
assert!(type_is_zero_size(bcx.ccx(), block_ty));
expr::Ignore
}
};
Expand All @@ -1672,6 +1796,13 @@ pub fn trans_closure(ccx: &CrateContext,
// (trans_block, trans_expr, et cetera).
bcx = controlflow::trans_block(bcx, body, dest);

match dest {
expr::SaveIn(slot) if fcx.needs_ret_allocas => {
Store(bcx, slot, fcx.llretslotptr.get().unwrap());
}
_ => {}
}

match fcx.llreturn.get() {
Some(_) => {
Br(bcx, fcx.return_exit_block());
Expand Down Expand Up @@ -1836,21 +1967,24 @@ fn trans_enum_variant_or_tuple_like_struct(ccx: &CrateContext,
param_substs, None, &arena, TranslateItems);
let bcx = init_function(&fcx, false, result_ty);

assert!(!fcx.needs_ret_allocas);

let arg_tys = ty::ty_fn_args(ctor_ty);

let arg_datums = create_datums_for_fn_args(&fcx, arg_tys.as_slice());

if !type_is_zero_size(fcx.ccx, result_ty) {
let dest = fcx.get_ret_slot(bcx, result_ty, "eret_slot");
let repr = adt::represent_type(ccx, result_ty);
for (i, arg_datum) in arg_datums.move_iter().enumerate() {
let lldestptr = adt::trans_field_ptr(bcx,
&*repr,
fcx.llretptr.get().unwrap(),
dest,
disr,
i);
arg_datum.store_to(bcx, lldestptr);
}
adt::trans_set_discr(bcx, &*repr, fcx.llretptr.get().unwrap(), disr);
adt::trans_set_discr(bcx, &*repr, dest, disr);
}

finish_fn(&fcx, bcx, result_ty);
Expand Down
16 changes: 10 additions & 6 deletions src/librustc/middle/trans/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ pub fn trans_unboxing_shim(bcx: &Block,
let return_type = ty::ty_fn_ret(boxed_function_type);
let fcx = new_fn_ctxt(ccx,
llfn,
-1,
ast::DUMMY_NODE_ID,
false,
return_type,
&empty_param_substs,
Expand Down Expand Up @@ -389,6 +389,11 @@ pub fn trans_unboxing_shim(bcx: &Block,
for i in range(1, arg_types.len()) {
llshimmedargs.push(get_param(fcx.llfn, fcx.arg_pos(i) as u32));
}
assert!(!fcx.needs_ret_allocas);
let dest = match fcx.llretslotptr.get() {
Some(_) => Some(expr::SaveIn(fcx.get_ret_slot(bcx, return_type, "ret_slot"))),
None => None
};
bcx = trans_call_inner(bcx,
None,
function_type,
Expand All @@ -399,10 +404,7 @@ pub fn trans_unboxing_shim(bcx: &Block,
}
},
ArgVals(llshimmedargs.as_slice()),
match fcx.llretptr.get() {
None => None,
Some(llretptr) => Some(expr::SaveIn(llretptr)),
}).bcx;
dest).bcx;

bcx = fcx.pop_and_trans_custom_cleanup_scope(bcx, arg_scope);
finish_fn(&fcx, bcx, return_type);
Expand Down Expand Up @@ -758,9 +760,11 @@ pub fn trans_call_inner<'a>(
assert!(abi == synabi::RustIntrinsic);
assert!(dest.is_some());

let call_info = call_info.expect("no call info for intrinsic call?");
return intrinsic::trans_intrinsic_call(bcx, node, callee_ty,
arg_cleanup_scope, args,
dest.unwrap(), substs);
dest.unwrap(), substs,
call_info);
}
NamedTupleConstructor(substs, disr) => {
assert!(dest.is_some());
Expand Down
7 changes: 4 additions & 3 deletions src/librustc/middle/trans/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -574,24 +574,25 @@ pub fn get_wrapper_for_bare_fn(ccx: &CrateContext,

let arena = TypedArena::new();
let empty_param_substs = param_substs::empty();
let fcx = new_fn_ctxt(ccx, llfn, -1, true, f.sig.output,
let fcx = new_fn_ctxt(ccx, llfn, ast::DUMMY_NODE_ID, true, f.sig.output,
&empty_param_substs, None, &arena, TranslateItems);
let bcx = init_function(&fcx, true, f.sig.output);

let args = create_datums_for_fn_args(&fcx,
ty::ty_fn_args(closure_ty)
.as_slice());
let mut llargs = Vec::new();
match fcx.llretptr.get() {
match fcx.llretslotptr.get() {
Some(llretptr) => {
assert!(!fcx.needs_ret_allocas);
llargs.push(llretptr);
}
None => {}
}
llargs.extend(args.iter().map(|arg| arg.val));

let retval = Call(bcx, fn_ptr, llargs.as_slice(), None);
if type_is_zero_size(ccx, f.sig.output) || fcx.llretptr.get().is_some() {
if type_is_zero_size(ccx, f.sig.output) || fcx.llretslotptr.get().is_some() {
RetVoid(bcx);
} else {
Ret(bcx, retval);
Expand Down
Loading