Patchwork [gccgo] Don't crash on argument count mismatch

login
register
mail settings
Submitter Ian Taylor
Date July 3, 2010, 6:05 a.m.
Message ID <mcrwrtdgkxf.fsf@google.com>
Download mbox | patch
Permalink /patch/57792/
State New
Headers show

Comments

Ian Taylor - July 3, 2010, 6:05 a.m.
Code like
	a, b := f()
would crash the compiler when f returns a single result, rather than
issuing a sensible error message.  This patch fixes that.  Committed to
gccgo branch.

Ian

Patch

diff -r 19486205521b go/expressions.cc
--- a/go/expressions.cc	Fri Jul 02 12:59:20 2010 -0700
+++ b/go/expressions.cc	Fri Jul 02 22:34:05 2010 -0700
@@ -8614,6 +8614,9 @@ 
   void
   do_determine_type(const Type_context*);
 
+  void
+  do_check_types(Gogo*);
+
   Expression*
   do_copy()
   {
@@ -8661,23 +8664,50 @@ 
 Type*
 Call_result_expression::do_type()
 {
-  if (this->call_->is_error_expression())
+  // THIS->CALL_ can be replaced with a temporary reference due to
+  // Call_expression::do_must_eval_in_order when there is an error.
+  Call_expression* ce = this->call_->call_expression();
+  if (ce == NULL)
     return Type::make_error_type();
-  Function_type* fntype =
-    this->call_->call_expression()->get_function_type();
+  Function_type* fntype = ce->get_function_type();
   if (fntype == NULL)
     return Type::make_error_type();
   const Typed_identifier_list* results = fntype->results();
   Typed_identifier_list::const_iterator pr = results->begin();
   for (unsigned int i = 0; i < this->index_; ++i)
     {
-      gcc_assert(pr != results->end());
+      if (pr == results->end())
+	return Type::make_error_type();
       ++pr;
     }
-  gcc_assert(pr != results->end());
+  if (pr == results->end())
+    return Type::make_error_type();
   return pr->type();
 }
 
+// Check the type.  This is where we give an error if we're trying to
+// extract too many values from a call.
+
+void
+Call_result_expression::do_check_types(Gogo*)
+{
+  bool ok = true;
+  Call_expression* ce = this->call_->call_expression();
+  if (ce != NULL)
+    ok = this->index_ < ce->result_count();
+  else
+    {
+      // This can happen when the call returns a single value but we
+      // are asking for the second result.
+      if (this->call_->is_error_expression())
+	return;
+      ok = false;
+    }
+  if (!ok)
+    error_at(this->location(),
+	     "number of results does not match number of values");
+}
+
 // Determine the type.  We have nothing to do here, but the 0 result
 // needs to pass down to the caller.
 
diff -r 19486205521b go/statements.cc
--- a/go/statements.cc	Fri Jul 02 12:59:20 2010 -0700
+++ b/go/statements.cc	Fri Jul 02 22:34:05 2010 -0700
@@ -358,7 +358,10 @@ 
   gcc_assert(this->decl_ == NULL_TREE);
   tree type_tree = this->type()->get_tree(context->gogo());
   if (type_tree == error_mark_node)
-    return error_mark_node;
+    {
+      this->decl_ = error_mark_node;
+      return error_mark_node;
+    }
   // We can only use create_tmp_var if the type is not addressable.
   if (!TREE_ADDRESSABLE(type_tree))
     {