compiler: ensure evaluation order in type hash/eq functions

The type hash and equality functions are generated after the
order_evaluations pass. They may contain shortcut operators and
Set_and_use_temporary_expressions (e.g. from lowering a
Binary_exprssion) that need to be ordered. Run order_evaluations
and remove_shortcuts on these functions. (The hash functions
may be fine, but to be on the safe side we run on them anyway.
We do need to run on the equality functions.)

A Set_and_use_temporary_expression is effectively an assignment,
so it needs to be ordered. Otherwise if we insert a temporary
statement before it, we may get wrong evaluation order.

A test case is CL 185818.

Fixes golang/go#33062.

Change-Id: I165f7e2c690d1cc7fa55fb23e5621320cf58bd77
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/185817
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/go/expressions.h b/go/expressions.h
index 2c6a080..3b65e7a 100644
--- a/go/expressions.h
+++ b/go/expressions.h
@@ -1628,6 +1628,10 @@
   }
 
   bool
+  do_must_eval_in_order() const
+  { return true; }
+
+  bool
   do_is_addressable() const
   { return true; }
 
diff --git a/go/gogo.cc b/go/gogo.cc
index 234a4f4..f9a18bc 100644
--- a/go/gogo.cc
+++ b/go/gogo.cc
@@ -4097,6 +4097,15 @@
   this->traverse(&order_eval);
 }
 
+// Order evaluations in a block.
+
+void
+Gogo::order_block(Block* block)
+{
+  Order_eval order_eval(this);
+  block->traverse(&order_eval);
+}
+
 // A traversal class used to find a single shortcut operator within an
 // expression.
 
@@ -4306,6 +4315,15 @@
   this->traverse(&shortcuts);
 }
 
+// Turn shortcut operators into explicit if statements in a block.
+
+void
+Gogo::remove_shortcuts_in_block(Block* block)
+{
+  Shortcuts shortcuts(this);
+  block->traverse(&shortcuts);
+}
+
 // Traversal to flatten parse tree after order of evaluation rules are applied.
 
 class Flatten : public Traverse
diff --git a/go/gogo.h b/go/gogo.h
index cb8e25f..6ffdc59 100644
--- a/go/gogo.h
+++ b/go/gogo.h
@@ -749,10 +749,18 @@
   void
   remove_shortcuts();
 
+  // Turn short-cut operators into explicit if statements in a block.
+  void
+  remove_shortcuts_in_block(Block*);
+
   // Use temporary variables to force order of evaluation.
   void
   order_evaluations();
 
+  // Order evaluations in a block.
+  void
+  order_block(Block*);
+
   // Add write barriers as needed.
   void
   add_write_barriers();
diff --git a/go/types.cc b/go/types.cc
index cc65bd8..b46525d 100644
--- a/go/types.cc
+++ b/go/types.cc
@@ -2098,6 +2098,8 @@
   Block* b = gogo->finish_block(bloc);
   gogo->add_block(b, bloc);
   gogo->lower_block(hash_fn, b);
+  gogo->order_block(b);
+  gogo->remove_shortcuts_in_block(b);
   gogo->finish_function(bloc);
 
   Named_object *equal_fn = gogo->start_function(equal_name, equal_fntype,
@@ -2119,6 +2121,8 @@
   b = gogo->finish_block(bloc);
   gogo->add_block(b, bloc);
   gogo->lower_block(equal_fn, b);
+  gogo->order_block(b);
+  gogo->remove_shortcuts_in_block(b);
   gogo->finish_function(bloc);
 
   // Build the function descriptors for the type descriptor to refer to.