Use temporary variables for calls with multiple results.
authorian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>
Mon, 1 Aug 2011 01:44:36 +0000 (01:44 +0000)
committerian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>
Mon, 1 Aug 2011 01:44:36 +0000 (01:44 +0000)
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@176998 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/go/gofrontend/expressions.cc
gcc/go/gofrontend/expressions.h
gcc/go/gofrontend/gogo.cc
gcc/go/gofrontend/gogo.h
gcc/go/gofrontend/statements.cc
gcc/go/gofrontend/statements.h

index bd437c4..433a6d7 100644 (file)
@@ -926,7 +926,8 @@ Parser_expression::do_type()
 // if necessary.
 
 Expression*
-Var_expression::do_lower(Gogo* gogo, Named_object* function, int)
+Var_expression::do_lower(Gogo* gogo, Named_object* function,
+                        Statement_inserter* inserter, int)
 {
   if (this->variable_->is_variable())
     {
@@ -935,8 +936,11 @@ Var_expression::do_lower(Gogo* gogo, Named_object* function, int)
       // reference to a variable which is local to an enclosing
       // function will be a reference to a field in a closure.
       if (var->is_global())
-       function = NULL;
-      var->lower_init_expression(gogo, function);
+       {
+         function = NULL;
+         inserter = NULL;
+       }
+      var->lower_init_expression(gogo, function, inserter);
     }
   return this;
 }
@@ -1061,7 +1065,9 @@ Temporary_reference_expression::do_get_tree(Translate_context* context)
   // that here by adding a type cast.  We need to use base() to push
   // the circularity down one level.
   tree ret = var_to_tree(bvar);
-  if (POINTER_TYPE_P(TREE_TYPE(ret)) && VOID_TYPE_P(TREE_TYPE(TREE_TYPE(ret))))
+  if (!this->is_lvalue_
+      && POINTER_TYPE_P(TREE_TYPE(ret))
+      && VOID_TYPE_P(TREE_TYPE(TREE_TYPE(ret))))
     {
       Btype* type_btype = this->type()->base()->get_backend(context->gogo());
       tree type_tree = type_to_tree(type_btype);
@@ -1072,7 +1078,7 @@ Temporary_reference_expression::do_get_tree(Translate_context* context)
 
 // Make a reference to a temporary variable.
 
-Expression*
+Temporary_reference_expression*
 Expression::make_temporary_reference(Temporary_statement* statement,
                                     source_location location)
 {
@@ -1302,7 +1308,7 @@ Unknown_expression::name() const
 // Lower a reference to an unknown name.
 
 Expression*
-Unknown_expression::do_lower(Gogo*, Named_object*, int)
+Unknown_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int)
 {
   source_location location = this->location();
   Named_object* no = this->named_object_;
@@ -2394,7 +2400,7 @@ class Const_expression : public Expression
   do_traverse(Traverse*);
 
   Expression*
-  do_lower(Gogo*, Named_object*, int);
+  do_lower(Gogo*, Named_object*, Statement_inserter*, int);
 
   bool
   do_is_constant() const
@@ -2462,7 +2468,8 @@ Const_expression::do_traverse(Traverse* traverse)
 // predeclared constant iota into an integer value.
 
 Expression*
-Const_expression::do_lower(Gogo* gogo, Named_object*, int iota_value)
+Const_expression::do_lower(Gogo* gogo, Named_object*,
+                          Statement_inserter*, int iota_value)
 {
   if (this->constant_->const_value()->expr()->classification()
       == EXPRESSION_IOTA)
@@ -2931,7 +2938,7 @@ class Iota_expression : public Parser_expression
 
  protected:
   Expression*
-  do_lower(Gogo*, Named_object*, int)
+  do_lower(Gogo*, Named_object*, Statement_inserter*, int)
   { go_unreachable(); }
 
   // There should only ever be one of these.
@@ -2988,7 +2995,7 @@ class Type_conversion_expression : public Expression
   do_traverse(Traverse* traverse);
 
   Expression*
-  do_lower(Gogo*, Named_object*, int);
+  do_lower(Gogo*, Named_object*, Statement_inserter*, int);
 
   bool
   do_is_constant() const
@@ -3057,7 +3064,8 @@ Type_conversion_expression::do_traverse(Traverse* traverse)
 // Convert to a constant at lowering time.
 
 Expression*
-Type_conversion_expression::do_lower(Gogo*, Named_object*, int)
+Type_conversion_expression::do_lower(Gogo*, Named_object*,
+                                    Statement_inserter*, int)
 {
   Type* type = this->type_;
   Expression* val = this->expr_;
@@ -3753,7 +3761,7 @@ class Unary_expression : public Expression
   { return Expression::traverse(&this->expr_, traverse); }
 
   Expression*
-  do_lower(Gogo*, Named_object*, int);
+  do_lower(Gogo*, Named_object*, Statement_inserter*, int);
 
   bool
   do_is_constant() const;
@@ -3808,7 +3816,7 @@ class Unary_expression : public Expression
 // instead.
 
 Expression*
-Unary_expression::do_lower(Gogo*, Named_object*, int)
+Unary_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int)
 {
   source_location loc = this->location();
   Operator op = this->op_;
@@ -5137,7 +5145,7 @@ Binary_expression::eval_complex(Operator op, Type* left_type,
 // constants.
 
 Expression*
-Binary_expression::do_lower(Gogo*, Named_object*, int)
+Binary_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int)
 {
   source_location location = this->location();
   Operator op = this->op_;
@@ -6656,7 +6664,7 @@ class Builtin_call_expression : public Call_expression
  protected:
   // This overrides Call_expression::do_lower.
   Expression*
-  do_lower(Gogo*, Named_object*, int);
+  do_lower(Gogo*, Named_object*, Statement_inserter*, int);
 
   bool
   do_is_constant() const;
@@ -6864,7 +6872,8 @@ Find_call_expression::expression(Expression** pexpr)
 // specific expressions.  We also convert to a constant if we can.
 
 Expression*
-Builtin_call_expression::do_lower(Gogo* gogo, Named_object* function, int)
+Builtin_call_expression::do_lower(Gogo* gogo, Named_object* function,
+                                 Statement_inserter* inserter, int)
 {
   if (this->classification() == EXPRESSION_ERROR)
     return this;
@@ -6974,7 +6983,7 @@ Builtin_call_expression::do_lower(Gogo* gogo, Named_object* function, int)
          this->set_is_error();
          return this;
        }
-      return this->lower_varargs(gogo, function, slice_type, 2);
+      return this->lower_varargs(gogo, function, inserter, slice_type, 2);
     }
 
   return this;
@@ -8553,9 +8562,10 @@ Call_expression::do_traverse(Traverse* traverse)
 // Lower a call statement.
 
 Expression*
-Call_expression::do_lower(Gogo* gogo, Named_object* function, int)
+Call_expression::do_lower(Gogo* gogo, Named_object* function,
+                         Statement_inserter* inserter, int)
 {
-  // A type case can look like a function call.
+  // A type cast can look like a function call.
   if (this->fn_->is_type_expression()
       && this->args_ != NULL
       && this->args_->size() == 1)
@@ -8597,6 +8607,29 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function, int)
        }
     }
 
+  // If this call returns multiple results, create a temporary
+  // variable for each result.
+  size_t rc = this->result_count();
+  if (rc > 1 && this->results_ == NULL)
+    {
+      std::vector<Temporary_statement*>* temps =
+       new std::vector<Temporary_statement*>;
+      temps->reserve(rc);
+      const Typed_identifier_list* results =
+       this->fn_->type()->function_type()->results();
+      for (Typed_identifier_list::const_iterator p = results->begin();
+          p != results->end();
+          ++p)
+       {
+         Temporary_statement* temp = Statement::make_temporary(p->type(),
+                                                               NULL,
+                                                               p->location());
+         inserter->insert(temp);
+         temps->push_back(temp);
+       }
+      this->results_ = temps;
+    }
+
   // Handle a call to a varargs function by packaging up the extra
   // parameters.
   if (this->fn_->type()->function_type() != NULL
@@ -8606,7 +8639,7 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function, int)
       const Typed_identifier_list* parameters = fntype->parameters();
       go_assert(parameters != NULL && !parameters->empty());
       Type* varargs_type = parameters->back().type();
-      return this->lower_varargs(gogo, function, varargs_type,
+      return this->lower_varargs(gogo, function, inserter, varargs_type,
                                 parameters->size());
     }
 
@@ -8622,6 +8655,7 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function, int)
 
 Expression*
 Call_expression::lower_varargs(Gogo* gogo, Named_object* function,
+                              Statement_inserter* inserter,
                               Type* varargs_type, size_t param_count)
 {
   if (this->varargs_are_lowered_)
@@ -8702,13 +8736,12 @@ Call_expression::lower_varargs(Gogo* gogo, Named_object* function,
 
   // Lower all the new subexpressions.
   Expression* ret = this;
-  gogo->lower_expression(function, &ret);
+  gogo->lower_expression(function, inserter, &ret);
   go_assert(ret == this);
   return ret;
 }
 
-// Get the function type.  Returns NULL if we don't know the type.  If
-// this returns NULL, and if_ERROR is true, issues an error.
+// Get the function type.  This can return NULL in error cases.
 
 Function_type*
 Call_expression::get_function_type() const
@@ -8729,6 +8762,16 @@ Call_expression::result_count() const
   return fntype->results()->size();
 }
 
+// Return the temporary which holds a result.
+
+Temporary_statement*
+Call_expression::result(size_t i) const
+{
+  go_assert(this->results_ != NULL
+           && this->results_->size() > i);
+  return (*this->results_)[i];
+}
+
 // Return whether this is a call to the predeclared function recover.
 
 bool
@@ -8759,6 +8802,21 @@ Call_expression::do_set_recover_arg(Expression*)
   go_unreachable();
 }
 
