This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[gccgo] Implement order of evaluation rules
- From: Ian Lance Taylor <iant at google dot com>
- To: gcc-patches at gcc dot gnu dot org
- Date: Fri, 18 Dec 2009 20:42:25 -0800
- Subject: [gccgo] Implement order of evaluation rules
Go partially defines the order of evaluation in expressions. In
particular function calls are evaluated from left to right--that is,
in the call a(b(), c()) the order of calls is defined to be b(), c(),
a(). This patch implements the order of evaluation as a new pass. In
order to make it work smoothly, I also added a pass to convert the
logical shortcut operators || and && into if statements. That let me
cleanly add temporary variables to enforce the order of evaluation.
This patch doesn't work for expressions which appear in switch
statements, because I don't have a place to put the temporary
variables. A future patch will fix this, probably by converting
switch statements with non-constant case expressions into a series of
if statements. I have already written a test case for the Go
testsuite to check this case.
Committed to gccgo branch.
Ian
Index: gogo.cc
===================================================================
--- gogo.cc (revision 155270)
+++ gogo.cc (working copy)
@@ -1066,13 +1066,22 @@ Lower_parse_tree::function(Named_object*
// Lower statement parse trees.
int
-Lower_parse_tree::statement(Block* block, size_t* pindex, Statement* s)
+Lower_parse_tree::statement(Block* block, size_t* pindex, Statement* sorig)
{
- Statement* snew = s->lower(this->gogo_);
- if (snew == s)
+ // Keep lowering until nothing changes.
+ Statement* s = sorig;
+ while (true)
+ {
+ Statement* snew = s->lower(this->gogo_);
+ if (snew == s)
+ break;
+ s = snew;
+ }
+ if (s == sorig)
return TRAVERSE_CONTINUE;
- block->replace_statement(*pindex, snew);
- int t = snew->traverse(block, pindex, this);
+
+ block->replace_statement(*pindex, s);
+ int t = s->traverse(block, pindex, this);
if (t != TRAVERSE_CONTINUE)
return t;
return TRAVERSE_SKIP_COMPONENTS;
@@ -1336,6 +1345,436 @@ Gogo::check_types_in_block(Block* block)
block->traverse(&traverse);
}
+// A traversal class used to find a single shortcut operator within an
+// expression.
+
+class Find_shortcut : public Traverse
+{
+ public:
+ Find_shortcut()
+ : Traverse(traverse_blocks
+ | traverse_statements
+ | traverse_expressions),
+ found_(NULL)
+ { }
+
+ // A pointer to the expression which was found, or NULL if none was
+ // found.
+ Expression**
+ found() const
+ { return this->found_; }
+
+ protected:
+ int
+ block(Block*)
+ { return TRAVERSE_SKIP_COMPONENTS; }
+
+ int
+ statement(Block*, size_t*, Statement*)
+ { return TRAVERSE_SKIP_COMPONENTS; }
+
+ int
+ expression(Expression**);
+
+ private:
+ Expression** found_;
+};
+
+// Find a shortcut expression.
+
+int
+Find_shortcut::expression(Expression** pexpr)
+{
+ Expression* expr = *pexpr;
+ Binary_expression* be = expr->binary_expression();
+ if (be == NULL)
+ return TRAVERSE_CONTINUE;
+ Operator op = be->op();
+ if (op != OPERATOR_OROR && op != OPERATOR_ANDAND)
+ return TRAVERSE_CONTINUE;
+ gcc_assert(this->found_ == NULL);
+ this->found_ = pexpr;
+ return TRAVERSE_EXIT;
+}
+
+// A traversal class used to turn shortcut operators into explicit if
+// statements.
+
+class Shortcuts : public Traverse
+{
+ public:
+ Shortcuts()
+ : Traverse(traverse_variables
+ | traverse_statements)
+ { }
+
+ protected:
+ int
+ variable(Named_object*);
+
+ int
+ statement(Block*, size_t*, Statement*);
+
+ private:
+ // Convert a shortcut operator.
+ Statement*
+ convert_shortcut(Block* enclosing, Expression** pshortcut);
+};
+
+// Remove shortcut operators in a single statement.
+
+int
+Shortcuts::statement(Block* block, size_t* pindex, Statement* s)
+{
+ // FIXME: This approach doesn't work for switch statements, because
+ // we add the new statements before the whole switch when we need to
+ // instead add them just before the switch expression. The right
+ // fix is probably to lower switch statements with nonconstant cases
+ // to a series of conditionals.
+ if (s->switch_statement() != NULL)
+ return TRAVERSE_CONTINUE;
+
+ while (true)
+ {
+ Find_shortcut find_shortcut;
+
+ // If S is a variable declaration, then ordinary traversal won't
+ // do anything. We want to explicitly traverse the
+ // initialization expression if there is one.
+ Variable_declaration_statement* vds = s->variable_declaration_statement();
+ Expression* init = NULL;
+ if (vds == NULL)
+ s->traverse_contents(&find_shortcut);
+ else
+ {
+ init = vds->var()->var_value()->init();
+ if (init == NULL)
+ return TRAVERSE_CONTINUE;
+ init->traverse(&init, &find_shortcut);
+ }
+ Expression** pshortcut = find_shortcut.found();
+ if (pshortcut == NULL)
+ return TRAVERSE_CONTINUE;
+
+ Statement* snew = this->convert_shortcut(block, pshortcut);
+ if (s->for_statement() == NULL)
+ {
+ block->insert_statement_before(*pindex, snew);
+ ++*pindex;
+ }
+ else
+ {
+ // The only expression in a for statement is the conditional
+ // expression. We want to run the statement each time the
+ // conditional is run, not once before the for loop.
+ s->for_statement()->insert_before_conditional(block, snew);
+ }
+
+ if (pshortcut == &init)
+ vds->var()->var_value()->set_init(init);
+ }
+}
+
+// Remove shortcut operators in the initializer of a global variable.
+
+int
+Shortcuts::variable(Named_object* no)
+{
+ if (no->is_result_variable())
+ return TRAVERSE_CONTINUE;
+ Variable* var = no->var_value();
+ Expression* init = var->init();
+ if (!var->is_global() || init == NULL)
+ return TRAVERSE_CONTINUE;
+
+ while (true)
+ {
+ Find_shortcut find_shortcut;
+ init->traverse(&init, &find_shortcut);
+ Expression** pshortcut = find_shortcut.found();
+ if (pshortcut == NULL)
+ return TRAVERSE_CONTINUE;
+
+ Statement* snew = this->convert_shortcut(NULL, pshortcut);
+ var->add_preinit_statement(snew);
+ if (pshortcut == &init)
+ var->set_init(init);
+ }
+}
+
+// Given an expression which uses a shortcut operator, return a
+// statement which implements it, and update *PSHORTCUT accordingly.
+
+Statement*
+Shortcuts::convert_shortcut(Block* enclosing, Expression** pshortcut)
+{
+ Binary_expression* shortcut = (*pshortcut)->binary_expression();
+ Expression* left = shortcut->left();
+ Expression* right = shortcut->right();
+ source_location loc = shortcut->location();
+
+ Block* retblock = new Block(enclosing, loc);
+ retblock->set_end_location(loc);
+
+ Temporary_statement* ts = Statement::make_temporary(Type::make_boolean_type(),
+ left, loc);
+ retblock->add_statement(ts);
+
+ Block* block = new Block(retblock, loc);
+ block->set_end_location(loc);
+ Expression* tmpref = Expression::make_temporary_reference(ts, loc);
+ Statement* assign = Statement::make_assignment(OPERATOR_EQ, tmpref, right,
+ loc);
+ block->add_statement(assign);
+
+ Expression* cond = Expression::make_temporary_reference(ts, loc);
+ if (shortcut->binary_expression()->op() == OPERATOR_OROR)
+ cond = Expression::make_unary(OPERATOR_NOT, cond, loc);
+
+ Statement* if_statement = Statement::make_if_statement(cond, block, NULL,
+ loc);
+ retblock->add_statement(if_statement);
+
+ *pshortcut = Expression::make_temporary_reference(ts, loc);
+
+ delete shortcut;
+
+ // Now convert any shortcut operators in LEFT and RIGHT.
+ Shortcuts shortcuts;
+ retblock->traverse(&shortcuts);
+
+ return Statement::make_block_statement(retblock, loc);
+}
+
+// Turn shortcut operators into explicit if statements. Doing this
+// considerably simplifies the order of evaluation rules.
+
+void
+Gogo::remove_shortcuts()
+{
+ Shortcuts shortcuts;
+ this->traverse(&shortcuts);
+}
+
+// A traversal class which finds all the expressions which must be
+// evaluated in order within a statement or larger expression. This
+// is used to implement the rules about order of evaluation.
+
+class Find_eval_ordering : public Traverse
+{
+ private:
+ typedef std::vector<Expression**> Expression_pointers;
+
+ public:
+ Find_eval_ordering()
+ : Traverse(traverse_blocks
+ | traverse_statements
+ | traverse_expressions),
+ exprs_()
+ { }
+
+ size_t
+ size() const
+ { return this->exprs_.size(); }
+
+ typedef Expression_pointers::const_iterator const_iterator;
+
+ const_iterator
+ begin() const
+ { return this->exprs_.begin(); }
+
+ const_iterator
+ end() const
+ { return this->exprs_.end(); }
+
+ protected:
+ int
+ block(Block*)
+ { return TRAVERSE_SKIP_COMPONENTS; }
+
+ int
+ statement(Block*, size_t*, Statement*)
+ { return TRAVERSE_SKIP_COMPONENTS; }
+
+ int
+ expression(Expression**);
+
+ private:
+ // A list of pointers to expressions with side-effects.
+ Expression_pointers exprs_;
+};
+
+// If an expression must be evaluated in order, put it on the list.
+
+int
+Find_eval_ordering::expression(Expression** expression_pointer)
+{
+ // We have to look at subexpressions before this one.
+ if ((*expression_pointer)->traverse_subexpressions(this) == TRAVERSE_EXIT)
+ return TRAVERSE_EXIT;
+ if ((*expression_pointer)->must_eval_in_order())
+ this->exprs_.push_back(expression_pointer);
+ return TRAVERSE_SKIP_COMPONENTS;
+}
+
+// A traversal class for ordering evaluations.
+
+class Order_eval : public Traverse
+{
+ public:
+ Order_eval()
+ : Traverse(traverse_variables
+ | traverse_statements)
+ { }
+
+ int
+ variable(Named_object*);
+
+ int
+ statement(Block*, size_t*, Statement*);
+};
+
+// Implement the order of evaluation rules for a statement.
+
+int
+Order_eval::statement(Block* block, size_t* pindex, Statement* s)
+{
+ // FIXME: This approach doesn't work for switch statements, because
+ // we add the new statements before the whole switch when we need to
+ // instead add them just before the switch expression. The right
+ // fix is probably to lower switch statements with nonconstant cases
+ // to a series of conditionals.
+ if (s->switch_statement() != NULL)
+ return TRAVERSE_CONTINUE;
+
+ Find_eval_ordering find_eval_ordering;
+
+ // If S is a variable declaration, then ordinary traversal won't do
+ // anything. We want to explicitly traverse the initialization
+ // expression if there is one.
+ Variable_declaration_statement* vds = s->variable_declaration_statement();
+ if (vds == NULL)
+ s->traverse_contents(&find_eval_ordering);
+ else
+ {
+ Expression* init = vds->var()->var_value()->init();
+ if (init == NULL)
+ return TRAVERSE_CONTINUE;
+ init->traverse_subexpressions(&find_eval_ordering);
+ }
+
+ if (find_eval_ordering.size() <= 1)
+ {
+ // If there is only one expression with a side-effect, we can
+ // leave it in place.
+ return TRAVERSE_CONTINUE;
+ }
+
+ bool is_thunk = s->thunk_statement() != NULL;
+ for (Find_eval_ordering::const_iterator p = find_eval_ordering.begin();
+ p != find_eval_ordering.end();
+ ++p)
+ {
+ Expression** pexpr = *p;
+
+ // If the last expression is a send or receive expression, we
+ // may be ignoring the value; we don't want to evaluate it
+ // early.
+ if (p + 1 == find_eval_ordering.end()
+ && ((*pexpr)->classification() == Expression::EXPRESSION_SEND
+ || (*pexpr)->classification() == Expression::EXPRESSION_RECEIVE))
+ break;
+
+ // The last expression in a thunk will be the call passed to go
+ // or defer, which we must not evaluate early.
+ if (is_thunk && p + 1 == find_eval_ordering.end())
+ break;
+
+ source_location loc = (*pexpr)->location();
+ Temporary_statement* ts = Statement::make_temporary(NULL, *pexpr,
+ loc);
+ if (s->for_statement() == NULL)
+ {
+ block->insert_statement_before(*pindex, ts);
+ ++*pindex;
+ }
+ else
+ {
+ // The only expression in a for statement is the conditional
+ // expression. We want to run the statement each time the
+ // conditional is run, not once before the for loop.
+ s->for_statement()->insert_before_conditional(block, ts);
+ }
+ *pexpr = Expression::make_temporary_reference(ts, loc);
+ Statement* sdestroy = Statement::destroy_temporary(ts);
+ if (sdestroy != NULL)
+ {
+ // FIXME: This does the wrong thing for an expression in a
+ // compound statement (if, switch, select) and also does the
+ // wrong thing for a return statement. The destruction gets
+ // put after the entire statement rather than after the
+ // expression. The reference counting code needs to be
+ // revamped anyhow.
+ if (s->for_statement() != NULL)
+ s->for_statement()->insert_after_conditional(block, sdestroy);
+ else if (s->return_statement() == NULL)
+ block->insert_statement_after(*pindex, sdestroy);
+ }
+ }
+
+ return TRAVERSE_CONTINUE;
+}
+
+// Implement the order of evaluation rules for the initializer of a
+// global variable.
+
+int
+Order_eval::variable(Named_object* no)
+{
+ if (no->is_result_variable())
+ return TRAVERSE_CONTINUE;
+ Variable* var = no->var_value();
+ Expression* init = var->init();
+ if (!var->is_global() || init == NULL)
+ return TRAVERSE_CONTINUE;
+
+ Find_eval_ordering find_eval_ordering;
+ init->traverse_subexpressions(&find_eval_ordering);
+
+ if (find_eval_ordering.size() <= 1)
+ {
+ // If there is only one expression with a side-effect, we can
+ // leave it in place.
+ return TRAVERSE_SKIP_COMPONENTS;
+ }
+
+ for (Find_eval_ordering::const_iterator p = find_eval_ordering.begin();
+ p != find_eval_ordering.end();
+ ++p)
+ {
+ Expression** pexpr = *p;
+ source_location loc = (*pexpr)->location();
+ Temporary_statement* ts = Statement::make_temporary(NULL, *pexpr,
+ loc);
+ var->add_preinit_statement(ts);
+ *pexpr = Expression::make_temporary_reference(ts, loc);
+ Statement* sdestroy = Statement::destroy_temporary(ts);
+ if (sdestroy != NULL)
+ var->add_postinit_statement(sdestroy);
+ }
+
+ return TRAVERSE_SKIP_COMPONENTS;
+}
+
+// Use temporary variables to implement the order of evaluation rules.
+
+void
+Gogo::order_evaluations()
+{
+ Order_eval order_eval;
+ this->traverse(&order_eval);
+}
+
// A dump for find_only_arg_vars.
static Go_dump find_only_arg_vars_dump("argvars");
@@ -1882,9 +2321,19 @@ Block::replace_statement(size_t index, S
void
Block::insert_statement_before(size_t index, Statement* s)
{
+ gcc_assert(index < this->statements_.size());
this->statements_.insert(this->statements_.begin() + index, s);
}
+// Add a statement after another statement.
+
+void
+Block::insert_statement_after(size_t index, Statement* s)
+{
+ gcc_assert(index < this->statements_.size());
+ this->statements_.insert(this->statements_.begin() + index + 1, s);
+}
+
// Traverse the tree.
int
@@ -1997,7 +2446,11 @@ Block::traverse(Traverse* traverse)
// No point in checking traverse_mask here--if we got here we always
// want to walk the statements. The traversal can insert new
- // statements, but only before the current statement.
+ // statements before or after the current statement. Inserting
+ // statements before the current statement requires updating I via
+ // the pointer; those statements will not be traversed. Any new
+ // statements inserted after the current statement will be traversed
+ // in their turn.
for (size_t i = 0; i < this->statements_.size(); ++i)
{
if (this->statements_[i]->traverse(this, &i, traverse) == TRAVERSE_EXIT)
@@ -2044,7 +2497,8 @@ Block::may_fall_through() const
Variable::Variable(Type* type, Expression* init, bool is_global,
bool is_parameter, bool is_receiver,
source_location location)
- : type_(type), init_(init), location_(location), is_global_(is_global),
+ : type_(type), init_(init), preinit_(NULL), postinit_(NULL),
+ location_(location), is_global_(is_global),
is_parameter_(is_parameter), is_receiver_(is_receiver),
is_varargs_parameter_(false), is_address_taken_(false),
holds_only_args_(false), init_is_lowered_(false),
@@ -2061,9 +2515,22 @@ Variable::Variable(Type* type, Expressio
int
Variable::traverse_expression(Traverse* traverse)
{
- if (this->init_ == NULL)
- return TRAVERSE_CONTINUE;
- return Expression::traverse(&this->init_, traverse);
+ if (this->preinit_ != NULL)
+ {
+ if (this->preinit_->traverse(traverse) == TRAVERSE_EXIT)
+ return TRAVERSE_EXIT;
+ }
+ if (this->init_ != NULL)
+ {
+ if (Expression::traverse(&this->init_, traverse) == TRAVERSE_EXIT)
+ return TRAVERSE_EXIT;
+ }
+ if (this->postinit_ != NULL)
+ {
+ if (this->postinit_->traverse(traverse) == TRAVERSE_EXIT)
+ return TRAVERSE_EXIT;
+ }
+ return TRAVERSE_CONTINUE;
}
// Lower the initialization expression after parsing is complete.
@@ -2079,6 +2546,30 @@ Variable::lower_init_expression(Gogo* go
}
}
+// Add a statement to be run before the initialization expression.
+
+void
+Variable::add_preinit_statement(Statement* s)
+{
+ gcc_assert(this->is_global_);
+ if (this->preinit_ == NULL)
+ this->preinit_ = new Block(NULL, s->location());
+ this->preinit_->add_statement(s);
+ this->preinit_->set_end_location(s->location());
+}
+
+// Add a statement to be run after the initialization expression.
+
+void
+Variable::add_postinit_statement(Statement* s)
+{
+ gcc_assert(this->is_global_);
+ if (this->postinit_ == NULL)
+ this->postinit_ = new Block(NULL, s->location());
+ this->postinit_->add_statement(s);
+ this->postinit_->set_end_location(s->location());
+}
+
// In an assignment which sets a variable to a tuple of EXPR, return
// the type of the first element of the tuple.
Index: gogo.h
===================================================================
--- gogo.h (revision 155270)
+++ gogo.h (working copy)
@@ -336,6 +336,14 @@ class Gogo
add_import_init_fn(const std::string& name, int prio)
{ this->imported_init_fns_.insert(Import_init(name, prio)); }
+ // Turn short-cut operators (&&, ||) into explicit if statements.
+ void
+ remove_shortcuts();
+
+ // Use temporary variables to force order of evaluation.
+ void
+ order_evaluations();
+
// Simplify statements which might use thunks: go and defer
// statements.
void
@@ -535,9 +543,13 @@ class Gogo
const std::string&
get_init_fn_name();
+ // Get the decl for the magic initialization function.
+ tree
+ initialization_function_decl();
+
// Write the magic initialization function.
void
- write_initialization_function(tree);
+ write_initialization_function(tree fndecl, tree init_stmt_list);
// Initialize imported packages.
void
@@ -749,10 +761,14 @@ class Block
void
replace_statement(size_t index, Statement*);
- // Add a Statement before Statement number INDEX.
+ // Add a Statement before statement number INDEX.
void
insert_statement_before(size_t index, Statement*);
+ // Add a Statement after statement number INDEX.
+ void
+ insert_statement_after(size_t index, Statement*);
+
// Set the end location of the block.
void
set_end_location(source_location location)
@@ -1028,6 +1044,21 @@ class Variable
init() const
{ return this->init_; }
+ // Return whether there are preinit or postinit expressions.
+ bool
+ has_pre_or_post_init() const
+ { return this->preinit_ != NULL || this->postinit_ != NULL; }
+
+ // Return the preinit expressions if any.
+ Block*
+ preinit() const
+ { return this->preinit_; }
+
+ // Return the postinit expressions if any.
+ Block*
+ postinit() const
+ { return this->postinit_; }
+
// Return whether this is a global variable.
bool
is_global() const
@@ -1081,12 +1112,22 @@ class Variable
clear_init()
{ this->init_ = NULL; }
- // Set the initial value; used for reference counts and
- // type_from_init_tuple.
+ // Set the initial value; used for reference counts and converting
+ // shortcuts.
void
set_init(Expression* init)
{ this->init_ = init; }
+ // Add a statement to be run before the initialization expression.
+ // This is only used for global variables.
+ void
+ add_preinit_statement(Statement*);
+
+ // Add a statement to be run after the initialization expression.
+ // This is only used for global variables.
+ void
+ add_postinit_statement(Statement*);
+
// Lower the initialization expression after parsing is complete.
void
lower_init_expression(Gogo*);
@@ -1147,9 +1188,11 @@ class Variable
set_holds_only_args()
{ this->holds_only_args_ = true; }
- // Get the initial value of the variable as a tree.
+ // Get the initial value of the variable as a tree. Sets *PREINIT
+ // and *POSTINIT to statements to run before and after the
+ // initialization.
tree
- get_init_tree(Gogo*, Named_object* function);
+ get_init_tree(Gogo*, Named_object* function, tree* preinit, tree* postinit);
// Export the variable.
void
@@ -1178,6 +1221,10 @@ class Variable
// The initial value. This may be NULL if the variable should be
// initialized to the default value for the type.
Expression* init_;
+ // Statements to run before the init statement.
+ Block* preinit_;
+ // Statements to run after the init statement.
+ Block* postinit_;
// Location of variable definition.
source_location location_;
// Whether this is a global variable.
Index: go.cc
===================================================================
--- go.cc (revision 154387)
+++ go.cc (working copy)
@@ -97,6 +97,12 @@ go_parse_input_files(const char** filena
// Export global identifiers as appropriate.
::gogo->do_exports();
+ // Turn short-cut operators (&&, ||) into explicit if statements.
+ ::gogo->remove_shortcuts();
+
+ // Use temporary variables to force order of evaluation.
+ ::gogo->order_evaluations();
+
// Convert complicated go and defer statements into simpler ones.
::gogo->simplify_thunk_statements();
Index: expressions.h
===================================================================
--- expressions.h (revision 154387)
+++ expressions.h (working copy)
@@ -22,6 +22,7 @@ class Map_type;
class Expression_list;
class Var_expression;
class String_expression;
+class Binary_expression;
class Call_expression;
class Func_expression;
class Unknown_expression;
@@ -37,7 +38,7 @@ class Refcount_decrement_lvalue_expressi
class Named_object;
class Export;
class Import;
-class Temporary_initialization_statement;
+class Temporary_statement;
class Refcounts;
class Refcount_entry;
@@ -55,6 +56,7 @@ class Expression
EXPRESSION_BINARY,
EXPRESSION_CONST_REFERENCE,
EXPRESSION_VAR_REFERENCE,
+ EXPRESSION_TEMPORARY_REFERENCE,
EXPRESSION_SINK,
EXPRESSION_FUNC_REFERENCE,
EXPRESSION_UNKNOWN_REFERENCE,
@@ -86,7 +88,6 @@ class Expression
EXPRESSION_HEAP_COMPOSITE,
EXPRESSION_RECEIVE,
EXPRESSION_SEND,
- EXPRESSION_TEMPORARY_REFERENCE,
EXPRESSION_REFCOUNT_ADJUST,
EXPRESSION_REFCOUNT_DECREMENT_LVALUE
};
@@ -121,6 +122,12 @@ class Expression
static Expression*
make_var_reference(Named_object*, source_location);
+ // Make a reference to a temporary variable. Temporary variables
+ // are always created by a single statement, which is what we use to
+ // refer to them.
+ static Expression*
+ make_temporary_reference(Temporary_statement*, source_location);
+
// Make a sink expression--a reference to the blank identifier _.
static Expression*
make_sink(source_location);
@@ -254,11 +261,6 @@ class Expression
static Expression*
make_send(Expression* channel, Expression* val, source_location);
- // Make a reference to a temporary variable created to decrement a
- // reference count.
- static Expression*
- make_temporary_reference(Temporary_initialization_statement*);
-
// Make an expression which evaluates another expression and stores
// the value into the reference queue. This expression then
// evaluates to the same value. FOR_LOCAL is true if this is for a
@@ -355,6 +357,12 @@ class Expression
is_nil_expression() const
{ return this->classification_ == EXPRESSION_NIL; }
+ // If this is a binary expression, return the Binary_expression
+ // structure. Otherwise return NULL.
+ Binary_expression*
+ binary_expression()
+ { return this->convert<Binary_expression, EXPRESSION_BINARY>(); }
+
// If this is a call expression, return the Call_expression
// structure. Otherwise, return NULL. This is a controlled dynamic
// cast.
@@ -520,6 +528,13 @@ class Expression
address_taken(source_location location, bool escapes)
{ return this->do_address_taken(location, escapes); }
+ // Return whether this expression must be evaluated in order
+ // according to the order of evaluation rules. This is basically
+ // true of all expressions with side-effects.
+ bool
+ must_eval_in_order() const
+ { return this->do_must_eval_in_order(); }
+
// This is called when the value of an expression is being stored
// somewhere. In some cases this requires that the reference count
// be incremented. FOR_LOCAL is true if this is called when either
@@ -655,6 +670,12 @@ class Expression
virtual bool
do_address_taken(source_location, bool);
+ // Child class implements whether this expression must be evaluated
+ // in order.
+ virtual bool
+ do_must_eval_in_order() const
+ { return false; }
+
// Child class implements incrementing reference count.
virtual Expression*
do_being_copied(Refcounts*, bool)
@@ -964,6 +985,21 @@ class Binary_expression : public Express
op_(op), left_(left), right_(right), is_being_copied_(false)
{ }
+ // Return the operator.
+ Operator
+ op()
+ { return this->op_; }
+
+ // Return the left hand expression.
+ Expression*
+ left()
+ { return this->left_; }
+
+ // Return the right hand expression.
+ Expression*
+ right()
+ { return this->right_; }
+
// Apply binary opcode OP to LEFT_VAL and RIGHT_VAL, setting VAL.
// LEFT_TYPE is the type of LEFT_VAL, RIGHT_TYPE is the type of
// RIGHT_VAL; LEFT_TYPE and/or RIGHT_TYPE may be NULL. Return true
@@ -1083,11 +1119,11 @@ class Call_expression : public Expressio
// Get the function type.
Function_type*
- get_function_type(bool issue_error);
+ get_function_type() const;
// Return the number of values this call will return.
size_t
- result_count();
+ result_count() const;
protected:
int
@@ -1116,6 +1152,9 @@ class Call_expression : public Expressio
this->location());
}
+ bool
+ do_must_eval_in_order() const;
+
Expression*
do_being_copied(Refcounts*, bool);
@@ -1749,6 +1788,10 @@ class Receive_expression : public Expres
return Expression::make_receive(this->channel_->copy(), this->location());
}
+ bool
+ do_must_eval_in_order() const
+ { return true; }
+
Expression*
do_being_copied(Refcounts*, bool);
Index: statements.cc
===================================================================
--- statements.cc (revision 154507)
+++ statements.cc (working copy)
@@ -64,6 +64,14 @@ Statement::traverse(Block* block, size_t
return this->do_traverse(traverse);
}
+// Traverse the contents of a statement.
+
+int
+Statement::traverse_contents(Traverse* traverse)
+{
+ return this->do_traverse(traverse);
+}
+
// Traverse assignments.
bool
@@ -201,31 +209,23 @@ Statement::make_error_statement(source_l
return new Error_statement(location);
}
-// A variable declaration. This exists because the middle-end wants
-// to see a DECL_EXPR.
+// Class Variable_declaration_statement.
-class Variable_declaration_statement : public Statement
+Variable_declaration_statement::Variable_declaration_statement(
+ Named_object* var)
+ : Statement(STATEMENT_VARIABLE_DECLARATION, var->var_value()->location()),
+ var_(var)
{
- public:
- Variable_declaration_statement(Named_object* var)
- : Statement(STATEMENT_VARIABLE_DECLARATION, var->var_value()->location()),
- var_(var)
- { }
-
- protected:
- int
- do_traverse(Traverse*)
- { return TRAVERSE_CONTINUE; }
+}
- bool
- do_traverse_assignments(Traverse_assignments*);
+// We don't actually traverse the variable here; it was traversed
+// while traversing the Block.
- tree
- do_get_tree(Translate_context*);
-
- private:
- Named_object* var_;
-};
+int
+Variable_declaration_statement::do_traverse(Traverse*)
+{
+ return TRAVERSE_CONTINUE;
+}
// Traverse the assignments in a variable declaration. Note that this
// traversal is different from the usual traversal.
@@ -248,9 +248,12 @@ Variable_declaration_statement::do_get_t
return error_mark_node;
Variable* variable = this->var_->var_value();
- tree init = variable->get_init_tree(context->gogo(), context->function());
+ tree preinit, postinit;
+ tree init = variable->get_init_tree(context->gogo(), context->function(),
+ &preinit, &postinit);
if (init == error_mark_node)
return error_mark_node;
+ gcc_assert(preinit == NULL_TREE && postinit == NULL_TREE);
// If this variable lives on the heap, we need to allocate it now.
if (!variable->is_in_heap())
@@ -283,6 +286,119 @@ Statement::make_variable_declaration(Nam
return new Variable_declaration_statement(var);
}
+// Class Temporary_statement.
+
+// Return the type of the temporary variable.
+
+Type*
+Temporary_statement::type() const
+{
+ return this->type_ != NULL ? this->type_ : this->init_->type();
+}
+
+// Traversal.
+
+int
+Temporary_statement::do_traverse(Traverse* traverse)
+{
+ if (this->init_ == NULL)
+ return TRAVERSE_CONTINUE;
+ else
+ return this->traverse_expression(traverse, &this->init_);
+}
+
+// Traverse assignments.
+
+bool
+Temporary_statement::do_traverse_assignments(Traverse_assignments* tassign)
+{
+ if (this->init_ == NULL)
+ return false;
+ tassign->value(&this->init_, true, true);
+ return true;
+}
+
+// Check types.
+
+void
+Temporary_statement::do_check_types(Gogo*)
+{
+ if (this->type_ != NULL && this->init_ != NULL)
+ gcc_assert(Type::are_compatible_for_assign(this->type_,
+ this->init_->type(),
+ NULL));
+}
+
+// Return a tree.
+
+tree
+Temporary_statement::do_get_tree(Translate_context* context)
+{
+ gcc_assert(this->decl_ == NULL_TREE);
+ this->decl_ = create_tmp_var(this->type()->get_tree(context->gogo()),
+ "GOTMP");
+ DECL_SOURCE_LOCATION(this->decl_) = this->location();
+ if (this->init_ != NULL)
+ DECL_INITIAL(this->decl_) =
+ Expression::convert_for_assignment(context, this->type(),
+ this->init_->type(),
+ this->init_->get_tree(context),
+ this->location());
+ return this->build_stmt_1(DECL_EXPR, this->decl_);
+}
+
+// Make and initialize a temporary variable.
+
+Temporary_statement*
+Statement::make_temporary(Type* type, Expression* init,
+ source_location location)
+{
+ return new Temporary_statement(type, init, location);
+}
+
+// Destroy a temporary variable.
+
+class Destroy_temporary_statement : public Statement
+{
+ public:
+ Destroy_temporary_statement(Temporary_statement* statement)
+ : Statement(STATEMENT_DESTROY_TEMPORARY, statement->location()),
+ ref_(Expression::make_temporary_reference(statement,
+ statement->location()))
+ { }
+
+ protected:
+ int
+ do_traverse(Traverse*)
+ { return TRAVERSE_CONTINUE; }
+
+ bool
+ do_traverse_assignments(Traverse_assignments* tassign)
+ {
+ // Destroying a temporary is like assigning an empty value to it.
+ tassign->assignment(&this->ref_, NULL);
+ return true;
+ }
+
+ tree
+ do_get_tree(Translate_context* context)
+ { return this->ref_->get_tree(context); }
+
+ private:
+ // A reference to the temporary variable being destroyed.
+ Expression* ref_;
+};
+
+// Make a statement which destroys a temporary variable.
+
+Statement*
+Statement::destroy_temporary(Temporary_statement* statement)
+{
+ if (!statement->type()->has_refcounted_component())
+ return NULL;
+ return new Destroy_temporary_statement(statement);
+}
+
// An assignment statement.
class Assignment_statement : public Statement
@@ -1980,7 +2096,7 @@ Thunk_statement::do_determine_types()
// Now that we know the types of the call, build the struct used to
// pass parameters.
Function_type* fntype =
- this->call_->call_expression()->get_function_type(false);
+ this->call_->call_expression()->get_function_type();
if (fntype != NULL && !this->is_simple(fntype))
this->struct_type_ = this->build_struct(fntype);
}
@@ -1991,7 +2107,7 @@ void
Thunk_statement::do_check_types(Gogo*)
{
Call_expression* ce = this->call_->call_expression();
- Function_type* fntype = ce->get_function_type(false);
+ Function_type* fntype = ce->get_function_type();
if (fntype != NULL && fntype->is_method())
{
Expression* fn = ce->fn();
@@ -2058,7 +2174,7 @@ Thunk_statement::simplify_statement(Gogo
return false;
Call_expression* ce = this->call_->call_expression();
- Function_type* fntype = ce->get_function_type(false);
+ Function_type* fntype = ce->get_function_type();
if (fntype == NULL || this->is_simple(fntype))
return false;
@@ -4484,6 +4600,26 @@ Statement::make_select_statement(source_
// Class For_statement.
+// Insert a statement to run before the conditional.
+
+void
+For_statement::insert_before_conditional(Block* enclosing, Statement* s)
+{
+ if (this->precond_ == NULL)
+ this->precond_ = new Block(enclosing, s->location());
+ this->precond_->add_statement(s);
+}
+
+// Insert a statement to run after the conditional.
+
+void
+For_statement::insert_after_conditional(Block* enclosing, Statement* s)
+{
+ if (this->postcond_ == NULL)
+ this->postcond_ = new Block(enclosing, s->location());
+ this->postcond_->add_statement(s);
+}
+
// Traversal.
int
@@ -4494,11 +4630,21 @@ For_statement::do_traverse(Traverse* tra
if (this->init_->traverse(traverse) == TRAVERSE_EXIT)
return TRAVERSE_EXIT;
}
+ if (this->precond_ != NULL)
+ {
+ if (this->precond_->traverse(traverse) == TRAVERSE_EXIT)
+ return TRAVERSE_EXIT;
+ }
if (this->cond_ != NULL)
{
if (this->traverse_expression(traverse, &this->cond_) == TRAVERSE_EXIT)
return TRAVERSE_EXIT;
}
+ if (this->postcond_ != NULL)
+ {
+ if (this->postcond_->traverse(traverse) == TRAVERSE_EXIT)
+ return TRAVERSE_EXIT;
+ }
if (this->post_ != NULL)
{
if (this->post_->traverse(traverse) == TRAVERSE_EXIT)
@@ -4512,6 +4658,7 @@ For_statement::do_determine_types()
{
if (this->init_ != NULL)
this->init_->determine_types();
+ gcc_assert(this->precond_ == NULL && this->postcond_ == NULL);
if (this->cond_ != NULL)
{
Type_context context(Type::lookup_bool_type(), false);
@@ -4640,6 +4787,7 @@ For_statement::do_get_tree(Translate_con
&statements);
if (this->cond_ == NULL)
{
+ gcc_assert(this->precond_ == NULL && this->postcond_ == NULL);
tree t = build_and_jump(&LABEL_EXPR_LABEL(top));
SET_EXPR_LOCATION(t, this->location());
append_to_statement_list(t, &statements);
@@ -4648,12 +4796,24 @@ For_statement::do_get_tree(Translate_con
{
append_to_statement_list(entry, &statements);
tree bottom = build1(LABEL_EXPR, void_type_node, NULL_TREE);
- tree t = fold_build3(COND_EXPR, void_type_node,
- this->cond_->get_tree(context),
- build_and_jump(&LABEL_EXPR_LABEL(top)),
- build_and_jump(&LABEL_EXPR_LABEL(bottom)));
- if (CAN_HAVE_LOCATION_P(t))
- SET_EXPR_LOCATION(t, this->location());
+ if (this->precond_ != NULL)
+ append_to_statement_list(this->precond_->get_tree(context),
+ &statements);
+ tree cond_tree = this->cond_->get_tree(context);
+ if (this->postcond_ != NULL)
+ {
+ tree tmp = create_tmp_var(TREE_TYPE(cond_tree), get_name(cond_tree));
+ DECL_INITIAL(tmp) = cond_tree;
+ append_to_statement_list(build1(DECL_EXPR, void_type_node, tmp),
+ &statements);
+ cond_tree = tmp;
+ append_to_statement_list(this->postcond_->get_tree(context),
+ &statements);
+ }
+ tree t = fold_build3_loc(this->location(), COND_EXPR, void_type_node,
+ cond_tree,
+ build_and_jump(&LABEL_EXPR_LABEL(top)),
+ build_and_jump(&LABEL_EXPR_LABEL(bottom)));
append_to_statement_list(t, &statements);
append_to_statement_list(bottom, &statements);
}
Index: gogo-tree.cc
===================================================================
--- gogo-tree.cc (revision 155275)
+++ gogo-tree.cc (working copy)
@@ -129,32 +129,47 @@ Gogo::get_init_fn_name()
if (this->init_fn_name_.empty())
{
gcc_assert(this->package_ != NULL);
- std::string s = "Go.import.";
- Bindings* bindings = this->package_->bindings();
- for (Bindings::const_definitions_iterator p =
- bindings->begin_definitions();
- p != bindings->end_definitions();
- ++p)
+ if (this->package_name() == "main")
+ {
+ // In the "main" package, we run the initialization function
+ // as a constructor.
+ char buf[16];
+ snprintf(buf, sizeof buf, "I_%.5d_0", DEFAULT_INIT_PRIORITY);
+ tree name = get_file_function_name(buf);
+ this->init_fn_name_.assign(IDENTIFIER_POINTER(name),
+ IDENTIFIER_LENGTH(name));
+ }
+ else
{
- if (((*p)->is_variable() || (*p)->is_function())
- && (*p)->package() == NULL
- && (!go_default_private || !Gogo::is_hidden_name((*p)->name())))
+ std::string s = "Go.import.";
+ Bindings* bindings = this->package_->bindings();
+ for (Bindings::const_definitions_iterator p =
+ bindings->begin_definitions();
+ p != bindings->end_definitions();
+ ++p)
{
- tree id = (*p)->get_id(this);
- s.append(IDENTIFIER_POINTER(id), IDENTIFIER_LENGTH(id));
- break;
+ if (((*p)->is_variable() || (*p)->is_function())
+ && (*p)->package() == NULL
+ && (!go_default_private
+ || !Gogo::is_hidden_name((*p)->name())))
+ {
+ tree id = (*p)->get_id(this);
+ s.append(IDENTIFIER_POINTER(id), IDENTIFIER_LENGTH(id));
+ break;
+ }
}
- }
- if (s.empty())
- {
- // This is the unlikely case when we have an init function but
- // nothing to export.
- tree name_tree = get_file_function_name("N");
- s.append(IDENTIFIER_POINTER(name_tree), IDENTIFIER_LENGTH(name_tree));
- }
+ if (s.empty())
+ {
+ // This is the unlikely case when we have an init
+ // function but nothing to export.
+ tree name_tree = get_file_function_name("N");
+ s.append(IDENTIFIER_POINTER(name_tree),
+ IDENTIFIER_LENGTH(name_tree));
+ }
- this->init_fn_name_ = s;
+ this->init_fn_name_ = s;
+ }
}
return this->init_fn_name_;
@@ -196,29 +211,48 @@ Gogo::init_imports(tree* init_stmt_list)
}
}
+// Build the decl for the initialization function.
+
+tree
+Gogo::initialization_function_decl()
+{
+ // The tedious details of building your own function. There doesn't
+ // seem to be a helper function for this.
+ const std::string& name(this->get_init_fn_name());
+ tree fndecl = build_decl(BUILTINS_LOCATION, FUNCTION_DECL,
+ get_identifier(name.c_str()),
+ build_function_type(void_type_node,
+ void_list_node));
+
+ tree resdecl = build_decl(BUILTINS_LOCATION, RESULT_DECL, NULL_TREE,
+ void_type_node);
+ DECL_ARTIFICIAL(resdecl) = 1;
+ DECL_CONTEXT(resdecl) = fndecl;
+ DECL_RESULT(fndecl) = resdecl;
+
+ TREE_STATIC(fndecl) = 1;
+ TREE_USED(fndecl) = 1;
+ DECL_ARTIFICIAL(fndecl) = 1;
+ TREE_PUBLIC(fndecl) = 1;
+
+ DECL_INITIAL(fndecl) = make_node(BLOCK);
+ TREE_USED(DECL_INITIAL(fndecl)) = 1;
+
+ if (this->package_name() == "main")
+ DECL_STATIC_CONSTRUCTOR(fndecl) = 1;
+
+ return fndecl;
+}
+
// Create the magic initialization function. INIT_STMT_LIST is the
// code that it needs to run.
void
-Gogo::write_initialization_function(tree init_stmt_list)
+Gogo::write_initialization_function(tree fndecl, tree init_stmt_list)
{
- // In the "main" package, we run the initialization function as a
- // constructor. We use a constructor priority based on the package
- // priority to ensure that if there are multiple files in the "main"
- // package, the initialization functions are run in order.
- if (this->package_name() == "main")
- {
- int priority = (this->package_priority()
- + MAX_RESERVED_INIT_PRIORITY
- + 10);
- gcc_assert(priority <= MAX_INIT_PRIORITY);
- cgraph_build_static_cdtor('I', init_stmt_list, priority);
- return;
- }
-
// Make sure that we thought we needed an initialization function,
// as otherwise we will not have reported it in the export data.
- gcc_assert(this->need_init_fn_);
+ gcc_assert(this->package_name() == "main" || this->need_init_fn_);
// If there are multiple files in the "main" package, then it is
// possible for the import function to be called more than once.
@@ -252,32 +286,16 @@ Gogo::write_initialization_function(tree
fold_convert(boolean_type_node, varref),
NULL_TREE, init_stmt_list);
- // Here come the tedious details of building your own function.
- // There doesn't seem to be a helper function for this.
+ if (fndecl == NULL_TREE)
+ fndecl = this->initialization_function_decl();
- const std::string& name(this->get_init_fn_name());
- tree fndecl = build_decl(BUILTINS_LOCATION, FUNCTION_DECL,
- get_identifier(name.c_str()),
- build_function_type(void_type_node,
- void_list_node));
-
- tree resdecl = build_decl(BUILTINS_LOCATION, RESULT_DECL, NULL_TREE,
- void_type_node);
- DECL_ARTIFICIAL(resdecl) = 1;
- DECL_CONTEXT(resdecl) = fndecl;
- DECL_RESULT(fndecl) = resdecl;
-
- TREE_STATIC(fndecl) = 1;
- TREE_USED(fndecl) = 1;
- DECL_ARTIFICIAL(fndecl) = 1;
DECL_SAVED_TREE(fndecl) = init_stmt_list;
- TREE_PUBLIC(fndecl) = 1;
-
- DECL_INITIAL(fndecl) = make_node(BLOCK);
- TREE_USED(DECL_INITIAL(fndecl)) = 1;
current_function_decl = fndecl;
- push_struct_function(fndecl);
+ if (DECL_STRUCT_FUNCTION(fndecl) == NULL)
+ push_struct_function(fndecl);
+ else
+ push_cfun(DECL_STRUCT_FUNCTION(fndecl));
cfun->function_end_locus = BUILTINS_LOCATION;
gimplify_function_tree(fndecl);
@@ -382,11 +400,17 @@ Find_var::expression(Expression** pexpr)
// Return true if EXPR refers to VAR.
static bool
-expression_requires(Expression* expr, Named_object* var)
+expression_requires(Expression* expr, Block* preinit, Block* postinit,
+ Named_object* var)
{
Find_var::Seen_objects seen_objects;
Find_var find_var(var, &seen_objects);
Expression::traverse(&expr, &find_var);
+ if (preinit != NULL)
+ preinit->traverse(&find_var);
+ if (postinit != NULL)
+ postinit->traverse(&find_var);
+
return find_var.found();
}
@@ -451,6 +475,8 @@ sort_var_inits(Var_inits* var_inits)
Var_inits::iterator p1 = var_inits->begin();
Named_object* var = p1->var();
Expression* init = var->var_value()->init();
+ Block* preinit = var->var_value()->preinit();
+ Block* postinit = var->var_value()->postinit();
// Start walking through the list to see which variables VAR
// needs to wait for. We can skip P1->WAITING variables--that
@@ -462,10 +488,13 @@ sort_var_inits(Var_inits* var_inits)
for (; p2 != var_inits->end(); ++p2)
{
- if (expression_requires(init, p2->var()))
+ if (expression_requires(init, preinit, postinit, p2->var()))
{
// Check for cycles.
- if (expression_requires(p2->var()->var_value()->init(), var))
+ if (expression_requires(p2->var()->var_value()->init(),
+ p2->var()->var_value()->preinit(),
+ p2->var()->var_value()->postinit(),
+ var))
{
std::string n1 = Gogo::unpack_hidden_name(var->name());
std::string n2 = Gogo::unpack_hidden_name(p2->var()->name());
@@ -512,6 +541,7 @@ Gogo::write_globals()
tree* vec = new tree[count];
+ tree init_fndecl = NULL_TREE;
tree init_stmt_list = NULL_TREE;
if (this->package_name() == "main")
@@ -574,8 +604,26 @@ Gogo::write_globals()
if (TREE_CODE(vec[i]) == VAR_DECL)
{
gcc_assert((*p)->is_variable());
- tree init = (*p)->var_value()->get_init_tree(this, NULL);
- if (init == error_mark_node)
+
+ if ((*p)->var_value()->has_pre_or_post_init())
+ {
+ // We are going to create temporary variables which
+ // means we need an fndecl.
+ if (init_fndecl == NULL_TREE)
+ init_fndecl = this->initialization_function_decl();
+ current_function_decl = init_fndecl;
+ if (DECL_STRUCT_FUNCTION(init_fndecl) == NULL)
+ push_struct_function(init_fndecl);
+ else
+ push_cfun(DECL_STRUCT_FUNCTION(init_fndecl));
+ }
+
+ tree preinit, postinit;
+ tree init = (*p)->var_value()->get_init_tree(this, NULL, &preinit,
+ &postinit);
+ if (init == NULL_TREE)
+ gcc_assert(preinit == NULL_TREE && postinit == NULL_TREE);
+ else if (init == error_mark_node)
gcc_assert(errorcount || sorrycount);
else if ((*p)->name()[0] == '_' && (*p)->name()[1] == '.')
{
@@ -585,20 +633,41 @@ Gogo::write_globals()
--i;
--count;
gcc_assert((*p)->var_value()->init() != NULL);
- if (!TREE_CONSTANT(init))
- var_inits.push_back(Var_init(*p, init));
+ if (!TREE_CONSTANT(init)
+ || preinit != NULL_TREE
+ || postinit != NULL_TREE)
+ {
+ if (preinit != NULL_TREE)
+ init = build2(COMPOUND_EXPR, void_type_node, preinit, init);
+ if (postinit != NULL_TREE)
+ init = build2(COMPOUND_EXPR, void_type_node, init,
+ postinit);
+ var_inits.push_back(Var_init(*p, init));
+ }
}
- else if (TREE_CONSTANT(init))
+ else if (TREE_CONSTANT(init)
+ && preinit == NULL_TREE
+ && postinit == NULL_TREE)
DECL_INITIAL(vec[i]) = init;
else
{
tree set = fold_build2(MODIFY_EXPR, void_type_node, vec[i], init);
+ if (preinit != NULL_TREE)
+ set = build2(COMPOUND_EXPR, void_type_node, preinit, set);
+ if (postinit != NULL_TREE)
+ set = build2(COMPOUND_EXPR, void_type_node, set, postinit);
Expression* vinit = (*p)->var_value()->init();
if (vinit == NULL)
append_to_statement_list(set, &init_stmt_list);
else
var_inits.push_back(Var_init(*p, set));
}
+
+ if ((*p)->var_value()->has_pre_or_post_init())
+ {
+ current_function_decl = NULL_TREE;
+ pop_cfun();
+ }
}
}
@@ -628,7 +697,7 @@ Gogo::write_globals()
// Set up a magic function to do all the initialization actions.
// This will be called if this package is imported.
if (init_stmt_list != NULL_TREE || this->need_init_fn_)
- this->write_initialization_function(init_stmt_list);
+ this->write_initialization_function(init_fndecl, init_stmt_list);
// Pass everything back to the middle-end.
@@ -931,20 +1000,33 @@ Named_object::get_tree(Gogo* gogo, Named
// initial value as though it were always stored in the stack.
tree
-Variable::get_init_tree(Gogo* gogo, Named_object* function)
+Variable::get_init_tree(Gogo* gogo, Named_object* function, tree* preinit,
+ tree* postinit)
{
+ *preinit = NULL_TREE;
+ *postinit = NULL_TREE;
if (this->init_ == NULL)
{
gcc_assert(!this->is_parameter_);
- return this->type_->get_init_tree(gogo, false);
+ gcc_assert(this->preinit_ == NULL && this->postinit_ == NULL);
+ return this->type_->get_init_tree(gogo, this->is_global_);
}
else
{
Translate_context context(gogo, function, NULL, NULL_TREE);
+
+ if (this->preinit_ != NULL)
+ append_to_statement_list(this->preinit_->get_tree(&context), preinit);
+
tree rhs_tree = this->init_->get_tree(&context);
- return Expression::convert_for_assignment(&context, this->type(),
- this->init_->type(),
- rhs_tree, this->location_);
+ tree val = Expression::convert_for_assignment(&context, this->type(),
+ this->init_->type(),
+ rhs_tree, this->location_);
+
+ if (this->postinit_ != NULL)
+ append_to_statement_list(this->postinit_->get_tree(&context), postinit);
+
+ return val;
}
}
@@ -1360,7 +1442,13 @@ Block::get_tree(Translate_context* conte
if (context->block() == NULL)
{
- tree fndecl = context->function()->func_value()->get_decl();
+ tree fndecl;
+ if (context->function() != NULL)
+ fndecl = context->function()->func_value()->get_decl();
+ else
+ fndecl = current_function_decl;
+ gcc_assert(fndecl != NULL_TREE);
+
// We may have already created a block for the receiver.
if (DECL_INITIAL(fndecl) == NULL_TREE)
{
Index: statements.h
===================================================================
--- statements.h (revision 154387)
+++ statements.h (working copy)
@@ -15,6 +15,8 @@ class Gogo;
class Traverse;
class Block;
class Function;
+class Temporary_statement;
+class Variable_declaration_statement;
class Return_statement;
class Thunk_statement;
class Label_statement;
@@ -85,6 +87,8 @@ class Statement
{
STATEMENT_ERROR,
STATEMENT_VARIABLE_DECLARATION,
+ STATEMENT_TEMPORARY,
+ STATEMENT_DESTROY_TEMPORARY,
STATEMENT_ASSIGNMENT,
STATEMENT_TUPLE_ASSIGNMENT,
STATEMENT_TUPLE_MAP_ASSIGNMENT,
@@ -117,6 +121,19 @@ class Statement
static Statement*
make_variable_declaration(Named_object*);
+ // Make a statement which creates a temporary variable and
+ // initializes it to an expression. References to the temporary
+ // variable may be constructed using make_temporary_reference.
+ // Either or the type or the initialization expression may be NULL,
+ // but not both.
+ static Temporary_statement*
+ make_temporary(Type*, Expression*, source_location);
+
+ // Make a statement which destroys a temporary variable. This may
+ // return NULL if there is nothing to do.
+ static Statement*
+ destroy_temporary(Temporary_statement*);
+
// Make an assignment statement.
static Statement*
make_assignment(Operator, Expression*, Expression*, source_location);
@@ -240,6 +257,11 @@ class Statement
int
traverse(Block*, size_t* index, Traverse*);
+ // Traverse the contents of this statement--the expressions and
+ // statements which it contains.
+ int
+ traverse_contents(Traverse*);
+
// If this statement assigns some values, it calls a function for
// each value to which this statement assigns a value, and returns
// true. If this statement does not assign any values, it returns
@@ -269,6 +291,15 @@ class Statement
is_block_statement() const
{ return this->classification_ == STATEMENT_BLOCK; }
+ // If this is a variable declaration statement, return it.
+ // Otherwise return NULL.
+ Variable_declaration_statement*
+ variable_declaration_statement()
+ {
+ return this->convert<Variable_declaration_statement,
+ STATEMENT_VARIABLE_DECLARATION>();
+ }
+
// If this is a return statement, return it. Otherwise return NULL.
Return_statement*
return_statement()
@@ -419,6 +450,83 @@ class Statement
source_location location_;
};
+// A statement which creates and initializes a temporary variable.
+
+class Temporary_statement : public Statement
+{
+ public:
+ Temporary_statement(Type* type, Expression* init, source_location location)
+ : Statement(STATEMENT_TEMPORARY, location),
+ type_(type), init_(init), decl_(NULL)
+ { }
+
+ // Return the type of the temporary variable.
+ Type*
+ type() const;
+
+ // Return the initialization expression.
+ Expression*
+ init() const
+ { return this->init_; }
+
+ // Return the tree for the temporary variable itself. This should
+ // not be called until after the statement itself has been expanded.
+ tree
+ get_decl() const
+ {
+ gcc_assert(this->decl_ != NULL);
+ return this->decl_;
+ }
+
+ protected:
+ int
+ do_traverse(Traverse*);
+
+ bool
+ do_traverse_assignments(Traverse_assignments*);
+
+ void
+ do_check_types(Gogo*);
+
+ tree
+ do_get_tree(Translate_context*);
+
+ private:
+ // The type of the temporary variable.
+ Type* type_;
+ // The initial value of the temporary variable. This may be NULL.
+ Expression* init_;
+ // The DECL for the temporary variable.
+ tree decl_;
+};
+
+// A variable declaration. This marks the point in the code where a
+// variable is declared. The Variable is also attached to a Block.
+
+class Variable_declaration_statement : public Statement
+{
+ public:
+ Variable_declaration_statement(Named_object* var);
+
+ // The variable being declared.
+ Named_object*
+ var()
+ { return this->var_; }
+
+ protected:
+ int
+ do_traverse(Traverse*);
+
+ bool
+ do_traverse_assignments(Traverse_assignments*);
+
+ tree
+ do_get_tree(Translate_context*);
+
+ private:
+ Named_object* var_;
+};
+
// A return statement.
class Return_statement : public Statement
@@ -834,7 +942,8 @@ class For_statement : public Statement
source_location location)
: Statement(STATEMENT_FOR, location),
init_(init), cond_(cond), post_(post), statements_(NULL),
- break_label_(NULL), continue_label_(NULL), needs_break_label_(false),
+ precond_(NULL), postcond_(NULL), break_label_(NULL),
+ continue_label_(NULL), needs_break_label_(false),
needs_continue_label_(false)
{ }
@@ -846,6 +955,14 @@ class For_statement : public Statement
this->statements_ = statements;
}
+ // Insert a statement to run before the conditional expression.
+ void
+ insert_before_conditional(Block* enclosing, Statement*);
+
+ // Insert a statement to run after the conditional expression.
+ void
+ insert_after_conditional(Block* enclosing, Statement*);
+
// Record that a break statement is used for this for statement.
// This is called during parsing.
void
@@ -893,6 +1010,12 @@ class For_statement : public Statement
Block* post_;
// The statements in the loop itself.
Block* statements_;
+ // Statements to run before the conditional expression. This may be
+ // NULL.
+ Block* precond_;
+ // Statements to run after the conditional expression. This may be
+ // NULL.
+ Block* postcond_;
// The break LABEL_EXPR, if needed.
tree break_label_;
// The continue LABEL_EXPR, if needed.
Index: types.cc
===================================================================
--- types.cc (revision 155270)
+++ types.cc (working copy)
@@ -2297,7 +2297,7 @@ class Call_multiple_result_type : public
tree
Call_multiple_result_type::do_get_tree(Gogo* gogo)
{
- Function_type* fntype = this->call_->get_function_type(false);
+ Function_type* fntype = this->call_->get_function_type();
gcc_assert(fntype != NULL);
const Typed_identifier_list* results = fntype->results();
gcc_assert(results != NULL && results->size() > 1);
Index: expressions.cc
===================================================================
--- expressions.cc (revision 155270)
+++ expressions.cc (working copy)
@@ -917,6 +917,83 @@ Expression::make_var_reference(Named_obj
return new Var_expression(var, location);
}
+// A reference to a temporary variable.
+
+class Temporary_reference_expression : public Expression
+{
+ public:
+ Temporary_reference_expression(Temporary_statement* statement,
+ source_location location)
+ : Expression(EXPRESSION_TEMPORARY_REFERENCE, location),
+ statement_(statement)
+ { }
+
+ protected:
+ Type*
+ do_type()
+ { return this->statement_->type(); }
+
+ void
+ do_determine_type(const Type_context*)
+ { }
+
+ Expression*
+ do_copy()
+ { return make_temporary_reference(this->statement_, this->location()); }
+
+ bool
+ do_is_lvalue() const
+ { return true; }
+
+ Expression*
+ do_being_copied(Refcounts*, bool);
+
+ Expression*
+ do_being_set(Refcounts*);
+
+ tree
+ do_get_tree(Translate_context*)
+ { return this->statement_->get_decl(); }
+
+ private:
+ // The statement where the temporary variable is defined.
+ Temporary_statement* statement_;
+};
+
+// The temporary variable is being copied. We may need to increment
+// the reference count.
+
+Expression*
+Temporary_reference_expression::do_being_copied(Refcounts* refcounts,
+ bool for_local)
+{
+ if (!this->type()->has_refcounted_component())
+ return this;
+ return Expression::make_refcount_adjust(refcounts,
+ REFCOUNT_INCREMENT_COPIED,
+ this, for_local);
+}
+
+// The variable is being set. We may need to decrement the reference
+// count of the old value.
+
+Expression*
+Temporary_reference_expression::do_being_set(Refcounts* refcounts)
+{
+ if (!this->type()->has_refcounted_component())
+ return this;
+ return Expression::make_refcount_decrement_lvalue(refcounts, this);
+}
+
+// Make a reference to a temporary variable.
+
+Expression*
+Expression::make_temporary_reference(Temporary_statement* statement,
+ source_location location)
+{
+ return new Temporary_reference_expression(statement, location);
+}
+
// A sink expression--a use of the blank identifier _.
class Sink_expression : public Expression
@@ -3788,7 +3865,7 @@ Binary_expression::eval_float(Operator o
}
// Lower a binary expression. We have to evaluate constant
-// expressions now, in order t implemented Go's unlimited precision
+// expressions now, in order to implement Go's unlimited precision
// constants.
Expression*
@@ -6030,23 +6107,17 @@ Call_expression::do_lower(Gogo* gogo, in
// this returns NULL, and if_ERROR is true, issues an error.
Function_type*
-Call_expression::get_function_type(bool issue_error)
+Call_expression::get_function_type() const
{
- Type* type = this->fn_->type()->deref();
- Function_type* fntype = type->function_type();
- if (fntype != NULL)
- return fntype;
- if (issue_error && !type->is_error_type())
- this->report_error("expected function");
- return NULL;
+ return this->fn_->type()->function_type();
}
// Return the number of values which this call will return.
size_t
-Call_expression::result_count()
+Call_expression::result_count() const
{
- Function_type* fntype = this->get_function_type(false);
+ const Function_type* fntype = this->get_function_type();
if (fntype == NULL)
return 0;
if (fntype->results() == NULL)
@@ -6063,7 +6134,7 @@ Call_expression::do_type()
return this->type_;
Type* ret;
- Function_type* fntype = this->get_function_type(false);
+ Function_type* fntype = this->get_function_type();
if (fntype == NULL)
return Type::make_error_type();
@@ -6087,7 +6158,7 @@ void
Call_expression::do_determine_type(const Type_context*)
{
this->fn_->determine_type_no_context();
- Function_type* fntype = this->get_function_type(false);
+ Function_type* fntype = this->get_function_type();
const Typed_identifier_list* parameters = NULL;
if (fntype != NULL)
parameters = fntype->parameters();
@@ -6138,9 +6209,13 @@ Call_expression::check_argument_type(int
void
Call_expression::do_check_types(Gogo*)
{
- Function_type* fntype = this->get_function_type(true);
+ Function_type* fntype = this->get_function_type();
if (fntype == NULL)
- return;
+ {
+ if (!this->fn_->type()->is_error_type())
+ this->report_error(_("expected function"));
+ return;
+ }
if (fntype->is_method())
{
@@ -6198,6 +6273,19 @@ Call_expression::do_check_types(Gogo*)
}
}
+// Return whether we have to use a temporary variable to ensure that
+// we evaluate this call expression in order. If the call returns no
+// results then it will inevitably be executed last. If the call
+// returns more than one result then it will be used with Call_result
+// expressions. So we only have to use a temporary variable if the
+// call returns exactly one result.
+
+bool
+Call_expression::do_must_eval_in_order() const
+{
+ return this->result_count() == 1;
+}
+
// Get the function and the first argument to use when calling a bound
// method.
@@ -6295,7 +6383,7 @@ Call_expression::do_note_decrements(Refc
{
if (this->is_being_copied_)
return this;
- Function_type* fntype = this->get_function_type(false);
+ Function_type* fntype = this->get_function_type();
if (fntype == NULL)
return this;
const Typed_identifier_list* results = fntype->results();
@@ -6342,7 +6430,7 @@ Call_expression::do_get_tree(Translate_c
if (this->tree_ != NULL_TREE)
return this->tree_;
- Function_type* fntype = this->get_function_type(false);
+ Function_type* fntype = this->get_function_type();
if (fntype == NULL)
return error_mark_node;
@@ -6605,7 +6693,7 @@ Call_expression::set_refcount_queue_entr
Gogo* gogo = context->gogo();
Refcounts* refcounts = context->function()->func_value()->refcounts();
tree val = ret;
- Function_type* fntype = this->get_function_type(false);
+ Function_type* fntype = this->get_function_type();
const Typed_identifier_list* results = fntype->results();
gcc_assert(TREE_CODE(TREE_TYPE(val)) == RECORD_TYPE);
std::vector<Refcount_entry>::const_iterator pre =
@@ -6667,6 +6755,10 @@ class Call_result_expression : public Ex
this->index_);
}
+ bool
+ do_must_eval_in_order() const
+ { return true; }
+
Expression*
do_being_copied(Refcounts*, bool);
@@ -6704,7 +6796,7 @@ Call_result_expression::do_type()
if (this->call_->is_error_expression())
return Type::make_error_type();
Function_type* fntype =
- this->call_->call_expression()->get_function_type(false);
+ this->call_->call_expression()->get_function_type();
if (fntype == NULL)
return Type::make_error_type();
const Typed_identifier_list* results = fntype->results();
@@ -7991,7 +8083,7 @@ Interface_field_reference_expression::do
if (method == NULL)
return Type::make_error_type();
- return Type::make_pointer_type(method->type());
+ return method->type();
}
// Determine types.
@@ -10220,6 +10312,10 @@ class Send_expression : public Expressio
this->location());
}
+ bool
+ do_must_eval_in_order() const
+ { return true; }
+
Expression*
do_being_copied(Refcounts*, bool);