This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[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);
 

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]