+// We have found an error with this call expression; return true if
+// we should report it.
+
+bool
+Call_expression::issue_error()
+{
+  if (this->issued_error_)
+    return false;
+  else
+    {
+      this->issued_error_ = true;
+      return true;
+    }
+}
+
 // Get the type.
 
 Type*
@@ -8941,15 +8999,12 @@ 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.
+// results then it will inevitably be executed last.
 
 bool
 Call_expression::do_must_eval_in_order() const
 {
-  return this->result_count() == 1;
+  return this->result_count() > 0;
 }
 
 // Get the function and the first argument to use when calling a bound
@@ -9193,16 +9248,56 @@ Call_expression::do_get_tree(Translate_context* context)
       ret = build1(NOP_EXPR, rettype, ret);
     }
 
-  // If there is more than one result, we will refer to the call
-  // multiple times.
-  if (fntype->results() != NULL && fntype->results()->size() > 1)
-    ret = save_expr(ret);
+  if (this->results_ != NULL)
+    ret = this->set_results(context, ret);
 
   this->tree_ = ret;
 
   return ret;
 }
 
+// Set the result variables if this call returns multiple results.
+
+tree
+Call_expression::set_results(Translate_context* context, tree call_tree)
+{
+  tree stmt_list = NULL_TREE;
+
+  call_tree = save_expr(call_tree);
+
+  if (TREE_CODE(TREE_TYPE(call_tree)) != RECORD_TYPE)
+    {
+      go_assert(saw_errors());
+      return call_tree;
+    }
+
+  source_location loc = this->location();
+  tree field = TYPE_FIELDS(TREE_TYPE(call_tree));
+  size_t rc = this->result_count();
+  for (size_t i = 0; i < rc; ++i, field = DECL_CHAIN(field))
+    {
+      go_assert(field != NULL_TREE);
+
+      Temporary_statement* temp = this->result(i);
+      Temporary_reference_expression* ref =
+       Expression::make_temporary_reference(temp, loc);
+      ref->set_is_lvalue();
+      tree temp_tree = ref->get_tree(context);
+      if (temp_tree == error_mark_node)
+       continue;
+
+      tree val_tree = build3_loc(loc, COMPONENT_REF, TREE_TYPE(field),
+                                call_tree, field, NULL_TREE);
+      tree set_tree = build2_loc(loc, MODIFY_EXPR, void_type_node, temp_tree,
+                                val_tree);
+
+      append_to_statement_list(set_tree, &stmt_list);
+    }
+  go_assert(field == NULL_TREE);
+
+  return save_expr(stmt_list);
+}
+
 // Make a call expression.
 
 Call_expression*
@@ -9292,10 +9387,11 @@ Call_result_expression::do_type()
       return Type::make_error_type();
     }
   const Typed_identifier_list* results = fntype->results();
-  if (results == NULL)
+  if (results == NULL || results->size() < 2)
     {
-      this->report_error(_("number of results does not match "
-                          "number of values"));
+      if (ce->issue_error())
+       this->report_error(_("number of results does not match "
+                            "number of values"));
       return Type::make_error_type();
     }
   Typed_identifier_list::const_iterator pr = results->begin();
@@ -9307,8 +9403,9 @@ Call_result_expression::do_type()
     }
   if (pr == results->end())
     {
-      this->report_error(_("number of results does not match "
-                          "number of values"));
+      if (ce->issue_error())
+       this->report_error(_("number of results does not match "
+                            "number of values"));
       return Type::make_error_type();
     }
   return pr->type();
@@ -9332,27 +9429,18 @@ Call_result_expression::do_determine_type(const Type_context*)
   this->call_->determine_type_no_context();
 }
 
-// Return the tree.
+// Return the tree.  We just refer to the temporary set by the call
+// expression.  We don't do this at lowering time because it makes it
+// hard to evaluate the call at the right time.
 
 tree
 Call_result_expression::do_get_tree(Translate_context* context)
 {
-  tree call_tree = this->call_->get_tree(context);
-  if (call_tree == error_mark_node)
-    return error_mark_node;
-  if (TREE_CODE(TREE_TYPE(call_tree)) != RECORD_TYPE)
-    {
-      go_assert(saw_errors());
-      return error_mark_node;
-    }
-  tree field = TYPE_FIELDS(TREE_TYPE(call_tree));
-  for (unsigned int i = 0; i < this->index_; ++i)
-    {
-      go_assert(field != NULL_TREE);
-      field = DECL_CHAIN(field);
-    }
-  go_assert(field != NULL_TREE);
-  return build3(COMPONENT_REF, TREE_TYPE(field), call_tree, field, NULL_TREE);
+  Call_expression* ce = this->call_->call_expression();
+  go_assert(ce != NULL);
+  Temporary_statement* ts = ce->result(this->index_);
+  Expression* ref = Expression::make_temporary_reference(ts, this->location());
+  return ref->get_tree(context);
 }
 
 // Make a reference to a single result of a call which returns
@@ -9383,7 +9471,7 @@ Index_expression::do_traverse(Traverse* traverse)
 // expression into an array index, a string index, or a map index.
 
 Expression*
-Index_expression::do_lower(Gogo*, Named_object*, int)
+Index_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int)
 {
   source_location location = this->location();
   Expression* left = this->left_;
@@ -10542,7 +10630,7 @@ class Selector_expression : public Parser_expression
   { return Expression::traverse(&this->left_, traverse); }
 
   Expression*
-  do_lower(Gogo*, Named_object*, int);
+  do_lower(Gogo*, Named_object*, Statement_inserter*, int);
 
   Expression*
   do_copy()
@@ -10565,7 +10653,8 @@ class Selector_expression : public Parser_expression
 // hand side.
 
 Expression*
-Selector_expression::do_lower(Gogo* gogo, Named_object*, int)
+Selector_expression::do_lower(Gogo* gogo, Named_object*, Statement_inserter*,
+                             int)
 {
   Expression* left = this->left_;
   if (left->is_type_expression())
@@ -10734,6 +10823,8 @@ Selector_expression::lower_method_expression(Gogo* gogo)
        }
     }
 
+  gogo->start_block(location);
+
   Call_expression* call = Expression::make_call(bm, args,
                                                method_type->is_varargs(),
                                                location);
@@ -10756,6 +10847,13 @@ Selector_expression::lower_method_expression(Gogo* gogo)
     }
   gogo->add_statement(s);
 
+  Block* b = gogo->finish_block(location);
+
+  gogo->add_block(b, location);
+
+  // Lower the call in case there are multiple results.
+  gogo->lower_block(no, b);
+
   gogo->finish_function(location);
 
   return Expression::make_func_reference(no, NULL, location);
@@ -11860,7 +11958,7 @@ class Composite_literal_expression : public Parser_expression
   do_traverse(Traverse* traverse);
 
   Expression*
-  do_lower(Gogo*, Named_object*, int);
+  do_lower(Gogo*, Named_object*, Statement_inserter*, int);
 
   Expression*
   do_copy()
@@ -11884,7 +11982,7 @@ class Composite_literal_expression : public Parser_expression
   make_array(Type*, Expression_list*);
 
   Expression*
-  lower_map(Gogo*, Named_object*, Type*);
+  lower_map(Gogo*, Named_object*, Statement_inserter*, Type*);
 
   // The type of the composite literal.
   Type* type_;
@@ -11913,7 +12011,8 @@ Composite_literal_expression::do_traverse(Traverse* traverse)
 // the type.
 
 Expression*
-Composite_literal_expression::do_lower(Gogo* gogo, Named_object* function, int)
+Composite_literal_expression::do_lower(Gogo* gogo, Named_object* function,
+                                      Statement_inserter* inserter, int)
 {
   Type* type = this->type_;
 
@@ -11940,7 +12039,7 @@ Composite_literal_expression::do_lower(Gogo* gogo, Named_object* function, int)
   else if (type->array_type() != NULL)
     return this->lower_array(type);
   else if (type->map_type() != NULL)
-    return this->lower_map(gogo, function, type);
+    return this->lower_map(gogo, function, inserter, type);
   else
     {
       error_at(this->location(),
@@ -12244,6 +12343,7 @@ Composite_literal_expression::make_array(Type* type, Expression_list* vals)
 
 Expression*
 Composite_literal_expression::lower_map(Gogo* gogo, Named_object* function,
+                                       Statement_inserter* inserter,
                                        Type* type)
 {
   source_location location = this->location();
@@ -12272,7 +12372,7 @@ Composite_literal_expression::lower_map(Gogo* gogo, Named_object* function,
          if ((*p)->unknown_expression() != NULL)
            {
              (*p)->unknown_expression()->clear_is_composite_literal_key();
-             gogo->lower_expression(function, &*p);
+             gogo->lower_expression(function, inserter, &*p);
              go_assert((*p)->is_error_expression());
              return Expression::make_error(location);
            }
index 271b1bb..da31f14 100644 (file)
@@ -15,6 +15,7 @@
 class Gogo;
 class Translate_context;
 class Traverse;
+class Statement_inserter;
 class Type;
 struct Type_context;
 class Function_type;
@@ -128,7 +129,7 @@ class Expression
   // 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*
+  static Temporary_reference_expression*
   make_temporary_reference(Temporary_statement*, source_location);
 
   // Make a sink expression--a reference to the blank identifier _.
@@ -521,13 +522,18 @@ class Expression
   traverse_subexpressions(Traverse*);
 
   // Lower an expression.  This is called immediately after parsing.
-  // IOTA_VALUE is the value that we should give to any iota
-  // expressions.  This function must resolve expressions which could
-  // not be fully parsed into their final form.  It returns the same
-  // Expression or a new one.
+  // FUNCTION is the function we are in; it will be NULL for an
+  // expression initializing a global variable.  INSERTER may be used
+  // to insert statements before the statement or initializer
+  // containing this expression; it is normally used to create
+  // temporary variables.  IOTA_VALUE is the value that we should give
+  // to any iota expressions.  This function must resolve expressions
+  // which could not be fully parsed into their final form.  It
+  // returns the same Expression or a new one.
   Expression*
-  lower(Gogo* gogo, Named_object* function, int iota_value)
-  { return this->do_lower(gogo, function, iota_value); }
+  lower(Gogo* gogo, Named_object* function, Statement_inserter* inserter,
+       int iota_value)
+  { return this->do_lower(gogo, function, inserter, iota_value); }
 
   // Determine the real type of an expression with abstract integer,
   // floating point, or complex type.  TYPE_CONTEXT describes the
@@ -636,7 +642,7 @@ class Expression
 
   // Return a lowered expression.
   virtual Expression*
-  do_lower(Gogo*, Named_object*, int)
+  do_lower(Gogo*, Named_object*, Statement_inserter*, int)
   { return this; }
 
   // Return whether this is a constant expression.
@@ -871,7 +877,7 @@ class Parser_expression : public Expression
 
  protected:
   virtual Expression*
-  do_lower(Gogo*, Named_object*, int) = 0;
+  do_lower(Gogo*, Named_object*, Statement_inserter*, int) = 0;
 
   Type*
   do_type();
@@ -906,7 +912,7 @@ class Var_expression : public Expression
 
  protected:
   Expression*
-  do_lower(Gogo*, Named_object*, int);
+  do_lower(Gogo*, Named_object*, Statement_inserter*, int);
 
   Type*
   do_type();
@@ -941,9 +947,15 @@ class Temporary_reference_expression : public Expression
   Temporary_reference_expression(Temporary_statement* statement,
                                 source_location location)
     : Expression(EXPRESSION_TEMPORARY_REFERENCE, location),
-      statement_(statement)
+      statement_(statement), is_lvalue_(false)
   { }
 
+  // Indicate that this reference appears on the left hand side of an
+  // assignment statement.
+  void
+  set_is_lvalue()
+  { this->is_lvalue_ = true; }
+
  protected:
   Type*
   do_type();
@@ -969,6 +981,9 @@ class Temporary_reference_expression : public Expression
  private:
   // The statement where the temporary variable is defined.
   Temporary_statement* statement_;
+  // Whether this reference appears on the left hand side of an
+  // assignment statement.
+  bool is_lvalue_;
 };
 
 // A string expression.
@@ -1099,7 +1114,7 @@ class Binary_expression : public Expression
   do_traverse(Traverse* traverse);
 
   Expression*
-  do_lower(Gogo*, Named_object*, int);
+  do_lower(Gogo*, Named_object*, Statement_inserter*, int);
 
   bool
   do_is_constant() const
@@ -1156,9 +1171,9 @@ class Call_expression : public Expression
   Call_expression(Expression* fn, Expression_list* args, bool is_varargs,
                  source_location location)
     : Expression(EXPRESSION_CALL, location),
-      fn_(fn), args_(args), type_(NULL), tree_(NULL), is_varargs_(is_varargs),
-      varargs_are_lowered_(false), types_are_determined_(false),
-      is_deferred_(false)
+      fn_(fn), args_(args), type_(NULL), results_(NULL), tree_(NULL),
+      is_varargs_(is_varargs), varargs_are_lowered_(false),
+      types_are_determined_(false), is_deferred_(false), issued_error_(false)
   { }
 
   // The function to call.
@@ -1183,6 +1198,12 @@ class Call_expression : public Expression
   size_t
   result_count() const;
 
+  // Return the temporary variable which holds result I.  This is only
+  // valid after the expression has been lowered, and is only valid
+  // for calls which return multiple results.
+  Temporary_statement*
+  result(size_t i) const;
+
   // Return whether this is a call to the predeclared function
   // recover.
   bool
@@ -1207,12 +1228,17 @@ class Call_expression : public Expression
   set_is_deferred()
   { this->is_deferred_ = true; }
 
+  // We have found an error with this call expression; return true if
+  // we should report it.
+  bool
+  issue_error();
+
  protected:
   int
   do_traverse(Traverse*);
 
   virtual Expression*
-  do_lower(Gogo*, Named_object*, int);
+  do_lower(Gogo*, Named_object*, Statement_inserter*, int);
 
   void
   do_discarding_value()
@@ -1256,8 +1282,8 @@ class Call_expression : public Expression
 
   // Let a builtin expression lower varargs.
   Expression*
-  lower_varargs(Gogo*, Named_object* function, Type* varargs_type,
-               size_t param_count);
+  lower_varargs(Gogo*, Named_object* function, Statement_inserter* inserter,
+               Type* varargs_type, size_t param_count);
 
   // Let a builtin expression check whether types have been
   // determined.
@@ -1276,6 +1302,9 @@ class Call_expression : public Expression
                            Interface_field_reference_expression*,
                            tree*);
 
+  tree
+  set_results(Translate_context*, tree);
+
   // The function to call.
   Expression* fn_;
   // The arguments to pass.  This may be NULL if there are no
@@ -1283,6 +1312,9 @@ class Call_expression : public Expression
   Expression_list* args_;
   // The type of the expression, to avoid recomputing it.
   Type* type_;
+  // The list of temporaries which will hold the results if the
+  // function returns a tuple.
+  std::vector<Temporary_statement*>* results_;
   // The tree for the call, used for a call which returns a tuple.
   tree tree_;
   // True if the last argument is a varargs argument (f(a...)).
@@ -1293,6 +1325,10 @@ class Call_expression : public Expression
   bool types_are_determined_;
   // True if the call is an argument to a defer statement.
   bool is_deferred_;
+  // True if we reported an error about a mismatch between call
+  // results and uses.  This is to avoid producing multiple errors
+  // when there are multiple Call_result_expressions.
+  bool issued_error_;
 };
 
 // An expression which represents a pointer to a function.
@@ -1390,7 +1426,7 @@ class Unknown_expression : public Parser_expression
 
  protected:
   Expression*
-  do_lower(Gogo*, Named_object*, int);
+  do_lower(Gogo*, Named_object*, Statement_inserter*, int);
 
   Expression*
   do_copy()
@@ -1425,7 +1461,7 @@ class Index_expression : public Parser_expression
   do_traverse(Traverse*);
 
   Expression*
-  do_lower(Gogo*, Named_object*, int);
+  do_lower(Gogo*, Named_object*, Statement_inserter*, int);
 
   Expression*
   do_copy()
index 194caca..4aafe41 100644 (file)
@@ -1148,9 +1148,13 @@ class Lower_parse_tree : public Traverse
               | traverse_functions
               | traverse_statements
               | traverse_expressions),
-      gogo_(gogo), function_(function), iota_value_(-1)
+      gogo_(gogo), function_(function), iota_value_(-1), inserter_()
   { }
 
+  void
+  set_inserter(const Statement_inserter* inserter)
+  { this->inserter_ = *inserter; }
+
   int
   variable(Named_object*);
 
@@ -1173,18 +1177,44 @@ class Lower_parse_tree : public Traverse
   Named_object* function_;
   // Value to use for the predeclared constant iota.
   int iota_value_;
+  // Current statement inserter for use by expressions.
+  Statement_inserter inserter_;
 };
 
