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
This commit is contained in:
Ian Lance Taylor 2017-06-22 04:13:36 +00:00
parent bc216de9f6
commit 55ea0ea07d
4 changed files with 42 additions and 4 deletions

View File

@ -1,4 +1,4 @@
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.

View File

@ -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();

View File

@ -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,
// 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,
// 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,
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.

View File

@ -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)
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");
} }