@@ -155,8 +155,8 @@
go/go-dump.o: go/go-dump.cc $(GO_SYSTEM_H) $(GO_C_H) go/go-dump.h
go/go-lang.o: go/go-lang.c $(GO_SYSTEM_H) coretypes.h opts.h $(TREE_H) \
$(GIMPLE_H) $(GGC_H) toplev.h debug.h options.h $(FLAGS_H) convert.h \
- langhooks.h langhooks-def.h $(TARGET_H) $(DIAGNOSTIC_H) $(GO_C_H) \
- gt-go-go-lang.h gtype-go.h
+ langhooks.h langhooks-def.h $(EXCEPT_H) $(TARGET_H) $(DIAGNOSTIC_H) \
+ $(GO_C_H) gt-go-go-lang.h gtype-go.h
go/gogo-tree.o: go/gogo-tree.cc $(GO_SYSTEM_H) $(TREE_H) $(GIMPLE_H) \
tree-iterator.h $(CGRAPH_H) langhooks.h convert.h output.h \
$(TM_P_H) $(DIAGNOSTIC_H) $(GO_TYPES_H) $(GO_EXPRESSIONS_H) \
@@ -133,14 +133,6 @@
exports.push_back(p->second);
}
- if (exports.empty()
- && import_init_fn.empty()
- && imported_init_fns.empty())
- {
- // Nothing to export.
- return;
- }
-
std::sort(exports.begin(), exports.end(), Sort_bindings());
// Although the export data is readable, at least this version is,
@@ -417,6 +417,8 @@
fold_convert(ptr_type_node, rhs_tree),
build_pointer_type(boolean_type_node),
null_pointer_node);
+ // This will panic if the interface conversion fails.
+ TREE_NOTHROW(convert_interface_decl) = 0;
return fold_convert(lhs_type_tree, call);
}
}
@@ -442,6 +444,8 @@
const_ptr_type_node,
fold_convert(const_ptr_type_node,
rhs_tree));
+ // This call will panic if the conversion fails.
+ TREE_NOTHROW(interface_to_pointer_decl) = 0;
gcc_assert(POINTER_TYPE_P(lhs_type_tree));
return fold_convert(lhs_type_tree, call);
}
@@ -471,6 +475,8 @@
ptr_type_node,
fold_convert(ptr_type_node,
rhs_tree));
+ // This call will panic if the conversion fails.
+ TREE_NOTHROW(interface_to_object_decl) = 0;
return build2(COMPOUND_EXPR, lhs_type_tree, make_tmp,
build2(COMPOUND_EXPR, lhs_type_tree, call, tmp));
}
@@ -568,6 +574,7 @@
"__go_bad_index",
0,
void_type_node);
+ TREE_NOTHROW(bad_index_fndecl) = 0;
TREE_THIS_VOLATILE(bad_index_fndecl) = 1;
val = build2(COMPOUND_EXPR, TREE_TYPE(val),
build3(COND_EXPR, void_type_node,
@@ -924,7 +931,7 @@
// may want to move the variable onto the heap.
bool
-Var_expression::do_address_taken(source_location location, bool escapes)
+Var_expression::do_address_taken(source_location, bool escapes)
{
if (!escapes)
return true;
@@ -935,10 +942,8 @@
}
else if (this->variable_->is_result_variable())
{
- // There is no way to make a result variable permanent; it has
- // to disappear after the function returns.
- error_at(location, "may not take address of out parameter");
- return false;
+ this->variable_->result_var_value()->set_address_taken();
+ return true;
}
else
gcc_unreachable();
@@ -6112,6 +6117,8 @@
const_ptr_type_node,
fold_convert(const_ptr_type_node,
right_tree));
+ // This can panic if the type is uncomparable.
+ TREE_NOTHROW(interface_compare_decl) = 0;
right_tree = build_int_cst_type(integer_type_node, 0);
}
@@ -6275,6 +6282,12 @@
void
do_export(Export*) const;
+ virtual bool
+ do_is_recover_call() const;
+
+ virtual void
+ do_set_recover_arg(Expression*);
+
private:
// The builtin functions.
enum Builtin_function_code
@@ -6292,10 +6305,10 @@
BUILTIN_MAKE,
BUILTIN_NEW,
BUILTIN_PANIC,
- BUILTIN_PANICLN,
BUILTIN_PRINT,
BUILTIN_PRINTLN,
BUILTIN_REAL,
+ BUILTIN_RECOVER,
// Builtin functions from the unsafe package.
BUILTIN_ALIGNOF,
@@ -6352,14 +6365,14 @@
this->code_ = BUILTIN_NEW;
else if (name == "panic")
this->code_ = BUILTIN_PANIC;
- else if (name == "panicln")
- this->code_ = BUILTIN_PANICLN;
else if (name == "print")
this->code_ = BUILTIN_PRINT;
else if (name == "println")
this->code_ = BUILTIN_PRINTLN;
else if (name == "real")
this->code_ = BUILTIN_REAL;
+ else if (name == "recover")
+ this->code_ = BUILTIN_RECOVER;
else if (name == "Alignof")
this->code_ = BUILTIN_ALIGNOF;
else if (name == "Offsetof")
@@ -6370,11 +6383,34 @@
gcc_unreachable();
}
+// Return whether this is a call to recover. This is a virtual
+// function called from the parent class.
+
+bool
+Builtin_call_expression::do_is_recover_call() const
+{
+ if (this->classification() == EXPRESSION_ERROR)
+ return false;
+ return this->code_ == BUILTIN_RECOVER;
+}
+
+// Set the argument for a call to recover.
+
+void
+Builtin_call_expression::do_set_recover_arg(Expression* arg)
+{
+ const Expression_list* args = this->args();
+ gcc_assert(args == NULL || args->empty());
+ Expression_list* new_args = new Expression_list();
+ new_args->push_back(arg);
+ this->set_args(new_args);
+}
+
// Lower a builtin call expression. This turns new and make into
// specific expressions. We also convert to a constant if we can.
Expression*
-Builtin_call_expression::do_lower(Gogo*, Named_object*, int)
+Builtin_call_expression::do_lower(Gogo*, Named_object* function, int)
{
if (this->code_ == BUILTIN_NEW)
{
@@ -6463,6 +6499,20 @@
mpfr_clear(rval);
mpfr_clear(imag);
}
+ else if (this->code_ == BUILTIN_RECOVER)
+ {
+ if (function != NULL)
+ function->func_value()->set_calls_recover();
+ else
+ {
+ // Calling recover outside of a function always returns the
+ // nil empty interface.
+ Type* eface = Type::make_interface_type(NULL, this->location());
+ return Expression::make_cast(eface,
+ Expression::make_nil(this->location()),
+ this->location());
+ }
+ }
return this;
}
@@ -6827,7 +6877,6 @@
case BUILTIN_CLOSE:
case BUILTIN_PANIC:
- case BUILTIN_PANICLN:
case BUILTIN_PRINT:
case BUILTIN_PRINTLN:
return Type::make_void_type();
@@ -6835,6 +6884,9 @@
case BUILTIN_CLOSED:
return Type::lookup_bool_type();
+ case BUILTIN_RECOVER:
+ return Type::make_interface_type(NULL, BUILTINS_LOCATION);
+
case BUILTIN_REAL:
case BUILTIN_IMAG:
{
@@ -6881,8 +6933,6 @@
Type* arg_type = NULL;
switch (this->code_)
{
- case BUILTIN_PANIC:
- case BUILTIN_PANICLN:
case BUILTIN_PRINT:
case BUILTIN_PRINTLN:
// Do not force a large integer constant to "int".
@@ -7025,8 +7075,6 @@
}
break;
- case BUILTIN_PANIC:
- case BUILTIN_PANICLN:
case BUILTIN_PRINT:
case BUILTIN_PRINTLN:
{
@@ -7077,11 +7125,17 @@
}
break;
+ case BUILTIN_PANIC:
case BUILTIN_SIZEOF:
case BUILTIN_ALIGNOF:
this->check_one_arg();
break;
+ case BUILTIN_RECOVER:
+ if (this->args() != NULL && !this->args()->empty())
+ this->report_error(_("too many arguments"));
+ break;
+
case BUILTIN_OFFSETOF:
if (this->check_one_arg())
{
@@ -7266,19 +7320,12 @@
return fold(convert_to_integer(type_tree, val_tree));
}
- case BUILTIN_PANIC:
- case BUILTIN_PANICLN:
case BUILTIN_PRINT:
case BUILTIN_PRINTLN:
{
- const bool is_panic = (this->code_ == BUILTIN_PANIC
- || this->code_ == BUILTIN_PANICLN);
- const bool is_ln = (this->code_ == BUILTIN_PANICLN
- || this->code_ == BUILTIN_PRINTLN);
+ const bool is_ln = this->code_ == BUILTIN_PRINTLN;
tree stmt_list = NULL_TREE;
- tree panic_arg = is_panic ? boolean_true_node : boolean_false_node;
-
const Expression_list* call_args = this->args();
if (call_args != NULL)
{
@@ -7292,10 +7339,8 @@
tree call = Gogo::call_builtin(&print_space_fndecl,
location,
"__go_print_space",
- 1,
- void_type_node,
- boolean_type_node,
- panic_arg);
+ 0,
+ void_type_node);
append_to_statement_list(call, &stmt_list);
}
@@ -7376,10 +7421,8 @@
tree call = Gogo::call_builtin(pfndecl,
location,
fnname,
- 2,
+ 1,
void_type_node,
- boolean_type_node,
- panic_arg,
TREE_TYPE(arg),
arg);
append_to_statement_list(call, &stmt_list);
@@ -7392,29 +7435,90 @@
tree call = Gogo::call_builtin(&print_nl_fndecl,
location,
"__go_print_nl",
- 1,
- void_type_node,
- boolean_type_node,
- panic_arg);
- append_to_statement_list(call, &stmt_list);
- }
-
- if (is_panic)
- {
- static tree panic_fndecl;
- tree call = Gogo::call_builtin(&panic_fndecl,
- location,
- "__go_panic",
0,
void_type_node);
- // Mark the function as not returning.
- TREE_THIS_VOLATILE(panic_fndecl) = 1;
append_to_statement_list(call, &stmt_list);
}
-
+
return stmt_list;
}
+ case BUILTIN_PANIC:
+ {
+ const Expression_list* args = this->args();
+ gcc_assert(args != NULL && args->size() == 1);
+ Expression* arg = args->front();
+ tree arg_tree = arg->get_tree(context);
+ if (arg_tree == error_mark_node)
+ return error_mark_node;
+ Type *empty = Type::make_interface_type(NULL, BUILTINS_LOCATION);
+ arg_tree = Expression::convert_for_assignment(context, empty,
+ arg->type(),
+ arg_tree, location);
+ static tree panic_fndecl;
+ tree call = Gogo::call_builtin(&panic_fndecl,
+ location,
+ "__go_panic",
+ 1,
+ void_type_node,
+ TREE_TYPE(arg_tree),
+ arg_tree);
+ // This function will throw an exception.
+ TREE_NOTHROW(panic_fndecl) = 0;
+ // This function will not return.
+ TREE_THIS_VOLATILE(panic_fndecl) = 1;
+ return call;
+ }
+
+ case BUILTIN_RECOVER:
+ {
+ // The argument is set when building recover thunks. It's a
+ // boolean value which is true if we can recover a value now.
+ const Expression_list* args = this->args();
+ gcc_assert(args != NULL && args->size() == 1);
+ Expression* arg = args->front();
+ tree arg_tree = arg->get_tree(context);
+ if (arg_tree == error_mark_node)
+ return error_mark_node;
+
+ Type *empty = Type::make_interface_type(NULL, BUILTINS_LOCATION);
+ tree empty_tree = empty->get_tree(context->gogo());
+
+ Type* nil_type = Type::make_nil_type();
+ Expression* nil = Expression::make_nil(location);
+ tree nil_tree = nil->get_tree(context);
+ tree empty_nil_tree = Expression::convert_for_assignment(context,
+ empty,
+ nil_type,
+ nil_tree,
+ location);
+
+ // We need to handle a deferred call to recover specially,
+ // because it changes whether it can recover a panic or not.
+ // See test7 in test/recover1.go.
+ tree call;
+ if (this->is_deferred())
+ {
+ static tree deferred_recover_fndecl;
+ call = Gogo::call_builtin(&deferred_recover_fndecl,
+ location,
+ "__go_deferred_recover",
+ 0,
+ empty_tree);
+ }
+ else
+ {
+ static tree recover_fndecl;
+ call = Gogo::call_builtin(&recover_fndecl,
+ location,
+ "__go_recover",
+ 0,
+ empty_tree);
+ }
+ return fold_build3_loc(location, COND_EXPR, empty_tree, arg_tree,
+ call, empty_nil_tree);
+ }
+
case BUILTIN_CLOSE:
case BUILTIN_CLOSED:
{
@@ -7949,6 +8053,36 @@
return fntype->results()->size();
}
+// Return whether this is a call to the predeclared function recover.
+
+bool
+Call_expression::is_recover_call() const
+{
+ return this->do_is_recover_call();
+}
+
+// Set the argument to the recover function.
+
+void
+Call_expression::set_recover_arg(Expression* arg)
+{
+ this->do_set_recover_arg(arg);
+}
+
+// Virtual functions also implemented by Builtin_call_expression.
+
+bool
+Call_expression::do_is_recover_call() const
+{
+ return false;
+}
+
+void
+Call_expression::do_set_recover_arg(Expression*)
+{
+ gcc_unreachable();
+}
+
// Get the type.
Type*
@@ -8940,6 +9074,7 @@
"__go_bad_index",
0,
void_type_node);
+ TREE_NOTHROW(bad_index_fndecl) = 0;
TREE_THIS_VOLATILE(bad_index_fndecl) = 1;
if (this->end_ == NULL)
@@ -9274,6 +9409,7 @@
"__go_bad_index",
0,
void_type_node);
+ TREE_NOTHROW(bad_index_fndecl) = 0;
TREE_THIS_VOLATILE(bad_index_fndecl) = 1;
tree bytes_tree = String_type::bytes_tree(context->gogo(), string_tree);
@@ -9300,17 +9436,21 @@
}
end_tree = fold_convert(integer_type_node, end_tree);
static tree strslice_fndecl;
- return Gogo::call_builtin(&strslice_fndecl,
- this->location(),
- "__go_string_slice",
- 3,
- string_type,
- string_type,
- string_tree,
- integer_type_node,
- start_tree,
- integer_type_node,
- end_tree);
+ tree ret = Gogo::call_builtin(&strslice_fndecl,
+ this->location(),
+ "__go_string_slice",
+ 3,
+ string_type,
+ string_type,
+ string_tree,
+ integer_type_node,
+ start_tree,
+ integer_type_node,
+ end_tree);
+ // This will panic if the bounds are out of range for the
+ // string.
+ TREE_NOTHROW(strslice_fndecl) = 0;
+ return ret;
}
}
@@ -9517,6 +9657,9 @@
(insert
? boolean_true_node
: boolean_false_node));
+ // This can panic on a map of interface type if the interface holds
+ // an uncomparable or unhashable type.
+ TREE_NOTHROW(map_index_fndecl) = 0;
tree val_type_tree = type->val_type()->get_tree(context->gogo());
if (val_type_tree == error_mark_node)
@@ -9645,6 +9788,7 @@
"__go_bad_index",
0,
void_type_node);
+ TREE_NOTHROW(bad_index_fndecl) = 0;
TREE_THIS_VOLATILE(bad_index_fndecl) = 1;
struct_tree = build2(COMPOUND_EXPR, TREE_TYPE(struct_tree),
build3(COND_EXPR, void_type_node, compare,
@@ -10001,8 +10145,8 @@
for (size_t i = 0; i < count; ++i)
retvals->push_back(Expression::make_call_result(call, i));
}
- s = Statement::make_return_statement(no->func_value(), retvals,
- location);
+ s = Statement::make_return_statement(no->func_value()->type()->results(),
+ retvals, location);
}
gogo->add_statement(s);
@@ -12392,6 +12536,46 @@
return new Type_descriptor_expression(type, location);
}
+// An expression which evaluates to the address of an unnamed label.
+
+class Label_addr_expression : public Expression
+{
+ public:
+ Label_addr_expression(Label* label, source_location location)
+ : Expression(EXPRESSION_LABEL_ADDR, location),
+ label_(label)
+ { }
+
+ protected:
+ Type*
+ do_type()
+ { return Type::make_pointer_type(Type::make_void_type()); }
+
+ void
+ do_determine_type(const Type_context*)
+ { }
+
+ Expression*
+ do_copy()
+ { return new Label_addr_expression(this->label_, this->location()); }
+
+ tree
+ do_get_tree(Translate_context*)
+ { return this->label_->get_addr(this->location()); }
+
+ private:
+ // The label whose address we are taking.
+ Label* label_;
+};
+
+// Make an expression for the address of an unnamed label.
+
+Expression*
+Expression::make_label_addr(Label* label, source_location location)
+{
+ return new Label_addr_expression(label, location);
+}
+
// Make a reference count decrement of an lvalue.
Expression*
@@ -43,6 +43,7 @@
class Temporary_statement;
class Refcounts;
class Refcount_entry;
+class Label;
// The base class for all expressions.
@@ -93,7 +94,8 @@
EXPRESSION_SEND,
EXPRESSION_REFCOUNT_ADJUST,
EXPRESSION_REFCOUNT_DECREMENT_LVALUE,
- EXPRESSION_TYPE_DESCRIPTOR
+ EXPRESSION_TYPE_DESCRIPTOR,
+ EXPRESSION_LABEL_ADDR
};
Expression(Expression_classification, source_location);
@@ -248,7 +250,7 @@
static Expression*
make_cast(Type*, Expression*, source_location);
- // Make a composit_literal.
+ // Make a composite literal.
static Expression*
make_composite_literal(Type*, bool has_keys, Expression_list*,
source_location);
@@ -288,6 +290,11 @@
static Expression*
make_type_descriptor(Type* type, source_location);
+ // Make an expression which evaluates to the address of an unnamed
+ // label.
+ static Expression*
+ make_label_addr(Label*, source_location);
+
// Return the expression classification.
Expression_classification
classification() const
@@ -1203,7 +1210,7 @@
: Expression(EXPRESSION_CALL, location),
fn_(fn), args_(args), type_(NULL), tree_(NULL), refcount_entries_(NULL),
is_value_discarded_(false), varargs_are_lowered_(false),
- is_being_copied_(false)
+ is_being_copied_(false), is_deferred_(false)
{ }
// The function to call.
@@ -1228,6 +1235,25 @@
size_t
result_count() const;
+ // Return whether this is a call to the predeclared function
+ // recover.
+ bool
+ is_recover_call() const;
+
+ // Set the argument for a call to recover.
+ void
+ set_recover_arg(Expression*);
+
+ // Whether this call is being deferred.
+ bool
+ is_deferred() const
+ { return this->is_deferred_; }
+
+ // Note that the call is being deferred.
+ void
+ set_is_deferred()
+ { this->is_deferred_ = true; }
+
protected:
int
do_traverse(Traverse*);
@@ -1267,6 +1293,17 @@
virtual tree
do_get_tree(Translate_context*);
+ virtual bool
+ do_is_recover_call() const;
+
+ virtual void
+ do_set_recover_arg(Expression*);
+
+ // Let a builtin expression change the argument list.
+ void
+ set_args(Expression_list* args)
+ { this->args_ = args; }
+
private:
Expression*
lower_varargs(Gogo*, Named_object*);
@@ -1307,6 +1344,8 @@
bool varargs_are_lowered_;
// True if the value is being copied.
bool is_being_copied_;
+ // True if the call is an argument to a defer statement.
+ bool is_deferred_;
};
// An expression which represents a pointer to a function.
@@ -20,6 +20,7 @@
#include "diagnostic.h"
#include "langhooks.h"
#include "langhooks-def.h"
+#include "except.h"
#include "target.h"
#include <mpfr.h>
@@ -133,6 +134,10 @@
if (targetm.supports_split_stack (false))
flag_split_stack = 1;
+ /* Exceptions are used to handle recovering from panics. */
+ flag_exceptions = 1;
+ using_eh_for_cleanups ();
+
return CL_Go;
}
@@ -262,6 +267,24 @@
return GS_UNHANDLED;
}
+/* Return a decl for the exception personality function. The function
+ itself is implemented in libgo/runtime/go-unwind.c. */
+
+static tree
+go_langhook_eh_personality (void)
+{
+ static tree personality_decl;
+ if (personality_decl == NULL_TREE)
+ {
+ const char* name = (USING_SJLJ_EXCEPTIONS
+ ? "__gccgo_personality_sj0"
+ : "__gccgo_personality_v0");
+ personality_decl = build_personality_function (name);
+ go_preserve_from_gc (personality_decl);
+ }
+ return personality_decl;
+}
+
/* Functions called directly by the generic backend. */
tree
@@ -323,6 +346,7 @@
#undef LANG_HOOKS_GETDECLS
#undef LANG_HOOKS_WRITE_GLOBALS
#undef LANG_HOOKS_GIMPLIFY_EXPR
+#undef LANG_HOOKS_EH_PERSONALITY
#define LANG_HOOKS_NAME "GNU Go"
#define LANG_HOOKS_INIT go_langhook_init
@@ -338,6 +362,7 @@
#define LANG_HOOKS_GETDECLS go_langhook_getdecls
#define LANG_HOOKS_WRITE_GLOBALS go_langhook_write_globals
#define LANG_HOOKS_GIMPLIFY_EXPR go_langhook_gimplify_expr
+#define LANG_HOOKS_EH_PERSONALITY go_langhook_eh_personality
struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
@@ -130,6 +130,9 @@
// Use temporary variables to force order of evaluation.
::gogo->order_evaluations();
+ // Build thunks for functions which call recover.
+ ::gogo->build_recover_thunks();
+
// Convert complicated go and defer statements into simpler ones.
::gogo->simplify_thunk_statements();
@@ -133,6 +133,20 @@
long_double_type_node,
NULL_TREE),
true);
+
+ // We use __builtin_return_address in the thunk we build for
+ // functions which call recover.
+ define_builtin(BUILT_IN_RETURN_ADDRESS, "__builtin_return_address", NULL,
+ build_function_type_list(ptr_type_node,
+ unsigned_type_node,
+ NULL_TREE),
+ false);
+
+ // The compiler uses __builtin_trap for some exception handling
+ // cases.
+ define_builtin(BUILT_IN_TRAP, "__builtin_trap", NULL,
+ build_function_type(void_type_node, void_list_node),
+ false);
}
// Get the name to use for the import control function. If there is a
@@ -731,16 +745,17 @@
{
if (this->tree_ != NULL_TREE)
{
- // If this is a local variable whose address is taken, we must
- // rebuild the INDIRECT_REF each time to avoid invalid sharing.
+ // If this is a variable whose address is taken, we must rebuild
+ // the INDIRECT_REF each time to avoid invalid sharing.
tree ret = this->tree_;
- if (this->classification_ == NAMED_OBJECT_VAR
- && this->var_value()->is_in_heap()
+ if (((this->classification_ == NAMED_OBJECT_VAR
+ && this->var_value()->is_in_heap())
+ || (this->classification_ == NAMED_OBJECT_RESULT_VAR
+ && this->result_var_value()->is_in_heap()))
&& ret != error_mark_node)
{
gcc_assert(TREE_CODE(ret) == INDIRECT_REF);
- ret = build_fold_indirect_ref_loc(this->location(),
- TREE_OPERAND(ret, 0));
+ ret = build_fold_indirect_ref(TREE_OPERAND(ret, 0));
}
return ret;
}
@@ -886,25 +901,48 @@
case NAMED_OBJECT_RESULT_VAR:
{
Result_variable* result = this->u_.result_var_value;
- int index = result->index();
-
- Function* function = result->function();
- tree return_value = function->return_value();
- const Typed_identifier_list* results = function->type()->results();
- if (results->size() == 1)
- {
- gcc_assert(index == 0);
- return return_value;
+ Type* type = result->type();
+ if (type->is_error_type() || type->is_undefined())
+ {
+ // Force the error.
+ type->base();
+ decl = error_mark_node;
}
else
{
- tree field;
- for (field = TYPE_FIELDS(TREE_TYPE(return_value));
- index > 0;
- --index, field = TREE_CHAIN(field))
- gcc_assert(field != NULL_TREE);
- return build3(COMPONENT_REF, TREE_TYPE(field), return_value,
- field, NULL_TREE);
+ gcc_assert(result->function() == function->func_value());
+ source_location loc = function->location();
+ tree result_type = type->get_tree(gogo);
+ tree init;
+ if (!result->is_in_heap())
+ init = type->get_init_tree(gogo, false);
+ else
+ {
+ result_type = build_pointer_type(result_type);
+ tree space = gogo->allocate_memory(TYPE_SIZE_UNIT(result_type),
+ loc);
+ tree subinit = type->get_init_tree(gogo, true);
+ if (subinit == NULL_TREE)
+ init = fold_convert_loc(loc, result_type, space);
+ else
+ {
+ space = save_expr(space);
+ space = fold_convert_loc(loc, result_type, space);
+ tree spaceref = build_fold_indirect_ref_loc(loc, space);
+ tree set = fold_build2_loc(loc, MODIFY_EXPR, void_type_node,
+ spaceref, subinit);
+ init = fold_build2_loc(loc, COMPOUND_EXPR, TREE_TYPE(space),
+ set, space);
+ }
+ }
+ decl = build_decl(loc, VAR_DECL, name, result_type);
+ tree fnid = function->get_id(gogo);
+ tree fndecl = function->func_value()->get_or_make_decl(gogo,
+ function,
+ fnid);
+ DECL_CONTEXT(decl) = fndecl;
+ DECL_INITIAL(decl) = init;
+ TREE_USED(decl) = 1;
}
}
break;
@@ -954,8 +992,10 @@
// If this is a local variable whose address is taken, then we
// actually store it in the heap. For uses of the variable we need
// to return a reference to that heap location.
- if (this->classification_ == NAMED_OBJECT_VAR
- && this->var_value()->is_in_heap()
+ if (((this->classification_ == NAMED_OBJECT_VAR
+ && this->var_value()->is_in_heap())
+ || (this->classification_ == NAMED_OBJECT_RESULT_VAR
+ && this->result_var_value()->is_in_heap()))
&& ret != error_mark_node)
{
gcc_assert(POINTER_TYPE_P(TREE_TYPE(ret)));
@@ -1002,14 +1042,16 @@
gcc_assert(this->preinit_ != NULL);
// We want to add the variable assignment to the end of the preinit
- // block. The preinit block may have a TRY_FINALLY_EXPR; if it
- // does, we want to add to the end of the regular statements.
+ // block. The preinit block may have a TRY_FINALLY_EXPR and a
+ // TRY_CATCH_EXPR; if it does, we want to add to the end of the
+ // regular statements.
Translate_context context(gogo, function, NULL, NULL_TREE);
tree block_tree = this->preinit_->get_tree(&context);
gcc_assert(TREE_CODE(block_tree) == BIND_EXPR);
tree statements = BIND_EXPR_BODY(block_tree);
- if (TREE_CODE(statements) == TRY_FINALLY_EXPR)
+ while (TREE_CODE(statements) == TRY_FINALLY_EXPR
+ || TREE_CODE(statements) == TRY_CATCH_EXPR)
statements = TREE_OPERAND(statements, 0);
// It's possible to have pre-init statements without an initializer
@@ -1091,6 +1133,21 @@
if (this->enclosing_ != NULL)
DECL_STATIC_CHAIN(decl) = 1;
+ // If a function calls the predeclared recover function, we
+ // can't inline it, because recover behaves differently in a
+ // function passed directly to defer.
+ if (this->calls_recover_ && !this->is_recover_thunk_)
+ DECL_UNINLINABLE(decl) = 1;
+
+ // If this is a thunk created to call a function which calls
+ // the predeclared recover function, we need to disable
+ // stack splitting for the thunk.
+ if (this->is_recover_thunk_)
+ {
+ tree attr = get_identifier("__no_split_stack__");
+ DECL_ATTRIBUTES(decl) = tree_cons(attr, NULL_TREE, NULL_TREE);
+ }
+
go_preserve_from_gc(decl);
if (this->closure_var_ != NULL)
@@ -1275,17 +1332,6 @@
tree params = NULL_TREE;
tree* pp = ¶ms;
- // If we have named return values, we allocate a tree to hold them
- // in case there are any return statements which don't mention any
- // expressions. We can't just use DECL_RESULT because it might be a
- // list of registers.
- const Typed_identifier_list* results = this->type_->results();
- if (results != NULL
- && !results->empty()
- && !results->front().name().empty())
- this->return_value_ = create_tmp_var(TREE_TYPE(TREE_TYPE(fndecl)),
- "RETURN");
-
tree declare_vars = NULL_TREE;
for (Bindings::const_definitions_iterator p =
this->block_->bindings()->begin_definitions();
@@ -1330,6 +1376,18 @@
pp = &TREE_CHAIN(*pp);
}
}
+ else if ((*p)->is_result_variable())
+ {
+ tree var_decl = (*p)->get_tree(gogo, named_function);
+ if ((*p)->result_var_value()->is_in_heap())
+ {
+ gcc_assert(TREE_CODE(var_decl) == INDIRECT_REF);
+ var_decl = TREE_OPERAND(var_decl, 0);
+ }
+ gcc_assert(TREE_CODE(var_decl) == VAR_DECL);
+ TREE_CHAIN(var_decl) = declare_vars;
+ declare_vars = var_decl;
+ }
}
*pp = NULL_TREE;
@@ -1358,6 +1416,7 @@
tree code = this->block_->get_tree(&context);
tree init = NULL_TREE;
+ tree except = NULL_TREE;
tree fini = NULL_TREE;
source_location end_loc = this->block_->end_location();
@@ -1366,10 +1425,7 @@
{
tree dv = build1(DECL_EXPR, void_type_node, v);
SET_EXPR_LOCATION(dv, DECL_SOURCE_LOCATION(v));
- if (init == NULL_TREE)
- init = dv;
- else
- init = build2(COMPOUND_EXPR, void_type_node, init, dv);
+ append_to_statement_list(dv, &init);
}
// If there is a reference count queue, initialize it at the
@@ -1379,11 +1435,13 @@
if (have_refcounts)
{
tree iq = this->refcounts_->init_queue(gogo, this->location_);
- if (init == NULL_TREE)
- init = iq;
- else
- init = build2(COMPOUND_EXPR, void_type_node, init, iq);
- }
+ append_to_statement_list(iq, &init);
+ }
+
+ // Flush the reference count queue when we leave the function.
+ tree flush = NULL_TREE;
+ if (have_refcounts)
+ flush = this->refcounts_->flush_queue(gogo, true, end_loc);
// If we have a defer stack, initialize it at the start of a
// function.
@@ -1391,69 +1449,219 @@
{
tree defer_init = build1(DECL_EXPR, void_type_node,
this->defer_stack_);
- if (init == NULL_TREE)
- init = defer_init;
- else
- init = build2(COMPOUND_EXPR, void_type_node, init, defer_init);
- }
-
- // Clean up the defer stack when we leave the function.
- if (this->defer_stack_ != NULL_TREE)
+ SET_EXPR_LOCATION(defer_init, this->block_->start_location());
+ append_to_statement_list(defer_init, &init);
+
+ // Clean up the defer stack when we leave the function.
+ this->build_defer_wrapper(gogo, named_function, flush, &except,
+ &fini);
+ flush = NULL_TREE;
+ }
+
+ if (flush != NULL_TREE)
{
gcc_assert(fini == NULL_TREE);
- static tree undefer_fndecl;
- fini = Gogo::call_builtin(&undefer_fndecl,
+ fini = flush;
+ }
+
+ if (code != NULL_TREE && code != error_mark_node)
+ {
+ if (init != NULL_TREE)
+ code = build2(COMPOUND_EXPR, void_type_node, init, code);
+ if (except != NULL_TREE)
+ code = build2(TRY_CATCH_EXPR, void_type_node, code,
+ build2(CATCH_EXPR, void_type_node, NULL, except));
+ if (fini != NULL_TREE)
+ code = build2(TRY_FINALLY_EXPR, void_type_node, code, fini);
+ }
+
+ // Stick the code into the block we built for the receiver, if
+ // we built on.
+ if (bind != NULL_TREE && code != NULL_TREE && code != error_mark_node)
+ {
+ BIND_EXPR_BODY(bind) = code;
+ code = bind;
+ }
+
+ DECL_SAVED_TREE(fndecl) = code;
+ }
+}
+
+// Build the wrappers around function code needed if the function has
+// any defer statements. This sets *EXCEPT to an exception handler
+// and *FINI to a finally handler. FLUSH is run in *FINI if not NULL.
+
+void
+Function::build_defer_wrapper(Gogo* gogo, Named_object* named_function,
+ tree flush, tree *except, tree *fini)
+{
+ source_location end_loc = this->block_->end_location();
+
+ // Add an exception handler. This is used if a panic occurs. Its
+ // purpose is to stop the stack unwinding if a deferred function
+ // calls recover. There are more details in
+ // libgo/runtime/go-unwind.c.
+ tree stmt_list = NULL_TREE;
+ static tree check_fndecl;
+ tree call = Gogo::call_builtin(&check_fndecl,
+ end_loc,
+ "__go_check_defer",
+ 1,
+ void_type_node,
+ ptr_type_node,
+ this->defer_stack(end_loc));
+ append_to_statement_list(call, &stmt_list);
+
+ tree retval = this->return_value(gogo, named_function, end_loc, &stmt_list);
+ tree set;
+ if (retval == NULL_TREE)
+ set = NULL_TREE;
+ else
+ set = fold_build2_loc(end_loc, MODIFY_EXPR, void_type_node,
+ DECL_RESULT(this->fndecl_), retval);
+ tree ret_stmt = fold_build1_loc(end_loc, RETURN_EXPR, void_type_node, set);
+ append_to_statement_list(ret_stmt, &stmt_list);
+
+ gcc_assert(*except == NULL_TREE);
+ *except = stmt_list;
+
+ // Add some finally code to run the defer functions. This is used
+ // both in the normal case, when no panic occurs, and also if a
+ // panic occurs to run any further defer functions. Of course, it
+ // is possible for a defer function to call panic which should be
+ // caught by another defer function. To handle that we use a loop.
+ // finish:
+ // try { __go_undefer(); } catch { __go_check_defer(); goto finish; }
+ // if (return values are named) return named_vals;
+
+ stmt_list = NULL;
+
+ tree label = create_artificial_label(end_loc);
+ tree define_label = fold_build1_loc(end_loc, LABEL_EXPR, void_type_node,
+ label);
+ append_to_statement_list(define_label, &stmt_list);
+
+ static tree undefer_fndecl;
+ tree undefer = Gogo::call_builtin(&undefer_fndecl,
end_loc,
"__go_undefer",
1,
void_type_node,
ptr_type_node,
- this->defer_stack_);
- }
-
- // Flush the reference count queue when we leave the function.
- if (have_refcounts)
- {
- tree flush = this->refcounts_->flush_queue(gogo, true, end_loc);
- if (fini == NULL_TREE)
- fini = flush;
+ this->defer_stack(end_loc));
+ TREE_NOTHROW(undefer_fndecl) = 0;
+
+ tree defer = Gogo::call_builtin(&check_fndecl,
+ end_loc,
+ "__go_check_defer",
+ 1,
+ void_type_node,
+ ptr_type_node,
+ this->defer_stack(end_loc));
+ tree jump = fold_build1_loc(end_loc, GOTO_EXPR, void_type_node, label);
+ tree catch_body = build2(COMPOUND_EXPR, void_type_node, defer, jump);
+ catch_body = build2(CATCH_EXPR, void_type_node, NULL, catch_body);
+ tree try_catch = build2(TRY_CATCH_EXPR, void_type_node, undefer, catch_body);
+
+ append_to_statement_list(try_catch, &stmt_list);
+
+ if (flush != NULL_TREE)
+ append_to_statement_list(flush, &stmt_list);
+
+ if (this->type_->results() != NULL
+ && !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.
+ 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);
+ }
+
+ gcc_assert(*fini == NULL_TREE);
+ *fini = stmt_list;
+}
+
+// Return the value to assign to DECL_RESULT(this->fndecl_). This may
+// also add statements to STMT_LIST, which need to be executed before
+// the assignment. This is used for a return statement with no
+// explicit values.
+
+tree
+Function::return_value(Gogo* gogo, Named_object* named_function,
+ source_location location, tree* stmt_list) const
+{
+ const Typed_identifier_list* results = this->type_->results();
+ if (results == NULL || results->empty())
+ return NULL_TREE;
+
+ // In the case of an exception handler created for functions with
+ // defer statements, the result variables may be unnamed.
+ bool is_named = !results->front().name().empty();
+ if (is_named)
+ gcc_assert(this->named_results_ != NULL
+ && this->named_results_->size() == results->size());
+
+ tree retval;
+ if (results->size() == 1)
+ {
+ if (is_named)
+ return this->named_results_->front()->get_tree(gogo, named_function);
+ else
+ return results->front().type()->get_init_tree(gogo, false);
+ }
+ else
+ {
+ tree rettype = TREE_TYPE(DECL_RESULT(this->fndecl_));
+ retval = create_tmp_var(rettype, "RESULT");
+ tree field = TYPE_FIELDS(rettype);
+ int index = 0;
+ for (Typed_identifier_list::const_iterator pr = results->begin();
+ pr != results->end();
+ ++pr, ++index, field = TREE_CHAIN(field))
+ {
+ gcc_assert(field != NULL);
+ tree val;
+ if (is_named)
+ val = (*this->named_results_)[index]->get_tree(gogo,
+ named_function);
else
- fini = build2(COMPOUND_EXPR, void_type_node, fini, flush);
- }
-
- if (code != NULL_TREE && code != error_mark_node)
- {
- if (init != NULL_TREE)
- code = build2(COMPOUND_EXPR, void_type_node, init, code);
- if (fini != NULL_TREE)
- code = build2(TRY_FINALLY_EXPR, void_type_node, code, fini);
- }
-
- // Stick the code into the block we built for the receiver, if
- // we built on.
- if (bind != NULL_TREE && code != NULL_TREE && code != error_mark_node)
- {
- BIND_EXPR_BODY(bind) = code;
- code = bind;
- }
-
- DECL_SAVED_TREE(fndecl) = code;
+ val = pr->type()->get_init_tree(gogo, false);
+ tree set = fold_build2_loc(location, MODIFY_EXPR, void_type_node,
+ build3(COMPONENT_REF, TREE_TYPE(field),
+ retval, field, NULL_TREE),
+ val);
+ append_to_statement_list(set, stmt_list);
+ }
+ return retval;
}
}
// Get the tree for the variable holding the defer stack for this
-// function.
-
-tree
-Function::defer_stack()
+// function. 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 function which uses defer.
+
+tree
+Function::defer_stack(source_location location)
{
if (this->defer_stack_ == NULL_TREE)
{
tree var = create_tmp_var(ptr_type_node, "DEFER");
DECL_INITIAL(var) = null_pointer_node;
+ DECL_SOURCE_LOCATION(var) = location;
+ TREE_ADDRESSABLE(var) = 1;
this->defer_stack_ = var;
}
- return this->defer_stack_;
+ return fold_convert_loc(location, ptr_type_node,
+ build_fold_addr_expr_loc(location,
+ this->defer_stack_));
}
// Get a tree for the statements in a block.
@@ -1535,25 +1743,6 @@
tree statements = NULL_TREE;
- // Named result variables--the only sort of result variable we will
- // see in the bindings--must be explicitly zero-initialized. Since
- // these are not regular DECLs, this is not done anywhere else.
- for (Bindings::const_definitions_iterator pv =
- this->bindings_->begin_definitions();
- pv != this->bindings_->end_definitions();
- ++pv)
- {
- if ((*pv)->is_result_variable())
- {
- Result_variable* rv = (*pv)->result_var_value();
- tree init_tree = rv->type()->get_init_tree(gogo, false);
- tree statement = build2(MODIFY_EXPR, void_type_node,
- (*pv)->get_tree(gogo, context->function()),
- init_tree);
- append_to_statement_list(statement, &statements);
- }
- }
-
// Expand the statements.
for (std::vector<Statement*>::const_iterator p = this->statements_.begin();
@@ -1613,6 +1802,18 @@
return this->decl_;
}
+// Return an expression for the address of this label.
+
+tree
+Label::get_addr(source_location location)
+{
+ tree decl = this->get_decl();
+ TREE_USED(decl) = 1;
+ TREE_ADDRESSABLE(decl) = 1;
+ return fold_convert_loc(location, ptr_type_node,
+ build_fold_addr_expr_loc(location, decl));
+}
+
// Get the LABEL_DECL for an unnamed label.
tree
@@ -3787,33 +3988,41 @@
if (blocking)
{
static tree send_small_fndecl;
- return Gogo::call_builtin(&send_small_fndecl,
- location,
- "__go_send_small",
- 3,
- void_type_node,
- ptr_type_node,
- channel,
- uint64_type_node,
- val,
- boolean_type_node,
- (for_select
- ? boolean_true_node
- : boolean_false_node));
+ tree ret = Gogo::call_builtin(&send_small_fndecl,
+ location,
+ "__go_send_small",
+ 3,
+ void_type_node,
+ ptr_type_node,
+ channel,
+ uint64_type_node,
+ val,
+ boolean_type_node,
+ (for_select
+ ? boolean_true_node
+ : boolean_false_node));
+ // This can panic if there are too many operations on a
+ // closed channel.
+ TREE_NOTHROW(send_small_fndecl) = 0;
+ return ret;
}
else
{
gcc_assert(!for_select);
static tree send_nonblocking_small_fndecl;
- return Gogo::call_builtin(&send_nonblocking_small_fndecl,
- location,
- "__go_send_nonblocking_small",
- 2,
- boolean_type_node,
- ptr_type_node,
- channel,
- uint64_type_node,
- val);
+ tree ret = Gogo::call_builtin(&send_nonblocking_small_fndecl,
+ location,
+ "__go_send_nonblocking_small",
+ 2,
+ boolean_type_node,
+ ptr_type_node,
+ channel,
+ uint64_type_node,
+ val);
+ // This can panic if there are too many operations on a
+ // closed channel.
+ TREE_NOTHROW(send_nonblocking_small_fndecl) = 0;
+ return ret;
}
}
else
@@ -3855,6 +4064,9 @@
(for_select
? boolean_true_node
: boolean_false_node));
+ // This can panic if there are too many operations on a
+ // closed channel.
+ TREE_NOTHROW(send_big_fndecl) = 0;
}
else
{
@@ -3869,6 +4081,9 @@
channel,
ptr_type_node,
val);
+ // This can panic if there are too many operations on a
+ // closed channel.
+ TREE_NOTHROW(send_nonblocking_big_fndecl) = 0;
}
if (make_tmp == NULL_TREE)
@@ -3907,6 +4122,9 @@
(for_select
? boolean_true_node
: boolean_false_node));
+ // This can panic if there are too many operations on a closed
+ // channel.
+ TREE_NOTHROW(receive_small_fndecl) = 0;
int bitsize = GET_MODE_BITSIZE(TYPE_MODE(type_tree));
tree int_type_tree = go_type_for_size(bitsize, 1);
return fold_convert_loc(location, type_tree,
@@ -3936,6 +4154,9 @@
(for_select
? boolean_true_node
: boolean_false_node));
+ // This can panic if there are too many operations on a closed
+ // channel.
+ TREE_NOTHROW(receive_big_fndecl) = 0;
return build2(COMPOUND_EXPR, type_tree, make_tmp,
build2(COMPOUND_EXPR, type_tree, call, tmp));
}
@@ -157,15 +157,21 @@
print_type->set_is_builtin();
this->globals_->add_function_declaration("println", NULL, print_type, loc);
- Function_type* panic_type = Type::make_function_type(NULL, NULL, NULL, loc);
- panic_type->set_is_varargs();
+ Type *empty = Type::make_interface_type(NULL, loc);
+ Typed_identifier_list* panic_parms = new Typed_identifier_list();
+ panic_parms->push_back(Typed_identifier("e", empty, loc));
+ Function_type *panic_type = Type::make_function_type(NULL, panic_parms,
+ NULL, loc);
panic_type->set_is_builtin();
this->globals_->add_function_declaration("panic", NULL, panic_type, loc);
- panic_type = Type::make_function_type(NULL, NULL, NULL, loc);
- panic_type->set_is_varargs();
- panic_type->set_is_builtin();
- this->globals_->add_function_declaration("panicln", NULL, panic_type, loc);
+ Typed_identifier_list* recover_result = new Typed_identifier_list();
+ recover_result->push_back(Typed_identifier("", empty, loc));
+ Function_type* recover_type = Type::make_function_type(NULL, NULL,
+ recover_result,
+ loc);
+ recover_type->set_is_builtin();
+ this->globals_->add_function_declaration("recover", NULL, recover_type, loc);
Function_type* close_type = Type::make_function_type(NULL, NULL, NULL, loc);
close_type->set_is_varargs();
@@ -590,21 +596,7 @@
}
}
- const Typed_identifier_list* results = type->results();
- if (results != NULL
- && !results->empty()
- && !results->front().name().empty())
- {
- int index = 0;
- for (Typed_identifier_list::const_iterator p = results->begin();
- p != results->end();
- ++p, ++index)
- {
- Result_variable* result = new Result_variable(p->type(), function,
- index);
- block->bindings()->add_result_variable(p->name(), result);
- }
- }
+ function->create_named_result_variables();
const std::string* pname;
std::string nested_name;
@@ -1897,6 +1889,331 @@
this->traverse(&order_eval);
}
+// Traversal to convert calls to the predeclared recover function to
+// pass in an argument indicating whether it can recover from a panic
+// or not.
+
+class Convert_recover : public Traverse
+{
+ public:
+ Convert_recover(Named_object* arg)
+ : Traverse(traverse_expressions),
+ arg_(arg)
+ { }
+
+ protected:
+ int
+ expression(Expression**);
+
+ private:
+ // The argument to pass to the function.
+ Named_object* arg_;
+};
+
+// Convert calls to recover.
+
+int
+Convert_recover::expression(Expression** pp)
+{
+ Call_expression* ce = (*pp)->call_expression();
+ if (ce != NULL && ce->is_recover_call())
+ ce->set_recover_arg(Expression::make_var_reference(this->arg_,
+ ce->location()));
+ return TRAVERSE_CONTINUE;
+}
+
+// Traversal for build_recover_thunks.
+
+class Build_recover_thunks : public Traverse
+{
+ public:
+ Build_recover_thunks(Gogo* gogo)
+ : Traverse(traverse_functions),
+ gogo_(gogo)
+ { }
+
+ int
+ function(Named_object*);
+
+ private:
+ Expression*
+ can_recover_arg(source_location);
+
+ // General IR.
+ Gogo* gogo_;
+};
+
+// If this function calls recover, turn it into a thunk.
+
+int
+Build_recover_thunks::function(Named_object* orig_no)
+{
+ Function* orig_func = orig_no->func_value();
+ if (!orig_func->calls_recover()
+ || orig_func->is_recover_thunk()
+ || orig_func->has_recover_thunk())
+ return TRAVERSE_CONTINUE;
+
+ Gogo* gogo = this->gogo_;
+ source_location location = orig_func->location();
+
+ static int count;
+ char buf[50];
+
+ Function_type* orig_fntype = orig_func->type();
+ Typed_identifier_list* new_params = new Typed_identifier_list();
+ std::string receiver_name;
+ if (orig_fntype->is_method())
+ {
+ const Typed_identifier* receiver = orig_fntype->receiver();
+ snprintf(buf, sizeof buf, "rt.%u", count);
+ ++count;
+ receiver_name = buf;
+ new_params->push_back(Typed_identifier(receiver_name, receiver->type(),
+ receiver->location()));
+ }
+ const Typed_identifier_list* orig_params = orig_fntype->parameters();
+ if (orig_params != NULL && !orig_params->empty())
+ {
+ for (Typed_identifier_list::const_iterator p = orig_params->begin();
+ p != orig_params->end();
+ ++p)
+ {
+ snprintf(buf, sizeof buf, "pt.%u", count);
+ ++count;
+ new_params->push_back(Typed_identifier(buf, p->type(),
+ p->location()));
+ }
+ }
+ snprintf(buf, sizeof buf, "pr.%u", count);
+ ++count;
+ std::string can_recover_name = buf;
+ new_params->push_back(Typed_identifier(can_recover_name,
+ Type::make_boolean_type(),
+ orig_fntype->location()));
+
+ const Typed_identifier_list* orig_results = orig_fntype->results();
+ Typed_identifier_list* new_results;
+ if (orig_results == NULL || orig_results->empty())
+ new_results = NULL;
+ else
+ {
+ new_results = new Typed_identifier_list();
+ for (Typed_identifier_list::const_iterator p = orig_results->begin();
+ p != orig_results->end();
+ ++p)
+ new_results->push_back(*p);
+ }
+
+ Function_type *new_fntype = Type::make_function_type(NULL, new_params,
+ new_results,
+ orig_fntype->location());
+ if (orig_fntype->is_varargs())
+ new_fntype->set_is_varargs();
+
+ std::string name = orig_no->name() + "$recover";
+ Named_object *new_no = gogo->start_function(name, new_fntype, false,
+ location);
+ Function *new_func = new_no->func_value();
+ if (orig_func->enclosing() != NULL)
+ new_func->set_enclosing(orig_func->enclosing());
+
+ // We build the code for the original function attached to the new
+ // function, and then swap the original and new function bodies.
+ // This means that existing references to the original function will
+ // then refer to the new function. That makes this code a little
+ // confusing, in that the reference to NEW_NO really refers to the
+ // other function, not the one we are building.
+
+ Expression* closure = NULL;
+ if (orig_func->needs_closure())
+ {
+ Named_object* orig_closure_no = orig_func->closure_var();
+ Variable* orig_closure_var = orig_closure_no->var_value();
+ Variable* new_var = new Variable(orig_closure_var->type(), NULL, false,
+ true, false, location);
+ snprintf(buf, sizeof buf, "closure.%u", count);
+ ++count;
+ Named_object* new_closure_no = Named_object::make_variable(buf, NULL,
+ new_var);
+ new_func->set_closure_var(new_closure_no);
+ closure = Expression::make_var_reference(new_closure_no, location);
+ }
+
+ Expression* fn = Expression::make_func_reference(new_no, closure, location);
+
+ Expression_list* args = new Expression_list();
+ if (orig_fntype->is_method())
+ {
+ Named_object* rec_no = gogo->lookup(receiver_name, NULL);
+ gcc_assert(rec_no != NULL
+ && rec_no->is_variable()
+ && rec_no->var_value()->is_parameter());
+ args->push_back(Expression::make_var_reference(rec_no, location));
+ }
+ if (new_params != NULL)
+ {
+ // Note that we skip the last parameter, which is the boolean
+ // indicating whether recover can succed.
+ for (Typed_identifier_list::const_iterator p = new_params->begin();
+ p + 1 != new_params->end();
+ ++p)
+ {
+ Named_object* p_no = gogo->lookup(p->name(), NULL);
+ gcc_assert(p_no != NULL
+ && p_no->is_variable()
+ && p_no->var_value()->is_parameter());
+ args->push_back(Expression::make_var_reference(p_no, location));
+ }
+ }
+ args->push_back(this->can_recover_arg(location));
+
+ Expression* call = Expression::make_call(fn, args, location);
+
+ Statement* s;
+ if (orig_fntype->results() == NULL || orig_fntype->results()->empty())
+ s = Statement::make_statement(call);
+ else
+ {
+ Expression_list* vals = new Expression_list();
+ vals->push_back(call);
+ s = Statement::make_return_statement(new_func->type()->results(),
+ vals, location);
+ }
+ s->determine_types();
+ gogo->add_statement(s);
+
+ gogo->finish_function(location);
+
+ // Swap the function bodies and types.
+ new_func->swap_for_recover(orig_func);
+ orig_func->set_is_recover_thunk();
+ new_func->set_calls_recover();
+ new_func->set_has_recover_thunk();
+
+ Bindings* orig_bindings = orig_func->block()->bindings();
+ Bindings* new_bindings = new_func->block()->bindings();
+ if (orig_fntype->is_method())
+ {
+ // We changed the receiver to be a regular parameter. We have
+ // to update the binding accordingly in both functions.
+ Named_object* orig_rec_no = orig_bindings->lookup_local(receiver_name);
+ gcc_assert(orig_rec_no != NULL
+ && orig_rec_no->is_variable()
+ && !orig_rec_no->var_value()->is_receiver());
+ orig_rec_no->var_value()->set_is_receiver();
+
+ Named_object* new_rec_no = new_bindings->lookup_local(receiver_name);
+ gcc_assert(new_rec_no != NULL
+ && new_rec_no->is_variable()
+ && !new_rec_no->var_value()->is_receiver());
+ new_rec_no->var_value()->set_is_not_receiver();
+ }
+
+ // Because we flipped blocks but not types, the can_recover
+ // parameter appears in the (now) old bindings as a parameter.
+ // Change it to a local variable, whereupon it will be discarded.
+ Named_object* can_recover_no = orig_bindings->lookup_local(can_recover_name);
+ gcc_assert(can_recover_no != NULL
+ && can_recover_no->is_variable()
+ && can_recover_no->var_value()->is_parameter());
+ orig_bindings->remove_binding(can_recover_no);
+
+ // Add the can_recover argument to the (now) new bindings, and
+ // attach it to any recover statements.
+ Variable* can_recover_var = new Variable(Type::make_boolean_type(), NULL,
+ false, true, false, location);
+ can_recover_no = new_bindings->add_variable(can_recover_name, NULL,
+ can_recover_var);
+ Convert_recover convert_recover(can_recover_no);
+ new_func->traverse(&convert_recover);
+
+ return TRAVERSE_CONTINUE;
+}
+
+// Return the expression to pass for the .can_recover parameter to the
+// new function. This indicates whether a call to recover may return
+// non-nil. The expression is
+// __go_can_recover(__builtin_return_address()).
+
+Expression*
+Build_recover_thunks::can_recover_arg(source_location location)
+{
+ static Named_object* builtin_return_address;
+ if (builtin_return_address == NULL)
+ {
+ const source_location bloc = BUILTINS_LOCATION;
+
+ Typed_identifier_list* param_types = new Typed_identifier_list();
+ Type* uint_type = Type::lookup_integer_type("uint");
+ param_types->push_back(Typed_identifier("l", uint_type, bloc));
+
+ Typed_identifier_list* return_types = new Typed_identifier_list();
+ Type* voidptr_type = Type::make_pointer_type(Type::make_void_type());
+ return_types->push_back(Typed_identifier("", voidptr_type, bloc));
+
+ Function_type* fntype = Type::make_function_type(NULL, param_types,
+ return_types, bloc);
+ builtin_return_address =
+ Named_object::make_function_declaration("__builtin_return_address",
+ NULL, fntype, bloc);
+ const char* n = "__builtin_return_address";
+ builtin_return_address->func_declaration_value()->set_asm_name(n);
+ }
+
+ static Named_object* can_recover;
+ if (can_recover == NULL)
+ {
+ const source_location bloc = BUILTINS_LOCATION;
+ Typed_identifier_list* param_types = new Typed_identifier_list();
+ Type* voidptr_type = Type::make_pointer_type(Type::make_void_type());
+ param_types->push_back(Typed_identifier("a", voidptr_type, bloc));
+ Type* boolean_type = Type::make_boolean_type();
+ Typed_identifier_list* results = new Typed_identifier_list();
+ results->push_back(Typed_identifier("", boolean_type, bloc));
+ Function_type* fntype = Type::make_function_type(NULL, param_types,
+ results, bloc);
+ can_recover = Named_object::make_function_declaration("__go_can_recover",
+ NULL, fntype,
+ bloc);
+ can_recover->func_declaration_value()->set_asm_name("__go_can_recover");
+ }
+
+ Expression* fn = Expression::make_func_reference(builtin_return_address,
+ NULL, location);
+
+ mpz_t zval;
+ mpz_init_set_ui(zval, 0UL);
+ Expression* zexpr = Expression::make_integer(&zval, NULL, location);
+ mpz_clear(zval);
+ Expression_list *args = new Expression_list();
+ args->push_back(zexpr);
+
+ Expression* call = Expression::make_call(fn, args, location);
+
+ args = new Expression_list();
+ args->push_back(call);
+
+ fn = Expression::make_func_reference(can_recover, NULL, location);
+ return Expression::make_call(fn, args, location);
+}
+
+// Build thunks for functions which call recover. We build a new
+// function with an extra parameter, which is whether a call to
+// recover can succeed. We then move the body of this function to
+// that one. We then turn this function into a thunk which calls the
+// new one, passing the value of
+// __go_can_recover(__builtin_return_address()). The function will be
+// marked as not splitting the stack. This will cooperate with the
+// implementation of defer to make recover do the right thing.
+
+void
+Gogo::build_recover_thunks()
+{
+ Build_recover_thunks build_recover_thunks(this);
+ this->traverse(&build_recover_thunks);
+}
+
// Look for named types to see whether we need to create an interface
// method table.
@@ -2199,10 +2516,39 @@
Function::Function(Function_type* type, Function* enclosing, Block* block,
source_location location)
- : type_(type), enclosing_(enclosing), closure_var_(NULL), refcounts_(NULL),
- block_(block), location_(location), fndecl_(NULL), return_value_(NULL),
- defer_stack_(NULL)
-{
+ : type_(type), enclosing_(enclosing), named_results_(NULL),
+ closure_var_(NULL), refcounts_(NULL), block_(block), location_(location),
+ fndecl_(NULL), defer_stack_(NULL), calls_recover_(false),
+ is_recover_thunk_(false), has_recover_thunk_(false)
+{
+}
+
+// Create the named result variables.
+
+void
+Function::create_named_result_variables()
+{
+ const Typed_identifier_list* results = this->type_->results();
+ if (results == NULL
+ || results->empty()
+ || results->front().name().empty())
+ return;
+
+ this->named_results_ = new Named_results();
+ this->named_results_->reserve(results->size());
+
+ Block* block = this->block_;
+ int index = 0;
+ for (Typed_identifier_list::const_iterator p = results->begin();
+ p != results->end();
+ ++p, ++index)
+ {
+ Result_variable* result = new Result_variable(p->type(), this,
+ index);
+ Named_object* no = block->bindings()->add_result_variable(p->name(),
+ result);
+ this->named_results_->push_back(no);
+ }
}
// Return the closure variable, creating it if necessary.
@@ -2243,7 +2589,11 @@
char buf[20];
snprintf(buf, sizeof buf, "%u", index);
std::string n = no->name() + buf;
- Type* var_type = no->var_value()->type();
+ Type* var_type;
+ if (no->is_variable())
+ var_type = no->var_value()->type();
+ else
+ var_type = no->result_var_value()->type();
Type* field_type = Type::make_pointer_type(var_type);
st->push_field(Struct_field(Typed_identifier(n, field_type, p->second)));
}
@@ -2325,6 +2675,23 @@
}
}
+// Swap one function with another. This is used when building the
+// thunk we use to call a function which calls recover. It may not
+// work for any other case.
+
+void
+Function::swap_for_recover(Function *x)
+{
+ gcc_assert(this->enclosing_ == x->enclosing_);
+ gcc_assert(this->named_results_ == x->named_results_);
+ std::swap(this->closure_var_, x->closure_var_);
+ gcc_assert(this->refcounts_ == NULL && x->refcounts_ == NULL);
+ std::swap(this->block_, x->block_);
+ gcc_assert(this->location_ == x->location_);
+ gcc_assert(this->fndecl_ == NULL && x->fndecl_ == NULL);
+ gcc_assert(this->defer_stack_ == NULL && x->defer_stack_ == NULL);
+}
+
// Traverse the tree.
int
@@ -3517,6 +3884,28 @@
return p->second;
}
+// Remove an object from a set of bindings. This is used for a
+// special case in thunks for functions which call recover.
+
+void
+Bindings::remove_binding(Named_object* no)
+{
+ Contour::iterator pb = this->bindings_.find(no->name());
+ gcc_assert(pb != this->bindings_.end());
+ this->bindings_.erase(pb);
+ for (std::vector<Named_object*>::iterator pn = this->named_objects_.begin();
+ pn != this->named_objects_.end();
+ ++pn)
+ {
+ if (*pn == no)
+ {
+ this->named_objects_.erase(pn);
+ return;
+ }
+ }
+ gcc_unreachable();
+}
+
// Add a method to the list of objects. This is not added to the
// lookup table. This is so that we have a single list of objects
// declared at the top level, which we walk through when it's time to
@@ -393,6 +393,10 @@
void
order_evaluations();
+ // Build thunks for functions which call recover.
+ void
+ build_recover_thunks();
+
// Simplify statements which might use thunks: go and defer
// statements.
void
@@ -785,6 +789,11 @@
public:
Block(Block* enclosing, source_location);
+ // Return the enclosing block.
+ const Block*
+ enclosing() const
+ { return this->enclosing_; }
+
// Return the bindings of the block.
Bindings*
bindings()
@@ -906,6 +915,19 @@
enclosing()
{ return this->enclosing_; }
+ // Set the enclosing function. This is used when building thunks
+ // for functions which call recover.
+ void
+ set_enclosing(Function* enclosing)
+ {
+ gcc_assert(this->enclosing_ == NULL);
+ this->enclosing_ = enclosing;
+ }
+
+ // Create the named result variables in the outer block.
+ void
+ create_named_result_variables();
+
// Add a new field to the closure variable.
void
add_closure_field(Named_object* var, source_location loc)
@@ -921,6 +943,15 @@
Named_object*
closure_var();
+ // Set the closure variable. This is used when building thunks for
+ // functions which call recover.
+ void
+ set_closure_var(Named_object* v)
+ {
+ gcc_assert(this->closure_var_ == NULL);
+ this->closure_var_ = v;
+ }
+
// Return the variable for a reference to field INDEX in the closure
// variable.
Named_object*
@@ -960,6 +991,43 @@
Label*
add_label_reference(const std::string& label_name);
+ // Whether this function calls the predeclared recover function.
+ bool
+ calls_recover() const
+ { return this->calls_recover_; }
+
+ // Record that this function calls the predeclared recover function.
+ // This is set during the lowering pass.
+ void
+ set_calls_recover()
+ { this->calls_recover_ = true; }
+
+ // Whether this is a recover thunk function.
+ bool
+ is_recover_thunk() const
+ { return this->is_recover_thunk_; }
+
+ // Record that this is a thunk built for a function which calls
+ // recover.
+ void
+ set_is_recover_thunk()
+ { this->is_recover_thunk_ = true; }
+
+ // Whether this function already has a recover thunk.
+ bool
+ has_recover_thunk() const
+ { return this->has_recover_thunk_; }
+
+ // Record that this function already has a recover thunk.
+ void
+ set_has_recover_thunk()
+ { this->has_recover_thunk_ = true; }
+
+ // Swap with another function. Used only for the thunk which calls
+ // recover.
+ void
+ swap_for_recover(Function *);
+
// Traverse the tree.
int
traverse(Traverse*);
@@ -984,17 +1052,14 @@
void
build_tree(Gogo*, Named_object*);
- // Get a tree for the return value.
+ // Get the value to return when not explicitly specified. May also
+ // add statements to execute first to STMT_LIST.
tree
- return_value()
- {
- gcc_assert(this->return_value_ != NULL);
- return this->return_value_;
- }
+ return_value(Gogo*, Named_object*, source_location, tree* stmt_list) const;
// Get a tree for the variable holding the defer stack.
tree
- defer_stack();
+ defer_stack(source_location);
// Export the function.
void
@@ -1021,6 +1086,11 @@
tree
copy_parm_to_heap(Gogo*, tree);
+ void
+ build_defer_wrapper(Gogo*, Named_object*, tree, tree*, tree*);
+
+ typedef std::vector<Named_object*> Named_results;
+
typedef std::vector<std::pair<Named_object*,
source_location> > Closure_fields;
@@ -1029,6 +1099,8 @@
// The enclosing function. This is NULL when there isn't one, which
// is the normal case.
Function* enclosing_;
+ // The named result variables, if any.
+ Named_results* named_results_;
// If there is a closure, this is the list of variables which appear
// in the closure. This is created by the parser, and then resolved
// to a real type when we lower parse trees.
@@ -1047,12 +1119,15 @@
Labels labels_;
// The function decl.
tree fndecl_;
- // If the function has named return values, this variable holds the
- // value returned by a return statement which does not name a value.
- tree return_value_;
- // A variable holding the defer stack. This is NULL unless we
- // actually need a defer stack.
+ // A variable holding the defer stack variable. This is NULL unless
+ // we actually need a defer stack.
tree defer_stack_;
+ // True if this function calls the predeclared recover function.
+ bool calls_recover_;
+ // True if this a thunk built for a function which calls recover.
+ bool is_recover_thunk_;
+ // True if this function already has a recover thunk.
+ bool has_recover_thunk_;
};
// A function declaration.
@@ -1149,6 +1224,24 @@
is_receiver() const
{ return this->is_receiver_; }
+ // Change this parameter to be a receiver. This is used when
+ // creating the thunks created for functions which call recover.
+ void
+ set_is_receiver()
+ {
+ gcc_assert(this->is_parameter_);
+ this->is_receiver_ = true;
+ }
+
+ // Change this parameter to not be a receiver. This is used when
+ // creating the thunks created for functions which call recover.
+ void
+ set_is_not_receiver()
+ {
+ gcc_assert(this->is_parameter_);
+ this->is_receiver_ = false;
+ }
+
// Return whether this is the varargs parameter of a function.
bool
is_varargs_parameter() const
@@ -1349,7 +1442,8 @@
{
public:
Result_variable(Type* type, Function* function, int index)
- : type_(type), function_(function), index_(index)
+ : type_(type), function_(function), index_(index),
+ is_address_taken_(false)
{ }
// Get the type of the result variable.
@@ -1367,6 +1461,21 @@
index() const
{ return this->index_; }
+ // Whether this variable's address is taken.
+ bool
+ is_address_taken() const
+ { return this->is_address_taken_; }
+
+ // Note that something takes the address of this variable.
+ void
+ set_address_taken()
+ { this->is_address_taken_ = true; }
+
+ // Whether this variable should live in the heap.
+ bool
+ is_in_heap() const
+ { return this->is_address_taken_; }
+
private:
// Type of result variable.
Type* type_;
@@ -1374,6 +1483,8 @@
Function* function_;
// Index in list of results.
int index_;
+ // Whether something takes the address of this variable.
+ bool is_address_taken_;
};
// The value we keep for a named constant. This lets us hold a type
@@ -2008,6 +2119,10 @@
Named_object*
lookup_local(const std::string&) const;
+ // Remove a name.
+ void
+ remove_binding(Named_object*);
+
// Traverse the tree. See the Traverse class.
int
traverse(Traverse*, bool is_global);
@@ -2115,6 +2230,10 @@
tree
get_decl();
+ // Return an expression for the address of this label.
+ tree
+ get_addr(source_location location);
+
private:
// The name of the label.
std::string name_;
@@ -2237,13 +2237,7 @@
Parse::enclosing_var_reference(Named_object* in_function, Named_object* var,
source_location location)
{
- if (var->is_result_variable())
- {
- error_at(location, "reference to out parameter in an enclosing function");
- return Expression::make_error(location);
- }
-
- gcc_assert(var->is_variable());
+ gcc_assert(var->is_variable() || var->is_result_variable());
Named_object* this_function = this->gogo_->current_function();
Named_object* closure = this_function->func_value()->closure_var();
@@ -3451,7 +3445,8 @@
if (this->expression_may_start_here())
vals = this->expression_list(NULL, false);
const Function* function = this->gogo_->current_function()->func_value();
- this->gogo_->add_statement(Statement::make_return_statement(function, vals,
+ const Typed_identifier_list* results = function->type()->results();
+ this->gogo_->add_statement(Statement::make_return_statement(results, vals,
location));
}
@@ -1548,10 +1548,8 @@
else
fntype = NULL;
- // The builtin functions panic and panicln do not return.
- if ((no->name() == "panic" || no->name() == "panicln")
- && fntype != NULL
- && fntype->is_builtin())
+ // The builtin function panic does not return.
+ if (fntype != NULL && fntype->is_builtin() && no->name() == "panic")
return false;
}
}
@@ -1699,6 +1697,11 @@
if (fntype->is_method() || fntype->is_varargs())
return false;
+ // A defer statement requires a thunk to set up for whether the
+ // function can call recover.
+ if (this->classification() == STATEMENT_DEFER)
+ return false;
+
// We can only permit a single parameter of pointer type.
const Typed_identifier_list* parameters = fntype->parameters();
if (parameters != NULL
@@ -1760,10 +1763,11 @@
bool
Thunk_statement::do_traverse_assignments(Traverse_assignments* tassign)
{
+ // FIXME: This doesn't work, because we might get a REFCOUNT_ADJUST
+ // which we will never execute.
Expression* fn = this->call_->call_expression()->fn();
Expression* fn2 = fn;
tassign->value(&fn2, true, false);
- gcc_assert(fn == fn2);
return true;
}
@@ -2003,6 +2007,15 @@
Typed_identifier tid(Go_statement::thunk_field_fn, fntype, location);
fields->push_back(Struct_field(tid));
}
+ else if (ce->is_recover_call())
+ {
+ // The predeclared recover function has no argument. However,
+ // we add an argument when building recover thunks. Handle that
+ // here.
+ fields->push_back(Struct_field(Typed_identifier("can_recover",
+ Type::make_boolean_type(),
+ location)));
+ }
if (fn->bound_method_expression() != NULL)
{
@@ -2043,6 +2056,24 @@
{
source_location location = this->location();
+ Call_expression* ce = this->call_->call_expression();
+
+ bool may_call_recover = false;
+ if (this->classification() == STATEMENT_DEFER)
+ {
+ Func_expression* fn = ce->fn()->func_expression();
+ if (fn == NULL)
+ may_call_recover = true;
+ else
+ {
+ const Named_object* no = fn->named_object();
+ if (!no->is_function())
+ may_call_recover = true;
+ else
+ may_call_recover = no->func_value()->calls_recover();
+ }
+ }
+
// Build the type of the thunk. The thunk takes a single parameter,
// which is a pointer to the special structure we build.
const char* const parameter_name = "__go_thunk_parameter";
@@ -2050,14 +2081,77 @@
Type* pointer_to_struct_type = Type::make_pointer_type(this->struct_type_);
thunk_parameters->push_back(Typed_identifier(parameter_name,
pointer_to_struct_type,
- this->location()));
+ location));
+
+ Typed_identifier_list* thunk_results = NULL;
+ if (may_call_recover)
+ {
+ // When deferring a function which may call recover, add a
+ // return value, to disable tail call optimizations which will
+ // break the way we check whether recover is permitted.
+ thunk_results = new Typed_identifier_list();
+ thunk_results->push_back(Typed_identifier("", Type::make_boolean_type(),
+ location));
+ }
+
Function_type* thunk_type = Type::make_function_type(NULL, thunk_parameters,
- NULL, this->location());
+ thunk_results,
+ location);
// Start building the thunk.
Named_object* function = gogo->start_function(thunk_name, thunk_type, true,
location);
+ // For a defer statement, start with a call to
+ // __go_set_defer_retaddr. */
+ Label* retaddr_label = NULL;
+ if (may_call_recover)
+ {
+ retaddr_label = gogo->add_label_reference("retaddr");
+ Expression* arg = Expression::make_label_addr(retaddr_label, location);
+ Expression_list* args = new Expression_list();
+ args->push_back(arg);
+
+ static Named_object* set_defer_retaddr;
+ if (set_defer_retaddr == NULL)
+ {
+ const source_location bloc = BUILTINS_LOCATION;
+ Typed_identifier_list* param_types = new Typed_identifier_list();
+ Type *voidptr_type = Type::make_pointer_type(Type::make_void_type());
+ param_types->push_back(Typed_identifier("r", voidptr_type, bloc));
+
+ Typed_identifier_list* result_types = new Typed_identifier_list();
+ result_types->push_back(Typed_identifier("",
+ Type::make_boolean_type(),
+ bloc));
+
+ Function_type* t = Type::make_function_type(NULL, param_types,
+ result_types, bloc);
+ set_defer_retaddr =
+ Named_object::make_function_declaration("__go_set_defer_retaddr",
+ NULL, t, bloc);
+ const char* n = "__go_set_defer_retaddr";
+ set_defer_retaddr->func_declaration_value()->set_asm_name(n);
+ }
+
+ Expression* fn = Expression::make_func_reference(set_defer_retaddr,
+ NULL, location);
+ Expression* call = Expression::make_call(fn, args, location);
+
+ // This is a hack to prevent the middle-end from deleting the
+ // label.
+ gogo->start_block(location);
+ gogo->add_statement(Statement::make_goto_statement(retaddr_label,
+ location));
+ Block* then_block = gogo->finish_block(location);
+ then_block->determine_types();
+
+ Statement* s = Statement::make_if_statement(call, then_block, NULL,
+ location);
+ s->determine_types();
+ gogo->add_statement(s);
+ }
+
// Get a reference to the parameter.
Named_object* named_parameter = gogo->lookup(parameter_name, NULL);
gcc_assert(named_parameter != NULL && named_parameter->is_variable());
@@ -2067,7 +2161,6 @@
Expression* thunk_parameter = Expression::make_var_reference(named_parameter,
location);
- Call_expression* ce = this->call_->call_expression();
Bound_method_expression* bound_method = ce->fn()->bound_method_expression();
Interface_field_reference_expression* interface_method =
ce->fn()->interface_field_reference_expression();
@@ -2125,6 +2218,12 @@
Expression* call = Expression::make_call(func_to_call, call_params, location);
// We need to lower in case this is a builtin function.
call = call->lower(gogo, function, -1);
+ if (may_call_recover)
+ {
+ Call_expression* ce = call->call_expression();
+ if (ce != NULL)
+ ce->set_is_deferred();
+ }
Statement* call_statement = Statement::make_statement(call);
@@ -2134,6 +2233,20 @@
gogo->add_statement(call_statement);
+ // If this is a defer statement, the label comes immediately after
+ // the call.
+ if (may_call_recover)
+ {
+ gogo->add_label_definition("retaddr", location);
+
+ Expression_list* vals = new Expression_list();
+ vals->push_back(Expression::make_boolean(false, location));
+ const Typed_identifier_list* results =
+ function->func_value()->type()->results();
+ gogo->add_statement(Statement::make_return_statement(results, vals,
+ location));
+ }
+
// FIXME: Now we need to decrement the reference count of the
// parameter.
@@ -2226,6 +2339,8 @@
tree
Defer_statement::do_get_tree(Translate_context* context)
{
+ source_location loc = this->location();
+
tree fn_tree;
tree arg_tree;
this->get_fn_and_arg(context, &fn_tree, &arg_tree);
@@ -2243,20 +2358,19 @@
fn_arg_type = build_pointer_type(subfntype);
}
- tree defer_stack = context->function()->func_value()->defer_stack();
-
- tree call = Gogo::call_builtin(&defer_fndecl,
- this->location(),
- "__go_defer",
- 3,
- ptr_type_node,
- ptr_type_node,
- defer_stack,
- fn_arg_type,
- fn_tree,
- ptr_type_node,
- arg_tree);
- return build2(MODIFY_EXPR, void_type_node, defer_stack, call);
+ tree defer_stack = context->function()->func_value()->defer_stack(loc);
+
+ return Gogo::call_builtin(&defer_fndecl,
+ loc,
+ "__go_defer",
+ 3,
+ void_type_node,
+ ptr_type_node,
+ defer_stack,
+ fn_arg_type,
+ fn_tree,
+ ptr_type_node,
+ arg_tree);
}
// Make a defer statement.
@@ -2311,6 +2425,125 @@
return true;
}
+// 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.
+
+Statement*
+Return_statement::do_lower(Gogo*, Block* enclosing)
+{
+ if (this->vals_ == NULL)
+ return this;
+
+ const Typed_identifier_list* results = this->results_;
+ if (results == NULL || results->empty())
+ return this;
+
+ // If the current function has multiple return values, and we are
+ // returning a single call expression, split up the call expression.
+ size_t results_count = results->size();
+ if (results_count > 1
+ && this->vals_->size() == 1
+ && this->vals_->front()->call_expression() != NULL)
+ {
+ Call_expression* call = this->vals_->front()->call_expression();
+ size_t count = results->size();
+ Expression_list* vals = new Expression_list;
+ for (size_t i = 0; i < count; ++i)
+ vals->push_back(Expression::make_call_result(call, i));
+ delete this->vals_;
+ this->vals_ = vals;
+ }
+
+ if (results->front().name().empty())
+ return this;
+
+ if (results_count != this->vals_->size())
+ {
+ // Presumably an error which will be reported in check_types.
+ return this;
+ }
+
+ // Assign to named return values and then return them.
+
+ source_location loc = this->location();
+ const Block* top = enclosing;
+ while (top->enclosing() != NULL)
+ top = top->enclosing();
+
+ const Bindings *bindings = top->bindings();
+ Block* b = new Block(enclosing, loc);
+
+ Expression_list* lhs = new Expression_list();
+ Expression_list* rhs = new Expression_list();
+
+ Expression_list::const_iterator pe = this->vals_->begin();
+ int i = 1;
+ for (Typed_identifier_list::const_iterator pr = results->begin();
+ pr != results->end();
+ ++pr, ++pe, ++i)
+ {
+ Named_object* rv = bindings->lookup_local(pr->name());
+ if (rv == NULL || !rv->is_result_variable())
+ {
+ // Presumably an error.
+ delete b;
+ delete lhs;
+ delete rhs;
+ return this;
+ }
+
+ Expression* e = *pe;
+
+ // Check types now so that we give a good error message. The
+ // result type is known. We determine the expression type
+ // early.
+
+ Type *rvtype = rv->result_var_value()->type();
+ Type_context type_context(rvtype, false);
+ e->determine_type(&type_context);
+
+ std::string reason;
+ if (Type::are_compatible_for_assign(rvtype, e->type(), &reason))
+ {
+ Expression* ve = Expression::make_var_reference(rv, e->location());
+ lhs->push_back(ve);
+ rhs->push_back(e);
+ }
+ else
+ {
+ if (reason.empty())
+ error_at(e->location(), "incompatible type for return value %d", i);
+ else
+ error_at(e->location(),
+ "incompatible type for return value %d (%s)",
+ i, reason.c_str());
+ }
+ }
+ gcc_assert(lhs->size() == rhs->size());
+
+ if (lhs->empty())
+ ;
+ else if (lhs->size() == 1)
+ {
+ b->add_statement(Statement::make_assignment(lhs->front(), rhs->front(),
+ loc));
+ delete lhs;
+ delete rhs;
+ }
+ else
+ b->add_statement(Statement::make_tuple_assignment(lhs, rhs, loc));
+
+ b->add_statement(Statement::make_return_statement(this->results_, NULL,
+ loc));
+
+ return Statement::make_block_statement(b, loc);
+}
+
// Determine types.
void
@@ -2318,40 +2551,16 @@
{
if (this->vals_ == NULL)
return;
- Function_type* type = this->function_->type();
- const Typed_identifier_list* results = type->results();
- if (results == NULL)
- return;
-
- // If the current function has multiple return values, and we are
- // returning a single function call expression, split up the call
- // expression. We have to determine the type of the call expression
- // first, because we don't know how many values it returns until
- // method references are resolved.
- if (results->size() > 1
- && this->vals_->size() == 1
- && this->vals_->front()->call_expression() != NULL)
- {
- Call_expression* call = this->vals_->front()->call_expression();
- call->determine_type_no_context();
- size_t count = call->result_count();
- if (count > 1)
- {
- Expression_list* vals = new Expression_list;
- for (size_t i = 0; i < count; ++i)
- vals->push_back(Expression::make_call_result(call, i));
- delete this->vals_;
- this->vals_ = vals;
- }
- return;
- }
-
- Typed_identifier_list::const_iterator pt = results->begin();
+ const Typed_identifier_list* results = this->results_;
+
+ Typed_identifier_list::const_iterator pt;
+ if (results != NULL)
+ pt = results->begin();
for (Expression_list::iterator pe = this->vals_->begin();
pe != this->vals_->end();
++pe)
{
- if (pt == results->end())
+ if (results == NULL || pt == results->end())
(*pe)->determine_type_no_context();
else
{
@@ -2370,9 +2579,7 @@
if (this->vals_ == NULL)
return;
- Function_type* type = this->function_->type();
-
- const Typed_identifier_list* results = type->results();
+ const Typed_identifier_list* results = this->results_;
if (results == NULL)
{
this->report_error(_("return with value in function "
@@ -2380,7 +2587,7 @@
return;
}
- int i = 0;
+ int i = 1;
Typed_identifier_list::const_iterator pt = results->begin();
for (Expression_list::const_iterator pe = this->vals_->begin();
pe != this->vals_->end();
@@ -2425,21 +2632,27 @@
tree
Return_statement::do_get_tree(Translate_context* context)
{
- tree fndecl = context->function()->func_value()->get_decl();
-
- gcc_assert(this->function_ == context->function()->func_value());
- Function_type* type = this->function_->type();
- const Typed_identifier_list* results = type->results();
+ Function* function = context->function()->func_value();
+ tree fndecl = function->get_decl();
+
+ const Typed_identifier_list* results = this->results_;
if (this->vals_ == NULL)
{
- tree retval;
- if (VOID_TYPE_P(TREE_TYPE(TREE_TYPE(fndecl))))
- retval = NULL_TREE;
- else
- retval = build2(MODIFY_EXPR, void_type_node, DECL_RESULT(fndecl),
- context->function()->func_value()->return_value());
- return this->build_stmt_1(RETURN_EXPR, retval);
+ tree stmt_list = NULL_TREE;
+ tree retval = function->return_value(context->gogo(),
+ context->function(),
+ this->location(),
+ &stmt_list);
+ tree set;
+ if (retval == NULL_TREE)
+ set = NULL_TREE;
+ else
+ set = fold_build2_loc(this->location(), MODIFY_EXPR, void_type_node,
+ DECL_RESULT(fndecl), retval);
+ append_to_statement_list(this->build_stmt_1(RETURN_EXPR, set),
+ &stmt_list);
+ return stmt_list;
}
else if (this->vals_->size() == 1)
{
@@ -2495,11 +2708,11 @@
// Make a return statement.
Statement*
-Statement::make_return_statement(const Function* function,
+Statement::make_return_statement(const Typed_identifier_list* results,
Expression_list* vals,
source_location location)
{
- return new Return_statement(function, vals, location);
+ return new Return_statement(results, vals, location);
}
// A break or continue statement.
@@ -39,6 +39,7 @@
class Case_clauses;
class Type_case_clauses;
class Select_clauses;
+class Typed_identifier_list;
class Refcounts;
class Refcount_entry;
@@ -206,7 +207,8 @@
// Make a return statement.
static Statement*
- make_return_statement(const Function*, Expression_list*, source_location);
+ make_return_statement(const Typed_identifier_list*, Expression_list*,
+ source_location);
// Make a break statement.
static Statement*
@@ -567,10 +569,10 @@
class Return_statement : public Statement
{
public:
- Return_statement(const Function* function, Expression_list* vals,
+ Return_statement(const Typed_identifier_list* results, Expression_list* vals,
source_location location)
: Statement(STATEMENT_RETURN, location),
- function_(function), vals_(vals), do_not_increment_(NULL)
+ results_(results), vals_(vals), do_not_increment_(NULL)
{ }
// The list of values being returned. This may be NULL.
@@ -597,6 +599,9 @@
bool
do_traverse_assignments(Traverse_assignments*);
+ Statement*
+ do_lower(Gogo*, Block*);
+
void
do_determine_types();
@@ -611,9 +616,10 @@
do_get_tree(Translate_context*);
private:
- // The function we are returning from. We use this to get the
- // return types.
- const Function* function_;
+ // The result types of the function we are returning from. This is
+ // here because in some of the traversals it is inconvenient to get
+ // it.
+ const Typed_identifier_list* results_;
// Return values. This may be NULL.
Expression_list* vals_;
// List of variables whose reference count should not be
@@ -6029,7 +6029,8 @@
retvals->push_back(Expression::make_call_result(call, i));
}
const Function* function = gogo->current_function()->func_value();
- Statement* retstat = Statement::make_return_statement(function, retvals,
+ const Typed_identifier_list* results = function->type()->results();
+ Statement* retstat = Statement::make_return_statement(results, retvals,
location);
gogo->add_statement(retstat);
}