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
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*);