diff mbox

Go patch committed: Handle recursive interfaces

Message ID mcrr4yu753g.fsf@dhcp-172-18-216-180.mtv.corp.google.com
State New
Headers show

Commit Message

Ian Lance Taylor Jan. 20, 2012, 3:42 p.m. UTC
It turns out that in Go interface types can be recursive without
directly referencing a named type, after inherited methods are
finalized.  This happens for code like this:

type I {
	F() interface { I }
}

The key here is that method F of interface I has a return type which is
an interface which inherits from I.  When the Go frontend sees interface
inheritance like this, it simply copies the methods into the interface.
This leads to a circular type structure with no named type in the
middle.  The compiler has a bunch of framework to handle recursion via
named types, but unfortunately that does not help here.

This patch fixes various cases which arise from these sorts of recursive
interfaces.  The main effect is to prevent the compiler from going into
an endless recursive loop when it sees this sort of construct.  This
patch also slightly changes the error handling, causing a couple of test
cases to change.

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

Ian


2012-01-20  Ian Lance Taylor  <iant@google.com>

	* go-gcc.cc (Gcc_backend::placeholder_struct_type): Permit name to
	be empty.
	(Gcc_backend::set_placeholder_struct_type): Likewise.
diff mbox

Patch

Index: gcc/go/go-gcc.cc
===================================================================
--- gcc/go/go-gcc.cc	(revision 183280)
+++ gcc/go/go-gcc.cc	(working copy)
@@ -656,10 +656,13 @@  Gcc_backend::placeholder_struct_type(con
 				     Location location)
 {
   tree ret = make_node(RECORD_TYPE);
-  tree decl = build_decl(location.gcc_location(), TYPE_DECL,
-			 get_identifier_from_string(name),
-			 ret);
-  TYPE_NAME(ret) = decl;
+  if (!name.empty())
+    {
+      tree decl = build_decl(location.gcc_location(), TYPE_DECL,
+			     get_identifier_from_string(name),
+			     ret);
+      TYPE_NAME(ret) = decl;
+    }
   return this->make_type(ret);
 }
 
@@ -674,10 +677,13 @@  Gcc_backend::set_placeholder_struct_type
   gcc_assert(TREE_CODE(t) == RECORD_TYPE && TYPE_FIELDS(t) == NULL_TREE);
   Btype* r = this->fill_in_struct(placeholder, fields);
 
-  // Build the data structure gcc wants to see for a typedef.
-  tree copy = build_distinct_type_copy(t);
-  TYPE_NAME(copy) = NULL_TREE;
-  DECL_ORIGINAL_TYPE(TYPE_NAME(t)) = copy;
+  if (TYPE_NAME(t) != NULL_TREE)
+    {
+      // Build the data structure gcc wants to see for a typedef.
+      tree copy = build_distinct_type_copy(t);
+      TYPE_NAME(copy) = NULL_TREE;
+      DECL_ORIGINAL_TYPE(TYPE_NAME(t)) = copy;
+    }
 
   return r->get_tree() != error_mark_node;
 }
Index: gcc/go/gofrontend/gogo.cc
===================================================================
--- gcc/go/gofrontend/gogo.cc	(revision 183280)
+++ gcc/go/gofrontend/gogo.cc	(working copy)
@@ -110,7 +110,8 @@  Gogo::Gogo(Backend* backend, Linemap* li
     results->push_back(Typed_identifier("", Type::lookup_string_type(), loc));
     Type *method_type = Type::make_function_type(NULL, NULL, results, loc);
     methods->push_back(Typed_identifier("Error", method_type, loc));
-    Type *error_iface = Type::make_interface_type(methods, loc);
+    Interface_type *error_iface = Type::make_interface_type(methods, loc);
+    error_iface->finalize_methods();
     Named_type *error_type = Named_object::make_type("error", NULL, error_iface, loc)->type_value();
     this->add_named_type(error_type);
   }
@@ -175,7 +176,7 @@  Gogo::Gogo(Backend* backend, Linemap* li
   print_type->set_is_builtin();
   this->globals_->add_function_declaration("println", NULL, print_type, loc);
 
-  Type *empty = Type::make_interface_type(NULL, loc);
+  Type *empty = Type::make_empty_interface_type(loc);
   Typed_identifier_list* panic_parms = new Typed_identifier_list();
   panic_parms->push_back(Typed_identifier("e", empty, loc));
   Function_type *panic_type = Type::make_function_type(NULL, panic_parms,
@@ -1564,7 +1565,8 @@  Finalize_methods::type(Type* t)
 	// finalize the methods of the field types, not of the struct
 	// type itself.  We don't want to add methods to the struct,
 	// since it has a name.
-	Type* rt = t->named_type()->real_type();
+	Named_type* nt = t->named_type();
+	Type* rt = nt->real_type();
 	if (rt->classification() != Type::TYPE_STRUCT)
 	  {
 	    if (Type::traverse(rt, this) == TRAVERSE_EXIT)
@@ -1576,7 +1578,21 @@  Finalize_methods::type(Type* t)
 	      return TRAVERSE_EXIT;
 	  }
 
-	t->named_type()->finalize_methods(this->gogo_);
+	nt->finalize_methods(this->gogo_);
+
+	// If this type is defined in a different package, then finalize the
+	// types of all the methods, since we won't see them otherwise.
+	if (nt->named_object()->package() != NULL && nt->has_any_methods())
+	  {
+	    const Methods* methods = nt->methods();
+	    for (Methods::const_iterator p = methods->begin();
+		 p != methods->end();
+		 ++p)
+	      {
+		if (Type::traverse(p->second->type(), this) == TRAVERSE_EXIT)
+		  return TRAVERSE_EXIT;
+	      }
+	  }
 
 	return TRAVERSE_SKIP_COMPONENTS;
       }
@@ -2622,6 +2638,9 @@  class Build_method_tables : public Trave
 void
 Gogo::build_interface_method_tables()
 {
+  if (saw_errors())
+    return;
+
   std::vector<Interface_type*> hidden_interfaces;
   hidden_interfaces.reserve(this->interface_types_.size());
   for (std::vector<Interface_type*>::const_iterator pi =
@@ -4922,10 +4941,7 @@  Bindings::traverse(Traverse* traverse, b
 		     | Traverse::traverse_statements
 		     | Traverse::traverse_expressions
 		     | Traverse::traverse_types)) != 0)
-	    {
-	      if (p->func_value()->traverse(traverse) == TRAVERSE_EXIT)
-		return TRAVERSE_EXIT;
-	    }
+	    t = p->func_value()->traverse(traverse);
 	  break;
 
 	case Named_object::NAMED_OBJECT_PACKAGE:
@@ -4952,6 +4968,26 @@  Bindings::traverse(Traverse* traverse, b
 	return TRAVERSE_EXIT;
     }
 
+  // If we need to traverse types, check the function declarations,
+  // which have types.  We don't need to check the type declarations,
+  // as those are just names.
+  if ((traverse_mask & e_or_t) != 0)
+    {
+      for (Bindings::const_declarations_iterator p =
+	     this->begin_declarations();
+	   p != this->end_declarations();
+	   ++p)
+	{
+	  if (p->second->is_function_declaration())
+	    {
+	      if (Type::traverse(p->second->func_declaration_value()->type(),
+				 traverse)
+		  == TRAVERSE_EXIT)
+		return TRAVERSE_EXIT;
+	    }
+	}
+    }
+
   return TRAVERSE_CONTINUE;
 }
 
@@ -5090,9 +5126,12 @@  Traverse::remember_type(const Type* type
     return true;
   go_assert((this->traverse_mask() & traverse_types) != 0
 	     || (this->traverse_mask() & traverse_expressions) != 0);
-  // We only have to remember named types, as they are the only ones
-  // we can see multiple times in a traversal.
-  if (type->classification() != Type::TYPE_NAMED)
+  // We mostly only have to remember named types.  But it turns out
+  // that an interface type can refer to itself without using a name
+  // by relying on interface inheritance, as in
+  // type I interface { F() interface{I} }
+  if (type->classification() != Type::TYPE_NAMED
+      && type->classification() != Type::TYPE_INTERFACE)
     return false;
   if (this->types_seen_ == NULL)
     this->types_seen_ = new Types_seen();
Index: gcc/go/gofrontend/gogo.h
===================================================================
--- gcc/go/gofrontend/gogo.h	(revision 183280)
+++ gcc/go/gofrontend/gogo.h	(working copy)
@@ -2578,8 +2578,13 @@  class Traverse
   type(Type*);
 
  private:
-  typedef Unordered_set_hash(const Type*, Type_hash_identical,
-			     Type_identical) Types_seen;
+  // A hash table for types we have seen during this traversal.  Note
+  // that this uses the default hash functions for pointers rather
+  // than Type_hash_identical and Type_identical.  This is because for
+  // traversal we care about seeing a specific type structure.  If
+  // there are two separate instances of identical types, we want to
+  // traverse both.
+  typedef Unordered_set(const Type*) Types_seen;
 
   typedef Unordered_set(const Expression*) Expressions_seen;
 
Index: gcc/go/gofrontend/types.h
===================================================================
--- gcc/go/gofrontend/types.h	(revision 183280)
+++ gcc/go/gofrontend/types.h	(working copy)
@@ -485,6 +485,9 @@  class Type
   static Interface_type*
   make_interface_type(Typed_identifier_list* methods, Location);
 
+  static Interface_type*
+  make_empty_interface_type(Location);
+
   static Type*
   make_type_descriptor_type();
 
@@ -1319,6 +1322,10 @@  class Typed_identifier_list
                                               Linemap::unknown_location()));
   }
 
+  void
+  reserve(size_t c)
+  { this->entries_.reserve(c); }
+
   // Iterators.
 
   typedef std::vector<Typed_identifier>::iterator iterator;
@@ -2429,7 +2436,9 @@  class Interface_type : public Type
  public:
   Interface_type(Typed_identifier_list* methods, Location location)
     : Type(TYPE_INTERFACE),
-      methods_(methods), location_(location)
+      parse_methods_(methods), all_methods_(NULL), location_(location),
+      interface_btype_(NULL), assume_identical_(NULL),
+      methods_are_finalized_(false), seen_(false)
   { go_assert(methods == NULL || !methods->empty()); }
 
   // The location where the interface type was defined.
@@ -2440,18 +2449,27 @@  class Interface_type : public Type
   // Return whether this is an empty interface.
   bool
   is_empty() const
-  { return this->methods_ == NULL; }
+  {
+    go_assert(this->methods_are_finalized_);
+    return this->all_methods_ == NULL;
+  }
 
   // Return the list of methods.  This will return NULL for an empty
   // interface.
   const Typed_identifier_list*
   methods() const
-  { return this->methods_; }
+  {
+    go_assert(this->methods_are_finalized_);
+    return this->all_methods_;
+  }
 
   // Return the number of methods.
   size_t
   method_count() const
-  { return this->methods_ == NULL ? 0 : this->methods_->size(); }
+  {
+    go_assert(this->methods_are_finalized_);
+    return this->all_methods_ == NULL ? 0 : this->all_methods_->size();
+  }
 
   // Return the method NAME, or NULL.
   const Typed_identifier*
@@ -2461,7 +2479,8 @@  class Interface_type : public Type
   size_t
   method_index(const std::string& name) const;
 
-  // Finalize the methods.  This handles interface inheritance.
+  // Finalize the methods.  This sets all_methods_.  This handles
+  // interface inheritance.
   void
   finalize_methods();
 
@@ -2528,11 +2547,41 @@  class Interface_type : public Type
   do_export(Export*) const;
 
  private:
-  // The list of methods associated with the interface.  This will be
-  // NULL for the empty interface.
-  Typed_identifier_list* methods_;
+  // This type guards against infinite recursion when comparing
+  // interface types.  We keep a list of interface types assumed to be
+  // identical during comparison.  We just keep the list on the stack.
+  // This permits us to compare cases like
+  // type I1 interface { F() interface{I1} }
+  // type I2 interface { F() interface{I2} }
+  struct Assume_identical
+  {
+    Assume_identical* next;
+    const Interface_type* t1;
+    const Interface_type* t2;
+  };
+
+  bool
+  assume_identical(const Interface_type*, const Interface_type*) const;
+
+  // The list of methods associated with the interface from the
+  // parser.  This will be NULL for the empty interface.  This may
+  // include unnamed interface types.
+  Typed_identifier_list* parse_methods_;
+  // The list of all methods associated with the interface.  This
+  // expands any interface types listed in methods_.  It is set by
+  // finalize_methods.  This will be NULL for the empty interface.
+  Typed_identifier_list* all_methods_;
   // The location where the interface was defined.
   Location location_;
+  // The backend representation of this type during backend conversion.
+  Btype* interface_btype_;
+  // A list of interface types assumed to be identical during
+  // interface comparison.
+  mutable Assume_identical* assume_identical_;
+  // Whether the methods have been finalized.
+  bool methods_are_finalized_;
+  // Used to avoid endless recursion in do_mangled_name.
+  mutable bool seen_;
 };
 
 // The value we keep for a named type.  This lets us get the right
