compiler: ignore struct field tags for type conversion

Go 1.8 includes a language change (https://golang.org/doc/go1.8#language):
in an explicit conversion from one struct type to another, any field
tags are ignored.

This CL implements this language change in the gofrontend.  The tests
for this are in the gc testsuite, which will be copied into the gccgo
repository in due course.

Updates golang/go#16085.

Change-Id: I64e49f103efced41a55757e2cb665f6c1098db80
Reviewed-on: https://go-review.googlesource.com/43614
Reviewed-by: Than McIntosh <thanm@google.com>
diff --git a/go/types.cc b/go/types.cc
index 8d542de..053e71d 100644
--- a/go/types.cc
+++ b/go/types.cc
@@ -317,6 +317,16 @@
 Type::are_identical(const Type* t1, const Type* t2, bool errors_are_identical,
 		    std::string* reason)
 {
+  return Type::are_identical_cmp_tags(t1, t2, COMPARE_TAGS,
+				      errors_are_identical, reason);
+}
+
+// Like are_identical, but with a CMP_TAGS parameter.
+
+bool
+Type::are_identical_cmp_tags(const Type* t1, const Type* t2, Cmp_tags cmp_tags,
+			     bool errors_are_identical, std::string* reason)
+{
   if (t1 == NULL || t2 == NULL)
     {
       // Something is wrong.
@@ -387,31 +397,33 @@
     case TYPE_FUNCTION:
       return t1->function_type()->is_identical(t2->function_type(),
 					       false,
+					       cmp_tags,
 					       errors_are_identical,
 					       reason);
 
     case TYPE_POINTER:
-      return Type::are_identical(t1->points_to(), t2->points_to(),
-				 errors_are_identical, reason);
+      return Type::are_identical_cmp_tags(t1->points_to(), t2->points_to(),
+					  cmp_tags, errors_are_identical,
+					  reason);
 
     case TYPE_STRUCT:
-      return t1->struct_type()->is_identical(t2->struct_type(),
+      return t1->struct_type()->is_identical(t2->struct_type(), cmp_tags,
 					     errors_are_identical);
 
     case TYPE_ARRAY:
-      return t1->array_type()->is_identical(t2->array_type(),
+      return t1->array_type()->is_identical(t2->array_type(), cmp_tags,
 					    errors_are_identical);
 
     case TYPE_MAP:
-      return t1->map_type()->is_identical(t2->map_type(),
+      return t1->map_type()->is_identical(t2->map_type(), cmp_tags,
 					  errors_are_identical);
 
     case TYPE_CHANNEL:
-      return t1->channel_type()->is_identical(t2->channel_type(),
+      return t1->channel_type()->is_identical(t2->channel_type(), cmp_tags,
 					      errors_are_identical);
 
     case TYPE_INTERFACE:
-      return t1->interface_type()->is_identical(t2->interface_type(),
+      return t1->interface_type()->is_identical(t2->interface_type(), cmp_tags,
 						errors_are_identical);
 
     case TYPE_CALL_MULTIPLE_RESULT:
@@ -735,23 +747,26 @@
     return true;
 
   // The types are convertible if they have identical underlying
-  // types.
+  // types, ignoring struct field tags.
   if ((lhs->named_type() != NULL || rhs->named_type() != NULL)
-      && Type::are_identical(lhs->base(), rhs->base(), true, reason))
+      && Type::are_identical_cmp_tags(lhs->base(), rhs->base(), IGNORE_TAGS,
+				      true, reason))
     return true;
 
   // The types are convertible if they are both unnamed pointer types
-  // and their pointer base types have identical underlying types.
+  // and their pointer base types have identical underlying types,
+  // ignoring struct field tags.
   if (lhs->named_type() == NULL
       && rhs->named_type() == NULL
       && lhs->points_to() != NULL
       && rhs->points_to() != NULL
       && (lhs->points_to()->named_type() != NULL
 	  || rhs->points_to()->named_type() != NULL)
-      && Type::are_identical(lhs->points_to()->base(),
-			     rhs->points_to()->base(),
-			     true,
-			     reason))
+      && Type::are_identical_cmp_tags(lhs->points_to()->base(),
+				      rhs->points_to()->base(),
+				      IGNORE_TAGS,
+				      true,
+				      reason))
     return true;
 
   // Integer and floating point types are convertible to each other.
@@ -4502,7 +4517,7 @@
 Function_type::is_valid_redeclaration(const Function_type* t,
 				      std::string* reason) const
 {
-  if (!this->is_identical(t, false, true, reason))
+  if (!this->is_identical(t, false, COMPARE_TAGS, true, reason))
     return false;
 
   // A redeclaration of a function is required to use the same names
@@ -4580,7 +4595,7 @@
 
 bool
 Function_type::is_identical(const Function_type* t, bool ignore_receiver,
-			    bool errors_are_identical,
+			    Cmp_tags cmp_tags, bool errors_are_identical,
 			    std::string* reason) const
 {
   if (!ignore_receiver)
@@ -4595,8 +4610,8 @@
 	}
       if (r1 != NULL)
 	{
-	  if (!Type::are_identical(r1->type(), r2->type(), errors_are_identical,
-				   reason))
+	  if (!Type::are_identical_cmp_tags(r1->type(), r2->type(), cmp_tags,
+					    errors_are_identical, reason))
 	    {
 	      if (reason != NULL && !reason->empty())
 		*reason = "receiver: " + *reason;
@@ -4627,8 +4642,8 @@
 	      return false;
 	    }
 
-	  if (!Type::are_identical(p1->type(), p2->type(),
-				   errors_are_identical, NULL))
+	  if (!Type::are_identical_cmp_tags(p1->type(), p2->type(), cmp_tags,
+					    errors_are_identical, NULL))
 	    {
 	      if (reason != NULL)
 		*reason = _("different parameter types");
@@ -4672,8 +4687,9 @@
 	      return false;
 	    }
 
-	  if (!Type::are_identical(res1->type(), res2->type(),
-				   errors_are_identical, NULL))
+	  if (!Type::are_identical_cmp_tags(res1->type(), res2->type(),
+					    cmp_tags, errors_are_identical,
+					    NULL))
 	    {
 	      if (reason != NULL)
 		*reason = _("different result types");
@@ -5803,7 +5819,7 @@
 // Whether this type is identical to T.
 
 bool
-Struct_type::is_identical(const Struct_type* t,
+Struct_type::is_identical(const Struct_type* t, Cmp_tags cmp_tags,
 			  bool errors_are_identical) const
 {
   if (this->is_struct_incomparable_ != t->is_struct_incomparable_)
@@ -5822,20 +5838,23 @@
       if (pf1->field_name() != pf2->field_name())
 	return false;
       if (pf1->is_anonymous() != pf2->is_anonymous()
-	  || !Type::are_identical(pf1->type(), pf2->type(),
-				  errors_are_identical, NULL))
+	  || !Type::are_identical_cmp_tags(pf1->type(), pf2->type(), cmp_tags,
+					   errors_are_identical, NULL))
 	return false;
-      if (!pf1->has_tag())
+      if (cmp_tags == COMPARE_TAGS)
 	{
-	  if (pf2->has_tag())
-	    return false;
-	}
-      else
-	{
-	  if (!pf2->has_tag())
-	    return false;
-	  if (pf1->tag() != pf2->tag())
-	    return false;
+	  if (!pf1->has_tag())
+	    {
+	      if (pf2->has_tag())
+		return false;
+	    }
+	  else
+	    {
+	      if (!pf2->has_tag())
+		return false;
+	      if (pf1->tag() != pf2->tag())
+		return false;
+	    }
 	}
     }
   if (pf2 != fields2->end())
@@ -7061,10 +7080,11 @@
 // Whether two array types are identical.
 
 bool
-Array_type::is_identical(const Array_type* t, bool errors_are_identical) const
+Array_type::is_identical(const Array_type* t, Cmp_tags cmp_tags,
+			 bool errors_are_identical) const
 {
-  if (!Type::are_identical(this->element_type(), t->element_type(),
-			   errors_are_identical, NULL))
+  if (!Type::are_identical_cmp_tags(this->element_type(), t->element_type(),
+				    cmp_tags, errors_are_identical, NULL))
     return false;
 
   if (this->is_array_incomparable_ != t->is_array_incomparable_)
@@ -7986,12 +8006,14 @@
 // Whether two map types are identical.
 
 bool
-Map_type::is_identical(const Map_type* t, bool errors_are_identical) const
+Map_type::is_identical(const Map_type* t, Cmp_tags cmp_tags,
+		       bool errors_are_identical) const
 {
-  return (Type::are_identical(this->key_type(), t->key_type(),
-			      errors_are_identical, NULL)
-	  && Type::are_identical(this->val_type(), t->val_type(),
-				 errors_are_identical, NULL));
+  return (Type::are_identical_cmp_tags(this->key_type(), t->key_type(),
+				       cmp_tags, errors_are_identical, NULL)
+	  && Type::are_identical_cmp_tags(this->val_type(), t->val_type(),
+					  cmp_tags, errors_are_identical,
+					  NULL));
 }
 
 // Hash code.
@@ -8510,11 +8532,11 @@
 // Whether this type is the same as T.
 
 bool
-Channel_type::is_identical(const Channel_type* t,
+Channel_type::is_identical(const Channel_type* t, Cmp_tags cmp_tags,
 			   bool errors_are_identical) const
 {
-  if (!Type::are_identical(this->element_type(), t->element_type(),
-			   errors_are_identical, NULL))
+  if (!Type::are_identical_cmp_tags(this->element_type(), t->element_type(),
+				    cmp_tags, errors_are_identical, NULL))
     return false;
   return (this->may_send_ == t->may_send_
 	  && this->may_receive_ == t->may_receive_);
@@ -8920,7 +8942,7 @@
 // Whether this type is identical with T.
 
 bool
-Interface_type::is_identical(const Interface_type* t,
+Interface_type::is_identical(const Interface_type* t, Cmp_tags cmp_tags,
 			     bool errors_are_identical) const
 {
   // If methods have not been finalized, then we are asking whether
@@ -8951,8 +8973,8 @@
       if (p1 == this->all_methods_->end())
 	break;
       if (p1->name() != p2->name()
-	  || !Type::are_identical(p1->type(), p2->type(),
-				  errors_are_identical, NULL))
+	  || !Type::are_identical_cmp_tags(p1->type(), p2->type(), cmp_tags,
+					   errors_are_identical, NULL))
 	break;
     }
 
@@ -9150,7 +9172,8 @@
       Function_type* m_fn_type = m->type()->function_type();
       go_assert(p_fn_type != NULL && m_fn_type != NULL);
       std::string subreason;
-      if (!p_fn_type->is_identical(m_fn_type, true, true, &subreason))
+      if (!p_fn_type->is_identical(m_fn_type, true, COMPARE_TAGS, true,
+				   &subreason))
 	{
 	  if (reason != NULL)
 	    {
diff --git a/go/types.h b/go/types.h
index bae7fd3..bfc0a8b 100644
--- a/go/types.h
+++ b/go/types.h
@@ -568,6 +568,22 @@
   are_identical(const Type* lhs, const Type* rhs, bool errors_are_identical,
 		std::string* reason);
 
+  // An argument to are_identical_cmp_tags, indicating whether or not
+  // to compare struct field tags.
+  enum Cmp_tags {
+    COMPARE_TAGS,
+    IGNORE_TAGS
+  };
+
+  // Return true if two types are identical.  This is like the
+  // are_identical function, but also takes a CMP_TAGS argument
+  // indicating whether to compare struct tags.  Otherwise the
+  // parameters are as for are_identical.
+  static bool
+  are_identical_cmp_tags(const Type* lhs, const Type* rhs,
+			 Cmp_tags, bool errors_are_identical,
+			 std::string* reason);
+
   // Return true if two types are compatible for use in a binary
   // operation, other than a shift, comparison, or channel send.  This
   // is an equivalence relation.
@@ -1899,7 +1915,7 @@
   // Whether this type is the same as T.
   bool
   is_identical(const Function_type* t, bool ignore_receiver,
-	       bool errors_are_identical, std::string*) const;
+	       Cmp_tags, bool errors_are_identical, std::string*) const;
 
   // Record that this is a varargs function.
   void
@@ -2293,7 +2309,8 @@
 
   // Whether this type is identical with T.
   bool
-  is_identical(const Struct_type* t, bool errors_are_identical) const;
+  is_identical(const Struct_type* t, Cmp_tags,
+	       bool errors_are_identical) const;
 
   // Return whether NAME is a local field which is not exported.  This
   // is only used for better error reporting.
@@ -2495,7 +2512,8 @@
 
   // Whether this type is identical with T.
   bool
-  is_identical(const Array_type* t, bool errors_are_identical) const;
+  is_identical(const Array_type* t, Cmp_tags,
+	       bool errors_are_identical) const;
 
   // Return an expression for the pointer to the values in an array.
   Expression*
@@ -2656,7 +2674,8 @@
 
   // Whether this type is identical with T.
   bool
-  is_identical(const Map_type* t, bool errors_are_identical) const;
+  is_identical(const Map_type* t, Cmp_tags,
+	       bool errors_are_identical) const;
 
   // Import a map type.
   static Map_type*
@@ -2773,7 +2792,8 @@
 
   // Whether this type is identical with T.
   bool
-  is_identical(const Channel_type* t, bool errors_are_identical) const;
+  is_identical(const Channel_type* t, Cmp_tags,
+	       bool errors_are_identical) const;
 
   // Import a channel type.
   static Channel_type*
@@ -2883,7 +2903,8 @@
   // Whether this type is identical with T.  REASON is as in
   // implements_interface.
   bool
-  is_identical(const Interface_type* t, bool errors_are_identical) const;
+  is_identical(const Interface_type* t, Cmp_tags,
+	       bool errors_are_identical) const;
 
   // Whether we can assign T to this type.  is_identical is known to
   // be false.