-// Lower variables.  We handle variables specially to break loops in
-// which a variable initialization expression refers to itself.  The
-// loop breaking is in lower_init_expression.
+// Lower variables.
 
 int
 Lower_parse_tree::variable(Named_object* no)
 {
-  if (no->is_variable())
-    no->var_value()->lower_init_expression(this->gogo_, this->function_);
-  return TRAVERSE_CONTINUE;
+  if (!no->is_variable())
+    return TRAVERSE_CONTINUE;
+
+  if (no->is_variable() && no->var_value()->is_global())
+    {
+      // Global variables can have loops in their initialization
+      // expressions.  This is handled in lower_init_expression.
+      no->var_value()->lower_init_expression(this->gogo_, this->function_,
+                                            &this->inserter_);
+      return TRAVERSE_CONTINUE;
+    }
+
+  // This is a local variable.  We are going to return
+  // TRAVERSE_SKIP_COMPONENTS here because we want to traverse the
+  // initialization expression when we reach the variable declaration
+  // statement.  However, that means that we need to traverse the type
+  // ourselves.
+  if (no->var_value()->has_type())
+    {
+      Type* type = no->var_value()->type();
+      if (type != NULL)
+       {
+         if (Type::traverse(type, this) == TRAVERSE_EXIT)
+           return TRAVERSE_EXIT;
+       }
+    }
+  go_assert(!no->var_value()->has_pre_init());
+
+  return TRAVERSE_SKIP_COMPONENTS;
 }
 
 // Lower constants.  We handle constants specially so that we can set
@@ -1238,27 +1268,38 @@ Lower_parse_tree::function(Named_object* no)
 int
 Lower_parse_tree::statement(Block* block, size_t* pindex, Statement* sorig)
 {
+  Statement_inserter hold_inserter(this->inserter_);
+  this->inserter_ = Statement_inserter(block, pindex);
+
   // Lower the expressions first.
   int t = sorig->traverse_contents(this);
   if (t == TRAVERSE_EXIT)
-    return t;
+    {
+      this->inserter_ = hold_inserter;
+      return t;
+    }
 
   // Keep lowering until nothing changes.
   Statement* s = sorig;
   while (true)
     {
-      Statement* snew = s->lower(this->gogo_, this->function_, block);
+      Statement* snew = s->lower(this->gogo_, this->function_, block,
+                                &this->inserter_);
       if (snew == s)
        break;
       s = snew;
       t = s->traverse_contents(this);
       if (t == TRAVERSE_EXIT)
-       return t;
+       {
+         this->inserter_ = hold_inserter;
+         return t;
+       }
     }
 
   if (s != sorig)
     block->replace_statement(*pindex, s);
 
+  this->inserter_ = hold_inserter;
   return TRAVERSE_SKIP_COMPONENTS;
 }
 
@@ -1277,7 +1318,7 @@ Lower_parse_tree::expression(Expression** pexpr)
     {
       Expression* e = *pexpr;
       Expression* enew = e->lower(this->gogo_, this->function_,
-                                 this->iota_value_);
+                                 &this->inserter_, this->iota_value_);
       if (enew == e)
        break;
       *pexpr = enew;
@@ -1304,12 +1345,16 @@ Gogo::lower_block(Named_object* function, Block* block)
   block->traverse(&lower_parse_tree);
 }
 
-// Lower an expression.
+// Lower an expression.  INSERTER may be NULL, in which case the
+// expression had better not need to create any temporaries.
 
 void
-Gogo::lower_expression(Named_object* function, Expression** pexpr)
+Gogo::lower_expression(Named_object* function, Statement_inserter* inserter,
+                      Expression** pexpr)
 {
   Lower_parse_tree lower_parse_tree(this, function);
+  if (inserter != NULL)
+    lower_parse_tree.set_inserter(inserter);
   lower_parse_tree.expression(pexpr);
 }
 
@@ -1951,11 +1996,26 @@ Order_eval::statement(Block* block, size_t* pindex, Statement* s)
        break;
 
       source_location loc = (*pexpr)->location();
-      Temporary_statement* ts = Statement::make_temporary(NULL, *pexpr, loc);
-      block->insert_statement_before(*pindex, ts);
-      ++*pindex;
+      Statement* s;
+      if ((*pexpr)->call_expression() == NULL
+         || (*pexpr)->call_expression()->result_count() < 2)
+       {
+         Temporary_statement* ts = Statement::make_temporary(NULL, *pexpr,
+                                                             loc);
+         s = ts;
+         *pexpr = Expression::make_temporary_reference(ts, loc);
+       }
+      else
+       {
+         // A call expression which returns multiple results needs to
+         // be handled specially.  We can't create a temporary
+         // because there is no type to give it.  Any actual uses of
+         // the values will be done via Call_result_expressions.
+         s = Statement::make_statement(*pexpr);
+       }
 
-      *pexpr = Expression::make_temporary_reference(ts, loc);
+      block->insert_statement_before(*pindex, s);
+      ++*pindex;
     }
 
   if (init != orig_init)
@@ -1978,7 +2038,7 @@ Order_eval::variable(Named_object* no)
     return TRAVERSE_CONTINUE;
 
   Find_eval_ordering find_eval_ordering;
-  init->traverse_subexpressions(&find_eval_ordering);
+  Expression::traverse(&init, &find_eval_ordering);
 
   if (find_eval_ordering.size() <= 1)
     {
@@ -1993,9 +2053,22 @@ Order_eval::variable(Named_object* no)
     {
       Expression** pexpr = *p;
       source_location loc = (*pexpr)->location();
-      Temporary_statement* ts = Statement::make_temporary(NULL, *pexpr, loc);
-      var->add_preinit_statement(this->gogo_, ts);
-      *pexpr = Expression::make_temporary_reference(ts, loc);
+      Statement* s;
+      if ((*pexpr)->call_expression() == NULL
+         || (*pexpr)->call_expression()->result_count() < 2)
+       {
+         Temporary_statement* ts = Statement::make_temporary(NULL, *pexpr,
+                                                             loc);
+         s = ts;
+         *pexpr = Expression::make_temporary_reference(ts, loc);
+       }
+      else
+       {
+         // A call expression which returns multiple results needs to
+         // be handled specially.
+         s = Statement::make_statement(*pexpr);
+       }
+      var->add_preinit_statement(this->gogo_, s);
     }
 
   return TRAVERSE_SKIP_COMPONENTS;
@@ -2181,6 +2254,8 @@ Build_recover_thunks::function(Named_object* orig_no)
     }
   args->push_back(this->can_recover_arg(location));
 
+  gogo->start_block(location);
+
   Call_expression* call = Expression::make_call(fn, args, false, location);
 
   Statement* s;
@@ -2202,6 +2277,13 @@ Build_recover_thunks::function(Named_object* orig_no)
   s->determine_types();
   gogo->add_statement(s);
 
+  Block* b = gogo->finish_block(location);
+
+  gogo->add_block(b, location);
+
+  // Lower the call in case it returns multiple results.
+  gogo->lower_block(new_no, b);
+
   gogo->finish_function(location);
 
   // Swap the function bodies and types.
