Patchwork Go patch committed: Fix parse of (<- chan <- chan <- int)(x)

login
register
mail settings
Submitter Ian Taylor
Date Oct. 2, 2012, 10:22 p.m.
Message ID <mcrmx04y0nj.fsf@google.com>
Download mbox | patch
Permalink /patch/188664/
State New
Headers show

Comments

Ian Taylor - Oct. 2, 2012, 10:22 p.m.
http://code.google.com/p/go/issues/detail?id=4110 points out that the Go
frontend is misparsing some channel operations.  The parsing in this
area is rather complex for a recursive descent parser (though
straightforward for an LALR parser).  In any case, this patch fixes the
problem.  Bootstrapped and ran Go testsuite on x86_64-unknown-linux-gnu.
Committed to mainline and 4.7 branch.

Ian

Patch

diff -r 0bdf9403f58e go/parse.cc
--- a/go/parse.cc	Fri Sep 28 15:19:18 2012 -0700
+++ b/go/parse.cc	Tue Oct 02 15:17:06 2012 -0700
@@ -3315,6 +3315,61 @@ 
 		  bool* is_type_switch)
 {
   const Token* token = this->peek_token();
+
+  // There is a complex parse for <- chan.  The choices are
+  // Convert x to type <- chan int:
+  //   (<- chan int)(x)         
+  // Receive from (x converted to type chan <- chan int):
+  //   (<- chan <- chan int (x))
+  // Convert x to type <- chan (<- chan int).
+  //   (<- chan <- chan int)(x)
+  if (token->is_op(OPERATOR_CHANOP))
+    {
+      Location location = token->location();
+      if (this->advance_token()->is_keyword(KEYWORD_CHAN))
+	{
+	  Expression* expr = this->primary_expr(false, may_be_composite_lit,
+						NULL);
+	  if (expr->is_error_expression())
+	    return expr;
+	  else if (!expr->is_type_expression())
+	    return Expression::make_receive(expr, location);
+	  else
+	    {
+	      if (expr->type()->is_error_type())
+		return expr;
+
+	      // We picked up "chan TYPE", but it is not a type
+	      // conversion.
+	      Channel_type* ct = expr->type()->channel_type();
+	      if (ct == NULL)
+		{
+		  // This is probably impossible.
+		  error_at(location, "expected channel type");
+		  return Expression::make_error(location);
+		}
+	      else if (ct->may_receive())
+		{
+		  // <- chan TYPE.
+		  Type* t = Type::make_channel_type(false, true,
+						    ct->element_type());
+		  return Expression::make_type(t, location);
+		}
+	      else
+		{
+		  // <- chan <- TYPE.  Because we skipped the leading
+		  // <-, we parsed this as chan <- TYPE.  With the
+		  // leading <-, we parse it as <- chan (<- TYPE).
+		  Type *t = this->reassociate_chan_direction(ct, location);
+		  return Expression::make_type(t, location);
+		}
+	    }
+	}
+
+      this->unget_token(Token::make_operator_token(OPERATOR_CHANOP, location));
+      token = this->peek_token();
+    }
+
   if (token->is_op(OPERATOR_PLUS)
       || token->is_op(OPERATOR_MINUS)
       || token->is_op(OPERATOR_NOT)
@@ -3327,14 +3382,6 @@ 
       Operator op = token->op();
       this->advance_token();
 
-      if (op == OPERATOR_CHANOP
-	  && this->peek_token()->is_keyword(KEYWORD_CHAN))
-	{
-	  // This is "<- chan" which must be the start of a type.
-	  this->unget_token(Token::make_operator_token(op, location));
-	  return Expression::make_type(this->type(), location);
-	}
-
       Expression* expr = this->unary_expr(false, may_be_composite_lit, NULL);
       if (expr->is_error_expression())
 	;
@@ -3354,6 +3401,32 @@ 
 			      is_type_switch);
 }
 
+// This is called for the obscure case of
+//   (<- chan <- chan int)(x)
+// In unary_expr we remove the leading <- and parse the remainder,
+// which gives us
+//   chan <- (chan int)
+// When we add the leading <- back in, we really want
+//   <- chan (<- chan int)
+// This means that we need to reassociate.
+
+Type*
+Parse::reassociate_chan_direction(Channel_type *ct, Location location)
+{
+  Channel_type* ele = ct->element_type()->channel_type();
+  if (ele == NULL)
+    {
+      error_at(location, "parse error");
+      return Type::make_error_type();
+    }
+  Type* sub = ele;
+  if (ele->may_send())
+    sub = Type::make_channel_type(false, true, ele->element_type());
+  else
+    sub = this->reassociate_chan_direction(ele, location);
+  return Type::make_channel_type(false, true, sub);
+}
+
 // Statement =
 //	Declaration | LabeledStmt | SimpleStmt |
 //	GoStmt | ReturnStmt | BreakStmt | ContinueStmt | GotoStmt |
diff -r 0bdf9403f58e go/parse.h
--- a/go/parse.h	Fri Sep 28 15:19:18 2012 -0700
+++ b/go/parse.h	Tue Oct 02 15:17:06 2012 -0700
@@ -14,6 +14,7 @@ 
 class Type;
 class Typed_identifier;
 class Typed_identifier_list;
+class Channel_type;
 class Function_type;
 class Block;
 class Expression;
@@ -229,6 +230,7 @@ 
   bool expression_may_start_here();
   Expression* unary_expr(bool may_be_sink, bool may_be_composite_lit,
 			 bool* is_type_switch);
+  Type* reassociate_chan_direction(Channel_type*, Location);
   Expression* qualified_expr(Expression*, Location);
   Expression* id_to_expression(const std::string&, Location);
   void statement(Label*);