Go patch committed: Avoid bogus init loop error

Submitted by Ian Taylor on Nov. 8, 2013, 5:35 p.m.

Details

Message ID mcrmwleg5k2.fsf@iant-glaptop.roam.corp.google.com
State New
Headers show

Commit Message

Ian Taylor Nov. 8, 2013, 5:35 p.m.
Code like this

type S struct {
	F int
}

var V = S{F: 1}

var F = V.F

could trigger an incorrect "variable initializer refers to itself" error
because the Go frontend would confuse itself into thinking that the "F"
in the composite literal was the same as the global variable "F", rather
than being the name of the field "F".

This patch fixes the problem.  Bootstrapped and ran Go testsuite on
x86_64-unknown-linux-gnu.  Committed to mainline.

Ian

Patch hide | download patch | download mbox

diff -r 60070f2ac887 go/expressions.cc
--- a/go/expressions.cc	Thu Nov 07 20:41:06 2013 -0800
+++ b/go/expressions.cc	Fri Nov 08 09:30:45 2013 -0800
@@ -13488,10 +13488,52 @@ 
 int
 Composite_literal_expression::do_traverse(Traverse* traverse)
 {
-  if (this->vals_ != NULL
-      && this->vals_->traverse(traverse) == TRAVERSE_EXIT)
+  if (Type::traverse(this->type_, traverse) == TRAVERSE_EXIT)
     return TRAVERSE_EXIT;
-  return Type::traverse(this->type_, traverse);
+
+  // If this is a struct composite literal with keys, then the keys
+  // are field names, not expressions.  We don't want to traverse them
+  // in that case.  If we do, we can give an erroneous error "variable
+  // initializer refers to itself."  See bug482.go in the testsuite.
+  if (this->has_keys_ && this->vals_ != NULL)
+    {
+      // The type may not be resolvable at this point.
+      Type* type = this->type_;
+      while (true)
+	{
+	  if (type->classification() == Type::TYPE_NAMED)
+	    type = type->named_type()->real_type();
+	  else if (type->classification() == Type::TYPE_FORWARD)
+	    {
+	      Type* t = type->forwarded();
+	      if (t == type)
+		break;
+	      type = t;
+	    }
+	  else
+	    break;
+	}
+
+      if (type->classification() == Type::TYPE_STRUCT)
+	{
+	  Expression_list::iterator p = this->vals_->begin();
+	  while (p != this->vals_->end())
+	    {
+	      // Skip key.
+	      ++p;
+	      go_assert(p != this->vals_->end());
+	      if (Expression::traverse(&*p, traverse) == TRAVERSE_EXIT)
+		return TRAVERSE_EXIT;
+	      ++p;
+	    }
+	  return TRAVERSE_CONTINUE;
+	}
+    }
+
+  if (this->vals_ != NULL)
+    return this->vals_->traverse(traverse);
+
+  return TRAVERSE_CONTINUE;
 }
 
 // Lower a generic composite literal into a specific version based on