Commit 55ea0ea0 by Ian Lance Taylor

compiler, runtime: better stack trace for `go f()` where f is nil

    
    The test for this is TestGoNil in the runtime package, which we don't
    run yet but will run with a subsequent gotools patch.
    
    Updates golang/go#8045
    
    Reviewed-on: https://go-review.googlesource.com/46392

From-SVN: r249494
parent bc216de9
dac4bb4f4ed8e7f2939d45439048dec2f6db14cf 075e67bdbcb730669c1af1aa2d53bb77cbb2a3c5
The first line of this file holds the git revision number of the last The first line of this file holds the git revision number of the last
merge done from the gofrontend repository. merge done from the gofrontend repository.
...@@ -3379,6 +3379,9 @@ static const int RUNTIME_ERROR_MAKE_CHAN_OUT_OF_BOUNDS = 9; ...@@ -3379,6 +3379,9 @@ static const int RUNTIME_ERROR_MAKE_CHAN_OUT_OF_BOUNDS = 9;
// Division by zero. // Division by zero.
static const int RUNTIME_ERROR_DIVISION_BY_ZERO = 10; static const int RUNTIME_ERROR_DIVISION_BY_ZERO = 10;
// Go statement with nil function.
static const int RUNTIME_ERROR_GO_NIL = 11;
// This is used by some of the langhooks. // This is used by some of the langhooks.
extern Gogo* go_get_gogo(); extern Gogo* go_get_gogo();
......
...@@ -2201,6 +2201,15 @@ Thunk_statement::simplify_statement(Gogo* gogo, Named_object* function, ...@@ -2201,6 +2201,15 @@ Thunk_statement::simplify_statement(Gogo* gogo, Named_object* function,
Location location = this->location(); Location location = this->location();
bool is_constant_function = this->is_constant_function();
Temporary_statement* fn_temp = NULL;
if (!is_constant_function)
{
fn_temp = Statement::make_temporary(NULL, fn, location);
block->insert_statement_before(block->statements()->size() - 1, fn_temp);
fn = Expression::make_temporary_reference(fn_temp, location);
}
std::string thunk_name = Gogo::thunk_name(); std::string thunk_name = Gogo::thunk_name();
// Build the thunk. // Build the thunk.
...@@ -2212,7 +2221,7 @@ Thunk_statement::simplify_statement(Gogo* gogo, Named_object* function, ...@@ -2212,7 +2221,7 @@ Thunk_statement::simplify_statement(Gogo* gogo, Named_object* function,
// argument to the thunk. // argument to the thunk.
Expression_list* vals = new Expression_list(); Expression_list* vals = new Expression_list();
if (!this->is_constant_function()) if (!is_constant_function)
vals->push_back(fn); vals->push_back(fn);
if (interface_method != NULL) if (interface_method != NULL)
...@@ -2238,6 +2247,23 @@ Thunk_statement::simplify_statement(Gogo* gogo, Named_object* function, ...@@ -2238,6 +2247,23 @@ Thunk_statement::simplify_statement(Gogo* gogo, Named_object* function,
// Allocate the initialized struct on the heap. // Allocate the initialized struct on the heap.
constructor = Expression::make_heap_expression(constructor, location); constructor = Expression::make_heap_expression(constructor, location);
// Throw an error if the function is nil. This is so that for `go
// nil` we get a backtrace from the go statement, rather than a
// useless backtrace from the brand new goroutine.
Expression* param = constructor;
if (!is_constant_function)
{
fn = Expression::make_temporary_reference(fn_temp, location);
Expression* nil = Expression::make_nil(location);
Expression* isnil = Expression::make_binary(OPERATOR_EQEQ, fn, nil,
location);
Expression* crash = gogo->runtime_error(RUNTIME_ERROR_GO_NIL, location);
crash = Expression::make_conditional(isnil, crash,
Expression::make_nil(location),
location);
param = Expression::make_compound(crash, constructor, location);
}
// Look up the thunk. // Look up the thunk.
Named_object* named_thunk = gogo->lookup(thunk_name, NULL); Named_object* named_thunk = gogo->lookup(thunk_name, NULL);
go_assert(named_thunk != NULL && named_thunk->is_function()); go_assert(named_thunk != NULL && named_thunk->is_function());
...@@ -2246,7 +2272,7 @@ Thunk_statement::simplify_statement(Gogo* gogo, Named_object* function, ...@@ -2246,7 +2272,7 @@ Thunk_statement::simplify_statement(Gogo* gogo, Named_object* function,
Expression* func = Expression::make_func_reference(named_thunk, NULL, Expression* func = Expression::make_func_reference(named_thunk, NULL,
location); location);
Expression_list* params = new Expression_list(); Expression_list* params = new Expression_list();
params->push_back(constructor); params->push_back(param);
Call_expression* call = Expression::make_call(func, params, false, location); Call_expression* call = Expression::make_call(func, params, false, location);
// Build the simple go or defer statement. // Build the simple go or defer statement.
......
...@@ -49,7 +49,10 @@ enum ...@@ -49,7 +49,10 @@ enum
MAKE_CHAN_OUT_OF_BOUNDS = 9, MAKE_CHAN_OUT_OF_BOUNDS = 9,
/* Integer division by zero. */ /* Integer division by zero. */
DIVISION_BY_ZERO = 10 DIVISION_BY_ZERO = 10,
/* Go statement with nil function. */
GO_NIL = 11
}; };
extern void __go_runtime_error () __attribute__ ((noreturn)); extern void __go_runtime_error () __attribute__ ((noreturn));
...@@ -84,6 +87,12 @@ __go_runtime_error (int32 i) ...@@ -84,6 +87,12 @@ __go_runtime_error (int32 i)
case DIVISION_BY_ZERO: case DIVISION_BY_ZERO:
runtime_panicstring ("integer divide by zero"); runtime_panicstring ("integer divide by zero");
case GO_NIL:
/* This one is a throw, rather than a panic. Set throwing to
not dump full stacks. */
runtime_g()->m->throwing = -1;
runtime_throw ("go of nil func value");
default: default:
runtime_panicstring ("unknown runtime error"); runtime_panicstring ("unknown runtime error");
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment