Patchwork Go patch committed: Avoid bogus init loop error

login
register
mail settings
Submitter Ian Taylor
Date Nov. 8, 2013, 5:35 p.m.
Message ID <mcrmwleg5k2.fsf@iant-glaptop.roam.corp.google.com>
Download mbox | patch
Permalink /patch/289897/
State New
Headers show

Comments

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

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