@@ -3152,78 +3234,64 @@ Block::traverse(Traverse* traverse)
          | Traverse::traverse_expressions
          | Traverse::traverse_types)) != 0)
     {
+      const unsigned int e_or_t = (Traverse::traverse_expressions
+                                  | Traverse::traverse_types);
+      const unsigned int e_or_t_or_s = (e_or_t
+                                       | Traverse::traverse_statements);
       for (Bindings::const_definitions_iterator pb =
             this->bindings_->begin_definitions();
           pb != this->bindings_->end_definitions();
           ++pb)
        {
+         int t = TRAVERSE_CONTINUE;
          switch ((*pb)->classification())
            {
            case Named_object::NAMED_OBJECT_CONST:
              if ((traverse_mask & Traverse::traverse_constants) != 0)
+               t = traverse->constant(*pb, false);
+             if (t == TRAVERSE_CONTINUE
+                 && (traverse_mask & e_or_t) != 0)
                {
-                 if (traverse->constant(*pb, false) == TRAVERSE_EXIT)
-                   return TRAVERSE_EXIT;
-               }
-             if ((traverse_mask & Traverse::traverse_types) != 0
-                 || (traverse_mask & Traverse::traverse_expressions) != 0)
-               {
-                 Type* t = (*pb)->const_value()->type();
-                 if (t != NULL
-                     && Type::traverse(t, traverse) == TRAVERSE_EXIT)
-                   return TRAVERSE_EXIT;
-               }
-             if ((traverse_mask & Traverse::traverse_expressions) != 0
-                 || (traverse_mask & Traverse::traverse_types) != 0)
-               {
-                 if ((*pb)->const_value()->traverse_expression(traverse)
-                     == TRAVERSE_EXIT)
+                 Type* tc = (*pb)->const_value()->type();
+                 if (tc != NULL
+                     && Type::traverse(tc, traverse) == TRAVERSE_EXIT)
                    return TRAVERSE_EXIT;
+                 t = (*pb)->const_value()->traverse_expression(traverse);
                }
              break;
 
            case Named_object::NAMED_OBJECT_VAR:
            case Named_object::NAMED_OBJECT_RESULT_VAR:
              if ((traverse_mask & Traverse::traverse_variables) != 0)
+               t = traverse->variable(*pb);
+             if (t == TRAVERSE_CONTINUE
+                 && (traverse_mask & e_or_t) != 0)
                {
-                 if (traverse->variable(*pb) == TRAVERSE_EXIT)
-                   return TRAVERSE_EXIT;
-               }
-             if (((traverse_mask & Traverse::traverse_types) != 0
-                  || (traverse_mask & Traverse::traverse_expressions) != 0)
-                 && ((*pb)->is_result_variable()
-                     || (*pb)->var_value()->has_type()))
-               {
-                 Type* t = ((*pb)->is_variable()
-                            ? (*pb)->var_value()->type()
-                            : (*pb)->result_var_value()->type());
-                 if (t != NULL
-                     && Type::traverse(t, traverse) == TRAVERSE_EXIT)
-                   return TRAVERSE_EXIT;
-               }
-             if ((*pb)->is_variable()
-                 && ((traverse_mask & Traverse::traverse_expressions) != 0
-                     || (traverse_mask & Traverse::traverse_types) != 0))
-               {
-                 if ((*pb)->var_value()->traverse_expression(traverse)
-                     == TRAVERSE_EXIT)
-                   return TRAVERSE_EXIT;
+                 if ((*pb)->is_result_variable()
+                     || (*pb)->var_value()->has_type())
+                   {
+                     Type* tv = ((*pb)->is_variable()
+                                 ? (*pb)->var_value()->type()
+                                 : (*pb)->result_var_value()->type());
+                     if (tv != NULL
+                         && Type::traverse(tv, traverse) == TRAVERSE_EXIT)
+                       return TRAVERSE_EXIT;
+                   }
                }
+             if (t == TRAVERSE_CONTINUE
+                 && (traverse_mask & e_or_t_or_s) != 0
+                 && (*pb)->is_variable())
+               t = (*pb)->var_value()->traverse_expression(traverse,
+                                                           traverse_mask);
              break;
 
            case Named_object::NAMED_OBJECT_FUNC:
            case Named_object::NAMED_OBJECT_FUNC_DECLARATION:
-             // FIXME: Where will nested functions be found?
              go_unreachable();
 
            case Named_object::NAMED_OBJECT_TYPE:
-             if ((traverse_mask & Traverse::traverse_types) != 0
-                 || (traverse_mask & Traverse::traverse_expressions) != 0)
-               {
-                 if (Type::traverse((*pb)->type_value(), traverse)
-                     == TRAVERSE_EXIT)
-                   return TRAVERSE_EXIT;
-               }
+             if ((traverse_mask & e_or_t) != 0)
+               t = Type::traverse((*pb)->type_value(), traverse);
              break;
 
            case Named_object::NAMED_OBJECT_TYPE_DECLARATION:
@@ -3237,6 +3305,9 @@ Block::traverse(Traverse* traverse)
            default:
              go_unreachable();
            }
+
+         if (t == TRAVERSE_EXIT)
+           return TRAVERSE_EXIT;
        }
     }
 
@@ -3351,14 +3422,17 @@ Variable::Variable(Type* type, Expression* init, bool is_global,
 // Traverse the initializer expression.
 
 int
-Variable::traverse_expression(Traverse* traverse)
+Variable::traverse_expression(Traverse* traverse, unsigned int traverse_mask)
 {
   if (this->preinit_ != NULL)
     {
       if (this->preinit_->traverse(traverse) == TRAVERSE_EXIT)
        return TRAVERSE_EXIT;
     }
-  if (this->init_ != NULL)
+  if (this->init_ != NULL
+      && ((traverse_mask
+          & (Traverse::traverse_expressions | Traverse::traverse_types))
+         != 0))
     {
       if (Expression::traverse(&this->init_, traverse) == TRAVERSE_EXIT)
        return TRAVERSE_EXIT;
@@ -3369,7 +3443,8 @@ Variable::traverse_expression(Traverse* traverse)
 // Lower the initialization expression after parsing is complete.
 
 void
-Variable::lower_init_expression(Gogo* gogo, Named_object* function)
+Variable::lower_init_expression(Gogo* gogo, Named_object* function,
+                               Statement_inserter* inserter)
 {
   if (this->init_ != NULL && !this->init_is_lowered_)
     {
@@ -3381,7 +3456,14 @@ Variable::lower_init_expression(Gogo* gogo, Named_object* function)
        }
       this->seen_ = true;
 
-      gogo->lower_expression(function, &this->init_);
+      Statement_inserter global_inserter;
+      if (this->is_global_)
+       {
+         global_inserter = Statement_inserter(gogo, this);
+         inserter = &global_inserter;
+       }
+
+      gogo->lower_expression(function, inserter, &this->init_);
 
       this->seen_ = false;
 
@@ -4508,77 +4590,67 @@ Bindings::traverse(Traverse* traverse, bool is_global)
 
   // We don't use an iterator because we permit the traversal to add
   // new global objects.
+  const unsigned int e_or_t = (Traverse::traverse_expressions
+                              | Traverse::traverse_types);
+  const unsigned int e_or_t_or_s = (e_or_t
+                                   | Traverse::traverse_statements);
   for (size_t i = 0; i < this->named_objects_.size(); ++i)
     {
       Named_object* p = this->named_objects_[i];
+      int t = TRAVERSE_CONTINUE;
       switch (p->classification())
        {
        case Named_object::NAMED_OBJECT_CONST:
          if ((traverse_mask & Traverse::traverse_constants) != 0)
+           t = traverse->constant(p, is_global);
+         if (t == TRAVERSE_CONTINUE
+             && (traverse_mask & e_or_t) != 0)
            {
-             if (traverse->constant(p, is_global) == TRAVERSE_EXIT)
-               return TRAVERSE_EXIT;
-           }
-         if ((traverse_mask & Traverse::traverse_types) != 0
-             || (traverse_mask & Traverse::traverse_expressions) != 0)
-           {
-             Type* t = p->const_value()->type();
-             if (t != NULL
-                 && Type::traverse(t, traverse) == TRAVERSE_EXIT)
-               return TRAVERSE_EXIT;
-             if (p->const_value()->traverse_expression(traverse)
-                 == TRAVERSE_EXIT)
+             Type* tc = p->const_value()->type();
+             if (tc != NULL
+                 && Type::traverse(tc, traverse) == TRAVERSE_EXIT)
                return TRAVERSE_EXIT;
+             t = p->const_value()->traverse_expression(traverse);
            }
          break;
 
        case Named_object::NAMED_OBJECT_VAR:
        case Named_object::NAMED_OBJECT_RESULT_VAR:
          if ((traverse_mask & Traverse::traverse_variables) != 0)
+           t = traverse->variable(p);
+         if (t == TRAVERSE_CONTINUE
+             && (traverse_mask & e_or_t) != 0)
            {
-             if (traverse->variable(p) == TRAVERSE_EXIT)
-               return TRAVERSE_EXIT;
-           }
-         if (((traverse_mask & Traverse::traverse_types) != 0
-              || (traverse_mask & Traverse::traverse_expressions) != 0)
-             && (p->is_result_variable()
-                 || p->var_value()->has_type()))
-           {
-             Type* t = (p->is_variable()
-                        ? p->var_value()->type()
-                        : p->result_var_value()->type());
-             if (t != NULL
-                 && Type::traverse(t, traverse) == TRAVERSE_EXIT)
-               return TRAVERSE_EXIT;
-           }
-         if (p->is_variable()
-             && ((traverse_mask & Traverse::traverse_types) != 0
-                 || (traverse_mask & Traverse::traverse_expressions) != 0))
-           {
-             if (p->var_value()->traverse_expression(traverse)
-                 == TRAVERSE_EXIT)
-               return TRAVERSE_EXIT;
+             if (p->is_result_variable()
+                 || p->var_value()->has_type())
+               {
+                 Type* tv = (p->is_variable()
+                             ? p->var_value()->type()
+                             : p->result_var_value()->type());
+                 if (tv != NULL
+                     && Type::traverse(tv, traverse) == TRAVERSE_EXIT)
+                   return TRAVERSE_EXIT;
+               }
            }
+         if (t == TRAVERSE_CONTINUE
+             && (traverse_mask & e_or_t_or_s) != 0
+             && p->is_variable())
+           t = p->var_value()->traverse_expression(traverse, traverse_mask);
          break;
 
        case Named_object::NAMED_OBJECT_FUNC:
          if ((traverse_mask & Traverse::traverse_functions) != 0)
-           {
-             int t = traverse->function(p);
-             if (t == TRAVERSE_EXIT)
-               return TRAVERSE_EXIT;
-             else if (t == TRAVERSE_SKIP_COMPONENTS)
-               break;
-           }
-
-         if ((traverse_mask
-              & (Traverse::traverse_variables
-                 | Traverse::traverse_constants
-                 | Traverse::traverse_functions
-                 | Traverse::traverse_blocks
-                 | Traverse::traverse_statements
-                 | Traverse::traverse_expressions
-                 | Traverse::traverse_types)) != 0)
+           t = traverse->function(p);
+
+         if (t == TRAVERSE_CONTINUE
+             && (traverse_mask
+                 & (Traverse::traverse_variables
+                    | Traverse::traverse_constants
+                    | Traverse::traverse_functions
+                    | Traverse::traverse_blocks
+                    | Traverse::traverse_statements
+                    | Traverse::traverse_expressions
+                    | Traverse::traverse_types)) != 0)
            {
              if (p->func_value()->traverse(traverse) == TRAVERSE_EXIT)
                return TRAVERSE_EXIT;
@@ -4591,12 +4663,8 @@ Bindings::traverse(Traverse* traverse, bool is_global)
          break;
 
        case Named_object::NAMED_OBJECT_TYPE:
-         if ((traverse_mask & Traverse::traverse_types) != 0
-             || (traverse_mask & Traverse::traverse_expressions) != 0)
-           {
-             if (Type::traverse(p->type_value(), traverse) == TRAVERSE_EXIT)
-               return TRAVERSE_EXIT;
-           }
+         if ((traverse_mask & e_or_t) != 0)
+           t = Type::traverse(p->type_value(), traverse);
          break;
 
        case Named_object::NAMED_OBJECT_TYPE_DECLARATION:
@@ -4608,6 +4676,9 @@ Bindings::traverse(Traverse* traverse, bool is_global)
        default:
          go_unreachable();
        }
+
+      if (t == TRAVERSE_EXIT)
+       return TRAVERSE_EXIT;
     }
 
   return TRAVERSE_CONTINUE;
@@ -4805,3 +4876,20 @@ Traverse::type(Type*)
 {
   go_unreachable();
 }
+
+// Class Statement_inserter.
+
+void
+Statement_inserter::insert(Statement* s)
+{
+  if (this->block_ != NULL)
+    {
+      go_assert(this->pindex_ != NULL);
+      this->block_->insert_statement_before(*this->pindex_, s);
+      ++*this->pindex_;
+    }
+  else if (this->var_ != NULL)
+    this->var_->add_preinit_statement(this->gogo_, s);
+  else
+    go_unreachable();
+}
index cc349af..c9d2971 100644 (file)
@@ -8,6 +8,7 @@
 #define GO_GOGO_H
 
 class Traverse;
+class Statement_inserter;
 class Type;
 class Type_hash_identical;
 class Type_equal;
@@ -366,7 +367,7 @@ class Gogo
 
   // Lower an expression.
   void
-  lower_expression(Named_object* function, Expression**);
+  lower_expression(Named_object* function, Statement_inserter*, Expression**);
 
   // Lower a constant.
   void
@@ -1157,7 +1158,7 @@ class Variable
 
   // Lower the initialization expression after parsing is complete.
   void
-  lower_init_expression(Gogo*, Named_object*);
+  lower_init_expression(Gogo*, Named_object*, Statement_inserter*);
 
   // A special case: the init value is used only to determine the
   // type.  This is used if the variable is defined using := with the
@@ -1208,7 +1209,7 @@ class Variable
 
   // Traverse the initializer expression.
   int
-  traverse_expression(Traverse*);
+  traverse_expression(Traverse*, unsigned int traverse_mask);
 
   // Determine the type of the variable if necessary.
   void
@@ -2463,6 +2464,46 @@ class Traverse
   Expressions_seen* expressions_seen_;
 };
 
+// A class which makes it easier to insert new statements before the
+// current statement during a traversal.
+
+class Statement_inserter
+{
+ public:
+  // Empty constructor.
+  Statement_inserter()
+    : block_(NULL), pindex_(NULL), gogo_(NULL), var_(NULL)
+  { }
+
+  // Constructor for a statement in a block.
+  Statement_inserter(Block* block, size_t *pindex)
+    : block_(block), pindex_(pindex), gogo_(NULL), var_(NULL)
+  { }
+
+  // Constructor for a global variable.
+  Statement_inserter(Gogo* gogo, Variable* var)
+    : block_(NULL), pindex_(NULL), gogo_(gogo), var_(var)
+  { go_assert(var->is_global()); }
+
+  // We use the default copy constructor and assignment operator.
+
+  // Insert S before the statement we are traversing, or before the
+  // initialization expression of a global variable.
+  void
+  insert(Statement* s);
+
+ private:
+  // The block that the statement is in.
+  Block* block_;
+  // The index of the statement that we are traversing.
+  size_t* pindex_;
+  // The IR, needed when looking at an initializer expression for a
+  // global variable.
+  Gogo* gogo_;
+  // The global variable, when looking at an initializer expression.
+  Variable* var_;
+};
+
 // When translating the gogo IR into the backend data structure, this
 // is the context we pass down the blocks and statements.
 
index 4d335bb..dd2aef6 100644 (file)
@@ -217,6 +217,16 @@ Variable_declaration_statement::do_traverse_assignments(
   return true;
 }
 
+// Lower the variable's initialization expression.
+
+Statement*
+Variable_declaration_statement::do_lower(Gogo* gogo, Named_object* function,
+                                        Block*, Statement_inserter* inserter)
+{
+  this->var_->var_value()->lower_init_expression(gogo, function, inserter);
+  return this;
+}
+
 // Convert a variable declaration to the backend representation.
 
 Bstatement*
@@ -244,7 +254,7 @@ Variable_declaration_statement::do_get_backend(Translate_context* context)
   Expression_list* params = new Expression_list();
   params->push_back(Expression::make_type(var->type(), loc));
   Expression* call = Expression::make_call(func, params, false, loc);
-  context->gogo()->lower_expression(context->function(), &call);
+  context->gogo()->lower_expression(context->function(), NULL, &call);
   Temporary_statement* temp = Statement::make_temporary(NULL, call, loc);
   Bstatement* btemp = temp->get_backend(context);
 
@@ -386,7 +396,7 @@ Temporary_statement::do_get_backend(Translate_context* context)
     {
       Expression* init = Expression::make_cast(this->type_, this->init_,
                                               this->location());
-      context->gogo()->lower_expression(context->function(), &init);
+      context->gogo()->lower_expression(context->function(), NULL, &init);
       binit = tree_to_expr(init->get_tree(context));
     }
 
@@ -598,7 +608,7 @@ class Assignment_operation_statement : public Statement
   { go_unreachable(); }
 
   Statement*
-  do_lower(Gogo*, Named_object*, Block*);
+  do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
 
   Bstatement*
   do_get_backend(Translate_context*)
@@ -628,7 +638,7 @@ Assignment_operation_statement::do_traverse(Traverse* traverse)
 
 Statement*
 Assignment_operation_statement::do_lower(Gogo*, Named_object*,
-                                        Block* enclosing)
+                                        Block* enclosing, Statement_inserter*)
 {
   source_location loc = this->location();
 
@@ -725,7 +735,7 @@ class Tuple_assignment_statement : public Statement
   { go_unreachable(); }
 
   Statement*
-  do_lower(Gogo*, Named_object*, Block*);
+  do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
 
   Bstatement*
   do_get_backend(Translate_context*)
@@ -752,7 +762,8 @@ Tuple_assignment_statement::do_traverse(Traverse* traverse)
 // up into a set of single assignments.
 
 Statement*
-Tuple_assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing)
+Tuple_assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing,
+                                    Statement_inserter*)
 {
   source_location loc = this->location();
 
@@ -852,7 +863,7 @@ public:
   { go_unreachable(); }
 
   Statement*
-  do_lower(Gogo*, Named_object*, Block*);
+  do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
 
   Bstatement*
   do_get_backend(Translate_context*)
@@ -882,7 +893,7 @@ Tuple_map_assignment_statement::do_traverse(Traverse* traverse)
 
 Statement*
 Tuple_map_assignment_statement::do_lower(Gogo*, Named_object*,
-                                        Block* enclosing)
+                                        Block* enclosing, Statement_inserter*)
 {
   source_location loc = this->location();
 
@@ -923,7 +934,8 @@ Tuple_map_assignment_statement::do_lower(Gogo*, Named_object*,
   b->add_statement(present_temp);
 
   // present_temp = mapaccess2(MAP, &key_temp, &val_temp)
-  Expression* ref = Expression::make_temporary_reference(key_temp, loc);
+  Temporary_reference_expression* ref =
+    Expression::make_temporary_reference(key_temp, loc);
   Expression* a1 = Expression::make_unary(OPERATOR_AND, ref, loc);
   ref = Expression::make_temporary_reference(val_temp, loc);
   Expression* a2 = Expression::make_unary(OPERATOR_AND, ref, loc);
@@ -931,6 +943,7 @@ Tuple_map_assignment_statement::do_lower(Gogo*, Named_object*,
                                        map_index->map(), a1, a2);
 
   ref = Expression::make_temporary_reference(present_temp, loc);
+  ref->set_is_lvalue();
   Statement* s = Statement::make_assignment(ref, call, loc);
   b->add_statement(s);
 
@@ -979,7 +992,7 @@ class Map_assignment_statement : public Statement
   { go_unreachable(); }
 
   Statement*
-  do_lower(Gogo*, Named_object*, Block*);
+  do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
 
   Bstatement*
   do_get_backend(Translate_context*)
@@ -1008,7 +1021,8 @@ Map_assignment_statement::do_traverse(Traverse* traverse)
 // Lower a map assignment to a function call.
 
 Statement*
-Map_assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing)
+Map_assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing,
+                                  Statement_inserter*)
 {
   source_location loc = this->location();
 
@@ -1093,7 +1107,7 @@ class Tuple_receive_assignment_statement : public Statement
   { go_unreachable(); }
 
   Statement*
-  do_lower(Gogo*, Named_object*, Block*);
+  do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
 
   Bstatement*
   do_get_backend(Translate_context*)
@@ -1125,7 +1139,8 @@ Tuple_receive_assignment_statement::do_traverse(Traverse* traverse)
 
 Statement*
 Tuple_receive_assignment_statement::do_lower(Gogo*, Named_object*,
-                                            Block* enclosing)
+                                            Block* enclosing,
+                                            Statement_inserter*)
 {
   source_location loc = this->location();
 
@@ -1160,13 +1175,15 @@ Tuple_receive_assignment_statement::do_lower(Gogo*, Named_object*,
   b->add_statement(closed_temp);
 
   // closed_temp = chanrecv[23](channel, &val_temp)
-  Expression* ref = Expression::make_temporary_reference(val_temp, loc);
+  Temporary_reference_expression* ref =
+    Expression::make_temporary_reference(val_temp, loc);
   Expression* p2 = Expression::make_unary(OPERATOR_AND, ref, loc);
   Expression* call = Runtime::make_call((this->for_select_
                                         ? Runtime::CHANRECV3
                                         : Runtime::CHANRECV2),
                                        loc, 2, this->channel_, p2);
   ref = Expression::make_temporary_reference(closed_temp, loc);
+  ref->set_is_lvalue();
   Statement* s = Statement::make_assignment(ref, call, loc);
   b->add_statement(s);
 
@@ -1217,7 +1234,7 @@ class Tuple_type_guard_assignment_statement : public Statement
   { go_unreachable(); }
 
   Statement*
-  do_lower(Gogo*, Named_object*, Block*);
+  do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
 
   Bstatement*
   do_get_backend(Translate_context*)
@@ -1256,7 +1273,8 @@ Tuple_type_guard_assignment_statement::do_traverse(Traverse* traverse)
 
 Statement*
 Tuple_type_guard_assignment_statement::do_lower(Gogo*, Named_object*,
-                                               Block* enclosing)
+                                               Block* enclosing,
+                                               Statement_inserter*)
 {
   source_location loc = this->location();
 
@@ -1378,6 +1396,10 @@ class Expression_statement : public Statement
       expr_(expr)
   { }
 
+  Expression*
+  expr()
+  { return this->expr_; }
+
  protected:
   int
   do_traverse(Traverse* traverse)
@@ -1513,7 +1535,7 @@ class Inc_dec_statement : public Statement
   { go_unreachable(); }
 
   Statement*
-  do_lower(Gogo*, Named_object*, Block*);
+  do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
 
   Bstatement*
   do_get_backend(Translate_context*)
@@ -1529,7 +1551,7 @@ class Inc_dec_statement : public Statement
 // Lower to += or -=.
 
 Statement*
-Inc_dec_statement::do_lower(Gogo*, Named_object*, Block*)
+Inc_dec_statement::do_lower(Gogo*, Named_object*, Block*, Statement_inserter*)
 {
   source_location loc = this->location();
 
@@ -2017,6 +2039,8 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name,
   Named_object* function = gogo->start_function(thunk_name, thunk_type, true,
                                                location);
 
+  gogo->start_block(location);
+
   // For a defer statement, start with a call to
   // __go_set_defer_retaddr.  */
   Label* retaddr_label = NULL; 
@@ -2122,26 +2146,10 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name,
       call_params = NULL;
     }
 
-  Expression* call = Expression::make_call(func_to_call, call_params, false,
-                                          location);
-  // We need to lower in case this is a builtin function.
-  call = call->lower(gogo, function, -1);
-  Call_expression* call_ce = call->call_expression();
-  if (call_ce != NULL && may_call_recover)
-    call_ce->set_is_deferred();
-
+  Call_expression* call = Expression::make_call(func_to_call, call_params,
+                                               false, location);
   Statement* call_statement = Statement::make_statement(call);
 
-  // We already ran the determine_types pass, so we need to run it
-  // just for this statement now.
-  call_statement->determine_types();
-
-  // Sanity check.
-  call->check_types(gogo);
-
-  if (call_ce != NULL && recover_arg != NULL)
-    call_ce->set_recover_arg(recover_arg);
-
   gogo->add_statement(call_statement);
 
   // If this is a defer statement, the label comes immediately after
@@ -2155,6 +2163,31 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name,
       gogo->add_statement(Statement::make_return_statement(vals, location));
     }
 
+  Block* b = gogo->finish_block(location);
+
+  gogo->add_block(b, location);
+
+  gogo->lower_block(function, b);
+
+  // We already ran the determine_types pass, so we need to run it
+  // just for the call statement now.  The other types are known.
+  call_statement->determine_types();
+
+  if (may_call_recover || recover_arg != NULL)
+    {
+      // Dig up the call expression, which may have been changed
+      // during lowering.
+      go_assert(call_statement->classification() == STATEMENT_EXPRESSION);
+      Expression_statement* es =
+       static_cast<Expression_statement*>(call_statement);
+      Call_expression* ce = es->expr()->call_expression();
+      go_assert(ce != NULL);
+      if (may_call_recover)
+       ce->set_is_deferred();
+      if (recover_arg != NULL)
+       ce->set_recover_arg(recover_arg);
+    }
+
   // That is all the thunk has to do.
   gogo->finish_function(location);
 }
@@ -2265,7 +2298,8 @@ Return_statement::do_traverse_assignments(Traverse_assignments* tassign)
 // panic/recover work correctly.
 
 Statement*
-Return_statement::do_lower(Gogo*, Named_object* function, Block* enclosing)
+Return_statement::do_lower(Gogo*, Named_object* function, Block* enclosing,
+                          Statement_inserter*)
 {
   if (this->is_lowered_)
     return this;
@@ -3305,7 +3339,8 @@ Switch_statement::do_traverse(Traverse* traverse)
 // of if statements.
 
 Statement*
-Switch_statement::do_lower(Gogo*, Named_object*, Block* enclosing)
+Switch_statement::do_lower(Gogo*, Named_object*, Block* enclosing,
+                          Statement_inserter*)
 {
   source_location loc = this->location();
 
@@ -3578,7 +3613,8 @@ Type_switch_statement::do_traverse(Traverse* traverse)
 // equality testing.
 
 Statement*
-Type_switch_statement::do_lower(Gogo*, Named_object*, Block* enclosing)
+Type_switch_statement::do_lower(Gogo*, Named_object*, Block* enclosing,
+                               Statement_inserter*)
 {
   const source_location loc = this->location();
 
@@ -3629,8 +3665,9 @@ Type_switch_statement::do_lower(Gogo*, Named_object*, Block* enclosing)
                                             ? Runtime::EFACETYPE
                                             : Runtime::IFACETYPE),
                                            loc, 1, ref);
-      Expression* lhs = Expression::make_temporary_reference(descriptor_temp,
-                                                            loc);
+      Temporary_reference_expression* lhs =
+       Expression::make_temporary_reference(descriptor_temp, loc);
+      lhs->set_is_lvalue();
       Statement* s = Statement::make_assignment(lhs, call, loc);
       b->add_statement(s);
     }
@@ -3815,7 +3852,7 @@ Send_statement::do_get_backend(Translate_context* context)
   call = Runtime::make_call(code, loc, 3, this->channel_, val,
                            Expression::make_boolean(this->for_select_, loc));
 
-  context->gogo()->lower_expression(context->function(), &call);
+  context->gogo()->lower_expression(context->function(), NULL, &call);
   Bexpression* bcall = tree_to_expr(call->get_tree(context));
   Bstatement* s = context->backend()->expression_statement(bcall);
 
@@ -4154,7 +4191,7 @@ Select_clauses::get_backend(Translate_context* context,
          Expression* nil2 = nil1->copy();
          Expression* call = Runtime::make_call(Runtime::SELECT, location, 4,
                                                zero, default_arg, nil1, nil2);
-         context->gogo()->lower_expression(context->function(), &call);
+         context->gogo()->lower_expression(context->function(), NULL, &call);
          Bexpression* bcall = tree_to_expr(call->get_tree(context));
          s = context->backend()->expression_statement(bcall);
        }
@@ -4175,7 +4212,7 @@ Select_clauses::get_backend(Translate_context* context,
   Expression* chans = Expression::make_composite_literal(chan_array_type, 0,
                                                         false, chan_init,
                                                         location);
-  context->gogo()->lower_expression(context->function(), &chans);
+  context->gogo()->lower_expression(context->function(), NULL, &chans);
   Temporary_statement* chan_temp = Statement::make_temporary(chan_array_type,
                                                             chans,
                                                             location);
@@ -4187,7 +4224,7 @@ Select_clauses::get_backend(Translate_context* context,
                                                            0, false,
                                                            is_send_init,
                                                            location);
-  context->gogo()->lower_expression(context->function(), &is_sends);
+  context->gogo()->lower_expression(context->function(), NULL, &is_sends);
   Temporary_statement* is_send_temp =
     Statement::make_temporary(is_send_array_type, is_sends, location);
   statements.push_back(is_send_temp->get_backend(context));
@@ -4213,7 +4250,7 @@ Select_clauses::get_backend(Translate_context* context,
   Expression* call = Runtime::make_call(Runtime::SELECT, location, 4,
                                        ecount->copy(), default_arg,
                                        chan_arg, is_send_arg);
-  context->gogo()->lower_expression(context->function(), &call);
+  context->gogo()->lower_expression(context->function(), NULL, &call);
   Bexpression* bcall = tree_to_expr(call->get_tree(context));
 
   std::vector<std::vector<Bexpression*> > cases;
@@ -4309,7 +4346,7 @@ Select_statement::break_label()
 
 Statement*
 Select_statement::do_lower(Gogo* gogo, Named_object* function,
-                          Block* enclosing)
+                          Block* enclosing, Statement_inserter*)
 {
   if (this->is_lowered_)
     return this;
@@ -4366,7 +4403,8 @@ For_statement::do_traverse(Traverse* traverse)
 // complex statements make it easier to handle garbage collection.
 
 Statement*
-For_statement::do_lower(Gogo*, Named_object*, Block* enclosing)
+For_statement::do_lower(Gogo*, Named_object*, Block* enclosing,
+                       Statement_inserter*)
 {
   Statement* s;
   source_location loc = this->location();
@@ -4497,7 +4535,8 @@ For_range_statement::do_traverse(Traverse* traverse)
 // statements.
 
 Statement*
-For_range_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing)
+For_range_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing,
+                             Statement_inserter*)
 {
   Type* range_type = this->range_->type();
   if (range_type->points_to() != NULL
@@ -4711,8 +4750,10 @@ For_range_statement::lower_range_array(Gogo* gogo,
   Expression* zexpr = Expression::make_integer(&zval, NULL, loc);
   mpz_clear(zval);
 
-  ref = Expression::make_temporary_reference(index_temp, loc);
-  Statement* s = Statement::make_assignment(ref, zexpr, loc);
+  Temporary_reference_expression* tref =
+    Expression::make_temporary_reference(index_temp, loc);
+  tref->set_is_lvalue();
+  Statement* s = Statement::make_assignment(tref, zexpr, loc);
   init->add_statement(s);
 
   *pinit = init;
@@ -4738,8 +4779,9 @@ For_range_statement::lower_range_array(Gogo* gogo,
       Expression* ref2 = Expression::make_temporary_reference(index_temp, loc);
       Expression* index = Expression::make_index(ref, ref2, NULL, loc);
 
-      ref = Expression::make_temporary_reference(value_temp, loc);
-      s = Statement::make_assignment(ref, index, loc);
+      tref = Expression::make_temporary_reference(value_temp, loc);
+      tref->set_is_lvalue();
+      s = Statement::make_assignment(tref, index, loc);
 
       iter_init->add_statement(s);
     }
@@ -4749,8 +4791,9 @@ For_range_statement::lower_range_array(Gogo* gogo,
   //   index_temp++
 
   Block* post = new Block(enclosing, loc);
-  ref = Expression::make_temporary_reference(index_temp, loc);
-  s = Statement::make_inc_statement(ref);
+  tref = Expression::make_temporary_reference(index_temp, loc);
+  tref->set_is_lvalue();
+  s = Statement::make_inc_statement(tref);
   post->add_statement(s);
   *ppost = post;
 }
@@ -4798,7 +4841,9 @@ For_range_statement::lower_range_string(Gogo*,
   mpz_init_set_ui(zval, 0UL);
   Expression* zexpr = Expression::make_integer(&zval, NULL, loc);
 
-  Expression* ref = Expression::make_temporary_reference(index_temp, loc);
+  Temporary_reference_expression* ref =
+    Expression::make_temporary_reference(index_temp, loc);
+  ref->set_is_lvalue();
   Statement* s = Statement::make_assignment(ref, zexpr, loc);
 
   init->add_statement(s);
@@ -4829,14 +4874,20 @@ For_range_statement::lower_range_string(Gogo*,
   if (value_temp == NULL)
     {
       ref = Expression::make_temporary_reference(next_index_temp, loc);
+      ref->set_is_lvalue();
       s = Statement::make_assignment(ref, call, loc);
     }
   else
     {
       Expression_list* lhs = new Expression_list();
-      lhs->push_back(Expression::make_temporary_reference(next_index_temp,
-                                                         loc));
-      lhs->push_back(Expression::make_temporary_reference(value_temp, loc));
+
+      ref = Expression::make_temporary_reference(next_index_temp, loc);
+      ref->set_is_lvalue();
+      lhs->push_back(ref);
+
+      ref = Expression::make_temporary_reference(value_temp, loc);
+      ref->set_is_lvalue();
+      lhs->push_back(ref);
 
       Expression_list* rhs = new Expression_list();
       rhs->push_back(Expression::make_call_result(call, 0));
@@ -4865,7 +4916,9 @@ For_range_statement::lower_range_string(Gogo*,
 
   Block* post = new Block(enclosing, loc);
 
-  Expression* lhs = Expression::make_temporary_reference(index_temp, loc);
+  Temporary_reference_expression* lhs =
+    Expression::make_temporary_reference(index_temp, loc);
+  lhs->set_is_lvalue();
   Expression* rhs = Expression::make_temporary_reference(next_index_temp, loc);
   s = Statement::make_assignment(lhs, rhs, loc);
 
@@ -5024,8 +5077,12 @@ For_range_statement::lower_range_channel(Gogo*,
   iter_init->add_statement(ok_temp);
 
   Expression* cref = this->make_range_ref(range_object, range_temp, loc);
-  Expression* iref = Expression::make_temporary_reference(index_temp, loc);
-  Expression* oref = Expression::make_temporary_reference(ok_temp, loc);
+  Temporary_reference_expression* iref =
+    Expression::make_temporary_reference(index_temp, loc);
+  iref->set_is_lvalue();
+  Temporary_reference_expression* oref =
+    Expression::make_temporary_reference(ok_temp, loc);
+  oref->set_is_lvalue();
   Statement* s = Statement::make_tuple_receive_assignment(iref, oref, cref,
                                                          false, loc);
   iter_init->add_statement(s);
index 5c27c11..44241ab 100644 (file)
@@ -11,6 +11,7 @@
 
 class Gogo;
 class Traverse;
+class Statement_inserter;
 class Block;
 class Function;
 class Unnamed_label;
@@ -290,9 +291,11 @@ class Statement
   // simplify statements for further processing.  It returns the same
   // Statement or a new one.  FUNCTION is the function containing this
   // statement.  BLOCK is the block containing this statement.
+  // INSERTER can be used to insert new statements before this one.
   Statement*
-  lower(Gogo* gogo, Named_object* function, Block* block)
-  { return this->do_lower(gogo, function, block); }
+  lower(Gogo* gogo, Named_object* function, Block* block,
+       Statement_inserter* inserter)
+  { return this->do_lower(gogo, function, block, inserter); }
 
   // Set type information for unnamed constants.
   void
@@ -385,7 +388,7 @@ class Statement
   // Implemented by the child class: lower this statement to a simpler
   // one.
   virtual Statement*
-  do_lower(Gogo*, Named_object*, Block*)
+  do_lower(Gogo*, Named_object*, Block*, Statement_inserter*)
   { return this; }
 
   // Implemented by child class: set type information for unnamed
@@ -535,6 +538,9 @@ class Variable_declaration_statement : public Statement
   bool
   do_traverse_assignments(Traverse_assignments*);
 
+  Statement*
+  do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
+
   Bstatement*
   do_get_backend(Translate_context*);
 
@@ -566,7 +572,7 @@ class Return_statement : public Statement
   do_traverse_assignments(Traverse_assignments*);
 
   Statement*
-  do_lower(Gogo*, Named_object*, Block*);
+  do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
 
   bool
   do_may_fall_through() const
@@ -806,7 +812,7 @@ class Select_statement : public Statement
   { return this->clauses_->traverse(traverse); }
 
   Statement*
-  do_lower(Gogo*, Named_object*, Block*);
+  do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
 
   void
   do_determine_types()
@@ -993,7 +999,7 @@ class For_statement : public Statement
   { go_unreachable(); }
 
   Statement*
-  do_lower(Gogo*, Named_object*, Block*);
+  do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
 
   Bstatement*
   do_get_backend(Translate_context*)
@@ -1051,7 +1057,7 @@ class For_range_statement : public Statement
   { go_unreachable(); }
 
   Statement*
-  do_lower(Gogo*, Named_object*, Block*);
+  do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
 
   Bstatement*
   do_get_backend(Translate_context*)
@@ -1280,7 +1286,7 @@ class Switch_statement : public Statement
   do_traverse(Traverse*);
 
   Statement*
-  do_lower(Gogo*, Named_object*, Block*);
+  do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
 
   Bstatement*
   do_get_backend(Translate_context*)
@@ -1426,7 +1432,7 @@ class Type_switch_statement : public Statement
   do_traverse(Traverse*);
 
   Statement*
-  do_lower(Gogo*, Named_object*, Block*);
+  do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
 
   Bstatement*
   do_get_backend(Translate_context*)