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:
parent
bc216de9f6
commit
55ea0ea07d
|
@ -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.
|
||||||
|
|
|
@ -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,
|
||||||
|
|
||||||
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.
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue