This patch to the Go frontend improves the handling of erroneous
function signatures, avoiding knock-on errors in various different ways.
It has no effect on correct programs.  Bootstrapped and ran Go testsuite
on x86_64-unknown-linux-gnu.  Committed to mainline.

Ian

Index: gcc/go/gofrontend/gogo.cc
===================================================================
--- gcc/go/gofrontend/gogo.cc	(revision 183458)
+++ gcc/go/gofrontend/gogo.cc	(working copy)
@@ -626,8 +626,8 @@ Gogo::start_function(const std::string& 
       const Typed_identifier* receiver = type->receiver();
       Variable* this_param = new Variable(receiver->type(), NULL, false,
 					  true, true, location);
-      std::string name = receiver->name();
-      if (name.empty())
+      std::string rname = receiver->name();
+      if (rname.empty())
 	{
 	  // We need to give receivers a name since they wind up in
 	  // DECL_ARGUMENTS.  FIXME.
@@ -635,10 +635,10 @@ Gogo::start_function(const std::string& 
 	  char buf[50];
 	  snprintf(buf, sizeof buf, "r.%u", count);
 	  ++count;
-	  name = buf;
+	  rname = buf;
 	}
-      if (!Gogo::is_sink_name(name))
-	block->bindings()->add_variable(name, NULL, this_param);
+      if (!Gogo::is_sink_name(rname))
+	block->bindings()->add_variable(rname, NULL, this_param);
     }
 
   const Typed_identifier_list* parameters = type->parameters();
@@ -654,8 +654,8 @@ Gogo::start_function(const std::string& 
 	  if (is_varargs && p + 1 == parameters->end())
 	    param->set_is_varargs_parameter();
 
-	  std::string name = p->name();
-	  if (name.empty() || Gogo::is_sink_name(name))
+	  std::string pname = p->name();
+	  if (pname.empty() || Gogo::is_sink_name(pname))
 	    {
 	      // We need to give parameters a name since they wind up
 	      // in DECL_ARGUMENTS.  FIXME.
@@ -663,9 +663,9 @@ Gogo::start_function(const std::string& 
 	      char buf[50];
 	      snprintf(buf, sizeof buf, "p.%u", count);
 	      ++count;
-	      name = buf;
+	      pname = buf;
 	    }
-	  block->bindings()->add_variable(name, NULL, param);
+	  block->bindings()->add_variable(pname, NULL, param);
 	}
     }
 
@@ -834,6 +834,14 @@ Gogo::finish_block(Location location)
   return block;
 }
 
+// Add an erroneous name.
+
+Named_object*
+Gogo::add_erroneous_name(const std::string& name)
+{
+  return this->package_->bindings()->add_erroneous_name(name);
+}
+
 // Add an unknown name.
 
 Named_object*
@@ -3522,6 +3530,7 @@ Block::traverse(Traverse* traverse)
 
 	    case Named_object::NAMED_OBJECT_TYPE_DECLARATION:
 	    case Named_object::NAMED_OBJECT_UNKNOWN:
+	    case Named_object::NAMED_OBJECT_ERRONEOUS:
 	      break;
 
 	    case Named_object::NAMED_OBJECT_PACKAGE:
@@ -4521,6 +4530,9 @@ Named_object::location() const
     case NAMED_OBJECT_UNINITIALIZED:
       go_unreachable();
 
+    case NAMED_OBJECT_ERRONEOUS:
+      return Linemap::unknown_location();
+
     case NAMED_OBJECT_UNKNOWN:
       return this->unknown_value()->location();
 
@@ -4565,6 +4577,9 @@ Named_object::export_named_object(Export
     case NAMED_OBJECT_UNKNOWN:
       go_unreachable();
 
+    case NAMED_OBJECT_ERRONEOUS:
+      break;
+
     case NAMED_OBJECT_CONST:
       this->const_value()->export_const(exp, this->name_);
       break;
@@ -4751,6 +4766,9 @@ Bindings::add_named_object_to_contour(Co
 Named_object*
 Bindings::new_definition(Named_object* old_object, Named_object* new_object)
 {
+  if (new_object->is_erroneous() && !old_object->is_erroneous())
+    return new_object;
+
   std::string reason;
   switch (old_object->classification())
     {
@@ -4758,6 +4776,9 @@ Bindings::new_definition(Named_object* o
     case Named_object::NAMED_OBJECT_UNINITIALIZED:
       go_unreachable();
 
+    case Named_object::NAMED_OBJECT_ERRONEOUS:
+      return old_object;
+
     case Named_object::NAMED_OBJECT_UNKNOWN:
       {
 	Named_object* real = old_object->unknown_value()->real_named_object();
@@ -5003,6 +5024,7 @@ Bindings::traverse(Traverse* traverse, b
 	case Named_object::NAMED_OBJECT_TYPE_DECLARATION:
 	case Named_object::NAMED_OBJECT_FUNC_DECLARATION:
 	case Named_object::NAMED_OBJECT_UNKNOWN:
+	case Named_object::NAMED_OBJECT_ERRONEOUS:
 	  break;
 
 	case Named_object::NAMED_OBJECT_SINK:
Index: gcc/go/gofrontend/gogo.h
===================================================================
--- gcc/go/gofrontend/gogo.h	(revision 183458)
+++ gcc/go/gofrontend/gogo.h	(working copy)
@@ -267,6 +267,11 @@ class Gogo
   Block*
   finish_block(Location);
 
+  // Declare an erroneous name.  This is used to avoid knock-on errors
+  // after a parsing error.
+  Named_object*
+  add_erroneous_name(const std::string& name);
+
   // Declare an unknown name.  This is used while parsing.  The name
   // must be resolved by the end of the parse.  Unknown names are
   // always added at the package level.
@@ -1688,6 +1693,9 @@ class Named_object
   {
     // An uninitialized Named_object.  We should never see this.
     NAMED_OBJECT_UNINITIALIZED,
+    // An erroneous name.  This indicates a parse error, to avoid
+    // later errors about undefined references.
+    NAMED_OBJECT_ERRONEOUS,
     // An unknown name.  This is used for forward references.  In a
     // correct program, these will all be resolved by the end of the
     // parse.
@@ -1720,6 +1728,10 @@ class Named_object
   // Classifiers.
 
   bool
+  is_erroneous() const
+  { return this->classification_ == NAMED_OBJECT_ERRONEOUS; }
+
+  bool
   is_unknown() const
   { return this->classification_ == NAMED_OBJECT_UNKNOWN; }
 
@@ -1762,6 +1774,10 @@ class Named_object
   // Creators.
 
   static Named_object*
+  make_erroneous_name(const std::string& name)
+  { return new Named_object(name, NULL, NAMED_OBJECT_ERRONEOUS); }
+
+  static Named_object*
   make_unknown_name(const std::string& name, Location);
 
   static Named_object*
@@ -2032,6 +2048,11 @@ class Bindings
 
   Bindings(Bindings* enclosing);
 
+  // Add an erroneous name.
+  Named_object*
+  add_erroneous_name(const std::string& name)
+  { return this->add_named_object(Named_object::make_erroneous_name(name)); }
+
   // Add an unknown name.
   Named_object*
   add_unknown_name(const std::string& name, Location location)
Index: gcc/go/gofrontend/parse.cc
===================================================================
--- gcc/go/gofrontend/parse.cc	(revision 183458)
+++ gcc/go/gofrontend/parse.cc	(working copy)
@@ -45,6 +45,7 @@ Parse::Parse(Lex* lex, Gogo* gogo)
     token_(Token::make_invalid_token(Linemap::unknown_location())),
     unget_token_(Token::make_invalid_token(Linemap::unknown_location())),
     unget_token_valid_(false),
+    is_erroneous_function_(false),
     gogo_(gogo),
     break_stack_(NULL),
     continue_stack_(NULL),
@@ -2123,8 +2124,6 @@ Parse::function_decl()
   this->advance_token();
 
   Function_type* fntype = this->signature(rec, this->location());
-  if (fntype == NULL)
-    return;
 
   Named_object* named_object = NULL;
 
@@ -2171,13 +2170,28 @@ Parse::function_decl()
   if (!this->peek_token()->is_op(OPERATOR_LCURLY))
     {
       if (named_object == NULL && !Gogo::is_sink_name(name))
-	this->gogo_->declare_function(name, fntype, location);
+	{
+	  if (fntype != NULL)
+	    this->gogo_->declare_function(name, fntype, location);
+	  else
+	    this->gogo_->add_erroneous_name(name);
+	}
     }
   else
     {
+      bool hold_is_erroneous_function = this->is_erroneous_function_;
+      if (fntype == NULL)
+	{
+	  fntype = Type::make_function_type(NULL, NULL, NULL, location);
+	  this->is_erroneous_function_ = true;
+	  if (!Gogo::is_sink_name(name))
+	    this->gogo_->add_erroneous_name(name);
+	  name = this->gogo_->pack_hidden_name("_", false);
+	}
       this->gogo_->start_function(name, fntype, true, location);
       Location end_loc = this->block();
       this->gogo_->finish_function(end_loc);
+      this->is_erroneous_function_ = hold_is_erroneous_function;
     }
 }
 
@@ -2392,7 +2406,15 @@ Parse::operand(bool may_be_sink)
 	    return Expression::make_func_reference(named_object, NULL,
 						   location);
 	  case Named_object::NAMED_OBJECT_UNKNOWN:
-	    return Expression::make_unknown_reference(named_object, location);
+	    {
+	      Unknown_expression* ue =
+		Expression::make_unknown_reference(named_object, location);
+	      if (this->is_erroneous_function_)
+		ue->set_no_error_message();
+	      return ue;
+	    }
+	  case Named_object::NAMED_OBJECT_ERRONEOUS:
+	    return Expression::make_error(location);
 	  default:
 	    go_unreachable();
 	  }
@@ -2698,14 +2720,22 @@ Parse::function_lit()
   hold_enclosing_vars.swap(this->enclosing_vars_);
 
   Function_type* type = this->signature(NULL, location);
+  bool fntype_is_error = false;
   if (type == NULL)
-    type = Type::make_function_type(NULL, NULL, NULL, location);
+    {
+      type = Type::make_function_type(NULL, NULL, NULL, location);
+      fntype_is_error = true;
+    }
 
   // For a function literal, the next token must be a '{'.  If we
   // don't see that, then we may have a type expression.
   if (!this->peek_token()->is_op(OPERATOR_LCURLY))
     return Expression::make_type(type, location);
 
+  bool hold_is_erroneous_function = this->is_erroneous_function_;
+  if (fntype_is_error)
+    this->is_erroneous_function_ = true;
+
   Bc_stack* hold_break_stack = this->break_stack_;
   Bc_stack* hold_continue_stack = this->continue_stack_;
   this->break_stack_ = NULL;
@@ -2724,6 +2754,8 @@ Parse::function_lit()
   this->break_stack_ = hold_break_stack;
   this->continue_stack_ = hold_continue_stack;
 
+  this->is_erroneous_function_ = hold_is_erroneous_function;
+
   hold_enclosing_vars.swap(this->enclosing_vars_);
 
   Expression* closure = this->create_closure(no, &hold_enclosing_vars,
@@ -3043,13 +3075,27 @@ Parse::id_to_expression(const std::strin
     case Named_object::NAMED_OBJECT_FUNC_DECLARATION:
       return Expression::make_func_reference(named_object, NULL, location);
     case Named_object::NAMED_OBJECT_UNKNOWN:
-      return Expression::make_unknown_reference(named_object, location);
+      {
+	Unknown_expression* ue =
+	  Expression::make_unknown_reference(named_object, location);
+	if (this->is_erroneous_function_)
+	  ue->set_no_error_message();
+	return ue;
+      }
     case Named_object::NAMED_OBJECT_PACKAGE:
     case Named_object::NAMED_OBJECT_TYPE:
     case Named_object::NAMED_OBJECT_TYPE_DECLARATION:
-      // These cases can arise for a field name in a composite
-      // literal.
-      return Expression::make_unknown_reference(named_object, location);
+      {
+	// These cases can arise for a field name in a composite
+	// literal.
+	Unknown_expression* ue =
+	  Expression::make_unknown_reference(named_object, location);
+	if (this->is_erroneous_function_)
+	  ue->set_no_error_message();
+	return ue;
+      }
+    case Named_object::NAMED_OBJECT_ERRONEOUS:
+      return Expression::make_error(location);
     default:
       error_at(this->location(), "unexpected type of identifier");
       return Expression::make_error(location);
Index: gcc/go/gofrontend/expressions.h
===================================================================
--- gcc/go/gofrontend/expressions.h	(revision 183374)
+++ gcc/go/gofrontend/expressions.h	(working copy)
@@ -153,7 +153,7 @@ class Expression
 
   // Make a reference to an unknown name.  In a correct program this
   // will always be lowered to a real const/var/func reference.
-  static Expression*
+  static Unknown_expression*
   make_unknown_reference(Named_object*, Location);
 
   // Make a constant bool expression.
@@ -1554,7 +1554,8 @@ class Unknown_expression : public Parser
  public:
   Unknown_expression(Named_object* named_object, Location location)
     : Parser_expression(EXPRESSION_UNKNOWN_REFERENCE, location),
-      named_object_(named_object), is_composite_literal_key_(false)
+      named_object_(named_object), no_error_message_(false),
+      is_composite_literal_key_(false)
   { }
 
   // The associated named object.
@@ -1566,6 +1567,13 @@ class Unknown_expression : public Parser
   const std::string&
   name() const;
 
+  // Call this to indicate that we should not give an error if this
+  // name is never defined.  This is used to avoid knock-on errors
+  // during an erroneous parse.
+  void
+  set_no_error_message()
+  { this->no_error_message_ = true; }
+
   // Note that this expression is being used as the key in a composite
   // literal, so it may be OK if it is not resolved.
   void
@@ -1592,6 +1600,9 @@ class Unknown_expression : public Parser
  private:
   // The unknown name.
   Named_object* named_object_;
+  // True if we should not give errors if this is undefined.  This is
+  // used if there was a parse failure.
+  bool no_error_message_;
   // True if this is the key in a composite literal.
   bool is_composite_literal_key_;
 };
Index: gcc/go/gofrontend/gogo-tree.cc
===================================================================
--- gcc/go/gofrontend/gogo-tree.cc	(revision 183280)
+++ gcc/go/gofrontend/gogo-tree.cc	(working copy)
@@ -1108,6 +1108,10 @@ Named_object::get_tree(Gogo* gogo, Named
       }
       break;
 
+    case NAMED_OBJECT_ERRONEOUS:
+      decl = error_mark_node;
+      break;
+
     default:
       go_unreachable();
     }
Index: gcc/go/gofrontend/parse.h
===================================================================
--- gcc/go/gofrontend/parse.h	(revision 183458)
+++ gcc/go/gofrontend/parse.h	(working copy)
@@ -305,6 +305,8 @@ class Parse
   Token unget_token_;
   // Whether unget_token_ is valid.
   bool unget_token_valid_;
+  // Whether the function we are parsing had errors in the signature.
+  bool is_erroneous_function_;
   // The code we are generating.
   Gogo* gogo_;
   // A stack of statements for which break may be used.
Index: gcc/go/gofrontend/expressions.cc
===================================================================
--- gcc/go/gofrontend/expressions.cc	(revision 183466)
+++ gcc/go/gofrontend/expressions.cc	(working copy)
@@ -1455,8 +1455,9 @@ Unknown_expression::do_lower(Gogo*, Name
 	{
 	  if (this->is_composite_literal_key_)
 	    return this;
-	  error_at(location, "reference to undefined name %qs",
-		   this->named_object_->message_name().c_str());
+	  if (!this->no_error_message_)
+	    error_at(location, "reference to undefined name %qs",
+		     this->named_object_->message_name().c_str());
 	  return Expression::make_error(location);
 	}
     }
@@ -1469,8 +1470,9 @@ Unknown_expression::do_lower(Gogo*, Name
     case Named_object::NAMED_OBJECT_TYPE_DECLARATION:
       if (this->is_composite_literal_key_)
 	return this;
-      error_at(location, "reference to undefined type %qs",
-	       real->message_name().c_str());
+      if (!this->no_error_message_)
+	error_at(location, "reference to undefined type %qs",
+		 real->message_name().c_str());
       return Expression::make_error(location);
     case Named_object::NAMED_OBJECT_VAR:
       real->var_value()->set_is_used();
@@ -1481,7 +1483,8 @@ Unknown_expression::do_lower(Gogo*, Name
     case Named_object::NAMED_OBJECT_PACKAGE:
       if (this->is_composite_literal_key_)
 	return this;
-      error_at(location, "unexpected reference to package");
+      if (!this->no_error_message_)
+	error_at(location, "unexpected reference to package");
       return Expression::make_error(location);
     default:
       go_unreachable();
@@ -1499,7 +1502,7 @@ Unknown_expression::do_dump_expression(A
 
 // Make a reference to an unknown name.
 
-Expression*
+Unknown_expression*
 Expression::make_unknown_reference(Named_object* no, Location location)
 {
   return new Unknown_expression(no, location);
@@ -8483,6 +8486,11 @@ Builtin_call_expression::do_check_types(
 		    || type->function_type() != NULL
 		    || type->is_slice_type())
 		  ;
+		else if ((*p)->is_type_expression())
+		  {
+		    // If this is a type expression it's going to give
+		    // an error anyhow, so we don't need one here.
+		  }
 		else
 		  this->report_error(_("unsupported argument type to "
 				       "builtin function"));

Reply via email to