// If we get here it means that a single nested function refers to
// two different variables defined in enclosing functions, and both
// variables have the same name. I think this is impossible.
- gcc_unreachable();
+ go_unreachable();
}
// Class Parse.
unget_token_(Token::make_invalid_token(0)),
unget_token_valid_(false),
gogo_(gogo),
- break_stack_(),
- continue_stack_(),
+ break_stack_(NULL),
+ continue_stack_(NULL),
iota_(0),
enclosing_vars_()
{
void
Parse::unget_token(const Token& token)
{
- gcc_assert(!this->unget_token_valid_);
+ go_assert(!this->unget_token_valid_);
this->unget_token_ = token;
this->unget_token_valid_ = true;
}
bool ok = true;
if (named_object == NULL)
{
- if (package != NULL)
- ok = false;
- else
+ if (package == NULL)
named_object = this->gogo_->add_unknown_name(name, location);
+ else
+ {
+ const std::string& packname(package->package_value()->name());
+ error_at(location, "reference to undefined identifier %<%s.%s%>",
+ Gogo::message_name(packname).c_str(),
+ Gogo::message_name(name).c_str());
+ issue_error = false;
+ ok = false;
+ }
}
else if (named_object->is_type())
{
else if (named_object->is_unknown() || named_object->is_type_declaration())
return Type::make_forward_declaration(named_object);
else
- gcc_unreachable();
+ go_unreachable();
}
// ArrayType = "[" [ ArrayLength ] "]" ElementType .
Type*
Parse::array_type(bool may_use_ellipsis)
{
- gcc_assert(this->peek_token()->is_op(OPERATOR_LSQUARE));
+ go_assert(this->peek_token()->is_op(OPERATOR_LSQUARE));
const Token* token = this->advance_token();
Expression* length = NULL;
Parse::map_type()
{
source_location location = this->location();
- gcc_assert(this->peek_token()->is_keyword(KEYWORD_MAP));
+ go_assert(this->peek_token()->is_keyword(KEYWORD_MAP));
if (!this->advance_token()->is_op(OPERATOR_LSQUARE))
{
error_at(this->location(), "expected %<[%>");
Type*
Parse::struct_type()
{
- gcc_assert(this->peek_token()->is_keyword(KEYWORD_STRUCT));
+ go_assert(this->peek_token()->is_keyword(KEYWORD_STRUCT));
source_location location = this->location();
if (!this->advance_token()->is_op(OPERATOR_LCURLY))
{
Type*
Parse::pointer_type()
{
- gcc_assert(this->peek_token()->is_op(OPERATOR_MULT));
+ go_assert(this->peek_token()->is_op(OPERATOR_MULT));
this->advance_token();
Type* type = this->type();
if (type->is_error_type())
}
else
{
- gcc_assert(token->is_keyword(KEYWORD_CHAN));
+ go_assert(token->is_keyword(KEYWORD_CHAN));
if (this->advance_token()->is_op(OPERATOR_CHANOP))
{
receive = false;
this->advance_token();
}
}
+
+ // Better error messages for the common error of omitting the
+ // channel element type.
+ if (!this->type_may_start_here())
+ {
+ token = this->peek_token();
+ if (token->is_op(OPERATOR_RCURLY))
+ error_at(this->location(), "unexpected %<}%> in channel type");
+ else if (token->is_op(OPERATOR_RPAREN))
+ error_at(this->location(), "unexpected %<)%> in channel type");
+ else if (token->is_op(OPERATOR_COMMA))
+ error_at(this->location(), "unexpected comma in channel type");
+ else
+ error_at(this->location(), "expected channel element type");
+ return Type::make_error_type();
+ }
+
Type* element_type = this->type();
return Type::make_channel_type(send, receive, element_type);
}
+// Give an error for a duplicate parameter or receiver name.
+
+void
+Parse::check_signature_names(const Typed_identifier_list* params,
+ Parse::Names* names)
+{
+ for (Typed_identifier_list::const_iterator p = params->begin();
+ p != params->end();
+ ++p)
+ {
+ if (p->name().empty() || Gogo::is_sink_name(p->name()))
+ continue;
+ std::pair<std::string, const Typed_identifier*> val =
+ std::make_pair(p->name(), &*p);
+ std::pair<Parse::Names::iterator, bool> ins = names->insert(val);
+ if (!ins.second)
+ {
+ error_at(p->location(), "redefinition of %qs",
+ Gogo::message_name(p->name()).c_str());
+ inform(ins.first->second->location(),
+ "previous definition of %qs was here",
+ Gogo::message_name(p->name()).c_str());
+ }
+ }
+}
+
// Signature = Parameters [ Result ] .
// RECEIVER is the receiver if there is one, or NULL. LOCATION is the
Typed_identifier_list* params;
bool params_ok = this->parameters(¶ms, &is_varargs);
- Typed_identifier_list* result = NULL;
+ Typed_identifier_list* results = NULL;
if (this->peek_token()->is_op(OPERATOR_LPAREN)
|| this->type_may_start_here())
{
- if (!this->result(&result))
+ if (!this->result(&results))
return NULL;
}
if (!params_ok)
return NULL;
- Function_type* ret = Type::make_function_type(receiver, params, result,
+ Parse::Names names;
+ if (params != NULL)
+ this->check_signature_names(params, &names);
+ if (results != NULL)
+ this->check_signature_names(results, &names);
+
+ Function_type* ret = Type::make_function_type(receiver, params, results,
location);
if (is_varargs)
ret->set_is_varargs();
if (parameters_have_names)
{
- gcc_assert(!just_saw_comma);
+ go_assert(!just_saw_comma);
// We have just seen ID1, ID2 xxx.
Type* type;
if (!this->peek_token()->is_op(OPERATOR_ELLIPSIS))
Type*
Parse::interface_type()
{
- gcc_assert(this->peek_token()->is_keyword(KEYWORD_INTERFACE));
+ go_assert(this->peek_token()->is_keyword(KEYWORD_INTERFACE));
source_location location = this->location();
if (!this->advance_token()->is_op(OPERATOR_LCURLY))
void
Parse::const_decl()
{
- gcc_assert(this->peek_token()->is_keyword(KEYWORD_CONST));
+ go_assert(this->peek_token()->is_keyword(KEYWORD_CONST));
this->advance_token();
this->reset_iota();
void
Parse::type_decl()
{
- gcc_assert(this->peek_token()->is_keyword(KEYWORD_TYPE));
+ go_assert(this->peek_token()->is_keyword(KEYWORD_TYPE));
this->advance_token();
this->decl(&Parse::type_spec, NULL);
}
this->gogo_->define_type(named_type,
Type::make_named_type(named_type, type,
location));
- gcc_assert(named_type->package() == NULL);
+ go_assert(named_type->package() == NULL);
}
else
{
void
Parse::var_decl()
{
- gcc_assert(this->peek_token()->is_keyword(KEYWORD_VAR));
+ go_assert(this->peek_token()->is_keyword(KEYWORD_VAR));
this->advance_token();
this->decl(&Parse::var_spec, NULL);
}
++p)
{
if (init != NULL)
- gcc_assert(pexpr != init->end());
+ go_assert(pexpr != init->end());
this->init_var(*p, type, init == NULL ? NULL : *pexpr, is_coloneq,
false, &any_new);
if (init != NULL)
++pexpr;
}
if (init != NULL)
- gcc_assert(pexpr == init->end());
+ go_assert(pexpr == init->end());
if (is_coloneq && !any_new)
error_at(location, "variables redeclared but no variable is new");
}
else if (!val_no->is_sink())
{
if (val_no->is_variable())
- val_no->var_value()->add_preinit_statement(s);
+ val_no->var_value()->add_preinit_statement(this->gogo_, s);
}
else if (!no->is_sink())
{
if (no->is_variable())
- no->var_value()->add_preinit_statement(s);
+ no->var_value()->add_preinit_statement(this->gogo_, s);
}
else
{
// the map is nil.
Named_object* dummy = this->create_dummy_global(Type::lookup_bool_type(),
NULL, location);
- dummy->var_value()->add_preinit_statement(s);
+ dummy->var_value()->add_preinit_statement(this->gogo_, s);
}
return true;
Statement* s = Statement::make_tuple_receive_assignment(val_var,
received_var,
receive->channel(),
+ false,
location);
if (!this->gogo_->in_global_scope())
else if (!val_no->is_sink())
{
if (val_no->is_variable())
- val_no->var_value()->add_preinit_statement(s);
+ val_no->var_value()->add_preinit_statement(this->gogo_, s);
}
else if (!no->is_sink())
{
if (no->is_variable())
- no->var_value()->add_preinit_statement(s);
+ no->var_value()->add_preinit_statement(this->gogo_, s);
}
else
{
Named_object* dummy = this->create_dummy_global(Type::lookup_bool_type(),
NULL, location);
- dummy->var_value()->add_preinit_statement(s);
+ dummy->var_value()->add_preinit_statement(this->gogo_, s);
}
return true;
else if (!val_no->is_sink())
{
if (val_no->is_variable())
- val_no->var_value()->add_preinit_statement(s);
+ val_no->var_value()->add_preinit_statement(this->gogo_, s);
}
else if (!no->is_sink())
{
if (no->is_variable())
- no->var_value()->add_preinit_statement(s);
+ no->var_value()->add_preinit_statement(this->gogo_, s);
}
else
{
Named_object* dummy = this->create_dummy_global(type, NULL, location);
- dummy->var_value()->add_preinit_statement(s);
+ dummy->var_value()->add_preinit_statement(this->gogo_, s);
}
return true;
if (!type_from_init && init != NULL)
{
if (!this->gogo_->in_global_scope())
- this->gogo_->add_statement(Statement::make_statement(init));
+ this->gogo_->add_statement(Statement::make_statement(init, true));
else
return this->create_dummy_global(type, init, location);
}
*is_new = true;
Variable* var = new Variable(type, init, this->gogo_->in_global_scope(),
false, false, location);
- return this->gogo_->add_variable(tid.name(), var);
+ Named_object* no = this->gogo_->add_variable(tid.name(), var);
+ if (!no->is_variable())
+ {
+ // The name is already defined, so we just gave an error.
+ return this->gogo_->add_sink();
+ }
+ return no;
}
// Create a dummy global variable to force an initializer to be run in
Parse::create_dummy_global(Type* type, Expression* init,
source_location location)
{
+ if (type == NULL && init == NULL)
+ type = Type::lookup_bool_type();
Variable* var = new Variable(type, init, true, false, false, location);
static int count;
char buf[30];
// "a, *p = 1, 2".
if (this->peek_token()->is_op(OPERATOR_COMMA))
{
- gcc_assert(p_type_switch == NULL);
+ go_assert(p_type_switch == NULL);
while (true)
{
const Token* token = this->advance_token();
}
}
- gcc_assert(this->peek_token()->is_op(OPERATOR_COLONEQ));
+ go_assert(this->peek_token()->is_op(OPERATOR_COLONEQ));
const Token* token = this->advance_token();
if (p_range_clause != NULL && token->is_keyword(KEYWORD_RANGE))
void
Parse::function_decl()
{
- gcc_assert(this->peek_token()->is_keyword(KEYWORD_FUNC));
+ go_assert(this->peek_token()->is_keyword(KEYWORD_FUNC));
source_location location = this->location();
const Token* token = this->advance_token();
return;
}
this->advance_token();
- named_object = this->gogo_->declare_function(name, fntype, location);
- if (named_object->is_function_declaration())
- named_object->func_declaration_value()->set_asm_name(asm_name);
+ if (!Gogo::is_sink_name(name))
+ {
+ named_object = this->gogo_->declare_function(name, fntype, location);
+ if (named_object->is_function_declaration())
+ named_object->func_declaration_value()->set_asm_name(asm_name);
+ }
}
// Check for the easy error of a newline before the opening brace.
if (!this->peek_token()->is_op(OPERATOR_LCURLY))
{
- if (named_object == NULL)
- named_object = this->gogo_->declare_function(name, fntype, location);
+ if (named_object == NULL && !Gogo::is_sink_name(name))
+ this->gogo_->declare_function(name, fntype, location);
}
else
{
Typed_identifier*
Parse::receiver()
{
- gcc_assert(this->peek_token()->is_op(OPERATOR_LPAREN));
+ go_assert(this->peek_token()->is_op(OPERATOR_LPAREN));
std::string name;
const Token* token = this->advance_token();
packed = this->gogo_->pack_hidden_name(id, is_exported);
named_object = package->lookup(packed);
location = this->location();
- gcc_assert(in_function == NULL);
+ go_assert(in_function == NULL);
}
this->advance_token();
&& named_object->is_type()
&& !named_object->type_value()->is_visible())
{
- gcc_assert(package != NULL);
+ go_assert(package != NULL);
error_at(location, "invalid reference to hidden type %<%s.%s%>",
Gogo::message_name(package->name()).c_str(),
Gogo::message_name(id).c_str());
case Named_object::NAMED_OBJECT_UNKNOWN:
return Expression::make_unknown_reference(named_object, location);
default:
- gcc_unreachable();
+ go_unreachable();
}
}
- gcc_unreachable();
+ go_unreachable();
case Token::TOKEN_STRING:
ret = Expression::make_string(token->string_value(), token->location());
Parse::enclosing_var_reference(Named_object* in_function, Named_object* var,
source_location location)
{
- gcc_assert(var->is_variable() || var->is_result_variable());
+ go_assert(var->is_variable() || var->is_result_variable());
Named_object* this_function = this->gogo_->current_function();
Named_object* closure = this_function->func_value()->closure_var();
// LiteralValue = "{" [ ElementList [ "," ] ] "}" .
// ElementList = Element { "," Element } .
// Element = [ Key ":" ] Value .
-// Key = Expression .
+// Key = FieldName | ElementIndex .
+// FieldName = identifier .
+// ElementIndex = Expression .
// Value = Expression | LiteralValue .
// We have already seen the type if there is one, and we are now
Expression*
Parse::composite_lit(Type* type, int depth, source_location location)
{
- gcc_assert(this->peek_token()->is_op(OPERATOR_LCURLY));
+ go_assert(this->peek_token()->is_op(OPERATOR_LCURLY));
this->advance_token();
if (this->peek_token()->is_op(OPERATOR_RCURLY))
const Token* token = this->peek_token();
- if (!token->is_op(OPERATOR_LCURLY))
+ if (token->is_identifier())
+ {
+ std::string identifier = token->identifier();
+ bool is_exported = token->is_identifier_exported();
+ source_location location = token->location();
+
+ if (this->advance_token()->is_op(OPERATOR_COLON))
+ {
+ // This may be a field name. We don't know for sure--it
+ // could also be an expression for an array index. We
+ // don't want to parse it as an expression because may
+ // trigger various errors, e.g., if this identifier
+ // happens to be the name of a package.
+ Gogo* gogo = this->gogo_;
+ val = this->id_to_expression(gogo->pack_hidden_name(identifier,
+ is_exported),
+ location);
+ }
+ else
+ {
+ this->unget_token(Token::make_identifier_token(identifier,
+ is_exported,
+ location));
+ val = this->expression(PRECEDENCE_NORMAL, false, true, NULL);
+ }
+ }
+ else if (!token->is_op(OPERATOR_LCURLY))
val = this->expression(PRECEDENCE_NORMAL, false, true, NULL);
else
{
Parse::function_lit()
{
source_location location = this->location();
- gcc_assert(this->peek_token()->is_keyword(KEYWORD_FUNC));
+ go_assert(this->peek_token()->is_keyword(KEYWORD_FUNC));
this->advance_token();
Enclosing_vars hold_enclosing_vars;
if (!this->peek_token()->is_op(OPERATOR_LCURLY))
return Expression::make_type(type, location);
+ Bc_stack* hold_break_stack = this->break_stack_;
+ Bc_stack* hold_continue_stack = this->continue_stack_;
+ this->break_stack_ = NULL;
+ this->continue_stack_ = NULL;
+
Named_object* no = this->gogo_->start_function("", type, true, location);
source_location end_loc = this->block();
this->gogo_->finish_function(end_loc);
+ if (this->break_stack_ != NULL)
+ delete this->break_stack_;
+ if (this->continue_stack_ != NULL)
+ delete this->continue_stack_;
+ this->break_stack_ = hold_break_stack;
+ this->continue_stack_ = hold_continue_stack;
+
hold_enclosing_vars.swap(this->enclosing_vars_);
Expression* closure = this->create_closure(no, &hold_enclosing_vars,
Expression_list* initializer = new Expression_list;
for (size_t i = 0; i < enclosing_var_count; ++i)
{
- gcc_assert(ev[i].index() == i);
+ go_assert(ev[i].index() == i);
Named_object* var = ev[i].var();
Expression* ref;
if (ev[i].in_function() == enclosing_function)
this->advance_token();
Expression* expr = this->expression(PRECEDENCE_NORMAL, false, true,
NULL);
+ if (this->peek_token()->is_op(OPERATOR_ELLIPSIS))
+ {
+ error_at(this->location(),
+ "invalid use of %<...%> in type conversion");
+ this->advance_token();
+ }
if (!this->peek_token()->is_op(OPERATOR_RPAREN))
error_at(this->location(), "expected %<)%>");
else
this->advance_token();
if (expr->is_error_expression())
- return expr;
- ret = Expression::make_cast(ret->type(), expr, loc);
+ ret = expr;
+ else
+ {
+ Type* t = ret->type();
+ if (t->classification() == Type::TYPE_ARRAY
+ && t->array_type()->length() != NULL
+ && t->array_type()->length()->is_nil_expression())
+ {
+ error_at(ret->location(),
+ "invalid use of %<...%> in type conversion");
+ ret = Expression::make_error(loc);
+ }
+ else
+ ret = Expression::make_cast(t, expr, loc);
+ }
}
}
Expression*
Parse::selector(Expression* left, bool* is_type_switch)
{
- gcc_assert(this->peek_token()->is_op(OPERATOR_DOT));
+ go_assert(this->peek_token()->is_op(OPERATOR_DOT));
source_location location = this->location();
const Token* token = this->advance_token();
Parse::index(Expression* expr)
{
source_location location = this->location();
- gcc_assert(this->peek_token()->is_op(OPERATOR_LSQUARE));
+ go_assert(this->peek_token()->is_op(OPERATOR_LSQUARE));
this->advance_token();
Expression* start;
Expression*
Parse::call(Expression* func)
{
- gcc_assert(this->peek_token()->is_op(OPERATOR_LPAREN));
+ go_assert(this->peek_token()->is_op(OPERATOR_LPAREN));
Expression_list* args = NULL;
bool is_varargs = false;
const Token* token = this->advance_token();
return Expression::make_func_reference(named_object, NULL, location);
case Named_object::NAMED_OBJECT_UNKNOWN:
return Expression::make_unknown_reference(named_object, location);
+ 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);
default:
error_at(this->location(), "unexpected type of identifier");
return Expression::make_error(location);
case OPERATOR_ANDAND:
right_precedence = PRECEDENCE_ANDAND;
break;
- case OPERATOR_CHANOP:
- right_precedence = PRECEDENCE_CHANOP;
- break;
case OPERATOR_EQEQ:
case OPERATOR_NOTEQ:
case OPERATOR_LT:
Expression* right = this->expression(right_precedence, false,
may_be_composite_lit,
NULL);
- if (op == OPERATOR_CHANOP)
- left = Expression::make_send(left, right, binop_location);
- else
- left = Expression::make_binary(op, left, right, binop_location);
+ left = Expression::make_binary(op, left, right, binop_location);
}
}
case Token::TOKEN_IMAGINARY:
return true;
default:
- gcc_unreachable();
+ go_unreachable();
}
}
// LABEL is the label of this statement if it has one.
void
-Parse::statement(const Label* label)
+Parse::statement(Label* label)
{
const Token* token = this->peek_token();
switch (token->classification())
case KEYWORD_MAP:
case KEYWORD_STRUCT:
case KEYWORD_INTERFACE:
- this->simple_stat(true, false, NULL, NULL);
+ this->simple_stat(true, NULL, NULL, NULL);
break;
case KEYWORD_GO:
case KEYWORD_DEFER:
this->unget_token(Token::make_identifier_token(identifier,
is_exported,
location));
- this->simple_stat(true, false, NULL, NULL);
+ this->simple_stat(true, NULL, NULL, NULL);
}
}
break;
location);
}
else if (!token->is_op(OPERATOR_SEMICOLON))
- this->simple_stat(true, false, NULL, NULL);
+ this->simple_stat(true, NULL, NULL, NULL);
break;
case Token::TOKEN_STRING:
case Token::TOKEN_INTEGER:
case Token::TOKEN_FLOAT:
case Token::TOKEN_IMAGINARY:
- this->simple_stat(true, false, NULL, NULL);
+ this->simple_stat(true, NULL, NULL, NULL);
break;
default:
if (!this->statement_may_start_here())
{
+ // Mark the label as used to avoid a useless error about an
+ // unused label.
+ label->set_is_used();
+
error_at(location, "missing statement after label");
this->unget_token(Token::make_operator_token(OPERATOR_SEMICOLON,
location));
this->statement(label);
}
-// SimpleStat =
-// ExpressionStat | IncDecStat | Assignment | SimpleVarDecl .
+// SimpleStmt = EmptyStmt | ExpressionStmt | SendStmt | IncDecStmt |
+// Assignment | ShortVarDecl .
+
+// EmptyStmt was handled in Parse::statement.
// In order to make this work for if and switch statements, if
-// RETURN_EXP is true, and we see an ExpressionStat, we return the
+// RETURN_EXP is not NULL, and we see an ExpressionStat, we return the
// expression rather than adding an expression statement to the
// current block. If we see something other than an ExpressionStat,
-// we add the statement and return NULL.
+// we add the statement, set *RETURN_EXP to true if we saw a send
+// statement, and return NULL. The handling of send statements is for
+// better error messages.
// If P_RANGE_CLAUSE is not NULL, then this will recognize a
// RangeClause.
// guard (var := expr.("type") using the literal keyword "type").
Expression*
-Parse::simple_stat(bool may_be_composite_lit, bool return_exp,
+Parse::simple_stat(bool may_be_composite_lit, bool* return_exp,
Range_clause* p_range_clause, Type_switch* p_type_switch)
{
const Token* token = this->peek_token();
return NULL;
}
token = this->peek_token();
- if (token->is_op(OPERATOR_PLUSPLUS) || token->is_op(OPERATOR_MINUSMINUS))
+ if (token->is_op(OPERATOR_CHANOP))
+ {
+ this->send_stmt(this->verify_not_sink(exp));
+ if (return_exp != NULL)
+ *return_exp = true;
+ }
+ else if (token->is_op(OPERATOR_PLUSPLUS)
+ || token->is_op(OPERATOR_MINUSMINUS))
this->inc_dec_stat(this->verify_not_sink(exp));
else if (token->is_op(OPERATOR_COMMA)
|| token->is_op(OPERATOR_EQ))
|| token->is_op(OPERATOR_ANDEQ)
|| token->is_op(OPERATOR_BITCLEAREQ))
this->assignment(this->verify_not_sink(exp), p_range_clause);
- else if (return_exp)
+ else if (return_exp != NULL)
return this->verify_not_sink(exp);
else
this->expression_stat(this->verify_not_sink(exp));
void
Parse::expression_stat(Expression* exp)
{
- exp->discarding_value();
- this->gogo_->add_statement(Statement::make_statement(exp));
+ this->gogo_->add_statement(Statement::make_statement(exp, false));
+}
+
+// SendStmt = Channel "<-" Expression .
+// Channel = Expression .
+
+void
+Parse::send_stmt(Expression* channel)
+{
+ go_assert(this->peek_token()->is_op(OPERATOR_CHANOP));
+ source_location loc = this->location();
+ this->advance_token();
+ Expression* val = this->expression(PRECEDENCE_NORMAL, false, true, NULL);
+ Statement* s = Statement::make_send_statement(channel, val, loc);
+ this->gogo_->add_statement(s);
}
// IncDecStat = Expression ( "++" | "--" ) .
else if (token->is_op(OPERATOR_MINUSMINUS))
this->gogo_->add_statement(Statement::make_dec_statement(exp));
else
- gcc_unreachable();
+ go_unreachable();
this->advance_token();
}
Expression* channel = receive->channel();
Statement* s = Statement::make_tuple_receive_assignment(val, success,
channel,
+ false,
location);
this->gogo_->add_statement(s);
}
void
Parse::go_or_defer_stat()
{
- gcc_assert(this->peek_token()->is_keyword(KEYWORD_GO)
+ go_assert(this->peek_token()->is_keyword(KEYWORD_GO)
|| this->peek_token()->is_keyword(KEYWORD_DEFER));
bool is_go = this->peek_token()->is_keyword(KEYWORD_GO);
source_location stat_location = this->location();
void
Parse::return_stat()
{
- gcc_assert(this->peek_token()->is_keyword(KEYWORD_RETURN));
+ go_assert(this->peek_token()->is_keyword(KEYWORD_RETURN));
source_location location = this->location();
this->advance_token();
Expression_list* vals = NULL;
if (this->expression_may_start_here())
vals = this->expression_list(NULL, false);
- const Function* function = this->gogo_->current_function()->func_value();
- const Typed_identifier_list* results = function->type()->results();
- this->gogo_->add_statement(Statement::make_return_statement(results, vals,
- location));
+ this->gogo_->add_statement(Statement::make_return_statement(vals, location));
}
-// IfStat = "if" [ [ SimpleStat ] ";" ] [ Condition ]
-// Block [ "else" Statement ] .
+// IfStmt = "if" [ SimpleStmt ";" ] Expression Block
+// [ "else" ( IfStmt | Block ) ] .
void
Parse::if_stat()
{
- gcc_assert(this->peek_token()->is_keyword(KEYWORD_IF));
+ go_assert(this->peek_token()->is_keyword(KEYWORD_IF));
source_location location = this->location();
this->advance_token();
this->gogo_->start_block(location);
+ bool saw_simple_stat = false;
Expression* cond = NULL;
+ bool saw_send_stmt;
if (this->simple_stat_may_start_here())
- cond = this->simple_stat(false, true, NULL, NULL);
+ {
+ cond = this->simple_stat(false, &saw_send_stmt, NULL, NULL);
+ saw_simple_stat = true;
+ }
if (cond != NULL && this->peek_token()->is_op(OPERATOR_SEMICOLON))
{
// The SimpleStat is an expression statement.
{
if (this->peek_token()->is_op(OPERATOR_SEMICOLON))
this->advance_token();
- if (!this->peek_token()->is_op(OPERATOR_LCURLY))
+ else if (saw_simple_stat)
+ {
+ if (saw_send_stmt)
+ error_at(this->location(),
+ ("send statement used as value; "
+ "use select for non-blocking send"));
+ else
+ error_at(this->location(),
+ "expected %<;%> after statement in if expression");
+ if (!this->expression_may_start_here())
+ cond = Expression::make_error(this->location());
+ }
+ if (cond == NULL && this->peek_token()->is_op(OPERATOR_LCURLY))
+ {
+ error_at(this->location(),
+ "missing condition in if statement");
+ cond = Expression::make_error(this->location());
+ }
+ if (cond == NULL)
cond = this->expression(PRECEDENCE_NORMAL, false, false, NULL);
}
Block* else_block = NULL;
if (this->peek_token()->is_keyword(KEYWORD_ELSE))
{
- this->advance_token();
- // We create a block to gather the statement.
this->gogo_->start_block(this->location());
- this->statement(NULL);
+ const Token* token = this->advance_token();
+ if (token->is_keyword(KEYWORD_IF))
+ this->if_stat();
+ else if (token->is_op(OPERATOR_LCURLY))
+ this->block();
+ else
+ {
+ error_at(this->location(), "expected %<if%> or %<{%>");
+ this->statement(NULL);
+ }
else_block = this->gogo_->finish_block(this->location());
}
// TypeSwitchGuard = [ identifier ":=" ] Expression "." "(" "type" ")" .
void
-Parse::switch_stat(const Label* label)
+Parse::switch_stat(Label* label)
{
- gcc_assert(this->peek_token()->is_keyword(KEYWORD_SWITCH));
+ go_assert(this->peek_token()->is_keyword(KEYWORD_SWITCH));
source_location location = this->location();
this->advance_token();
this->gogo_->start_block(location);
+ bool saw_simple_stat = false;
Expression* switch_val = NULL;
+ bool saw_send_stmt;
Type_switch type_switch;
if (this->simple_stat_may_start_here())
- switch_val = this->simple_stat(false, true, NULL, &type_switch);
+ {
+ switch_val = this->simple_stat(false, &saw_send_stmt, NULL,
+ &type_switch);
+ saw_simple_stat = true;
+ }
if (switch_val != NULL && this->peek_token()->is_op(OPERATOR_SEMICOLON))
{
// The SimpleStat is an expression statement.
{
if (this->peek_token()->is_op(OPERATOR_SEMICOLON))
this->advance_token();
+ else if (saw_simple_stat)
+ {
+ if (saw_send_stmt)
+ error_at(this->location(),
+ ("send statement used as value; "
+ "use select for non-blocking send"));
+ else
+ error_at(this->location(),
+ "expected %<;%> after statement in switch expression");
+ }
if (!this->peek_token()->is_op(OPERATOR_LCURLY))
{
if (this->peek_token()->is_identifier())
if (is_coloneq)
{
// This must be a TypeSwitchGuard.
- switch_val = this->simple_stat(false, true, NULL,
+ switch_val = this->simple_stat(false, &saw_send_stmt, NULL,
&type_switch);
- if (!type_switch.found
- && !switch_val->is_error_expression())
+ if (!type_switch.found)
{
- error_at(id_loc, "expected type switch assignment");
- switch_val = Expression::make_error(id_loc);
+ if (switch_val == NULL
+ || !switch_val->is_error_expression())
+ {
+ error_at(id_loc, "expected type switch assignment");
+ switch_val = Expression::make_error(id_loc);
+ }
}
}
}
if (this->peek_token()->is_op(OPERATOR_SEMICOLON)
&& this->advance_token()->is_op(OPERATOR_LCURLY))
error_at(token_loc, "unexpected semicolon or newline before %<{%>");
+ else if (this->peek_token()->is_op(OPERATOR_COLONEQ))
+ {
+ error_at(token_loc, "invalid variable name");
+ this->advance_token();
+ this->expression(PRECEDENCE_NORMAL, false, false,
+ &type_switch.found);
+ if (this->peek_token()->is_op(OPERATOR_SEMICOLON))
+ this->advance_token();
+ if (!this->peek_token()->is_op(OPERATOR_LCURLY))
+ return;
+ if (type_switch.found)
+ type_switch.expr = Expression::make_error(location);
+ }
else
{
error_at(this->location(), "expected %<{%>");
// "{" { ExprCaseClause } "}"
Statement*
-Parse::expr_switch_body(const Label* label, Expression* switch_val,
+Parse::expr_switch_body(Label* label, Expression* switch_val,
source_location location)
{
Switch_statement* statement = Statement::make_switch_statement(switch_val,
// "{" { TypeCaseClause } "}" .
Statement*
-Parse::type_switch_body(const Label* label, const Type_switch& type_switch,
+Parse::type_switch_body(Label* label, const Type_switch& type_switch,
source_location location)
{
Named_object* switch_no = NULL;
if (is_default)
{
- gcc_assert(types.empty());
+ go_assert(types.empty());
if (*saw_default)
{
error_at(location, "multiple defaults in type switch");
// SelectStat = "select" "{" { CommClause } "}" .
void
-Parse::select_stat(const Label* label)
+Parse::select_stat(Label* label)
{
- gcc_assert(this->peek_token()->is_keyword(KEYWORD_SELECT));
+ go_assert(this->peek_token()->is_keyword(KEYWORD_SELECT));
source_location location = this->location();
const Token* token = this->advance_token();
this->gogo_->add_statement(statement);
}
-// CommClause = CommCase [ StatementList ] .
+// CommClause = CommCase ":" { Statement ";" } .
void
Parse::comm_clause(Select_clauses* clauses, bool* saw_default)
bool is_send = false;
Expression* channel = NULL;
Expression* val = NULL;
+ Expression* closed = NULL;
std::string varname;
+ std::string closedname;
bool is_default = false;
- bool got_case = this->comm_case(&is_send, &channel, &val, &varname,
- &is_default);
+ bool got_case = this->comm_case(&is_send, &channel, &val, &closed,
+ &varname, &closedname, &is_default);
- Block* statements = NULL;
- Named_object* var = NULL;
- if (this->peek_token()->is_op(OPERATOR_SEMICOLON))
+ if (!is_send
+ && varname.empty()
+ && closedname.empty()
+ && val != NULL
+ && val->index_expression() != NULL)
+ val->index_expression()->set_is_lvalue();
+
+ if (this->peek_token()->is_op(OPERATOR_COLON))
this->advance_token();
- else if (this->statement_list_may_start_here())
- {
- this->gogo_->start_block(this->location());
+ else
+ error_at(this->location(), "expected colon");
- if (!varname.empty())
- {
- // FIXME: LOCATION is slightly wrong here.
- Variable* v = new Variable(NULL, channel, false, false, false,
- location);
- v->set_type_from_chan_element();
- var = this->gogo_->add_variable(varname, v);
- }
+ this->gogo_->start_block(this->location());
- this->statement_list();
- statements = this->gogo_->finish_block(this->location());
+ Named_object* var = NULL;
+ if (!varname.empty())
+ {
+ // FIXME: LOCATION is slightly wrong here.
+ Variable* v = new Variable(NULL, channel, false, false, false,
+ location);
+ v->set_type_from_chan_element();
+ var = this->gogo_->add_variable(varname, v);
+ }
+
+ Named_object* closedvar = NULL;
+ if (!closedname.empty())
+ {
+ // FIXME: LOCATION is slightly wrong here.
+ Variable* v = new Variable(Type::lookup_bool_type(), NULL,
+ false, false, false, location);
+ closedvar = this->gogo_->add_variable(closedname, v);
}
+ this->statement_list();
+
+ Block* statements = this->gogo_->finish_block(this->location());
+
if (is_default)
{
if (*saw_default)
}
if (got_case)
- clauses->add(is_send, channel, val, var, is_default, statements, location);
+ clauses->add(is_send, channel, val, closed, var, closedvar, is_default,
+ statements, location);
+ else if (statements != NULL)
+ {
+ // Add the statements to make sure that any names they define
+ // are traversed.
+ this->gogo_->add_block(statements, location);
+ }
}
-// CommCase = ( "default" | ( "case" ( SendExpr | RecvExpr) ) ) ":" .
+// CommCase = "case" ( SendStmt | RecvStmt ) | "default" .
bool
Parse::comm_case(bool* is_send, Expression** channel, Expression** val,
- std::string* varname, bool* is_default)
+ Expression** closed, std::string* varname,
+ std::string* closedname, bool* is_default)
{
const Token* token = this->peek_token();
if (token->is_keyword(KEYWORD_DEFAULT))
else if (token->is_keyword(KEYWORD_CASE))
{
this->advance_token();
- if (!this->send_or_recv_expr(is_send, channel, val, varname))
+ if (!this->send_or_recv_stmt(is_send, channel, val, closed, varname,
+ closedname))
return false;
}
else
return false;
}
- if (!this->peek_token()->is_op(OPERATOR_COLON))
- {
- error_at(this->location(), "expected colon");
- return false;
- }
-
- this->advance_token();
-
return true;
}
-// SendExpr = Expression "<-" Expression .
-// RecvExpr = [ Expression ( "=" | ":=" ) ] "<-" Expression .
+// RecvStmt = [ Expression [ "," Expression ] ( "=" | ":=" ) ] RecvExpr .
+// RecvExpr = Expression .
bool
-Parse::send_or_recv_expr(bool* is_send, Expression** channel, Expression** val,
- std::string* varname)
+Parse::send_or_recv_stmt(bool* is_send, Expression** channel, Expression** val,
+ Expression** closed, std::string* varname,
+ std::string* closedname)
{
const Token* token = this->peek_token();
- source_location location = token->location();
+ bool saw_comma = false;
+ bool closed_is_id = false;
if (token->is_identifier())
{
+ Gogo* gogo = this->gogo_;
std::string recv_var = token->identifier();
- bool is_var_exported = token->is_identifier_exported();
- if (!this->advance_token()->is_op(OPERATOR_COLONEQ))
- this->unget_token(Token::make_identifier_token(recv_var,
- is_var_exported,
- location));
- else
+ bool is_rv_exported = token->is_identifier_exported();
+ source_location recv_var_loc = token->location();
+ token = this->advance_token();
+ if (token->is_op(OPERATOR_COLONEQ))
{
+ // case rv := <-c:
if (!this->advance_token()->is_op(OPERATOR_CHANOP))
{
error_at(this->location(), "expected %<<-%>");
return false;
}
+ if (recv_var == "_")
+ {
+ error_at(recv_var_loc,
+ "no new variables on left side of %<:=%>");
+ recv_var = "blank";
+ }
*is_send = false;
- *varname = this->gogo_->pack_hidden_name(recv_var, is_var_exported);
+ *varname = gogo->pack_hidden_name(recv_var, is_rv_exported);
this->advance_token();
*channel = this->expression(PRECEDENCE_NORMAL, false, true, NULL);
return true;
}
+ else if (token->is_op(OPERATOR_COMMA))
+ {
+ token = this->advance_token();
+ if (token->is_identifier())
+ {
+ std::string recv_closed = token->identifier();
+ bool is_rc_exported = token->is_identifier_exported();
+ source_location recv_closed_loc = token->location();
+ closed_is_id = true;
+
+ token = this->advance_token();
+ if (token->is_op(OPERATOR_COLONEQ))
+ {
+ // case rv, rc := <-c:
+ if (!this->advance_token()->is_op(OPERATOR_CHANOP))
+ {
+ error_at(this->location(), "expected %<<-%>");
+ return false;
+ }
+ if (recv_var == "_" && recv_closed == "_")
+ {
+ error_at(recv_var_loc,
+ "no new variables on left side of %<:=%>");
+ recv_var = "blank";
+ }
+ *is_send = false;
+ if (recv_var != "_")
+ *varname = gogo->pack_hidden_name(recv_var,
+ is_rv_exported);
+ if (recv_closed != "_")
+ *closedname = gogo->pack_hidden_name(recv_closed,
+ is_rc_exported);
+ this->advance_token();
+ *channel = this->expression(PRECEDENCE_NORMAL, false, true,
+ NULL);
+ return true;
+ }
+
+ this->unget_token(Token::make_identifier_token(recv_closed,
+ is_rc_exported,
+ recv_closed_loc));
+ }
+
+ *val = this->id_to_expression(gogo->pack_hidden_name(recv_var,
+ is_rv_exported),
+ recv_var_loc);
+ saw_comma = true;
+ }
+ else
+ this->unget_token(Token::make_identifier_token(recv_var,
+ is_rv_exported,
+ recv_var_loc));
}
- if (this->peek_token()->is_op(OPERATOR_CHANOP))
+ // If SAW_COMMA is false, then we are looking at the start of the
+ // send or receive expression. If SAW_COMMA is true, then *VAL is
+ // set and we just read a comma.
+
+ Expression* e;
+ if (saw_comma || !this->peek_token()->is_op(OPERATOR_CHANOP))
+ e = this->expression(PRECEDENCE_NORMAL, true, true, NULL);
+ else
{
+ // case <-c:
*is_send = false;
this->advance_token();
*channel = this->expression(PRECEDENCE_NORMAL, false, true, NULL);
+
+ // The next token should be ':'. If it is '<-', then we have
+ // case <-c <- v:
+ // which is to say, send on a channel received from a channel.
+ if (!this->peek_token()->is_op(OPERATOR_CHANOP))
+ return true;
+
+ e = Expression::make_receive(*channel, (*channel)->location());
}
- else
- {
- Expression* left = this->expression(PRECEDENCE_CHANOP, true, true, NULL);
- if (this->peek_token()->is_op(OPERATOR_EQ))
+ if (this->peek_token()->is_op(OPERATOR_EQ))
+ {
+ if (!this->advance_token()->is_op(OPERATOR_CHANOP))
{
- if (!this->advance_token()->is_op(OPERATOR_CHANOP))
- {
- error_at(this->location(), "missing %<<-%>");
- return false;
- }
- *is_send = false;
- *val = left;
- this->advance_token();
- *channel = this->expression(PRECEDENCE_NORMAL, false, true, NULL);
+ error_at(this->location(), "missing %<<-%>");
+ return false;
}
- else if (this->peek_token()->is_op(OPERATOR_CHANOP))
+ *is_send = false;
+ this->advance_token();
+ *channel = this->expression(PRECEDENCE_NORMAL, false, true, NULL);
+ if (saw_comma)
{
- *is_send = true;
- *channel = this->verify_not_sink(left);
- this->advance_token();
- *val = this->expression(PRECEDENCE_NORMAL, false, true, NULL);
+ // case v, e = <-c:
+ // *VAL is already set.
+ if (!e->is_sink_expression())
+ *closed = e;
}
else
{
- error_at(this->location(), "expected %<<-%> or %<=%>");
- return false;
+ // case v = <-c:
+ if (!e->is_sink_expression())
+ *val = e;
}
+ return true;
}
- return true;
+ if (saw_comma)
+ {
+ if (closed_is_id)
+ error_at(this->location(), "expected %<=%> or %<:=%>");
+ else
+ error_at(this->location(), "expected %<=%>");
+ return false;
+ }
+
+ if (this->peek_token()->is_op(OPERATOR_CHANOP))
+ {
+ // case c <- v:
+ *is_send = true;
+ *channel = this->verify_not_sink(e);
+ this->advance_token();
+ *val = this->expression(PRECEDENCE_NORMAL, false, true, NULL);
+ return true;
+ }
+
+ error_at(this->location(), "expected %<<-%> or %<=%>");
+ return false;
}
// ForStat = "for" [ Condition | ForClause | RangeClause ] Block .
// Condition = Expression .
void
-Parse::for_stat(const Label* label)
+Parse::for_stat(Label* label)
{
- gcc_assert(this->peek_token()->is_keyword(KEYWORD_FOR));
+ go_assert(this->peek_token()->is_keyword(KEYWORD_FOR));
source_location location = this->location();
const Token* token = this->advance_token();
{
// We might be looking at a Condition, an InitStat, or a
// RangeClause.
- cond = this->simple_stat(false, true, &range_clause, NULL);
+ bool saw_send_stmt;
+ cond = this->simple_stat(false, &saw_send_stmt, &range_clause, NULL);
if (!this->peek_token()->is_op(OPERATOR_SEMICOLON))
{
if (cond == NULL && !range_clause.found)
- error_at(this->location(), "parse error in for statement");
+ {
+ if (saw_send_stmt)
+ error_at(this->location(),
+ ("send statement used as value; "
+ "use select for non-blocking send"));
+ else
+ error_at(this->location(), "parse error in for statement");
+ }
}
else
{
void
Parse::for_clause(Expression** cond, Block** post)
{
- gcc_assert(this->peek_token()->is_op(OPERATOR_SEMICOLON));
+ go_assert(this->peek_token()->is_op(OPERATOR_SEMICOLON));
this->advance_token();
if (this->peek_token()->is_op(OPERATOR_SEMICOLON))
*cond = NULL;
else
{
this->gogo_->start_block(this->location());
- this->simple_stat(false, false, NULL, NULL);
+ this->simple_stat(false, NULL, NULL, NULL);
*post = this->gogo_->finish_block(this->location());
}
}
Parse::range_clause_decl(const Typed_identifier_list* til,
Range_clause* p_range_clause)
{
- gcc_assert(this->peek_token()->is_keyword(KEYWORD_RANGE));
+ go_assert(this->peek_token()->is_keyword(KEYWORD_RANGE));
source_location location = this->location();
p_range_clause->found = true;
- gcc_assert(til->size() >= 1);
+ go_assert(til->size() >= 1);
if (til->size() > 2)
error_at(this->location(), "too many variables for range clause");
Parse::range_clause_expr(const Expression_list* vals,
Range_clause* p_range_clause)
{
- gcc_assert(this->peek_token()->is_keyword(KEYWORD_RANGE));
+ go_assert(this->peek_token()->is_keyword(KEYWORD_RANGE));
p_range_clause->found = true;
- gcc_assert(vals->size() >= 1);
+ go_assert(vals->size() >= 1);
if (vals->size() > 2)
error_at(this->location(), "too many variables for range clause");
// Push a statement on the break stack.
void
-Parse::push_break_statement(Statement* enclosing, const Label* label)
+Parse::push_break_statement(Statement* enclosing, Label* label)
{
- this->break_stack_.push_back(std::make_pair(enclosing, label));
+ if (this->break_stack_ == NULL)
+ this->break_stack_ = new Bc_stack();
+ this->break_stack_->push_back(std::make_pair(enclosing, label));
}
// Push a statement on the continue stack.
void
-Parse::push_continue_statement(Statement* enclosing, const Label* label)
+Parse::push_continue_statement(Statement* enclosing, Label* label)
{
- this->continue_stack_.push_back(std::make_pair(enclosing, label));
+ if (this->continue_stack_ == NULL)
+ this->continue_stack_ = new Bc_stack();
+ this->continue_stack_->push_back(std::make_pair(enclosing, label));
}
// Pop the break stack.
void
Parse::pop_break_statement()
{
- this->break_stack_.pop_back();
+ this->break_stack_->pop_back();
}
// Pop the continue stack.
void
Parse::pop_continue_statement()
{
- this->continue_stack_.pop_back();
+ this->continue_stack_->pop_back();
}
// Find a break or continue statement given a label name.
Statement*
Parse::find_bc_statement(const Bc_stack* bc_stack, const std::string& label)
{
+ if (bc_stack == NULL)
+ return NULL;
for (Bc_stack::const_reverse_iterator p = bc_stack->rbegin();
p != bc_stack->rend();
++p)
- if (p->second != NULL && p->second->name() == label)
- return p->first;
+ {
+ if (p->second != NULL && p->second->name() == label)
+ {
+ p->second->set_is_used();
+ return p->first;
+ }
+ }
return NULL;
}
void
Parse::break_stat()
{
- gcc_assert(this->peek_token()->is_keyword(KEYWORD_BREAK));
+ go_assert(this->peek_token()->is_keyword(KEYWORD_BREAK));
source_location location = this->location();
const Token* token = this->advance_token();
Statement* enclosing;
if (!token->is_identifier())
{
- if (this->break_stack_.empty())
+ if (this->break_stack_ == NULL || this->break_stack_->empty())
{
error_at(this->location(),
"break statement not within for or switch or select");
return;
}
- enclosing = this->break_stack_.back().first;
+ enclosing = this->break_stack_->back().first;
}
else
{
- enclosing = this->find_bc_statement(&this->break_stack_,
+ enclosing = this->find_bc_statement(this->break_stack_,
token->identifier());
if (enclosing == NULL)
{
- error_at(token->location(),
- ("break label %qs not associated with "
- "for or switch or select"),
+ // If there is a label with this name, mark it as used to
+ // avoid a useless error about an unused label.
+ this->gogo_->add_label_reference(token->identifier(), 0, false);
+
+ error_at(token->location(), "invalid break label %qs",
Gogo::message_name(token->identifier()).c_str());
this->advance_token();
return;
else if (enclosing->classification() == Statement::STATEMENT_SELECT)
label = enclosing->select_statement()->break_label();
else
- gcc_unreachable();
+ go_unreachable();
this->gogo_->add_statement(Statement::make_break_statement(label,
location));
void
Parse::continue_stat()
{
- gcc_assert(this->peek_token()->is_keyword(KEYWORD_CONTINUE));
+ go_assert(this->peek_token()->is_keyword(KEYWORD_CONTINUE));
source_location location = this->location();
const Token* token = this->advance_token();
Statement* enclosing;
if (!token->is_identifier())
{
- if (this->continue_stack_.empty())
+ if (this->continue_stack_ == NULL || this->continue_stack_->empty())
{
error_at(this->location(), "continue statement not within for");
return;
}
- enclosing = this->continue_stack_.back().first;
+ enclosing = this->continue_stack_->back().first;
}
else
{
- enclosing = this->find_bc_statement(&this->continue_stack_,
+ enclosing = this->find_bc_statement(this->continue_stack_,
token->identifier());
if (enclosing == NULL)
{
- error_at(token->location(),
- "continue label %qs not associated with for",
+ // If there is a label with this name, mark it as used to
+ // avoid a useless error about an unused label.
+ this->gogo_->add_label_reference(token->identifier(), 0, false);
+
+ error_at(token->location(), "invalid continue label %qs",
Gogo::message_name(token->identifier()).c_str());
this->advance_token();
return;
else if (enclosing->classification() == Statement::STATEMENT_FOR_RANGE)
label = enclosing->for_range_statement()->continue_label();
else
- gcc_unreachable();
+ go_unreachable();
this->gogo_->add_statement(Statement::make_continue_statement(label,
location));
void
Parse::goto_stat()
{
- gcc_assert(this->peek_token()->is_keyword(KEYWORD_GOTO));
+ go_assert(this->peek_token()->is_keyword(KEYWORD_GOTO));
source_location location = this->location();
const Token* token = this->advance_token();
if (!token->is_identifier())
error_at(this->location(), "expected label for goto");
else
{
- Label* label = this->gogo_->add_label_reference(token->identifier());
+ Label* label = this->gogo_->add_label_reference(token->identifier(),
+ location, true);
Statement* s = Statement::make_goto_statement(label, location);
this->gogo_->add_statement(s);
this->advance_token();
void
Parse::import_decl()
{
- gcc_assert(this->peek_token()->is_keyword(KEYWORD_IMPORT));
+ go_assert(this->peek_token()->is_keyword(KEYWORD_IMPORT));
this->advance_token();
this->decl(&Parse::import_spec, NULL);
}
token = this->advance_token();
else if (!token->is_eof() || !saw_errors())
{
- error_at(this->location(),
- "expected %<;%> or newline after top level declaration");
+ if (token->is_op(OPERATOR_CHANOP))
+ error_at(this->location(),
+ ("send statement used as value; "
+ "use select for non-blocking send"));
+ else
+ error_at(this->location(),
+ "expected %<;%> or newline after top level declaration");
this->skip_past_error(OPERATOR_INVALID);
}
}