Index: gcc/go/gofrontend/unsafe.cc
===================================================================
--- gcc/go/gofrontend/unsafe.cc	(revision 183280)
+++ gcc/go/gofrontend/unsafe.cc	(working copy)
@@ -87,7 +87,7 @@  Gogo::import_unsafe(const std::string& l
     this->add_named_object(no);
 
   // Typeof.
-  Type* empty_interface = Type::make_interface_type(NULL, bloc);
+  Type* empty_interface = Type::make_empty_interface_type(bloc);
   Typed_identifier_list* parameters = new Typed_identifier_list;
   parameters->push_back(Typed_identifier("i", empty_interface, bloc));
   results = new Typed_identifier_list;
Index: gcc/go/gofrontend/runtime.cc
===================================================================
--- gcc/go/gofrontend/runtime.cc	(revision 183280)
+++ gcc/go/gofrontend/runtime.cc	(working copy)
@@ -151,12 +151,14 @@  runtime_function_type(Runtime_function_t
 	    Typed_identifier_list* methods = new Typed_identifier_list();
 	    Type* mtype = Type::make_function_type(NULL, NULL, NULL, bloc);
 	    methods->push_back(Typed_identifier("x", mtype, bloc));
-	    t = Type::make_interface_type(methods, bloc);
+	    Interface_type* it = Type::make_interface_type(methods, bloc);
+	    it->finalize_methods();
+	    t = it;
 	  }
 	  break;
 
 	case RFT_EFACE:
-	  t = Type::make_interface_type(NULL, bloc);
+	  t = Type::make_empty_interface_type(bloc);
 	  break;
 
 	case RFT_FUNC_PTR:
Index: gcc/go/gofrontend/backend.h
===================================================================
--- gcc/go/gofrontend/backend.h	(revision 183280)
+++ gcc/go/gofrontend/backend.h	(working copy)
@@ -139,7 +139,8 @@  class Backend
   set_placeholder_function_type(Btype* placeholder, Btype* ft) = 0;
 
   // Create a placeholder struct type.  This is used for a named
-  // struct type, as with placeholder_pointer_type.
+  // struct type, as with placeholder_pointer_type.  It is also used
+  // for interface types, in which case NAME will be the empty string.
   virtual Btype*
   placeholder_struct_type(const std::string& name, Location) = 0;
 
Index: gcc/go/gofrontend/types.cc
===================================================================
--- gcc/go/gofrontend/types.cc	(revision 183280)
+++ gcc/go/gofrontend/types.cc	(working copy)
@@ -6148,9 +6148,12 @@  Type::make_channel_type(bool send, bool 
 int
 Interface_type::do_traverse(Traverse* traverse)
 {
-  if (this->methods_ == NULL)
+  Typed_identifier_list* methods = (this->methods_are_finalized_
+				    ? this->all_methods_
+				    : this->parse_methods_);
+  if (methods == NULL)
     return TRAVERSE_CONTINUE;
-  return this->methods_->traverse(traverse);
+  return methods->traverse(traverse);
 }
 
 // Finalize the methods.  This handles interface inheritance.
@@ -6158,56 +6161,57 @@  Interface_type::do_traverse(Traverse* tr
 void
 Interface_type::finalize_methods()
 {
-  if (this->methods_ == NULL)
+  if (this->methods_are_finalized_)
     return;
-  std::vector<Named_type*> seen;
-  bool is_recursive = false;
-  size_t from = 0;
-  size_t to = 0;
-  while (from < this->methods_->size())
+  this->methods_are_finalized_ = true;
+  if (this->parse_methods_ == NULL)
+    return;
+
+  this->all_methods_ = new Typed_identifier_list();
+  this->all_methods_->reserve(this->parse_methods_->size());
+  Typed_identifier_list inherit;
+  for (Typed_identifier_list::const_iterator pm =
+	 this->parse_methods_->begin();
+       pm != this->parse_methods_->end();
+       ++pm)
     {
-      const Typed_identifier* p = &this->methods_->at(from);
-      if (!p->name().empty())
-	{
-	  size_t i;
-	  for (i = 0; i < to; ++i)
-	    {
-	      if (this->methods_->at(i).name() == p->name())
-		{
-		  error_at(p->location(), "duplicate method %qs",
-			   Gogo::message_name(p->name()).c_str());
-		  break;
-		}
-	    }
-	  if (i == to)
-	    {
-	      if (from != to)
-		this->methods_->set(to, *p);
-	      ++to;
-	    }
-	  ++from;
-	  continue;
-	}
+      const Typed_identifier* p = &*pm;
+      if (p->name().empty())
+	inherit.push_back(*p);
+      else if (this->find_method(p->name()) == NULL)
+	this->all_methods_->push_back(*p);
+      else
+	error_at(p->location(), "duplicate method %qs",
+		 Gogo::message_name(p->name()).c_str());
+    }
 
-      Interface_type* it = p->type()->interface_type();
+  std::vector<Named_type*> seen;
+  seen.reserve(inherit.size());
+  bool issued_recursive_error = false;
+  while (!inherit.empty())
+    {
+      Type* t = inherit.back().type();
+      Location tl = inherit.back().location();
+      inherit.pop_back();
+
+      Interface_type* it = t->interface_type();
       if (it == NULL)
 	{
-	  error_at(p->location(), "interface contains embedded non-interface");
-	  ++from;
+	  if (!t->is_error())
+	    error_at(tl, "interface contains embedded non-interface");
 	  continue;
 	}
       if (it == this)
 	{
-	  if (!is_recursive)
+	  if (!issued_recursive_error)
 	    {
-	      error_at(p->location(), "invalid recursive interface");
-	      is_recursive = true;
+	      error_at(tl, "invalid recursive interface");
+	      issued_recursive_error = true;
 	    }
-	  ++from;
 	  continue;
 	}
 
-      Named_type* nt = p->type()->named_type();
+      Named_type* nt = t->named_type();
       if (nt != NULL)
 	{
 	  std::vector<Named_type*>::const_iterator q;
@@ -6215,73 +6219,39 @@  Interface_type::finalize_methods()
 	    {
 	      if (*q == nt)
 		{
-		  error_at(p->location(), "inherited interface loop");
+		  error_at(tl, "inherited interface loop");
 		  break;
 		}
 	    }
 	  if (q != seen.end())
-	    {
-	      ++from;
-	      continue;
-	    }
+	    continue;
 	  seen.push_back(nt);
 	}
 
-      const Typed_identifier_list* methods = it->methods();
-      if (methods == NULL)
-	{
-	  ++from;
-	  continue;
-	}
-      for (Typed_identifier_list::const_iterator q = methods->begin();
-	   q != methods->end();
+      const Typed_identifier_list* imethods = it->parse_methods_;
+      if (imethods == NULL)
+	continue;
+      for (Typed_identifier_list::const_iterator q = imethods->begin();
+	   q != imethods->end();
 	   ++q)
 	{
 	  if (q->name().empty())
-	    {
-	      if (q->type()->forwarded() == p->type()->forwarded())
-		error_at(p->location(), "interface inheritance loop");
-	      else
-		{
-		  size_t i;
-		  for (i = from + 1; i < this->methods_->size(); ++i)
-		    {
-		      const Typed_identifier* r = &this->methods_->at(i);
-		      if (r->name().empty()
-			  && r->type()->forwarded() == q->type()->forwarded())
-			{
-			  error_at(p->location(),
-				   "inherited interface listed twice");
-			  break;
-			}
-		    }
-		  if (i == this->methods_->size())
-		    this->methods_->push_back(Typed_identifier(q->name(),
-							       q->type(),
-							       p->location()));
-		}
-	    }
+	    inherit.push_back(*q);
 	  else if (this->find_method(q->name()) == NULL)
-	    this->methods_->push_back(Typed_identifier(q->name(), q->type(),
-						       p->location()));
+	    this->all_methods_->push_back(Typed_identifier(q->name(),
+							   q->type(), tl));
 	  else
-	    {
-	      if (!is_recursive)
-		error_at(p->location(), "inherited method %qs is ambiguous",
-			 Gogo::message_name(q->name()).c_str());
-	    }
+	    error_at(tl, "inherited method %qs is ambiguous",
+		     Gogo::message_name(q->name()).c_str());
 	}
-      ++from;
-    }
-  if (to == 0)
-    {
-      delete this->methods_;
-      this->methods_ = NULL;
     }
+
+  if (!this->all_methods_->empty())
+    this->all_methods_->sort_by_name();
   else
     {
-      this->methods_->resize(to);
-      this->methods_->sort_by_name();
+      delete this->all_methods_;
+      this->all_methods_ = NULL;
     }
 }
 
@@ -6290,10 +6260,11 @@  Interface_type::finalize_methods()
 const Typed_identifier*
 Interface_type::find_method(const std::string& name) const
 {
-  if (this->methods_ == NULL)
+  go_assert(this->methods_are_finalized_);
+  if (this->all_methods_ == NULL)
     return NULL;
-  for (Typed_identifier_list::const_iterator p = this->methods_->begin();
-       p != this->methods_->end();
+  for (Typed_identifier_list::const_iterator p = this->all_methods_->begin();
+       p != this->all_methods_->end();
        ++p)
     if (p->name() == name)
       return &*p;
@@ -6305,10 +6276,10 @@  Interface_type::find_method(const std::s
 size_t
 Interface_type::method_index(const std::string& name) const
 {
-  go_assert(this->methods_ != NULL);
+  go_assert(this->methods_are_finalized_ && this->all_methods_ != NULL);
   size_t ret = 0;
-  for (Typed_identifier_list::const_iterator p = this->methods_->begin();
-       p != this->methods_->end();
+  for (Typed_identifier_list::const_iterator p = this->all_methods_->begin();
+       p != this->all_methods_->end();
        ++p, ++ret)
     if (p->name() == name)
       return ret;
@@ -6321,10 +6292,11 @@  Interface_type::method_index(const std::
 bool
 Interface_type::is_unexported_method(Gogo* gogo, const std::string& name) const
 {
-  if (this->methods_ == NULL)
+  go_assert(this->methods_are_finalized_);
+  if (this->all_methods_ == NULL)
     return false;
-  for (Typed_identifier_list::const_iterator p = this->methods_->begin();
-       p != this->methods_->end();
+  for (Typed_identifier_list::const_iterator p = this->all_methods_->begin();
+       p != this->all_methods_->end();
        ++p)
     {
       const std::string& method_name(p->name());
@@ -6342,26 +6314,53 @@  bool
 Interface_type::is_identical(const Interface_type* t,
 			     bool errors_are_identical) const
 {
+  go_assert(this->methods_are_finalized_ && t->methods_are_finalized_);
+
   // We require the same methods with the same types.  The methods
   // have already been sorted.
-  if (this->methods() == NULL || t->methods() == NULL)
-    return this->methods() == t->methods();
+  if (this->all_methods_ == NULL || t->all_methods_ == NULL)
+    return this->all_methods_ == t->all_methods_;
+
+  if (this->assume_identical(this, t) || t->assume_identical(t, this))
+    return true;
 
-  Typed_identifier_list::const_iterator p1 = this->methods()->begin();
-  for (Typed_identifier_list::const_iterator p2 = t->methods()->begin();
-       p2 != t->methods()->end();
-       ++p1, ++p2)
+  Assume_identical* hold_ai = this->assume_identical_;
+  Assume_identical ai;
+  ai.t1 = this;
+  ai.t2 = t;
+  ai.next = hold_ai;
+  this->assume_identical_ = &ai;
+
+  Typed_identifier_list::const_iterator p1 = this->all_methods_->begin();
+  Typed_identifier_list::const_iterator p2;
+  for (p2 = t->all_methods_->begin(); p2 != t->all_methods_->end(); ++p1, ++p2)
     {
-      if (p1 == this->methods()->end())
-	return false;
+      if (p1 == this->all_methods_->end())
+	break;
       if (p1->name() != p2->name()
 	  || !Type::are_identical(p1->type(), p2->type(),
 				  errors_are_identical, NULL))
-	return false;
+	break;
     }
-  if (p1 != this->methods()->end())
-    return false;
-  return true;
+
+  this->assume_identical_ = hold_ai;
+
+  return p1 == this->all_methods_->end() && p2 == t->all_methods_->end();
+}
+
+// Return true if T1 and T2 are assumed to be identical during a type
+// comparison.
+
+bool
+Interface_type::assume_identical(const Interface_type* t1,
+				 const Interface_type* t2) const
+{
+  for (Assume_identical* p = this->assume_identical_;
+       p != NULL;
+       p = p->next)
+    if ((p->t1 == t1 && p->t2 == t2) || (p->t1 == t2 && p->t2 == t1))
+      return true;
+  return false;
 }
 
 // Whether we can assign the interface type T to this type.  The types
@@ -6373,10 +6372,11 @@  bool
 Interface_type::is_compatible_for_assign(const Interface_type* t,
 					 std::string* reason) const
 {
-  if (this->methods() == NULL)
+  go_assert(this->methods_are_finalized_ && t->methods_are_finalized_);
+  if (this->all_methods_ == NULL)
     return true;
-  for (Typed_identifier_list::const_iterator p = this->methods()->begin();
-       p != this->methods()->end();
+  for (Typed_identifier_list::const_iterator p = this->all_methods_->begin();
+       p != this->all_methods_->end();
        ++p)
     {
       const Typed_identifier* m = t->find_method(p->name());
@@ -6423,17 +6423,23 @@  Interface_type::is_compatible_for_assign
 // Hash code.
 
 unsigned int
-Interface_type::do_hash_for_method(Gogo* gogo) const
+Interface_type::do_hash_for_method(Gogo*) const
 {
+  go_assert(this->methods_are_finalized_);
   unsigned int ret = 0;
-  if (this->methods_ != NULL)
+  if (this->all_methods_ != NULL)
     {
-      for (Typed_identifier_list::const_iterator p = this->methods_->begin();
-	   p != this->methods_->end();
+      for (Typed_identifier_list::const_iterator p =
+	     this->all_methods_->begin();
+	   p != this->all_methods_->end();
 	   ++p)
 	{
 	  ret = Type::hash_string(p->name(), ret);
-	  ret += p->type()->hash_for_method(gogo);
+	  // We don't use the method type in the hash, to avoid
+	  // infinite recursion if an interface method uses a type
+	  // which is an interface which inherits from the interface
+	  // itself.
+	  // type T interface { F() interface {T}}
 	  ret <<= 1;
 	}
     }
@@ -6446,7 +6452,8 @@  Interface_type::do_hash_for_method(Gogo*
 bool
 Interface_type::implements_interface(const Type* t, std::string* reason) const
 {
-  if (this->methods_ == NULL)
+  go_assert(this->methods_are_finalized_);
+  if (this->all_methods_ == NULL)
     return true;
 
   bool is_pointer = false;
@@ -6499,8 +6506,8 @@  Interface_type::implements_interface(con
       return false;
     }
 
-  for (Typed_identifier_list::const_iterator p = this->methods_->begin();
-       p != this->methods_->end();
+  for (Typed_identifier_list::const_iterator p = this->all_methods_->begin();
+       p != this->all_methods_->end();
        ++p)
     {
       bool is_ambiguous = false;
@@ -6653,13 +6660,20 @@  get_backend_interface_fields(Gogo* gogo,
 Btype*
 Interface_type::do_get_backend(Gogo* gogo)
 {
-  if (this->methods_ == NULL)
+  if (this->is_empty())
     return Interface_type::get_backend_empty_interface_type(gogo);
   else
     {
+      if (this->interface_btype_ != NULL)
+	return this->interface_btype_;
+      this->interface_btype_ =
+	gogo->backend()->placeholder_struct_type("", this->location_);
       std::vector<Backend::Btyped_identifier> bfields;
       get_backend_interface_fields(gogo, this, &bfields);
-      return gogo->backend()->struct_type(bfields);
+      if (!gogo->backend()->set_placeholder_struct_type(this->interface_btype_,
+							bfields))
+	this->interface_btype_ = gogo->backend()->error_type();
+      return this->interface_btype_;
     }
 }
 
@@ -6721,13 +6735,14 @@  Interface_type::do_type_descriptor(Gogo*
   go_assert(pif->is_field_name("methods"));
 
   Expression_list* methods = new Expression_list();
-  if (this->methods_ != NULL && !this->methods_->empty())
+  if (this->all_methods_ != NULL)
     {
       Type* elemtype = pif->type()->array_type()->element_type();
 
-      methods->reserve(this->methods_->size());
-      for (Typed_identifier_list::const_iterator pm = this->methods_->begin();
-	   pm != this->methods_->end();
+      methods->reserve(this->all_methods_->size());
+      for (Typed_identifier_list::const_iterator pm =
+	     this->all_methods_->begin();
+	   pm != this->all_methods_->end();
 	   ++pm)
 	{
 	  const Struct_field_list* mfields = elemtype->struct_type()->fields();
@@ -6780,29 +6795,35 @@  void
 Interface_type::do_reflection(Gogo* gogo, std::string* ret) const
 {
   ret->append("interface {");
-  if (this->methods_ != NULL)
+  const Typed_identifier_list* methods = this->parse_methods_;
+  if (methods != NULL)
     {
       ret->push_back(' ');
-      for (Typed_identifier_list::const_iterator p = this->methods_->begin();
-	   p != this->methods_->end();
+      for (Typed_identifier_list::const_iterator p = methods->begin();
+	   p != methods->end();
 	   ++p)
 	{
-	  if (p != this->methods_->begin())
+	  if (p != methods->begin())
 	    ret->append("; ");
-	  if (!Gogo::is_hidden_name(p->name()))
-	    ret->append(p->name());
+	  if (p->name().empty())
+	    this->append_reflection(p->type(), gogo, ret);
 	  else
 	    {
-	      // This matches what the gc compiler does.
-	      std::string prefix = Gogo::hidden_name_prefix(p->name());
-	      ret->append(prefix.substr(prefix.find('.') + 1));
-	      ret->push_back('.');
-	      ret->append(Gogo::unpack_hidden_name(p->name()));
-	    }
-	  std::string sub = p->type()->reflection(gogo);
-	  go_assert(sub.compare(0, 4, "func") == 0);
-	  sub = sub.substr(4);
-	  ret->append(sub);
+	      if (!Gogo::is_hidden_name(p->name()))
+		ret->append(p->name());
+	      else
+		{
+		  // This matches what the gc compiler does.
+		  std::string prefix = Gogo::hidden_name_prefix(p->name());
+		  ret->append(prefix.substr(prefix.find('.') + 1));
+		  ret->push_back('.');
+		  ret->append(Gogo::unpack_hidden_name(p->name()));
+		}
+	      std::string sub = p->type()->reflection(gogo);
+	      go_assert(sub.compare(0, 4, "func") == 0);
+	      sub = sub.substr(4);
+	      ret->append(sub);
+	    }
 	}
       ret->push_back(' ');
     }
@@ -6814,23 +6835,30 @@  Interface_type::do_reflection(Gogo* gogo
 void
 Interface_type::do_mangled_name(Gogo* gogo, std::string* ret) const
 {
+  go_assert(this->methods_are_finalized_);
+
   ret->push_back('I');
 
-  const Typed_identifier_list* methods = this->methods_;
-  if (methods != NULL)
+  const Typed_identifier_list* methods = this->all_methods_;
+  if (methods != NULL && !this->seen_)
     {
+      this->seen_ = true;
       for (Typed_identifier_list::const_iterator p = methods->begin();
 	   p != methods->end();
 	   ++p)
 	{
-	  std::string n = Gogo::unpack_hidden_name(p->name());
-	  char buf[20];
-	  snprintf(buf, sizeof buf, "%u_",
-		   static_cast<unsigned int>(n.length()));
-	  ret->append(buf);
-	  ret->append(n);
+	  if (!p->name().empty())
+	    {
+	      std::string n = Gogo::unpack_hidden_name(p->name());
+	      char buf[20];
+	      snprintf(buf, sizeof buf, "%u_",
+		       static_cast<unsigned int>(n.length()));
+	      ret->append(buf);
+	      ret->append(n);
+	    }
 	  this->append_mangled_name(p->type(), gogo, ret);
 	}
+      this->seen_ = false;
     }
 
   ret->push_back('e');
@@ -6843,67 +6871,75 @@  Interface_type::do_export(Export* exp) c
 {
   exp->write_c_string("interface { ");
 
-  const Typed_identifier_list* methods = this->methods_;
+  const Typed_identifier_list* methods = this->parse_methods_;
   if (methods != NULL)
     {
       for (Typed_identifier_list::const_iterator pm = methods->begin();
 	   pm != methods->end();
 	   ++pm)
 	{
-	  exp->write_string(pm->name());
-	  exp->write_c_string(" (");
+	  if (pm->name().empty())
+	    {
+	      exp->write_c_string("$ ");
+	      exp->write_type(pm->type());
+	    }
+	  else
+	    {
+	      exp->write_string(pm->name());
+	      exp->write_c_string(" (");
 
-	  const Function_type* fntype = pm->type()->function_type();
+	      const Function_type* fntype = pm->type()->function_type();
 
-	  bool first = true;
-	  const Typed_identifier_list* parameters = fntype->parameters();
-	  if (parameters != NULL)
-	    {
-	      bool is_varargs = fntype->is_varargs();
-	      for (Typed_identifier_list::const_iterator pp =
-		     parameters->begin();
-		   pp != parameters->end();
-		   ++pp)
+	      bool first = true;
+	      const Typed_identifier_list* parameters = fntype->parameters();
+	      if (parameters != NULL)
 		{
-		  if (first)
-		    first = false;
-		  else
-		    exp->write_c_string(", ");
-		  if (!is_varargs || pp + 1 != parameters->end())
-		    exp->write_type(pp->type());
-		  else
+		  bool is_varargs = fntype->is_varargs();
+		  for (Typed_identifier_list::const_iterator pp =
+			 parameters->begin();
+		       pp != parameters->end();
+		       ++pp)
 		    {
-		      exp->write_c_string("...");
-		      Type *pptype = pp->type();
-		      exp->write_type(pptype->array_type()->element_type());
+		      if (first)
+			first = false;
+		      else
+			exp->write_c_string(", ");
+		      if (!is_varargs || pp + 1 != parameters->end())
+			exp->write_type(pp->type());
+		      else
+			{
+			  exp->write_c_string("...");
+			  Type *pptype = pp->type();
+			  exp->write_type(pptype->array_type()->element_type());
+			}
 		    }
 		}
-	    }
 
-	  exp->write_c_string(")");
+	      exp->write_c_string(")");
 
-	  const Typed_identifier_list* results = fntype->results();
-	  if (results != NULL)
-	    {
-	      exp->write_c_string(" ");
-	      if (results->size() == 1)
-		exp->write_type(results->begin()->type());
-	      else
+	      const Typed_identifier_list* results = fntype->results();
+	      if (results != NULL)
 		{
-		  first = true;
-		  exp->write_c_string("(");
-		  for (Typed_identifier_list::const_iterator p =
-			 results->begin();
-		       p != results->end();
-		       ++p)
+		  exp->write_c_string(" ");
+		  if (results->size() == 1)
+		    exp->write_type(results->begin()->type());
+		  else
 		    {
-		      if (first)
-			first = false;
-		      else
-			exp->write_c_string(", ");
-		      exp->write_type(p->type());
+		      first = true;
+		      exp->write_c_string("(");
+		      for (Typed_identifier_list::const_iterator p =
+			     results->begin();
+			   p != results->end();
+			   ++p)
+			{
+			  if (first)
+			    first = false;
+			  else
+			    exp->write_c_string(", ");
+			  exp->write_type(p->type());
+			}
+		      exp->write_c_string(")");
 		    }
-		  exp->write_c_string(")");
 		}
 	    }
 
@@ -6925,6 +6961,16 @@  Interface_type::do_import(Import* imp)
   while (imp->peek_char() != '}')
     {
       std::string name = imp->read_identifier();
+
+      if (name == "$")
+	{
+	  imp->require_c_string(" ");
+	  Type* t = imp->read_type();
+	  methods->push_back(Typed_identifier("", t, imp->location()));
+	  imp->require_c_string("; ");
+	  continue;
+	}
+
       imp->require_c_string(" (");
 
       Typed_identifier_list* parameters;
@@ -7014,6 +7060,16 @@  Type::make_interface_type(Typed_identifi
   return new Interface_type(methods, location);
 }
 
+// Make an empty interface type.
+
+Interface_type*
+Type::make_empty_interface_type(Location location)
+{
+  Interface_type* ret = new Interface_type(NULL, location);
+  ret->finalize_methods();
+  return ret;
+}
+
 // Class Method.
 
 // Bind a method to an object.
Index: gcc/go/gofrontend/expressions.cc
===================================================================
--- gcc/go/gofrontend/expressions.cc	(revision 183280)
+++ gcc/go/gofrontend/expressions.cc	(working copy)
@@ -7566,7 +7566,7 @@  Builtin_call_expression::do_lower(Gogo* 
 	{
 	  // Calling recover outside of a function always returns the
 	  // nil empty interface.
-	  Type* eface = Type::make_interface_type(NULL, loc);
+	  Type* eface = Type::make_empty_interface_type(loc);
 	  return Expression::make_cast(eface, Expression::make_nil(loc), loc);
 	}
       break;
@@ -8189,7 +8189,7 @@  Builtin_call_expression::do_type()
       return Type::make_void_type();
 
     case BUILTIN_RECOVER:
-      return Type::make_interface_type(NULL, Linemap::predeclared_location());
+      return Type::make_empty_interface_type(Linemap::predeclared_location());
 
     case BUILTIN_APPEND:
       {
@@ -8883,7 +8883,7 @@  Builtin_call_expression::do_get_tree(Tra
 	if (arg_tree == error_mark_node)
 	  return error_mark_node;
 	Type *empty =
-	  Type::make_interface_type(NULL, Linemap::predeclared_location());
+	  Type::make_empty_interface_type(Linemap::predeclared_location());
 	arg_tree = Expression::convert_for_assignment(context, empty,
 						      arg->type(),
 						      arg_tree, location);
@@ -8916,7 +8916,7 @@  Builtin_call_expression::do_get_tree(Tra
 	  return error_mark_node;
 
 	Type *empty =
-	  Type::make_interface_type(NULL, Linemap::predeclared_location());
+	  Type::make_empty_interface_type(Linemap::predeclared_location());
 	tree empty_tree = type_to_tree(empty->get_backend(context->gogo()));
 
 	Type* nil_type = Type::make_nil_type();
Index: gcc/testsuite/go.test/test/fixedbugs/bug251.go
===================================================================
--- gcc/testsuite/go.test/test/fixedbugs/bug251.go	(revision 183280)
+++ gcc/testsuite/go.test/test/fixedbugs/bug251.go	(working copy)
@@ -12,7 +12,7 @@  type I1 interface {
 }
 
 type I2 interface {
-	I1 // GC_ERROR "loop|interface"
+	I1 // ERROR "loop|interface"
 }
 
 
Index: gcc/testsuite/go.test/test/fixedbugs/bug195.go
===================================================================
--- gcc/testsuite/go.test/test/fixedbugs/bug195.go	(revision 183280)
+++ gcc/testsuite/go.test/test/fixedbugs/bug195.go	(working copy)
@@ -23,5 +23,5 @@  type I5 interface {
 }
 
 type I6 interface {
-	I5	// GC_ERROR "interface"
+	I5	// ERROR "interface"
 }