Fix defer when not calling recover in function with named results.
From-SVN: r178905
This commit is contained in:
parent
fae3f4598a
commit
b9f04a8461
8 changed files with 57 additions and 27 deletions
|
@ -1592,15 +1592,25 @@ Function::build_defer_wrapper(Gogo* gogo, Named_object* named_function,
|
|||
&& !this->type_->results()->empty()
|
||||
&& !this->type_->results()->front().name().empty())
|
||||
{
|
||||
// If the result variables are named, we need to return them
|
||||
// again, because they might have been changed by a defer
|
||||
// function.
|
||||
// If the result variables are named, and we are returning from
|
||||
// this function rather than panicing through it, we need to
|
||||
// return them again, because they might have been changed by a
|
||||
// defer function. The runtime routines set the defer_stack
|
||||
// variable to true if we are returning from this function.
|
||||
retval = this->return_value(gogo, named_function, end_loc,
|
||||
&stmt_list);
|
||||
set = fold_build2_loc(end_loc, MODIFY_EXPR, void_type_node,
|
||||
DECL_RESULT(this->fndecl_), retval);
|
||||
ret_stmt = fold_build1_loc(end_loc, RETURN_EXPR, void_type_node, set);
|
||||
append_to_statement_list(ret_stmt, &stmt_list);
|
||||
|
||||
Expression* ref =
|
||||
Expression::make_temporary_reference(this->defer_stack_, end_loc);
|
||||
tree tref = ref->get_tree(&context);
|
||||
tree s = build3_loc(end_loc, COND_EXPR, void_type_node, tref,
|
||||
ret_stmt, NULL_TREE);
|
||||
|
||||
append_to_statement_list(s, &stmt_list);
|
||||
|
||||
}
|
||||
|
||||
go_assert(*fini == NULL_TREE);
|
||||
|
|
|
@ -2976,27 +2976,27 @@ Function::determine_types()
|
|||
this->block_->determine_types();
|
||||
}
|
||||
|
||||
// Get a pointer to the variable holding the defer stack for this
|
||||
// function, making it if necessary. At least at present, the value
|
||||
// of this variable is not used. However, a pointer to this variable
|
||||
// is used as a marker for the functions on the defer stack associated
|
||||
// with this function. Doing things this way permits inlining a
|
||||
// Get a pointer to the variable representing the defer stack for this
|
||||
// function, making it if necessary. The value of the variable is set
|
||||
// by the runtime routines to true if the function is returning,
|
||||
// rather than panicing through. A pointer to this variable is used
|
||||
// as a marker for the functions on the defer stack associated with
|
||||
// this function. A function-specific variable permits inlining a
|
||||
// function which uses defer.
|
||||
|
||||
Expression*
|
||||
Function::defer_stack(source_location location)
|
||||
{
|
||||
Type* t = Type::make_pointer_type(Type::make_void_type());
|
||||
if (this->defer_stack_ == NULL)
|
||||
{
|
||||
Expression* n = Expression::make_nil(location);
|
||||
Type* t = Type::lookup_bool_type();
|
||||
Expression* n = Expression::make_boolean(false, location);
|
||||
this->defer_stack_ = Statement::make_temporary(t, n, location);
|
||||
this->defer_stack_->set_is_address_taken();
|
||||
}
|
||||
Expression* ref = Expression::make_temporary_reference(this->defer_stack_,
|
||||
location);
|
||||
Expression* addr = Expression::make_unary(OPERATOR_AND, ref, location);
|
||||
return Expression::make_unsafe_cast(t, addr, location);
|
||||
return Expression::make_unary(OPERATOR_AND, ref, location);
|
||||
}
|
||||
|
||||
// Export the function.
|
||||
|
|
|
@ -165,10 +165,10 @@ DEF_GO_RUNTIME(SET_DEFER_RETADDR, "__go_set_defer_retaddr", P1(POINTER),
|
|||
R1(BOOL))
|
||||
|
||||
// Check for a deferred function in an exception handler.
|
||||
DEF_GO_RUNTIME(CHECK_DEFER, "__go_check_defer", P1(POINTER), R0())
|
||||
DEF_GO_RUNTIME(CHECK_DEFER, "__go_check_defer", P1(BOOLPTR), R0())
|
||||
|
||||
// Run deferred functions.
|
||||
DEF_GO_RUNTIME(UNDEFER, "__go_undefer", P1(POINTER), R0())
|
||||
DEF_GO_RUNTIME(UNDEFER, "__go_undefer", P1(BOOLPTR), R0())
|
||||
|
||||
// Panic with a runtime error.
|
||||
DEF_GO_RUNTIME(RUNTIME_ERROR, "__go_runtime_error", P1(INT), R0())
|
||||
|
@ -207,7 +207,7 @@ DEF_GO_RUNTIME(GO, "__go_go", P2(FUNC_PTR, POINTER), R0())
|
|||
|
||||
|
||||
// Defer a function.
|
||||
DEF_GO_RUNTIME(DEFER, "__go_defer", P3(POINTER, FUNC_PTR, POINTER), R0())
|
||||
DEF_GO_RUNTIME(DEFER, "__go_defer", P3(BOOLPTR, FUNC_PTR, POINTER), R0())
|
||||
|
||||
|
||||
// Run a select statement.
|
||||
|
|
|
@ -2539,11 +2539,10 @@ Return_statement::do_traverse_assignments(Traverse_assignments* tassign)
|
|||
|
||||
// Lower a return statement. If we are returning a function call
|
||||
// which returns multiple values which match the current function,
|
||||
// split up the call's results. If the function has named result
|
||||
// variables, and the return statement lists explicit values, then
|
||||
// implement it by assigning the values to the result variables and
|
||||
// changing the statement to not list any values. This lets
|
||||
// panic/recover work correctly.
|
||||
// split up the call's results. If the return statement lists
|
||||
// explicit values, implement this statement by assigning the values
|
||||
// to the result variables and change this statement to a naked
|
||||
// return. This lets panic/recover work correctly.
|
||||
|
||||
Statement*
|
||||
Return_statement::do_lower(Gogo*, Named_object* function, Block* enclosing,
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
/* This function is called each time we need to defer a call. */
|
||||
|
||||
void
|
||||
__go_defer (void *frame, void (*pfn) (void *), void *arg)
|
||||
__go_defer (_Bool *frame, void (*pfn) (void *), void *arg)
|
||||
{
|
||||
struct __go_defer_stack *n;
|
||||
|
||||
|
@ -34,7 +34,7 @@ __go_defer (void *frame, void (*pfn) (void *), void *arg)
|
|||
/* This function is called when we want to undefer the stack. */
|
||||
|
||||
void
|
||||
__go_undefer (void *frame)
|
||||
__go_undefer (_Bool *frame)
|
||||
{
|
||||
if (__go_panic_defer == NULL)
|
||||
return;
|
||||
|
@ -53,6 +53,12 @@ __go_undefer (void *frame)
|
|||
|
||||
__go_panic_defer->__defer = d->__next;
|
||||
__go_free (d);
|
||||
|
||||
/* Since we are executing a defer function here, we know we are
|
||||
returning from the calling function. If the calling
|
||||
function, or one of its callees, paniced, then the defer
|
||||
functions would be executed by __go_panic. */
|
||||
*frame = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,9 +13,10 @@ struct __go_defer_stack
|
|||
/* The next entry in the stack. */
|
||||
struct __go_defer_stack *__next;
|
||||
|
||||
/* The frame pointer for the function which called this defer
|
||||
statement. */
|
||||
void *__frame;
|
||||
/* The stack variable for the function which called this defer
|
||||
statement. This is set to 1 if we are returning from that
|
||||
function, 0 if we are panicing through it. */
|
||||
_Bool *__frame;
|
||||
|
||||
/* The value of the panic stack when this function is deferred.
|
||||
This function can not recover this value from the panic stack.
|
||||
|
|
|
@ -87,6 +87,12 @@ __go_panic (struct __go_empty_interface arg)
|
|||
/* __go_unwind_stack should not return. */
|
||||
abort ();
|
||||
}
|
||||
|
||||
/* Because we executed that defer function by a panic, and
|
||||
it did not call recover, we know that we are not
|
||||
returning from the calling function--we are panicing
|
||||
through it. */
|
||||
*d->__frame = 0;
|
||||
}
|
||||
|
||||
__go_panic_defer->__defer = d->__next;
|
||||
|
|
|
@ -44,7 +44,7 @@ static const _Unwind_Exception_Class __go_exception_class =
|
|||
continue unwinding. */
|
||||
|
||||
void
|
||||
__go_check_defer (void *frame)
|
||||
__go_check_defer (_Bool *frame)
|
||||
{
|
||||
struct _Unwind_Exception *hdr;
|
||||
|
||||
|
@ -103,8 +103,12 @@ __go_check_defer (void *frame)
|
|||
if (was_recovered)
|
||||
{
|
||||
/* Just return and continue executing Go code. */
|
||||
*frame = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
/* We are panicing through this function. */
|
||||
*frame = 0;
|
||||
}
|
||||
else if (__go_panic_defer->__defer != NULL
|
||||
&& __go_panic_defer->__defer->__pfn == NULL
|
||||
|
@ -118,6 +122,10 @@ __go_check_defer (void *frame)
|
|||
d = __go_panic_defer->__defer;
|
||||
__go_panic_defer->__defer = d->__next;
|
||||
__go_free (d);
|
||||
|
||||
/* We are returning from this function. */
|
||||
*frame = 1;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue