This patch by Than McIntosh changes the Go frontend's backend
interface to tell the backend whether a variable reference is an
lvalue or an rvalue.

This adds a new flag on the Var_expression class that indicates
whether the var reference appears in an "lvalue" context (for example,
on the LHS of an assignment stmt) or an "rvalue" context (for example,
as an argument of a call).

It adds a traversal pass that visits assignment stmt LHS subtrees so
as to mark things prior to backend gen. Select the right context value
in other places where Backend::var_expression is called.

Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu.  Committed
to mainline.

Ian


2016-12-06  Than McIntosh  <th...@google.com>

* go-gcc.cc (Gcc_backend::var_expression): Add Varexpr_context
parameter.
Index: gcc/go/go-gcc.cc
===================================================================
--- gcc/go/go-gcc.cc    (revision 243084)
+++ gcc/go/go-gcc.cc    (working copy)
@@ -276,7 +276,7 @@ class Gcc_backend : public Backend
   { return this->make_expression(null_pointer_node); }
 
   Bexpression*
-  var_expression(Bvariable* var, Location);
+  var_expression(Bvariable* var, Varexpr_context, Location);
 
   Bexpression*
   indirect_expression(Btype*, Bexpression* expr, bool known_valid, Location);
@@ -1243,7 +1243,7 @@ Gcc_backend::zero_expression(Btype* btyp
 // An expression that references a variable.
 
 Bexpression*
-Gcc_backend::var_expression(Bvariable* var, Location location)
+Gcc_backend::var_expression(Bvariable* var, Varexpr_context, Location location)
 {
   tree ret = var->get_tree(location);
   if (ret == error_mark_node)
Index: gcc/go/gofrontend/MERGE
===================================================================
--- gcc/go/gofrontend/MERGE     (revision 243129)
+++ gcc/go/gofrontend/MERGE     (working copy)
@@ -1,4 +1,4 @@
-b7bad96ce0af50a1129eaab9aa110d68a601917b
+2102112e26a21589455f940ec6b409766d942c62
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
Index: gcc/go/gofrontend/backend.h
===================================================================
--- gcc/go/gofrontend/backend.h (revision 243084)
+++ gcc/go/gofrontend/backend.h (working copy)
@@ -254,7 +254,7 @@ class Backend
 
   // Create a reference to a variable.
   virtual Bexpression*
-  var_expression(Bvariable* var, Location) = 0;
+  var_expression(Bvariable* var, Varexpr_context in_lvalue_pos, Location) = 0;
 
   // Create an expression that indirects through the pointer expression EXPR
   // (i.e., return the expression for *EXPR). KNOWN_VALID is true if the 
pointer
Index: gcc/go/gofrontend/expressions.cc
===================================================================
--- gcc/go/gofrontend/expressions.cc    (revision 243129)
+++ gcc/go/gofrontend/expressions.cc    (working copy)
@@ -760,7 +760,8 @@ Var_expression::do_get_backend(Translate
   else
     go_unreachable();
 
-  Bexpression* ret = context->backend()->var_expression(bvar, loc);
+  Bexpression* ret =
+      context->backend()->var_expression(bvar, this->in_lvalue_pos_, loc);
   if (is_in_heap)
     ret = context->backend()->indirect_expression(btype, ret, true, loc);
   return ret;
@@ -887,7 +888,10 @@ Temporary_reference_expression::do_get_b
 {
   Gogo* gogo = context->gogo();
   Bvariable* bvar = this->statement_->get_backend_variable(context);
-  Bexpression* ret = gogo->backend()->var_expression(bvar, this->location());
+  Varexpr_context ve_ctxt = (this->is_lvalue_ ? VE_lvalue : VE_rvalue);
+
+  Bexpression* ret = gogo->backend()->var_expression(bvar, ve_ctxt,
+                                                     this->location());
 
   // The backend can't always represent the same set of recursive types
   // that the Go frontend can.  In some cases this means that a
@@ -958,11 +962,11 @@ Set_and_use_temporary_expression::do_get
   Location loc = this->location();
   Gogo* gogo = context->gogo();
   Bvariable* bvar = this->statement_->get_backend_variable(context);
-  Bexpression* var_ref = gogo->backend()->var_expression(bvar, loc);
+  Bexpression* lvar_ref = gogo->backend()->var_expression(bvar, VE_rvalue, 
loc);
 
   Bexpression* bexpr = this->expr_->get_backend(context);
-  Bstatement* set = gogo->backend()->assignment_statement(var_ref, bexpr, loc);
-  var_ref = gogo->backend()->var_expression(bvar, loc);
+  Bstatement* set = gogo->backend()->assignment_statement(lvar_ref, bexpr, 
loc);
+  Bexpression* var_ref = gogo->backend()->var_expression(bvar, VE_lvalue, loc);
   Bexpression* ret = gogo->backend()->compound_expression(set, var_ref, loc);
   return ret;
 }
@@ -1065,11 +1069,12 @@ Sink_expression::do_get_backend(Translat
       this->bvar_ =
        gogo->backend()->temporary_variable(fn_ctx, context->bblock(), bt, NULL,
                                            false, loc, &decl);
-      Bexpression* var_ref = gogo->backend()->var_expression(this->bvar_, loc);
+      Bexpression* var_ref =
+          gogo->backend()->var_expression(this->bvar_, VE_lvalue, loc);
       var_ref = gogo->backend()->compound_expression(decl, var_ref, loc);
       return var_ref;
     }
-  return gogo->backend()->var_expression(this->bvar_, loc);
+  return gogo->backend()->var_expression(this->bvar_, VE_lvalue, loc);
 }
 
 // Ast dump for sink expression.
@@ -1276,7 +1281,7 @@ Func_descriptor_expression::do_get_backe
   Named_object* no = this->fn_;
   Location loc = no->location();
   if (this->dvar_ != NULL)
-    return context->backend()->var_expression(this->dvar_, loc);
+    return context->backend()->var_expression(this->dvar_, VE_rvalue, loc);
 
   Gogo* gogo = context->gogo();
   std::string var_name;
@@ -1330,7 +1335,7 @@ Func_descriptor_expression::do_get_backe
     }
 
   this->dvar_ = bvar;
-  return gogo->backend()->var_expression(bvar, loc);
+  return gogo->backend()->var_expression(bvar, VE_rvalue, loc);
 }
 
 // Print a function descriptor expression.
@@ -4207,7 +4212,8 @@ Unary_expression::do_get_backend(Transla
        {
          Temporary_statement* temp = sut->temporary();
          Bvariable* bvar = temp->get_backend_variable(context);
-          Bexpression* bvar_expr = gogo->backend()->var_expression(bvar, loc);
+          Bexpression* bvar_expr =
+              gogo->backend()->var_expression(bvar, VE_lvalue, loc);
           Bexpression* bval = sut->expression()->get_backend(context);
 
           Bstatement* bassign =
@@ -4294,7 +4300,7 @@ Unary_expression::do_get_backend(Transla
          gogo->backend()->implicit_variable_set_init(implicit, buf, btype,
                                                      true, copy_to_heap, false,
                                                      bexpr);
-         bexpr = gogo->backend()->var_expression(implicit, loc);
+         bexpr = gogo->backend()->var_expression(implicit, VE_lvalue, loc);
 
          // If we are not copying a slice initializer to the heap,
          // then it can be changed by the program, so if it can
@@ -4304,7 +4310,7 @@ Unary_expression::do_get_backend(Transla
              && this->expr_->type()->has_pointer())
            {
              Bexpression* root =
-               gogo->backend()->var_expression(implicit, loc);
+                  gogo->backend()->var_expression(implicit, VE_lvalue, loc);
              root = gogo->backend()->address_expression(root, loc);
              Type* type = Type::make_pointer_type(this->expr_->type());
              gogo->add_gc_root(Expression::make_backend(root, type, loc));
@@ -4324,7 +4330,7 @@ Unary_expression::do_get_backend(Transla
                                                 true, false, btype, loc);
           gogo->backend()->immutable_struct_set_init(decl, buf, true, false,
                                                      btype, loc, bexpr);
-          bexpr = gogo->backend()->var_expression(decl, loc);
+          bexpr = gogo->backend()->var_expression(decl, VE_lvalue, loc);
         }
 
       go_assert(!this->create_temp_ || this->expr_->is_variable());
@@ -14116,7 +14122,7 @@ Heap_expression::do_get_backend(Translat
   Bvariable* space_temp =
     gogo->backend()->temporary_variable(fndecl, context->bblock(), btype,
                                        space, true, loc, &decl);
-  space = gogo->backend()->var_expression(space_temp, loc);
+  space = gogo->backend()->var_expression(space_temp, VE_lvalue, loc);
   Btype* expr_btype = this->expr_->type()->get_backend(gogo);
   Bexpression* ref =
     gogo->backend()->indirect_expression(expr_btype, space, true, loc);
@@ -14124,7 +14130,7 @@ Heap_expression::do_get_backend(Translat
   Bexpression* bexpr = this->expr_->get_backend(context);
   Bstatement* assn = gogo->backend()->assignment_statement(ref, bexpr, loc);
   decl = gogo->backend()->compound_statement(decl, assn);
-  space = gogo->backend()->var_expression(space_temp, loc);
+  space = gogo->backend()->var_expression(space_temp, VE_rvalue, loc);
   return gogo->backend()->compound_expression(decl, space, loc);
 }
 
@@ -15063,7 +15069,8 @@ Interface_mtable_expression::do_get_back
   Gogo* gogo = context->gogo();
   Location loc = Linemap::predeclared_location();
   if (this->bvar_ != NULL)
-    return gogo->backend()->var_expression(this->bvar_, this->location());
+    return gogo->backend()->var_expression(this->bvar_, VE_rvalue,
+                                           this->location());
 
   const Typed_identifier_list* interface_methods = this->itype_->methods();
   go_assert(!interface_methods->empty());
@@ -15099,7 +15106,8 @@ Interface_mtable_expression::do_get_back
       this->bvar_ =
           gogo->backend()->immutable_struct_reference(mangled_name, asm_name,
                                                       btype, loc);
-      return gogo->backend()->var_expression(this->bvar_, this->location());
+      return gogo->backend()->var_expression(this->bvar_, VE_rvalue,
+                                             this->location());
     }
 
   // The first element is the type descriptor.
@@ -15147,7 +15155,7 @@ Interface_mtable_expression::do_get_back
                                                  !is_public, btype, loc);
   gogo->backend()->immutable_struct_set_init(this->bvar_, mangled_name, false,
                                              !is_public, btype, loc, ctor);
-  return gogo->backend()->var_expression(this->bvar_, loc);
+  return gogo->backend()->var_expression(this->bvar_, VE_lvalue, loc);
 }
 
 void
Index: gcc/go/gofrontend/expressions.h
===================================================================
--- gcc/go/gofrontend/expressions.h     (revision 243129)
+++ gcc/go/gofrontend/expressions.h     (working copy)
@@ -1282,7 +1282,7 @@ class Var_expression : public Expression
  public:
   Var_expression(Named_object* variable, Location location)
     : Expression(EXPRESSION_VAR_REFERENCE, location),
-      variable_(variable)
+      variable_(variable), in_lvalue_pos_(VE_rvalue)
   { }
 
   // Return the variable.
@@ -1290,6 +1290,16 @@ class Var_expression : public Expression
   named_object() const
   { return this->variable_; }
 
+  // Does this var expression appear in an lvalue (assigned-to) context?
+  bool
+  in_lvalue_pos() const
+  { return this->in_lvalue_pos_ == VE_lvalue; }
+
+  // Mark a var_expression as appearing in an lvalue context.
+  void
+  set_in_lvalue_pos()
+  { this->in_lvalue_pos_ = VE_lvalue; }
+
  protected:
   Expression*
   do_lower(Gogo*, Named_object*, Statement_inserter*, int);
@@ -1320,6 +1330,8 @@ class Var_expression : public Expression
  private:
   // The variable we are referencing.
   Named_object* variable_;
+  // Set to TRUE if var expression appears in lvalue context
+  Varexpr_context in_lvalue_pos_;
 };
 
 // A reference to a variable within an enclosing function.
Index: gcc/go/gofrontend/gogo.cc
===================================================================
--- gcc/go/gofrontend/gogo.cc   (revision 243129)
+++ gcc/go/gofrontend/gogo.cc   (working copy)
@@ -1369,7 +1369,7 @@ Gogo::write_globals()
                 {
                   Location loc = var->location();
                   Bexpression* var_expr =
-                      this->backend()->var_expression(bvar, loc);
+                      this->backend()->var_expression(bvar, VE_lvalue, loc);
                   var_init_stmt =
                       this->backend()->assignment_statement(var_expr, 
var_binit,
                                                             loc);
@@ -5734,7 +5734,8 @@ Function::return_value(Gogo* gogo, Named
     {
       Named_object* no = (*this->results_)[i];
       Bvariable* bvar = no->get_backend_variable(gogo, named_function);
-      Bexpression* val = gogo->backend()->var_expression(bvar, location);
+      Bexpression* val = gogo->backend()->var_expression(bvar, VE_rvalue,
+                                                         location);
       if (no->result_var_value()->is_in_heap())
        {
          Btype* bt = no->result_var_value()->type()->get_backend(gogo);
@@ -6563,7 +6564,8 @@ Variable::get_init_block(Gogo* gogo, Nam
           Expression* val_expr =
               Expression::make_cast(this->type(), this->init_, loc);
           Bexpression* val = val_expr->get_backend(&context);
-          Bexpression* var_ref = gogo->backend()->var_expression(var_decl, 
loc);
+          Bexpression* var_ref =
+              gogo->backend()->var_expression(var_decl, VE_lvalue, loc);
           decl_init = gogo->backend()->assignment_statement(var_ref, val, loc);
        }
     }
Index: gcc/go/gofrontend/operator.h
===================================================================
--- gcc/go/gofrontend/operator.h        (revision 243084)
+++ gcc/go/gofrontend/operator.h        (working copy)
@@ -63,4 +63,10 @@ enum Operator
   OPERATOR_RSQUARE     // ]
 };
 
+// Whether a variable expression appears in lvalue (assignment) context.
+enum Varexpr_context {
+  VE_rvalue,
+  VE_lvalue
+};
+
 #endif // !defined(GO_OPERATOR_H)
Index: gcc/go/gofrontend/statements.cc
===================================================================
--- gcc/go/gofrontend/statements.cc     (revision 243084)
+++ gcc/go/gofrontend/statements.cc     (working copy)
@@ -825,6 +825,80 @@ Assignment_statement::do_flatten(Gogo*,
   return this;
 }
 
+
+// Helper class to locate a root Var_expression within an expression
+// tree and mark it as being in an "lvalue" or assignment
+// context. Examples:
+//
+//    x, y = 40, foo(w)
+//    x[2] = bar(v)
+//    x.z.w[blah(v + u)], y.another = 2, 3
+//
+// In the code above, vars "x" and "y" appear in lvalue / assignment
+// context, whereas the other vars "v", "u", etc are in rvalue context.
+//
+// Note: at the moment the Var_expression version of "do_copy()"
+// defaults to returning the original object, not a new object,
+// meaning that a given Var_expression can be referenced from more
+// than one place in the tree. This means that when we want to mark a
+// Var_expression as having lvalue semantics, we need to make a copy
+// of it. Example:
+//
+//    mystruct.myfield += 42
+//
+// When this is lowered to eliminate the += operator, we get a tree
+//
+//    mystruct.myfield = mystruct.field + 42
+//
+// in which the "mystruct" same Var_expression is referenced on both
+// LHS and RHS subtrees. This in turn means that if we try to mark the
+// LHS Var_expression the RHS Var_expression will also be marked.  To
+// address this issue, the code below clones any var_expression before
+// applying an lvalue marking.
+//
+
+class Mark_lvalue_varexprs : public Traverse
+{
+ public:
+  Mark_lvalue_varexprs()
+    : Traverse(traverse_expressions)
+  { }
+
+ protected:
+  int
+  expression(Expression**);
+
+ private:
+};
+
+int Mark_lvalue_varexprs::expression(Expression** ppexpr)
+{
+  Expression* e = *ppexpr;
+
+  Var_expression* ve = e->var_expression();
+  if (ve)
+    {
+      ve = new Var_expression(ve->named_object(), ve->location());
+      ve->set_in_lvalue_pos();
+      *ppexpr = ve;
+      return TRAVERSE_EXIT;
+    }
+
+  Field_reference_expression* fre = e->field_reference_expression();
+  if (fre != NULL)
+    return TRAVERSE_CONTINUE;
+
+  Array_index_expression* aie = e->array_index_expression();
+  if (aie != NULL)
+    {
+      Mark_lvalue_varexprs mlve;
+      aie->array()->traverse_subexpressions(&mlve);
+      return TRAVERSE_EXIT;
+    }
+
+  return TRAVERSE_EXIT;
+}
+
 // Convert an assignment statement to the backend representation.
 
 Bstatement*
@@ -836,6 +910,9 @@ Assignment_statement::do_get_backend(Tra
       return context->backend()->expression_statement(rhs);
     }
 
+  Mark_lvalue_varexprs mlve;
+  Expression::traverse(&this->lhs_, &mlve);
+
   Bexpression* lhs = this->lhs_->get_backend(context);
   Expression* conv =
       Expression::convert_for_assignment(context->gogo(), this->lhs_->type(),
Index: gcc/go/gofrontend/types.cc
===================================================================
--- gcc/go/gofrontend/types.cc  (revision 243084)
+++ gcc/go/gofrontend/types.cc  (working copy)
@@ -1173,7 +1173,8 @@ Type::type_descriptor_pointer(Gogo* gogo
       go_assert(t->type_descriptor_var_ != NULL);
     }
   Bexpression* var_expr =
-      gogo->backend()->var_expression(t->type_descriptor_var_, location);
+      gogo->backend()->var_expression(t->type_descriptor_var_,
+                                      VE_rvalue, location);
   return gogo->backend()->address_expression(var_expr, location);
 }
 
@@ -2146,7 +2147,7 @@ Type::gc_symbol_pointer(Gogo* gogo)
     }
   Location bloc = Linemap::predeclared_location();
   Bexpression* var_expr =
-      gogo->backend()->var_expression(t->gc_symbol_var_, bloc);
+      gogo->backend()->var_expression(t->gc_symbol_var_, VE_rvalue, bloc);
   return gogo->backend()->address_expression(var_expr, bloc);
 }
 

Reply via email to