diff options
Diffstat (limited to 'gcc-4.8.1/gcc/go/gofrontend/parse.cc')
-rw-r--r-- | gcc-4.8.1/gcc/go/gofrontend/parse.cc | 5676 |
1 files changed, 0 insertions, 5676 deletions
diff --git a/gcc-4.8.1/gcc/go/gofrontend/parse.cc b/gcc-4.8.1/gcc/go/gofrontend/parse.cc deleted file mode 100644 index d7a18d023..000000000 --- a/gcc-4.8.1/gcc/go/gofrontend/parse.cc +++ /dev/null @@ -1,5676 +0,0 @@ -// parse.cc -- Go frontend parser. - -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "go-system.h" - -#include "lex.h" -#include "gogo.h" -#include "types.h" -#include "statements.h" -#include "expressions.h" -#include "parse.h" - -// Struct Parse::Enclosing_var_comparison. - -// Return true if v1 should be considered to be less than v2. - -bool -Parse::Enclosing_var_comparison::operator()(const Enclosing_var& v1, - const Enclosing_var& v2) -{ - if (v1.var() == v2.var()) - return false; - - const std::string& n1(v1.var()->name()); - const std::string& n2(v2.var()->name()); - int i = n1.compare(n2); - if (i < 0) - return true; - else if (i > 0) - return false; - - // 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. - go_unreachable(); -} - -// Class Parse. - -Parse::Parse(Lex* lex, Gogo* gogo) - : lex_(lex), - 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), - iota_(0), - enclosing_vars_(), - type_switch_vars_() -{ -} - -// Return the current token. - -const Token* -Parse::peek_token() -{ - if (this->unget_token_valid_) - return &this->unget_token_; - if (this->token_.is_invalid()) - this->token_ = this->lex_->next_token(); - return &this->token_; -} - -// Advance to the next token and return it. - -const Token* -Parse::advance_token() -{ - if (this->unget_token_valid_) - { - this->unget_token_valid_ = false; - if (!this->token_.is_invalid()) - return &this->token_; - } - this->token_ = this->lex_->next_token(); - return &this->token_; -} - -// Push a token back on the input stream. - -void -Parse::unget_token(const Token& token) -{ - go_assert(!this->unget_token_valid_); - this->unget_token_ = token; - this->unget_token_valid_ = true; -} - -// The location of the current token. - -Location -Parse::location() -{ - return this->peek_token()->location(); -} - -// IdentifierList = identifier { "," identifier } . - -void -Parse::identifier_list(Typed_identifier_list* til) -{ - const Token* token = this->peek_token(); - while (true) - { - if (!token->is_identifier()) - { - error_at(this->location(), "expected identifier"); - return; - } - std::string name = - this->gogo_->pack_hidden_name(token->identifier(), - token->is_identifier_exported()); - til->push_back(Typed_identifier(name, NULL, token->location())); - token = this->advance_token(); - if (!token->is_op(OPERATOR_COMMA)) - return; - token = this->advance_token(); - } -} - -// ExpressionList = Expression { "," Expression } . - -// If MAY_BE_COMPOSITE_LIT is true, an expression may be a composite -// literal. - -// If MAY_BE_SINK is true, the expressions in the list may be "_". - -Expression_list* -Parse::expression_list(Expression* first, bool may_be_sink, - bool may_be_composite_lit) -{ - Expression_list* ret = new Expression_list(); - if (first != NULL) - ret->push_back(first); - while (true) - { - ret->push_back(this->expression(PRECEDENCE_NORMAL, may_be_sink, - may_be_composite_lit, NULL, NULL)); - - const Token* token = this->peek_token(); - if (!token->is_op(OPERATOR_COMMA)) - return ret; - - // Most expression lists permit a trailing comma. - Location location = token->location(); - this->advance_token(); - if (!this->expression_may_start_here()) - { - this->unget_token(Token::make_operator_token(OPERATOR_COMMA, - location)); - return ret; - } - } -} - -// QualifiedIdent = [ PackageName "." ] identifier . -// PackageName = identifier . - -// This sets *PNAME to the identifier and sets *PPACKAGE to the -// package or NULL if there isn't one. This returns true on success, -// false on failure in which case it will have emitted an error -// message. - -bool -Parse::qualified_ident(std::string* pname, Named_object** ppackage) -{ - const Token* token = this->peek_token(); - if (!token->is_identifier()) - { - error_at(this->location(), "expected identifier"); - return false; - } - - std::string name = token->identifier(); - bool is_exported = token->is_identifier_exported(); - name = this->gogo_->pack_hidden_name(name, is_exported); - - token = this->advance_token(); - if (!token->is_op(OPERATOR_DOT)) - { - *pname = name; - *ppackage = NULL; - return true; - } - - Named_object* package = this->gogo_->lookup(name, NULL); - if (package == NULL || !package->is_package()) - { - error_at(this->location(), "expected package"); - // We expect . IDENTIFIER; skip both. - if (this->advance_token()->is_identifier()) - this->advance_token(); - return false; - } - - package->package_value()->set_used(); - - token = this->advance_token(); - if (!token->is_identifier()) - { - error_at(this->location(), "expected identifier"); - return false; - } - - name = token->identifier(); - - if (name == "_") - { - error_at(this->location(), "invalid use of %<_%>"); - name = "blank"; - } - - if (package->name() == this->gogo_->package_name()) - name = this->gogo_->pack_hidden_name(name, - token->is_identifier_exported()); - - *pname = name; - *ppackage = package; - - this->advance_token(); - - return true; -} - -// Type = TypeName | TypeLit | "(" Type ")" . -// TypeLit = -// ArrayType | StructType | PointerType | FunctionType | InterfaceType | -// SliceType | MapType | ChannelType . - -Type* -Parse::type() -{ - const Token* token = this->peek_token(); - if (token->is_identifier()) - return this->type_name(true); - else if (token->is_op(OPERATOR_LSQUARE)) - return this->array_type(false); - else if (token->is_keyword(KEYWORD_CHAN) - || token->is_op(OPERATOR_CHANOP)) - return this->channel_type(); - else if (token->is_keyword(KEYWORD_INTERFACE)) - return this->interface_type(); - else if (token->is_keyword(KEYWORD_FUNC)) - { - Location location = token->location(); - this->advance_token(); - Type* type = this->signature(NULL, location); - if (type == NULL) - return Type::make_error_type(); - return type; - } - else if (token->is_keyword(KEYWORD_MAP)) - return this->map_type(); - else if (token->is_keyword(KEYWORD_STRUCT)) - return this->struct_type(); - else if (token->is_op(OPERATOR_MULT)) - return this->pointer_type(); - else if (token->is_op(OPERATOR_LPAREN)) - { - this->advance_token(); - Type* ret = this->type(); - if (this->peek_token()->is_op(OPERATOR_RPAREN)) - this->advance_token(); - else - { - if (!ret->is_error_type()) - error_at(this->location(), "expected %<)%>"); - } - return ret; - } - else - { - error_at(token->location(), "expected type"); - return Type::make_error_type(); - } -} - -bool -Parse::type_may_start_here() -{ - const Token* token = this->peek_token(); - return (token->is_identifier() - || token->is_op(OPERATOR_LSQUARE) - || token->is_op(OPERATOR_CHANOP) - || token->is_keyword(KEYWORD_CHAN) - || token->is_keyword(KEYWORD_INTERFACE) - || token->is_keyword(KEYWORD_FUNC) - || token->is_keyword(KEYWORD_MAP) - || token->is_keyword(KEYWORD_STRUCT) - || token->is_op(OPERATOR_MULT) - || token->is_op(OPERATOR_LPAREN)); -} - -// TypeName = QualifiedIdent . - -// If MAY_BE_NIL is true, then an identifier with the value of the -// predefined constant nil is accepted, returning the nil type. - -Type* -Parse::type_name(bool issue_error) -{ - Location location = this->location(); - - std::string name; - Named_object* package; - if (!this->qualified_ident(&name, &package)) - return Type::make_error_type(); - - Named_object* named_object; - if (package == NULL) - named_object = this->gogo_->lookup(name, NULL); - else - { - named_object = package->package_value()->lookup(name); - if (named_object == NULL - && issue_error - && package->name() != this->gogo_->package_name()) - { - // Check whether the name is there but hidden. - std::string s = ('.' + package->package_value()->pkgpath() - + '.' + name); - named_object = package->package_value()->lookup(s); - if (named_object != NULL) - { - Package* p = package->package_value(); - const std::string& packname(p->package_name()); - error_at(location, "invalid reference to hidden type %<%s.%s%>", - Gogo::message_name(packname).c_str(), - Gogo::message_name(name).c_str()); - issue_error = false; - } - } - } - - bool ok = true; - if (named_object == NULL) - { - if (package == NULL) - named_object = this->gogo_->add_unknown_name(name, location); - else - { - const std::string& packname(package->package_value()->package_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()) - { - if (!named_object->type_value()->is_visible()) - ok = false; - } - else if (named_object->is_unknown() || named_object->is_type_declaration()) - ; - else - ok = false; - - if (!ok) - { - if (issue_error) - error_at(location, "expected type"); - return Type::make_error_type(); - } - - if (named_object->is_type()) - return named_object->type_value(); - else if (named_object->is_unknown() || named_object->is_type_declaration()) - return Type::make_forward_declaration(named_object); - else - go_unreachable(); -} - -// ArrayType = "[" [ ArrayLength ] "]" ElementType . -// ArrayLength = Expression . -// ElementType = CompleteType . - -Type* -Parse::array_type(bool may_use_ellipsis) -{ - go_assert(this->peek_token()->is_op(OPERATOR_LSQUARE)); - const Token* token = this->advance_token(); - - Expression* length = NULL; - if (token->is_op(OPERATOR_RSQUARE)) - this->advance_token(); - else - { - if (!token->is_op(OPERATOR_ELLIPSIS)) - length = this->expression(PRECEDENCE_NORMAL, false, true, NULL, NULL); - else if (may_use_ellipsis) - { - // An ellipsis is used in composite literals to represent a - // fixed array of the size of the number of elements. We - // use a length of nil to represent this, and change the - // length when parsing the composite literal. - length = Expression::make_nil(this->location()); - this->advance_token(); - } - else - { - error_at(this->location(), - "use of %<[...]%> outside of array literal"); - length = Expression::make_error(this->location()); - this->advance_token(); - } - if (!this->peek_token()->is_op(OPERATOR_RSQUARE)) - { - error_at(this->location(), "expected %<]%>"); - return Type::make_error_type(); - } - this->advance_token(); - } - - Type* element_type = this->type(); - - return Type::make_array_type(element_type, length); -} - -// MapType = "map" "[" KeyType "]" ValueType . -// KeyType = CompleteType . -// ValueType = CompleteType . - -Type* -Parse::map_type() -{ - Location location = this->location(); - go_assert(this->peek_token()->is_keyword(KEYWORD_MAP)); - if (!this->advance_token()->is_op(OPERATOR_LSQUARE)) - { - error_at(this->location(), "expected %<[%>"); - return Type::make_error_type(); - } - this->advance_token(); - - Type* key_type = this->type(); - - if (!this->peek_token()->is_op(OPERATOR_RSQUARE)) - { - error_at(this->location(), "expected %<]%>"); - return Type::make_error_type(); - } - this->advance_token(); - - Type* value_type = this->type(); - - if (key_type->is_error_type() || value_type->is_error_type()) - return Type::make_error_type(); - - return Type::make_map_type(key_type, value_type, location); -} - -// StructType = "struct" "{" { FieldDecl ";" } "}" . - -Type* -Parse::struct_type() -{ - go_assert(this->peek_token()->is_keyword(KEYWORD_STRUCT)); - Location location = this->location(); - if (!this->advance_token()->is_op(OPERATOR_LCURLY)) - { - Location token_loc = this->location(); - 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 - { - error_at(this->location(), "expected %<{%>"); - return Type::make_error_type(); - } - } - this->advance_token(); - - Struct_field_list* sfl = new Struct_field_list; - while (!this->peek_token()->is_op(OPERATOR_RCURLY)) - { - this->field_decl(sfl); - if (this->peek_token()->is_op(OPERATOR_SEMICOLON)) - this->advance_token(); - else if (!this->peek_token()->is_op(OPERATOR_RCURLY)) - { - error_at(this->location(), "expected %<;%> or %<}%> or newline"); - if (!this->skip_past_error(OPERATOR_RCURLY)) - return Type::make_error_type(); - } - } - this->advance_token(); - - for (Struct_field_list::const_iterator pi = sfl->begin(); - pi != sfl->end(); - ++pi) - { - if (pi->type()->is_error_type()) - return pi->type(); - for (Struct_field_list::const_iterator pj = pi + 1; - pj != sfl->end(); - ++pj) - { - if (pi->field_name() == pj->field_name() - && !Gogo::is_sink_name(pi->field_name())) - error_at(pi->location(), "duplicate field name %<%s%>", - Gogo::message_name(pi->field_name()).c_str()); - } - } - - return Type::make_struct_type(sfl, location); -} - -// FieldDecl = (IdentifierList CompleteType | TypeName) [ Tag ] . -// Tag = string_lit . - -void -Parse::field_decl(Struct_field_list* sfl) -{ - const Token* token = this->peek_token(); - Location location = token->location(); - bool is_anonymous; - bool is_anonymous_pointer; - if (token->is_op(OPERATOR_MULT)) - { - is_anonymous = true; - is_anonymous_pointer = true; - } - else if (token->is_identifier()) - { - std::string id = token->identifier(); - bool is_id_exported = token->is_identifier_exported(); - Location id_location = token->location(); - token = this->advance_token(); - is_anonymous = (token->is_op(OPERATOR_SEMICOLON) - || token->is_op(OPERATOR_RCURLY) - || token->is_op(OPERATOR_DOT) - || token->is_string()); - is_anonymous_pointer = false; - this->unget_token(Token::make_identifier_token(id, is_id_exported, - id_location)); - } - else - { - error_at(this->location(), "expected field name"); - this->gogo_->mark_locals_used(); - while (!token->is_op(OPERATOR_SEMICOLON) - && !token->is_op(OPERATOR_RCURLY) - && !token->is_eof()) - token = this->advance_token(); - return; - } - - if (is_anonymous) - { - if (is_anonymous_pointer) - { - this->advance_token(); - if (!this->peek_token()->is_identifier()) - { - error_at(this->location(), "expected field name"); - this->gogo_->mark_locals_used(); - while (!token->is_op(OPERATOR_SEMICOLON) - && !token->is_op(OPERATOR_RCURLY) - && !token->is_eof()) - token = this->advance_token(); - return; - } - } - Type* type = this->type_name(true); - - std::string tag; - if (this->peek_token()->is_string()) - { - tag = this->peek_token()->string_value(); - this->advance_token(); - } - - if (!type->is_error_type()) - { - if (is_anonymous_pointer) - type = Type::make_pointer_type(type); - sfl->push_back(Struct_field(Typed_identifier("", type, location))); - if (!tag.empty()) - sfl->back().set_tag(tag); - } - } - else - { - Typed_identifier_list til; - while (true) - { - token = this->peek_token(); - if (!token->is_identifier()) - { - error_at(this->location(), "expected identifier"); - return; - } - std::string name = - this->gogo_->pack_hidden_name(token->identifier(), - token->is_identifier_exported()); - til.push_back(Typed_identifier(name, NULL, token->location())); - if (!this->advance_token()->is_op(OPERATOR_COMMA)) - break; - this->advance_token(); - } - - Type* type = this->type(); - - std::string tag; - if (this->peek_token()->is_string()) - { - tag = this->peek_token()->string_value(); - this->advance_token(); - } - - for (Typed_identifier_list::iterator p = til.begin(); - p != til.end(); - ++p) - { - p->set_type(type); - sfl->push_back(Struct_field(*p)); - if (!tag.empty()) - sfl->back().set_tag(tag); - } - } -} - -// PointerType = "*" Type . - -Type* -Parse::pointer_type() -{ - go_assert(this->peek_token()->is_op(OPERATOR_MULT)); - this->advance_token(); - Type* type = this->type(); - if (type->is_error_type()) - return type; - return Type::make_pointer_type(type); -} - -// ChannelType = Channel | SendChannel | RecvChannel . -// Channel = "chan" ElementType . -// SendChannel = "chan" "<-" ElementType . -// RecvChannel = "<-" "chan" ElementType . - -Type* -Parse::channel_type() -{ - const Token* token = this->peek_token(); - bool send = true; - bool receive = true; - if (token->is_op(OPERATOR_CHANOP)) - { - if (!this->advance_token()->is_keyword(KEYWORD_CHAN)) - { - error_at(this->location(), "expected %<chan%>"); - return Type::make_error_type(); - } - send = false; - this->advance_token(); - } - else - { - 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 -// location of the start of the type. - -// This returns NULL on a parse error. - -Function_type* -Parse::signature(Typed_identifier* receiver, Location location) -{ - bool is_varargs = false; - Typed_identifier_list* params; - bool params_ok = this->parameters(¶ms, &is_varargs); - - Typed_identifier_list* results = NULL; - if (this->peek_token()->is_op(OPERATOR_LPAREN) - || this->type_may_start_here()) - { - if (!this->result(&results)) - return NULL; - } - - if (!params_ok) - return NULL; - - 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(); - return ret; -} - -// Parameters = "(" [ ParameterList [ "," ] ] ")" . - -// This returns false on a parse error. - -bool -Parse::parameters(Typed_identifier_list** pparams, bool* is_varargs) -{ - *pparams = NULL; - - if (!this->peek_token()->is_op(OPERATOR_LPAREN)) - { - error_at(this->location(), "expected %<(%>"); - return false; - } - - Typed_identifier_list* params = NULL; - bool saw_error = false; - - const Token* token = this->advance_token(); - if (!token->is_op(OPERATOR_RPAREN)) - { - params = this->parameter_list(is_varargs); - if (params == NULL) - saw_error = true; - token = this->peek_token(); - } - - // The optional trailing comma is picked up in parameter_list. - - if (!token->is_op(OPERATOR_RPAREN)) - error_at(this->location(), "expected %<)%>"); - else - this->advance_token(); - - if (saw_error) - return false; - - *pparams = params; - return true; -} - -// ParameterList = ParameterDecl { "," ParameterDecl } . - -// This sets *IS_VARARGS if the list ends with an ellipsis. -// IS_VARARGS will be NULL if varargs are not permitted. - -// We pick up an optional trailing comma. - -// This returns NULL if some error is seen. - -Typed_identifier_list* -Parse::parameter_list(bool* is_varargs) -{ - Location location = this->location(); - Typed_identifier_list* ret = new Typed_identifier_list(); - - bool saw_error = false; - - // If we see an identifier and then a comma, then we don't know - // whether we are looking at a list of identifiers followed by a - // type, or a list of types given by name. We have to do an - // arbitrary lookahead to figure it out. - - bool parameters_have_names; - const Token* token = this->peek_token(); - if (!token->is_identifier()) - { - // This must be a type which starts with something like '*'. - parameters_have_names = false; - } - else - { - std::string name = token->identifier(); - bool is_exported = token->is_identifier_exported(); - Location location = token->location(); - token = this->advance_token(); - if (!token->is_op(OPERATOR_COMMA)) - { - if (token->is_op(OPERATOR_DOT)) - { - // This is a qualified identifier, which must turn out - // to be a type. - parameters_have_names = false; - } - else if (token->is_op(OPERATOR_RPAREN)) - { - // A single identifier followed by a parenthesis must be - // a type name. - parameters_have_names = false; - } - else - { - // An identifier followed by something other than a - // comma or a dot or a right parenthesis must be a - // parameter name followed by a type. - parameters_have_names = true; - } - - this->unget_token(Token::make_identifier_token(name, is_exported, - location)); - } - else - { - // An identifier followed by a comma may be the first in a - // list of parameter names followed by a type, or it may be - // the first in a list of types without parameter names. To - // find out we gather as many identifiers separated by - // commas as we can. - std::string id_name = this->gogo_->pack_hidden_name(name, - is_exported); - ret->push_back(Typed_identifier(id_name, NULL, location)); - bool just_saw_comma = true; - while (this->advance_token()->is_identifier()) - { - name = this->peek_token()->identifier(); - is_exported = this->peek_token()->is_identifier_exported(); - location = this->peek_token()->location(); - id_name = this->gogo_->pack_hidden_name(name, is_exported); - ret->push_back(Typed_identifier(id_name, NULL, location)); - if (!this->advance_token()->is_op(OPERATOR_COMMA)) - { - just_saw_comma = false; - break; - } - } - - if (just_saw_comma) - { - // We saw ID1 "," ID2 "," followed by something which - // was not an identifier. We must be seeing the start - // of a type, and ID1 and ID2 must be types, and the - // parameters don't have names. - parameters_have_names = false; - } - else if (this->peek_token()->is_op(OPERATOR_RPAREN)) - { - // We saw ID1 "," ID2 ")". ID1 and ID2 must be types, - // and the parameters don't have names. - parameters_have_names = false; - } - else if (this->peek_token()->is_op(OPERATOR_DOT)) - { - // We saw ID1 "," ID2 ".". ID2 must be a package name, - // ID1 must be a type, and the parameters don't have - // names. - parameters_have_names = false; - this->unget_token(Token::make_identifier_token(name, is_exported, - location)); - ret->pop_back(); - just_saw_comma = true; - } - else - { - // We saw ID1 "," ID2 followed by something other than - // ",", ".", or ")". We must be looking at the start of - // a type, and ID1 and ID2 must be parameter names. - parameters_have_names = true; - } - - if (parameters_have_names) - { - go_assert(!just_saw_comma); - // We have just seen ID1, ID2 xxx. - Type* type; - if (!this->peek_token()->is_op(OPERATOR_ELLIPSIS)) - type = this->type(); - else - { - error_at(this->location(), "%<...%> only permits one name"); - saw_error = true; - this->advance_token(); - type = this->type(); - } - for (size_t i = 0; i < ret->size(); ++i) - ret->set_type(i, type); - if (!this->peek_token()->is_op(OPERATOR_COMMA)) - return saw_error ? NULL : ret; - if (this->advance_token()->is_op(OPERATOR_RPAREN)) - return saw_error ? NULL : ret; - } - else - { - Typed_identifier_list* tret = new Typed_identifier_list(); - for (Typed_identifier_list::const_iterator p = ret->begin(); - p != ret->end(); - ++p) - { - Named_object* no = this->gogo_->lookup(p->name(), NULL); - Type* type; - if (no == NULL) - no = this->gogo_->add_unknown_name(p->name(), - p->location()); - - if (no->is_type()) - type = no->type_value(); - else if (no->is_unknown() || no->is_type_declaration()) - type = Type::make_forward_declaration(no); - else - { - error_at(p->location(), "expected %<%s%> to be a type", - Gogo::message_name(p->name()).c_str()); - saw_error = true; - type = Type::make_error_type(); - } - tret->push_back(Typed_identifier("", type, p->location())); - } - delete ret; - ret = tret; - if (!just_saw_comma - || this->peek_token()->is_op(OPERATOR_RPAREN)) - return saw_error ? NULL : ret; - } - } - } - - bool mix_error = false; - this->parameter_decl(parameters_have_names, ret, is_varargs, &mix_error); - while (this->peek_token()->is_op(OPERATOR_COMMA)) - { - if (this->advance_token()->is_op(OPERATOR_RPAREN)) - break; - if (is_varargs != NULL && *is_varargs) - { - error_at(this->location(), "%<...%> must be last parameter"); - saw_error = true; - } - this->parameter_decl(parameters_have_names, ret, is_varargs, &mix_error); - } - if (mix_error) - { - error_at(location, "invalid named/anonymous mix"); - saw_error = true; - } - if (saw_error) - { - delete ret; - return NULL; - } - return ret; -} - -// ParameterDecl = [ IdentifierList ] [ "..." ] Type . - -void -Parse::parameter_decl(bool parameters_have_names, - Typed_identifier_list* til, - bool* is_varargs, - bool* mix_error) -{ - if (!parameters_have_names) - { - Type* type; - Location location = this->location(); - if (!this->peek_token()->is_identifier()) - { - if (!this->peek_token()->is_op(OPERATOR_ELLIPSIS)) - type = this->type(); - else - { - if (is_varargs == NULL) - error_at(this->location(), "invalid use of %<...%>"); - else - *is_varargs = true; - this->advance_token(); - if (is_varargs == NULL - && this->peek_token()->is_op(OPERATOR_RPAREN)) - type = Type::make_error_type(); - else - { - Type* element_type = this->type(); - type = Type::make_array_type(element_type, NULL); - } - } - } - else - { - type = this->type_name(false); - if (type->is_error_type() - || (!this->peek_token()->is_op(OPERATOR_COMMA) - && !this->peek_token()->is_op(OPERATOR_RPAREN))) - { - *mix_error = true; - while (!this->peek_token()->is_op(OPERATOR_COMMA) - && !this->peek_token()->is_op(OPERATOR_RPAREN)) - this->advance_token(); - } - } - if (!type->is_error_type()) - til->push_back(Typed_identifier("", type, location)); - } - else - { - size_t orig_count = til->size(); - if (this->peek_token()->is_identifier()) - this->identifier_list(til); - else - *mix_error = true; - size_t new_count = til->size(); - - Type* type; - if (!this->peek_token()->is_op(OPERATOR_ELLIPSIS)) - type = this->type(); - else - { - if (is_varargs == NULL) - error_at(this->location(), "invalid use of %<...%>"); - else if (new_count > orig_count + 1) - error_at(this->location(), "%<...%> only permits one name"); - else - *is_varargs = true; - this->advance_token(); - Type* element_type = this->type(); - type = Type::make_array_type(element_type, NULL); - } - for (size_t i = orig_count; i < new_count; ++i) - til->set_type(i, type); - } -} - -// Result = Parameters | Type . - -// This returns false on a parse error. - -bool -Parse::result(Typed_identifier_list** presults) -{ - if (this->peek_token()->is_op(OPERATOR_LPAREN)) - return this->parameters(presults, NULL); - else - { - Location location = this->location(); - Type* type = this->type(); - if (type->is_error_type()) - { - *presults = NULL; - return false; - } - Typed_identifier_list* til = new Typed_identifier_list(); - til->push_back(Typed_identifier("", type, location)); - *presults = til; - return true; - } -} - -// Block = "{" [ StatementList ] "}" . - -// Returns the location of the closing brace. - -Location -Parse::block() -{ - if (!this->peek_token()->is_op(OPERATOR_LCURLY)) - { - Location loc = this->location(); - if (this->peek_token()->is_op(OPERATOR_SEMICOLON) - && this->advance_token()->is_op(OPERATOR_LCURLY)) - error_at(loc, "unexpected semicolon or newline before %<{%>"); - else - { - error_at(this->location(), "expected %<{%>"); - return Linemap::unknown_location(); - } - } - - const Token* token = this->advance_token(); - - if (!token->is_op(OPERATOR_RCURLY)) - { - this->statement_list(); - token = this->peek_token(); - if (!token->is_op(OPERATOR_RCURLY)) - { - if (!token->is_eof() || !saw_errors()) - error_at(this->location(), "expected %<}%>"); - - this->gogo_->mark_locals_used(); - - // Skip ahead to the end of the block, in hopes of avoiding - // lots of meaningless errors. - Location ret = token->location(); - int nest = 0; - while (!token->is_eof()) - { - if (token->is_op(OPERATOR_LCURLY)) - ++nest; - else if (token->is_op(OPERATOR_RCURLY)) - { - --nest; - if (nest < 0) - { - this->advance_token(); - break; - } - } - token = this->advance_token(); - ret = token->location(); - } - return ret; - } - } - - Location ret = token->location(); - this->advance_token(); - return ret; -} - -// InterfaceType = "interface" "{" [ MethodSpecList ] "}" . -// MethodSpecList = MethodSpec { ";" MethodSpec } [ ";" ] . - -Type* -Parse::interface_type() -{ - go_assert(this->peek_token()->is_keyword(KEYWORD_INTERFACE)); - Location location = this->location(); - - if (!this->advance_token()->is_op(OPERATOR_LCURLY)) - { - Location token_loc = this->location(); - 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 - { - error_at(this->location(), "expected %<{%>"); - return Type::make_error_type(); - } - } - this->advance_token(); - - Typed_identifier_list* methods = new Typed_identifier_list(); - if (!this->peek_token()->is_op(OPERATOR_RCURLY)) - { - this->method_spec(methods); - while (this->peek_token()->is_op(OPERATOR_SEMICOLON)) - { - if (this->advance_token()->is_op(OPERATOR_RCURLY)) - break; - this->method_spec(methods); - } - if (!this->peek_token()->is_op(OPERATOR_RCURLY)) - { - error_at(this->location(), "expected %<}%>"); - while (!this->advance_token()->is_op(OPERATOR_RCURLY)) - { - if (this->peek_token()->is_eof()) - return Type::make_error_type(); - } - } - } - this->advance_token(); - - if (methods->empty()) - { - delete methods; - methods = NULL; - } - - Interface_type* ret = Type::make_interface_type(methods, location); - this->gogo_->record_interface_type(ret); - return ret; -} - -// MethodSpec = MethodName Signature | InterfaceTypeName . -// MethodName = identifier . -// InterfaceTypeName = TypeName . - -void -Parse::method_spec(Typed_identifier_list* methods) -{ - const Token* token = this->peek_token(); - if (!token->is_identifier()) - { - error_at(this->location(), "expected identifier"); - return; - } - - std::string name = token->identifier(); - bool is_exported = token->is_identifier_exported(); - Location location = token->location(); - - if (this->advance_token()->is_op(OPERATOR_LPAREN)) - { - // This is a MethodName. - name = this->gogo_->pack_hidden_name(name, is_exported); - Type* type = this->signature(NULL, location); - if (type == NULL) - return; - methods->push_back(Typed_identifier(name, type, location)); - } - else - { - this->unget_token(Token::make_identifier_token(name, is_exported, - location)); - Type* type = this->type_name(false); - if (type->is_error_type() - || (!this->peek_token()->is_op(OPERATOR_SEMICOLON) - && !this->peek_token()->is_op(OPERATOR_RCURLY))) - { - if (this->peek_token()->is_op(OPERATOR_COMMA)) - error_at(this->location(), - "name list not allowed in interface type"); - else - error_at(location, "expected signature or type name"); - this->gogo_->mark_locals_used(); - token = this->peek_token(); - while (!token->is_eof() - && !token->is_op(OPERATOR_SEMICOLON) - && !token->is_op(OPERATOR_RCURLY)) - token = this->advance_token(); - return; - } - // This must be an interface type, but we can't check that now. - // We check it and pull out the methods in - // Interface_type::do_verify. - methods->push_back(Typed_identifier("", type, location)); - } -} - -// Declaration = ConstDecl | TypeDecl | VarDecl | FunctionDecl | MethodDecl . - -void -Parse::declaration() -{ - const Token* token = this->peek_token(); - - bool saw_nointerface = this->lex_->get_and_clear_nointerface(); - if (saw_nointerface && !token->is_keyword(KEYWORD_FUNC)) - warning_at(token->location(), 0, - "ignoring magic //go:nointerface comment before non-method"); - - if (token->is_keyword(KEYWORD_CONST)) - this->const_decl(); - else if (token->is_keyword(KEYWORD_TYPE)) - this->type_decl(); - else if (token->is_keyword(KEYWORD_VAR)) - this->var_decl(); - else if (token->is_keyword(KEYWORD_FUNC)) - this->function_decl(saw_nointerface); - else - { - error_at(this->location(), "expected declaration"); - this->advance_token(); - } -} - -bool -Parse::declaration_may_start_here() -{ - const Token* token = this->peek_token(); - return (token->is_keyword(KEYWORD_CONST) - || token->is_keyword(KEYWORD_TYPE) - || token->is_keyword(KEYWORD_VAR) - || token->is_keyword(KEYWORD_FUNC)); -} - -// Decl<P> = P | "(" [ List<P> ] ")" . - -void -Parse::decl(void (Parse::*pfn)(void*), void* varg) -{ - if (this->peek_token()->is_eof()) - { - if (!saw_errors()) - error_at(this->location(), "unexpected end of file"); - return; - } - - if (!this->peek_token()->is_op(OPERATOR_LPAREN)) - (this->*pfn)(varg); - else - { - if (!this->advance_token()->is_op(OPERATOR_RPAREN)) - { - this->list(pfn, varg, true); - if (!this->peek_token()->is_op(OPERATOR_RPAREN)) - { - error_at(this->location(), "missing %<)%>"); - while (!this->advance_token()->is_op(OPERATOR_RPAREN)) - { - if (this->peek_token()->is_eof()) - return; - } - } - } - this->advance_token(); - } -} - -// List<P> = P { ";" P } [ ";" ] . - -// In order to pick up the trailing semicolon we need to know what -// might follow. This is either a '}' or a ')'. - -void -Parse::list(void (Parse::*pfn)(void*), void* varg, bool follow_is_paren) -{ - (this->*pfn)(varg); - Operator follow = follow_is_paren ? OPERATOR_RPAREN : OPERATOR_RCURLY; - while (this->peek_token()->is_op(OPERATOR_SEMICOLON) - || this->peek_token()->is_op(OPERATOR_COMMA)) - { - if (this->peek_token()->is_op(OPERATOR_COMMA)) - error_at(this->location(), "unexpected comma"); - if (this->advance_token()->is_op(follow)) - break; - (this->*pfn)(varg); - } -} - -// ConstDecl = "const" ( ConstSpec | "(" { ConstSpec ";" } ")" ) . - -void -Parse::const_decl() -{ - go_assert(this->peek_token()->is_keyword(KEYWORD_CONST)); - this->advance_token(); - this->reset_iota(); - - Type* last_type = NULL; - Expression_list* last_expr_list = NULL; - - if (!this->peek_token()->is_op(OPERATOR_LPAREN)) - this->const_spec(&last_type, &last_expr_list); - else - { - this->advance_token(); - while (!this->peek_token()->is_op(OPERATOR_RPAREN)) - { - this->const_spec(&last_type, &last_expr_list); - if (this->peek_token()->is_op(OPERATOR_SEMICOLON)) - this->advance_token(); - else if (!this->peek_token()->is_op(OPERATOR_RPAREN)) - { - error_at(this->location(), "expected %<;%> or %<)%> or newline"); - if (!this->skip_past_error(OPERATOR_RPAREN)) - return; - } - } - this->advance_token(); - } - - if (last_expr_list != NULL) - delete last_expr_list; -} - -// ConstSpec = IdentifierList [ [ CompleteType ] "=" ExpressionList ] . - -void -Parse::const_spec(Type** last_type, Expression_list** last_expr_list) -{ - Typed_identifier_list til; - this->identifier_list(&til); - - Type* type = NULL; - if (this->type_may_start_here()) - { - type = this->type(); - *last_type = NULL; - *last_expr_list = NULL; - } - - Expression_list *expr_list; - if (!this->peek_token()->is_op(OPERATOR_EQ)) - { - if (*last_expr_list == NULL) - { - error_at(this->location(), "expected %<=%>"); - return; - } - type = *last_type; - expr_list = new Expression_list; - for (Expression_list::const_iterator p = (*last_expr_list)->begin(); - p != (*last_expr_list)->end(); - ++p) - expr_list->push_back((*p)->copy()); - } - else - { - this->advance_token(); - expr_list = this->expression_list(NULL, false, true); - *last_type = type; - if (*last_expr_list != NULL) - delete *last_expr_list; - *last_expr_list = expr_list; - } - - Expression_list::const_iterator pe = expr_list->begin(); - for (Typed_identifier_list::iterator pi = til.begin(); - pi != til.end(); - ++pi, ++pe) - { - if (pe == expr_list->end()) - { - error_at(this->location(), "not enough initializers"); - return; - } - if (type != NULL) - pi->set_type(type); - - if (!Gogo::is_sink_name(pi->name())) - this->gogo_->add_constant(*pi, *pe, this->iota_value()); - } - if (pe != expr_list->end()) - error_at(this->location(), "too many initializers"); - - this->increment_iota(); - - return; -} - -// TypeDecl = "type" Decl<TypeSpec> . - -void -Parse::type_decl() -{ - go_assert(this->peek_token()->is_keyword(KEYWORD_TYPE)); - this->advance_token(); - this->decl(&Parse::type_spec, NULL); -} - -// TypeSpec = identifier Type . - -void -Parse::type_spec(void*) -{ - const Token* token = this->peek_token(); - if (!token->is_identifier()) - { - error_at(this->location(), "expected identifier"); - return; - } - std::string name = token->identifier(); - bool is_exported = token->is_identifier_exported(); - Location location = token->location(); - token = this->advance_token(); - - // The scope of the type name starts at the point where the - // identifier appears in the source code. We implement this by - // declaring the type before we read the type definition. - Named_object* named_type = NULL; - if (name != "_") - { - name = this->gogo_->pack_hidden_name(name, is_exported); - named_type = this->gogo_->declare_type(name, location); - } - - Type* type; - if (!this->peek_token()->is_op(OPERATOR_SEMICOLON)) - type = this->type(); - else - { - error_at(this->location(), - "unexpected semicolon or newline in type declaration"); - type = Type::make_error_type(); - this->advance_token(); - } - - if (type->is_error_type()) - { - this->gogo_->mark_locals_used(); - while (!this->peek_token()->is_op(OPERATOR_SEMICOLON) - && !this->peek_token()->is_eof()) - this->advance_token(); - } - - if (name != "_") - { - if (named_type->is_type_declaration()) - { - Type* ftype = type->forwarded(); - if (ftype->forward_declaration_type() != NULL - && (ftype->forward_declaration_type()->named_object() - == named_type)) - { - error_at(location, "invalid recursive type"); - type = Type::make_error_type(); - } - - this->gogo_->define_type(named_type, - Type::make_named_type(named_type, type, - location)); - go_assert(named_type->package() == NULL); - } - else - { - // This will probably give a redefinition error. - this->gogo_->add_type(name, type, location); - } - } -} - -// VarDecl = "var" Decl<VarSpec> . - -void -Parse::var_decl() -{ - go_assert(this->peek_token()->is_keyword(KEYWORD_VAR)); - this->advance_token(); - this->decl(&Parse::var_spec, NULL); -} - -// VarSpec = IdentifierList -// ( CompleteType [ "=" ExpressionList ] | "=" ExpressionList ) . - -void -Parse::var_spec(void*) -{ - // Get the variable names. - Typed_identifier_list til; - this->identifier_list(&til); - - Location location = this->location(); - - Type* type = NULL; - Expression_list* init = NULL; - if (!this->peek_token()->is_op(OPERATOR_EQ)) - { - type = this->type(); - if (type->is_error_type()) - { - this->gogo_->mark_locals_used(); - while (!this->peek_token()->is_op(OPERATOR_EQ) - && !this->peek_token()->is_op(OPERATOR_SEMICOLON) - && !this->peek_token()->is_eof()) - this->advance_token(); - } - if (this->peek_token()->is_op(OPERATOR_EQ)) - { - this->advance_token(); - init = this->expression_list(NULL, false, true); - } - } - else - { - this->advance_token(); - init = this->expression_list(NULL, false, true); - } - - this->init_vars(&til, type, init, false, location); - - if (init != NULL) - delete init; -} - -// Create variables. TIL is a list of variable names. If TYPE is not -// NULL, it is the type of all the variables. If INIT is not NULL, it -// is an initializer list for the variables. - -void -Parse::init_vars(const Typed_identifier_list* til, Type* type, - Expression_list* init, bool is_coloneq, - Location location) -{ - // Check for an initialization which can yield multiple values. - if (init != NULL && init->size() == 1 && til->size() > 1) - { - if (this->init_vars_from_call(til, type, *init->begin(), is_coloneq, - location)) - return; - if (this->init_vars_from_map(til, type, *init->begin(), is_coloneq, - location)) - return; - if (this->init_vars_from_receive(til, type, *init->begin(), is_coloneq, - location)) - return; - if (this->init_vars_from_type_guard(til, type, *init->begin(), - is_coloneq, location)) - return; - } - - if (init != NULL && init->size() != til->size()) - { - if (init->empty() || !init->front()->is_error_expression()) - error_at(location, "wrong number of initializations"); - init = NULL; - if (type == NULL) - type = Type::make_error_type(); - } - - // Note that INIT was already parsed with the old name bindings, so - // we don't have to worry that it will accidentally refer to the - // newly declared variables. But we do have to worry about a mix of - // newly declared variables and old variables if the old variables - // appear in the initializations. - - Expression_list::const_iterator pexpr; - if (init != NULL) - pexpr = init->begin(); - bool any_new = false; - Expression_list* vars = new Expression_list(); - Expression_list* vals = new Expression_list(); - for (Typed_identifier_list::const_iterator p = til->begin(); - p != til->end(); - ++p) - { - if (init != NULL) - go_assert(pexpr != init->end()); - this->init_var(*p, type, init == NULL ? NULL : *pexpr, is_coloneq, - false, &any_new, vars, vals); - if (init != NULL) - ++pexpr; - } - if (init != NULL) - go_assert(pexpr == init->end()); - if (is_coloneq && !any_new) - error_at(location, "variables redeclared but no variable is new"); - this->finish_init_vars(vars, vals, location); -} - -// See if we need to initialize a list of variables from a function -// call. This returns true if we have set up the variables and the -// initialization. - -bool -Parse::init_vars_from_call(const Typed_identifier_list* vars, Type* type, - Expression* expr, bool is_coloneq, - Location location) -{ - Call_expression* call = expr->call_expression(); - if (call == NULL) - return false; - - // This is a function call. We can't check here whether it returns - // the right number of values, but it might. Declare the variables, - // and then assign the results of the call to them. - - Named_object* first_var = NULL; - unsigned int index = 0; - bool any_new = false; - Expression_list* ivars = new Expression_list(); - Expression_list* ivals = new Expression_list(); - for (Typed_identifier_list::const_iterator pv = vars->begin(); - pv != vars->end(); - ++pv, ++index) - { - Expression* init = Expression::make_call_result(call, index); - Named_object* no = this->init_var(*pv, type, init, is_coloneq, false, - &any_new, ivars, ivals); - - if (this->gogo_->in_global_scope() && no->is_variable()) - { - if (first_var == NULL) - first_var = no; - else - { - // The subsequent vars have an implicit dependency on - // the first one, so that everything gets initialized in - // the right order and so that we detect cycles - // correctly. - this->gogo_->record_var_depends_on(no->var_value(), first_var); - } - } - } - - if (is_coloneq && !any_new) - error_at(location, "variables redeclared but no variable is new"); - - this->finish_init_vars(ivars, ivals, location); - - return true; -} - -// See if we need to initialize a pair of values from a map index -// expression. This returns true if we have set up the variables and -// the initialization. - -bool -Parse::init_vars_from_map(const Typed_identifier_list* vars, Type* type, - Expression* expr, bool is_coloneq, - Location location) -{ - Index_expression* index = expr->index_expression(); - if (index == NULL) - return false; - if (vars->size() != 2) - return false; - - // This is an index which is being assigned to two variables. It - // must be a map index. Declare the variables, and then assign the - // results of the map index. - bool any_new = false; - Typed_identifier_list::const_iterator p = vars->begin(); - Expression* init = type == NULL ? index : NULL; - Named_object* val_no = this->init_var(*p, type, init, is_coloneq, - type == NULL, &any_new, NULL, NULL); - if (type == NULL && any_new && val_no->is_variable()) - val_no->var_value()->set_type_from_init_tuple(); - Expression* val_var = Expression::make_var_reference(val_no, location); - - ++p; - Type* var_type = type; - if (var_type == NULL) - var_type = Type::lookup_bool_type(); - Named_object* no = this->init_var(*p, var_type, NULL, is_coloneq, false, - &any_new, NULL, NULL); - Expression* present_var = Expression::make_var_reference(no, location); - - if (is_coloneq && !any_new) - error_at(location, "variables redeclared but no variable is new"); - - Statement* s = Statement::make_tuple_map_assignment(val_var, present_var, - index, location); - - if (!this->gogo_->in_global_scope()) - this->gogo_->add_statement(s); - else if (!val_no->is_sink()) - { - if (val_no->is_variable()) - 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(this->gogo_, s); - } - else - { - // Execute the map index expression just so that we can fail if - // the map is nil. - Named_object* dummy = this->create_dummy_global(Type::lookup_bool_type(), - NULL, location); - dummy->var_value()->add_preinit_statement(this->gogo_, s); - } - - return true; -} - -// See if we need to initialize a pair of values from a receive -// expression. This returns true if we have set up the variables and -// the initialization. - -bool -Parse::init_vars_from_receive(const Typed_identifier_list* vars, Type* type, - Expression* expr, bool is_coloneq, - Location location) -{ - Receive_expression* receive = expr->receive_expression(); - if (receive == NULL) - return false; - if (vars->size() != 2) - return false; - - // This is a receive expression which is being assigned to two - // variables. Declare the variables, and then assign the results of - // the receive. - bool any_new = false; - Typed_identifier_list::const_iterator p = vars->begin(); - Expression* init = type == NULL ? receive : NULL; - Named_object* val_no = this->init_var(*p, type, init, is_coloneq, - type == NULL, &any_new, NULL, NULL); - if (type == NULL && any_new && val_no->is_variable()) - val_no->var_value()->set_type_from_init_tuple(); - Expression* val_var = Expression::make_var_reference(val_no, location); - - ++p; - Type* var_type = type; - if (var_type == NULL) - var_type = Type::lookup_bool_type(); - Named_object* no = this->init_var(*p, var_type, NULL, is_coloneq, false, - &any_new, NULL, NULL); - Expression* received_var = Expression::make_var_reference(no, location); - - if (is_coloneq && !any_new) - error_at(location, "variables redeclared but no variable is new"); - - Statement* s = Statement::make_tuple_receive_assignment(val_var, - received_var, - receive->channel(), - location); - - if (!this->gogo_->in_global_scope()) - this->gogo_->add_statement(s); - else if (!val_no->is_sink()) - { - if (val_no->is_variable()) - 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(this->gogo_, s); - } - else - { - Named_object* dummy = this->create_dummy_global(Type::lookup_bool_type(), - NULL, location); - dummy->var_value()->add_preinit_statement(this->gogo_, s); - } - - return true; -} - -// See if we need to initialize a pair of values from a type guard -// expression. This returns true if we have set up the variables and -// the initialization. - -bool -Parse::init_vars_from_type_guard(const Typed_identifier_list* vars, - Type* type, Expression* expr, - bool is_coloneq, Location location) -{ - Type_guard_expression* type_guard = expr->type_guard_expression(); - if (type_guard == NULL) - return false; - if (vars->size() != 2) - return false; - - // This is a type guard expression which is being assigned to two - // variables. Declare the variables, and then assign the results of - // the type guard. - bool any_new = false; - Typed_identifier_list::const_iterator p = vars->begin(); - Type* var_type = type; - if (var_type == NULL) - var_type = type_guard->type(); - Named_object* val_no = this->init_var(*p, var_type, NULL, is_coloneq, false, - &any_new, NULL, NULL); - Expression* val_var = Expression::make_var_reference(val_no, location); - - ++p; - var_type = type; - if (var_type == NULL) - var_type = Type::lookup_bool_type(); - Named_object* no = this->init_var(*p, var_type, NULL, is_coloneq, false, - &any_new, NULL, NULL); - Expression* ok_var = Expression::make_var_reference(no, location); - - Expression* texpr = type_guard->expr(); - Type* t = type_guard->type(); - Statement* s = Statement::make_tuple_type_guard_assignment(val_var, ok_var, - texpr, t, - location); - - if (is_coloneq && !any_new) - error_at(location, "variables redeclared but no variable is new"); - - if (!this->gogo_->in_global_scope()) - this->gogo_->add_statement(s); - else if (!val_no->is_sink()) - { - if (val_no->is_variable()) - 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(this->gogo_, s); - } - else - { - Named_object* dummy = this->create_dummy_global(type, NULL, location); - dummy->var_value()->add_preinit_statement(this->gogo_, s); - } - - return true; -} - -// Create a single variable. If IS_COLONEQ is true, we permit -// redeclarations in the same block, and we set *IS_NEW when we find a -// new variable which is not a redeclaration. - -Named_object* -Parse::init_var(const Typed_identifier& tid, Type* type, Expression* init, - bool is_coloneq, bool type_from_init, bool* is_new, - Expression_list* vars, Expression_list* vals) -{ - Location location = tid.location(); - - if (Gogo::is_sink_name(tid.name())) - { - if (!type_from_init && init != NULL) - { - if (this->gogo_->in_global_scope()) - return this->create_dummy_global(type, init, location); - else if (type == NULL) - this->gogo_->add_statement(Statement::make_statement(init, true)); - else - { - // With both a type and an initializer, create a dummy - // variable so that we will check whether the - // initializer can be assigned to the type. - Variable* var = new Variable(type, init, false, false, false, - location); - var->set_is_used(); - static int count; - char buf[30]; - snprintf(buf, sizeof buf, "sink$%d", count); - ++count; - return this->gogo_->add_variable(buf, var); - } - } - if (type != NULL) - this->gogo_->add_type_to_verify(type); - return this->gogo_->add_sink(); - } - - if (is_coloneq) - { - Named_object* no = this->gogo_->lookup_in_block(tid.name()); - if (no != NULL - && (no->is_variable() || no->is_result_variable())) - { - // INIT may be NULL even when IS_COLONEQ is true for cases - // like v, ok := x.(int). - if (!type_from_init && init != NULL) - { - go_assert(vars != NULL && vals != NULL); - vars->push_back(Expression::make_var_reference(no, location)); - vals->push_back(init); - } - return no; - } - } - *is_new = true; - Variable* var = new Variable(type, init, this->gogo_->in_global_scope(), - false, false, location); - 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 -// the right place. This is used when a sink variable is initialized -// at global scope. - -Named_object* -Parse::create_dummy_global(Type* type, Expression* init, - 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]; - snprintf(buf, sizeof buf, "_.%d", count); - ++count; - return this->gogo_->add_variable(buf, var); -} - -// Finish the variable initialization by executing any assignments to -// existing variables when using :=. These must be done as a tuple -// assignment in case of something like n, a, b := 1, b, a. - -void -Parse::finish_init_vars(Expression_list* vars, Expression_list* vals, - Location location) -{ - if (vars->empty()) - { - delete vars; - delete vals; - } - else if (vars->size() == 1) - { - go_assert(!this->gogo_->in_global_scope()); - this->gogo_->add_statement(Statement::make_assignment(vars->front(), - vals->front(), - location)); - delete vars; - delete vals; - } - else - { - go_assert(!this->gogo_->in_global_scope()); - this->gogo_->add_statement(Statement::make_tuple_assignment(vars, vals, - location)); - } -} - -// SimpleVarDecl = identifier ":=" Expression . - -// We've already seen the identifier. - -// FIXME: We also have to implement -// IdentifierList ":=" ExpressionList -// In order to support both "a, b := 1, 0" and "a, b = 1, 0" we accept -// tuple assignments here as well. - -// If MAY_BE_COMPOSITE_LIT is true, the expression on the right hand -// side may be a composite literal. - -// If P_RANGE_CLAUSE is not NULL, then this will recognize a -// RangeClause. - -// If P_TYPE_SWITCH is not NULL, this will recognize a type switch -// guard (var := expr.("type") using the literal keyword "type"). - -void -Parse::simple_var_decl_or_assignment(const std::string& name, - Location location, - bool may_be_composite_lit, - Range_clause* p_range_clause, - Type_switch* p_type_switch) -{ - Typed_identifier_list til; - til.push_back(Typed_identifier(name, NULL, location)); - - // We've seen one identifier. If we see a comma now, this could be - // "a, *p = 1, 2". - if (this->peek_token()->is_op(OPERATOR_COMMA)) - { - go_assert(p_type_switch == NULL); - while (true) - { - const Token* token = this->advance_token(); - if (!token->is_identifier()) - break; - - std::string id = token->identifier(); - bool is_id_exported = token->is_identifier_exported(); - Location id_location = token->location(); - - token = this->advance_token(); - if (!token->is_op(OPERATOR_COMMA)) - { - if (token->is_op(OPERATOR_COLONEQ)) - { - id = this->gogo_->pack_hidden_name(id, is_id_exported); - til.push_back(Typed_identifier(id, NULL, location)); - } - else - this->unget_token(Token::make_identifier_token(id, - is_id_exported, - id_location)); - break; - } - - id = this->gogo_->pack_hidden_name(id, is_id_exported); - til.push_back(Typed_identifier(id, NULL, location)); - } - - // We have a comma separated list of identifiers in TIL. If the - // next token is COLONEQ, then this is a simple var decl, and we - // have the complete list of identifiers. If the next token is - // not COLONEQ, then the only valid parse is a tuple assignment. - // The list of identifiers we have so far is really a list of - // expressions. There are more expressions following. - - if (!this->peek_token()->is_op(OPERATOR_COLONEQ)) - { - Expression_list* exprs = new Expression_list; - for (Typed_identifier_list::const_iterator p = til.begin(); - p != til.end(); - ++p) - exprs->push_back(this->id_to_expression(p->name(), - p->location())); - - Expression_list* more_exprs = - this->expression_list(NULL, true, may_be_composite_lit); - for (Expression_list::const_iterator p = more_exprs->begin(); - p != more_exprs->end(); - ++p) - exprs->push_back(*p); - delete more_exprs; - - this->tuple_assignment(exprs, may_be_composite_lit, p_range_clause); - return; - } - } - - 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)) - { - this->range_clause_decl(&til, p_range_clause); - return; - } - - Expression_list* init; - if (p_type_switch == NULL) - init = this->expression_list(NULL, false, may_be_composite_lit); - else - { - bool is_type_switch = false; - Expression* expr = this->expression(PRECEDENCE_NORMAL, false, - may_be_composite_lit, - &is_type_switch, NULL); - if (is_type_switch) - { - p_type_switch->found = true; - p_type_switch->name = name; - p_type_switch->location = location; - p_type_switch->expr = expr; - return; - } - - if (!this->peek_token()->is_op(OPERATOR_COMMA)) - { - init = new Expression_list(); - init->push_back(expr); - } - else - { - this->advance_token(); - init = this->expression_list(expr, false, may_be_composite_lit); - } - } - - this->init_vars(&til, NULL, init, true, location); -} - -// FunctionDecl = "func" identifier Signature [ Block ] . -// MethodDecl = "func" Receiver identifier Signature [ Block ] . - -// Deprecated gcc extension: -// FunctionDecl = "func" identifier Signature -// __asm__ "(" string_lit ")" . -// This extension means a function whose real name is the identifier -// inside the asm. This extension will be removed at some future -// date. It has been replaced with //extern comments. - -// SAW_NOINTERFACE is true if we saw a magic //go:nointerface comment, -// which means that we omit the method from the type descriptor. - -void -Parse::function_decl(bool saw_nointerface) -{ - go_assert(this->peek_token()->is_keyword(KEYWORD_FUNC)); - Location location = this->location(); - std::string extern_name = this->lex_->extern_name(); - const Token* token = this->advance_token(); - - Typed_identifier* rec = NULL; - if (token->is_op(OPERATOR_LPAREN)) - { - rec = this->receiver(); - token = this->peek_token(); - } - else if (saw_nointerface) - { - warning_at(location, 0, - "ignoring magic //go:nointerface comment before non-method"); - saw_nointerface = false; - } - - if (!token->is_identifier()) - { - error_at(this->location(), "expected function name"); - return; - } - - std::string name = - this->gogo_->pack_hidden_name(token->identifier(), - token->is_identifier_exported()); - - this->advance_token(); - - Function_type* fntype = this->signature(rec, this->location()); - - Named_object* named_object = NULL; - - if (this->peek_token()->is_keyword(KEYWORD_ASM)) - { - if (!this->advance_token()->is_op(OPERATOR_LPAREN)) - { - error_at(this->location(), "expected %<(%>"); - return; - } - token = this->advance_token(); - if (!token->is_string()) - { - error_at(this->location(), "expected string"); - return; - } - std::string asm_name = token->string_value(); - if (!this->advance_token()->is_op(OPERATOR_RPAREN)) - { - error_at(this->location(), "expected %<)%>"); - return; - } - this->advance_token(); - 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_SEMICOLON)) - { - Location semi_loc = this->location(); - if (this->advance_token()->is_op(OPERATOR_LCURLY)) - error_at(this->location(), - "unexpected semicolon or newline before %<{%>"); - else - this->unget_token(Token::make_operator_token(OPERATOR_SEMICOLON, - semi_loc)); - } - - if (!this->peek_token()->is_op(OPERATOR_LCURLY)) - { - if (named_object == NULL && !Gogo::is_sink_name(name)) - { - if (fntype == NULL) - this->gogo_->add_erroneous_name(name); - else - { - named_object = this->gogo_->declare_function(name, fntype, - location); - if (!extern_name.empty() - && named_object->is_function_declaration()) - { - Function_declaration* fd = - named_object->func_declaration_value(); - fd->set_asm_name(extern_name); - } - } - } - - if (saw_nointerface) - warning_at(location, 0, - ("ignoring magic //go:nointerface comment " - "before declaration")); - } - 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); - } - named_object = this->gogo_->start_function(name, fntype, true, location); - Location end_loc = this->block(); - this->gogo_->finish_function(end_loc); - if (saw_nointerface - && !this->is_erroneous_function_ - && named_object->is_function()) - named_object->func_value()->set_nointerface(); - this->is_erroneous_function_ = hold_is_erroneous_function; - } -} - -// Receiver = "(" [ identifier ] [ "*" ] BaseTypeName ")" . -// BaseTypeName = identifier . - -Typed_identifier* -Parse::receiver() -{ - go_assert(this->peek_token()->is_op(OPERATOR_LPAREN)); - - std::string name; - const Token* token = this->advance_token(); - Location location = token->location(); - if (!token->is_op(OPERATOR_MULT)) - { - if (!token->is_identifier()) - { - error_at(this->location(), "method has no receiver"); - this->gogo_->mark_locals_used(); - while (!token->is_eof() && !token->is_op(OPERATOR_RPAREN)) - token = this->advance_token(); - if (!token->is_eof()) - this->advance_token(); - return NULL; - } - name = token->identifier(); - bool is_exported = token->is_identifier_exported(); - token = this->advance_token(); - if (!token->is_op(OPERATOR_DOT) && !token->is_op(OPERATOR_RPAREN)) - { - // An identifier followed by something other than a dot or a - // right parenthesis must be a receiver name followed by a - // type. - name = this->gogo_->pack_hidden_name(name, is_exported); - } - else - { - // This must be a type name. - this->unget_token(Token::make_identifier_token(name, is_exported, - location)); - token = this->peek_token(); - name.clear(); - } - } - - // Here the receiver name is in NAME (it is empty if the receiver is - // unnamed) and TOKEN is the first token in the type. - - bool is_pointer = false; - if (token->is_op(OPERATOR_MULT)) - { - is_pointer = true; - token = this->advance_token(); - } - - if (!token->is_identifier()) - { - error_at(this->location(), "expected receiver name or type"); - this->gogo_->mark_locals_used(); - int c = token->is_op(OPERATOR_LPAREN) ? 1 : 0; - while (!token->is_eof()) - { - token = this->advance_token(); - if (token->is_op(OPERATOR_LPAREN)) - ++c; - else if (token->is_op(OPERATOR_RPAREN)) - { - if (c == 0) - break; - --c; - } - } - if (!token->is_eof()) - this->advance_token(); - return NULL; - } - - Type* type = this->type_name(true); - - if (is_pointer && !type->is_error_type()) - type = Type::make_pointer_type(type); - - if (this->peek_token()->is_op(OPERATOR_RPAREN)) - this->advance_token(); - else - { - if (this->peek_token()->is_op(OPERATOR_COMMA)) - error_at(this->location(), "method has multiple receivers"); - else - error_at(this->location(), "expected %<)%>"); - this->gogo_->mark_locals_used(); - while (!token->is_eof() && !token->is_op(OPERATOR_RPAREN)) - token = this->advance_token(); - if (!token->is_eof()) - this->advance_token(); - return NULL; - } - - return new Typed_identifier(name, type, location); -} - -// Operand = Literal | QualifiedIdent | MethodExpr | "(" Expression ")" . -// Literal = BasicLit | CompositeLit | FunctionLit . -// BasicLit = int_lit | float_lit | imaginary_lit | char_lit | string_lit . - -// If MAY_BE_SINK is true, this operand may be "_". - -// If IS_PARENTHESIZED is not NULL, *IS_PARENTHESIZED is set to true -// if the entire expression is in parentheses. - -Expression* -Parse::operand(bool may_be_sink, bool* is_parenthesized) -{ - const Token* token = this->peek_token(); - Expression* ret; - switch (token->classification()) - { - case Token::TOKEN_IDENTIFIER: - { - Location location = token->location(); - std::string id = token->identifier(); - bool is_exported = token->is_identifier_exported(); - std::string packed = this->gogo_->pack_hidden_name(id, is_exported); - - Named_object* in_function; - Named_object* named_object = this->gogo_->lookup(packed, &in_function); - - Package* package = NULL; - if (named_object != NULL && named_object->is_package()) - { - if (!this->advance_token()->is_op(OPERATOR_DOT) - || !this->advance_token()->is_identifier()) - { - error_at(location, "unexpected reference to package"); - return Expression::make_error(location); - } - package = named_object->package_value(); - package->set_used(); - id = this->peek_token()->identifier(); - is_exported = this->peek_token()->is_identifier_exported(); - packed = this->gogo_->pack_hidden_name(id, is_exported); - named_object = package->lookup(packed); - location = this->location(); - go_assert(in_function == NULL); - } - - this->advance_token(); - - if (named_object != NULL - && named_object->is_type() - && !named_object->type_value()->is_visible()) - { - go_assert(package != NULL); - error_at(location, "invalid reference to hidden type %<%s.%s%>", - Gogo::message_name(package->package_name()).c_str(), - Gogo::message_name(id).c_str()); - return Expression::make_error(location); - } - - - if (named_object == NULL) - { - if (package != NULL) - { - std::string n1 = Gogo::message_name(package->package_name()); - std::string n2 = Gogo::message_name(id); - if (!is_exported) - error_at(location, - ("invalid reference to unexported identifier " - "%<%s.%s%>"), - n1.c_str(), n2.c_str()); - else - error_at(location, - "reference to undefined identifier %<%s.%s%>", - n1.c_str(), n2.c_str()); - return Expression::make_error(location); - } - - named_object = this->gogo_->add_unknown_name(packed, location); - } - - if (in_function != NULL - && in_function != this->gogo_->current_function() - && (named_object->is_variable() - || named_object->is_result_variable())) - return this->enclosing_var_reference(in_function, named_object, - location); - - switch (named_object->classification()) - { - case Named_object::NAMED_OBJECT_CONST: - return Expression::make_const_reference(named_object, location); - case Named_object::NAMED_OBJECT_TYPE: - return Expression::make_type(named_object->type_value(), location); - case Named_object::NAMED_OBJECT_TYPE_DECLARATION: - { - Type* t = Type::make_forward_declaration(named_object); - return Expression::make_type(t, location); - } - case Named_object::NAMED_OBJECT_VAR: - case Named_object::NAMED_OBJECT_RESULT_VAR: - this->mark_var_used(named_object); - return Expression::make_var_reference(named_object, location); - case Named_object::NAMED_OBJECT_SINK: - if (may_be_sink) - return Expression::make_sink(location); - else - { - error_at(location, "cannot use _ as value"); - return Expression::make_error(location); - } - case Named_object::NAMED_OBJECT_FUNC: - case Named_object::NAMED_OBJECT_FUNC_DECLARATION: - return Expression::make_func_reference(named_object, NULL, - location); - case Named_object::NAMED_OBJECT_UNKNOWN: - { - 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(); - } - } - go_unreachable(); - - case Token::TOKEN_STRING: - ret = Expression::make_string(token->string_value(), token->location()); - this->advance_token(); - return ret; - - case Token::TOKEN_CHARACTER: - ret = Expression::make_character(token->character_value(), NULL, - token->location()); - this->advance_token(); - return ret; - - case Token::TOKEN_INTEGER: - ret = Expression::make_integer(token->integer_value(), NULL, - token->location()); - this->advance_token(); - return ret; - - case Token::TOKEN_FLOAT: - ret = Expression::make_float(token->float_value(), NULL, - token->location()); - this->advance_token(); - return ret; - - case Token::TOKEN_IMAGINARY: - { - mpfr_t zero; - mpfr_init_set_ui(zero, 0, GMP_RNDN); - ret = Expression::make_complex(&zero, token->imaginary_value(), - NULL, token->location()); - mpfr_clear(zero); - this->advance_token(); - return ret; - } - - case Token::TOKEN_KEYWORD: - switch (token->keyword()) - { - case KEYWORD_FUNC: - return this->function_lit(); - case KEYWORD_CHAN: - case KEYWORD_INTERFACE: - case KEYWORD_MAP: - case KEYWORD_STRUCT: - { - Location location = token->location(); - return Expression::make_type(this->type(), location); - } - default: - break; - } - break; - - case Token::TOKEN_OPERATOR: - if (token->is_op(OPERATOR_LPAREN)) - { - this->advance_token(); - ret = this->expression(PRECEDENCE_NORMAL, may_be_sink, true, NULL, - NULL); - if (!this->peek_token()->is_op(OPERATOR_RPAREN)) - error_at(this->location(), "missing %<)%>"); - else - this->advance_token(); - if (is_parenthesized != NULL) - *is_parenthesized = true; - return ret; - } - else if (token->is_op(OPERATOR_LSQUARE)) - { - // Here we call array_type directly, as this is the only - // case where an ellipsis is permitted for an array type. - Location location = token->location(); - return Expression::make_type(this->array_type(true), location); - } - break; - - default: - break; - } - - error_at(this->location(), "expected operand"); - return Expression::make_error(this->location()); -} - -// Handle a reference to a variable in an enclosing function. We add -// it to a list of such variables. We return a reference to a field -// in a struct which will be passed on the static chain when calling -// the current function. - -Expression* -Parse::enclosing_var_reference(Named_object* in_function, Named_object* var, - Location location) -{ - go_assert(var->is_variable() || var->is_result_variable()); - - this->mark_var_used(var); - - Named_object* this_function = this->gogo_->current_function(); - Named_object* closure = this_function->func_value()->closure_var(); - - Enclosing_var ev(var, in_function, this->enclosing_vars_.size()); - std::pair<Enclosing_vars::iterator, bool> ins = - this->enclosing_vars_.insert(ev); - if (ins.second) - { - // This is a variable we have not seen before. Add a new field - // to the closure type. - this_function->func_value()->add_closure_field(var, location); - } - - Expression* closure_ref = Expression::make_var_reference(closure, - location); - closure_ref = Expression::make_unary(OPERATOR_MULT, closure_ref, location); - - // The closure structure holds pointers to the variables, so we need - // to introduce an indirection. - Expression* e = Expression::make_field_reference(closure_ref, - ins.first->index(), - location); - e = Expression::make_unary(OPERATOR_MULT, e, location); - return e; -} - -// CompositeLit = LiteralType LiteralValue . -// LiteralType = StructType | ArrayType | "[" "..." "]" ElementType | -// SliceType | MapType | TypeName . -// LiteralValue = "{" [ ElementList [ "," ] ] "}" . -// ElementList = Element { "," Element } . -// Element = [ Key ":" ] Value . -// Key = FieldName | ElementIndex . -// FieldName = identifier . -// ElementIndex = Expression . -// Value = Expression | LiteralValue . - -// We have already seen the type if there is one, and we are now -// looking at the LiteralValue. The case "[" "..." "]" ElementType -// will be seen here as an array type whose length is "nil". The -// DEPTH parameter is non-zero if this is an embedded composite -// literal and the type was omitted. It gives the number of steps up -// to the type which was provided. E.g., in [][]int{{1}} it will be -// 1. In [][][]int{{{1}}} it will be 2. - -Expression* -Parse::composite_lit(Type* type, int depth, Location location) -{ - go_assert(this->peek_token()->is_op(OPERATOR_LCURLY)); - this->advance_token(); - - if (this->peek_token()->is_op(OPERATOR_RCURLY)) - { - this->advance_token(); - return Expression::make_composite_literal(type, depth, false, NULL, - location); - } - - bool has_keys = false; - Expression_list* vals = new Expression_list; - while (true) - { - Expression* val; - bool is_type_omitted = false; - - const Token* token = this->peek_token(); - - if (token->is_identifier()) - { - std::string identifier = token->identifier(); - bool is_exported = token->is_identifier_exported(); - 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, - NULL); - } - } - else if (!token->is_op(OPERATOR_LCURLY)) - val = this->expression(PRECEDENCE_NORMAL, false, true, NULL, NULL); - else - { - // This must be a composite literal inside another composite - // literal, with the type omitted for the inner one. - val = this->composite_lit(type, depth + 1, token->location()); - is_type_omitted = true; - } - - token = this->peek_token(); - if (!token->is_op(OPERATOR_COLON)) - { - if (has_keys) - vals->push_back(NULL); - } - else - { - if (is_type_omitted && !val->is_error_expression()) - { - error_at(this->location(), "unexpected %<:%>"); - val = Expression::make_error(this->location()); - } - - this->advance_token(); - - if (!has_keys && !vals->empty()) - { - Expression_list* newvals = new Expression_list; - for (Expression_list::const_iterator p = vals->begin(); - p != vals->end(); - ++p) - { - newvals->push_back(NULL); - newvals->push_back(*p); - } - delete vals; - vals = newvals; - } - has_keys = true; - - if (val->unknown_expression() != NULL) - val->unknown_expression()->set_is_composite_literal_key(); - - vals->push_back(val); - - if (!token->is_op(OPERATOR_LCURLY)) - val = this->expression(PRECEDENCE_NORMAL, false, true, NULL, NULL); - else - { - // This must be a composite literal inside another - // composite literal, with the type omitted for the - // inner one. - val = this->composite_lit(type, depth + 1, token->location()); - } - - token = this->peek_token(); - } - - vals->push_back(val); - - if (token->is_op(OPERATOR_COMMA)) - { - if (this->advance_token()->is_op(OPERATOR_RCURLY)) - { - this->advance_token(); - break; - } - } - else if (token->is_op(OPERATOR_RCURLY)) - { - this->advance_token(); - break; - } - else - { - if (token->is_op(OPERATOR_SEMICOLON)) - error_at(this->location(), - "need trailing comma before newline in composite literal"); - else - error_at(this->location(), "expected %<,%> or %<}%>"); - - this->gogo_->mark_locals_used(); - int depth = 0; - while (!token->is_eof() - && (depth > 0 || !token->is_op(OPERATOR_RCURLY))) - { - if (token->is_op(OPERATOR_LCURLY)) - ++depth; - else if (token->is_op(OPERATOR_RCURLY)) - --depth; - token = this->advance_token(); - } - if (token->is_op(OPERATOR_RCURLY)) - this->advance_token(); - - return Expression::make_error(location); - } - } - - return Expression::make_composite_literal(type, depth, has_keys, vals, - location); -} - -// FunctionLit = "func" Signature Block . - -Expression* -Parse::function_lit() -{ - Location location = this->location(); - go_assert(this->peek_token()->is_keyword(KEYWORD_FUNC)); - this->advance_token(); - - Enclosing_vars hold_enclosing_vars; - 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); - 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; - this->continue_stack_ = NULL; - - Named_object* no = this->gogo_->start_function("", type, true, location); - - 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; - - this->is_erroneous_function_ = hold_is_erroneous_function; - - hold_enclosing_vars.swap(this->enclosing_vars_); - - Expression* closure = this->create_closure(no, &hold_enclosing_vars, - location); - - return Expression::make_func_reference(no, closure, location); -} - -// Create a closure for the nested function FUNCTION. This is based -// on ENCLOSING_VARS, which is a list of all variables defined in -// enclosing functions and referenced from FUNCTION. A closure is the -// address of a struct which contains the addresses of all the -// referenced variables. This returns NULL if no closure is required. - -Expression* -Parse::create_closure(Named_object* function, Enclosing_vars* enclosing_vars, - Location location) -{ - if (enclosing_vars->empty()) - return NULL; - - // Get the variables in order by their field index. - - size_t enclosing_var_count = enclosing_vars->size(); - std::vector<Enclosing_var> ev(enclosing_var_count); - for (Enclosing_vars::const_iterator p = enclosing_vars->begin(); - p != enclosing_vars->end(); - ++p) - ev[p->index()] = *p; - - // Build an initializer for a composite literal of the closure's - // type. - - Named_object* enclosing_function = this->gogo_->current_function(); - Expression_list* initializer = new Expression_list; - for (size_t i = 0; i < enclosing_var_count; ++i) - { - go_assert(ev[i].index() == i); - Named_object* var = ev[i].var(); - Expression* ref; - if (ev[i].in_function() == enclosing_function) - ref = Expression::make_var_reference(var, location); - else - ref = this->enclosing_var_reference(ev[i].in_function(), var, - location); - Expression* refaddr = Expression::make_unary(OPERATOR_AND, ref, - location); - initializer->push_back(refaddr); - } - - Named_object* closure_var = function->func_value()->closure_var(); - Struct_type* st = closure_var->var_value()->type()->deref()->struct_type(); - Expression* cv = Expression::make_struct_composite_literal(st, initializer, - location); - return Expression::make_heap_composite(cv, location); -} - -// PrimaryExpr = Operand { Selector | Index | Slice | TypeGuard | Call } . - -// If MAY_BE_SINK is true, this expression may be "_". - -// If MAY_BE_COMPOSITE_LIT is true, this expression may be a composite -// literal. - -// If IS_TYPE_SWITCH is not NULL, this will recognize a type switch -// guard (var := expr.("type") using the literal keyword "type"). - -// If IS_PARENTHESIZED is not NULL, *IS_PARENTHESIZED is set to true -// if the entire expression is in parentheses. - -Expression* -Parse::primary_expr(bool may_be_sink, bool may_be_composite_lit, - bool* is_type_switch, bool* is_parenthesized) -{ - Location start_loc = this->location(); - bool operand_is_parenthesized = false; - bool whole_is_parenthesized = false; - - Expression* ret = this->operand(may_be_sink, &operand_is_parenthesized); - - whole_is_parenthesized = operand_is_parenthesized; - - // An unknown name followed by a curly brace must be a composite - // literal, and the unknown name must be a type. - if (may_be_composite_lit - && !operand_is_parenthesized - && ret->unknown_expression() != NULL - && this->peek_token()->is_op(OPERATOR_LCURLY)) - { - Named_object* no = ret->unknown_expression()->named_object(); - Type* type = Type::make_forward_declaration(no); - ret = Expression::make_type(type, ret->location()); - } - - // We handle composite literals and type casts here, as it is the - // easiest way to handle types which are in parentheses, as in - // "((uint))(1)". - if (ret->is_type_expression()) - { - if (this->peek_token()->is_op(OPERATOR_LCURLY)) - { - whole_is_parenthesized = false; - if (!may_be_composite_lit) - { - Type* t = ret->type(); - if (t->named_type() != NULL - || t->forward_declaration_type() != NULL) - error_at(start_loc, - _("parentheses required around this composite literal " - "to avoid parsing ambiguity")); - } - else if (operand_is_parenthesized) - error_at(start_loc, - "cannot parenthesize type in composite literal"); - ret = this->composite_lit(ret->type(), 0, ret->location()); - } - else if (this->peek_token()->is_op(OPERATOR_LPAREN)) - { - whole_is_parenthesized = false; - Location loc = this->location(); - this->advance_token(); - Expression* expr = this->expression(PRECEDENCE_NORMAL, false, true, - NULL, NULL); - if (this->peek_token()->is_op(OPERATOR_COMMA)) - this->advance_token(); - 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()) - 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); - } - } - } - - while (true) - { - const Token* token = this->peek_token(); - if (token->is_op(OPERATOR_LPAREN)) - { - whole_is_parenthesized = false; - ret = this->call(this->verify_not_sink(ret)); - } - else if (token->is_op(OPERATOR_DOT)) - { - whole_is_parenthesized = false; - ret = this->selector(this->verify_not_sink(ret), is_type_switch); - if (is_type_switch != NULL && *is_type_switch) - break; - } - else if (token->is_op(OPERATOR_LSQUARE)) - { - whole_is_parenthesized = false; - ret = this->index(this->verify_not_sink(ret)); - } - else - break; - } - - if (whole_is_parenthesized && is_parenthesized != NULL) - *is_parenthesized = true; - - return ret; -} - -// Selector = "." identifier . -// TypeGuard = "." "(" QualifiedIdent ")" . - -// Note that Operand can expand to QualifiedIdent, which contains a -// ".". That is handled directly in operand when it sees a package -// name. - -// If IS_TYPE_SWITCH is not NULL, this will recognize a type switch -// guard (var := expr.("type") using the literal keyword "type"). - -Expression* -Parse::selector(Expression* left, bool* is_type_switch) -{ - go_assert(this->peek_token()->is_op(OPERATOR_DOT)); - Location location = this->location(); - - const Token* token = this->advance_token(); - if (token->is_identifier()) - { - // This could be a field in a struct, or a method in an - // interface, or a method associated with a type. We can't know - // which until we have seen all the types. - std::string name = - this->gogo_->pack_hidden_name(token->identifier(), - token->is_identifier_exported()); - if (token->identifier() == "_") - { - error_at(this->location(), "invalid use of %<_%>"); - name = this->gogo_->pack_hidden_name("blank", false); - } - this->advance_token(); - return Expression::make_selector(left, name, location); - } - else if (token->is_op(OPERATOR_LPAREN)) - { - this->advance_token(); - Type* type = NULL; - if (!this->peek_token()->is_keyword(KEYWORD_TYPE)) - type = this->type(); - else - { - if (is_type_switch != NULL) - *is_type_switch = true; - else - { - error_at(this->location(), - "use of %<.(type)%> outside type switch"); - type = Type::make_error_type(); - } - this->advance_token(); - } - if (!this->peek_token()->is_op(OPERATOR_RPAREN)) - error_at(this->location(), "missing %<)%>"); - else - this->advance_token(); - if (is_type_switch != NULL && *is_type_switch) - return left; - return Expression::make_type_guard(left, type, location); - } - else - { - error_at(this->location(), "expected identifier or %<(%>"); - return left; - } -} - -// Index = "[" Expression "]" . -// Slice = "[" Expression ":" [ Expression ] "]" . - -Expression* -Parse::index(Expression* expr) -{ - Location location = this->location(); - go_assert(this->peek_token()->is_op(OPERATOR_LSQUARE)); - this->advance_token(); - - Expression* start; - if (!this->peek_token()->is_op(OPERATOR_COLON)) - start = this->expression(PRECEDENCE_NORMAL, false, true, NULL, NULL); - else - { - mpz_t zero; - mpz_init_set_ui(zero, 0); - start = Expression::make_integer(&zero, NULL, location); - mpz_clear(zero); - } - - Expression* end = NULL; - if (this->peek_token()->is_op(OPERATOR_COLON)) - { - // We use nil to indicate a missing high expression. - if (this->advance_token()->is_op(OPERATOR_RSQUARE)) - end = Expression::make_nil(this->location()); - else - end = this->expression(PRECEDENCE_NORMAL, false, true, NULL, NULL); - } - if (!this->peek_token()->is_op(OPERATOR_RSQUARE)) - error_at(this->location(), "missing %<]%>"); - else - this->advance_token(); - return Expression::make_index(expr, start, end, location); -} - -// Call = "(" [ ArgumentList [ "," ] ] ")" . -// ArgumentList = ExpressionList [ "..." ] . - -Expression* -Parse::call(Expression* func) -{ - go_assert(this->peek_token()->is_op(OPERATOR_LPAREN)); - Expression_list* args = NULL; - bool is_varargs = false; - const Token* token = this->advance_token(); - if (!token->is_op(OPERATOR_RPAREN)) - { - args = this->expression_list(NULL, false, true); - token = this->peek_token(); - if (token->is_op(OPERATOR_ELLIPSIS)) - { - is_varargs = true; - token = this->advance_token(); - } - } - if (token->is_op(OPERATOR_COMMA)) - token = this->advance_token(); - if (!token->is_op(OPERATOR_RPAREN)) - error_at(this->location(), "missing %<)%>"); - else - this->advance_token(); - if (func->is_error_expression()) - return func; - return Expression::make_call(func, args, is_varargs, func->location()); -} - -// Return an expression for a single unqualified identifier. - -Expression* -Parse::id_to_expression(const std::string& name, Location location) -{ - Named_object* in_function; - Named_object* named_object = this->gogo_->lookup(name, &in_function); - if (named_object == NULL) - named_object = this->gogo_->add_unknown_name(name, location); - - if (in_function != NULL - && in_function != this->gogo_->current_function() - && (named_object->is_variable() || named_object->is_result_variable())) - return this->enclosing_var_reference(in_function, named_object, - location); - - switch (named_object->classification()) - { - case Named_object::NAMED_OBJECT_CONST: - return Expression::make_const_reference(named_object, location); - case Named_object::NAMED_OBJECT_VAR: - case Named_object::NAMED_OBJECT_RESULT_VAR: - this->mark_var_used(named_object); - return Expression::make_var_reference(named_object, location); - case Named_object::NAMED_OBJECT_SINK: - return Expression::make_sink(location); - case Named_object::NAMED_OBJECT_FUNC: - case Named_object::NAMED_OBJECT_FUNC_DECLARATION: - return Expression::make_func_reference(named_object, NULL, location); - case Named_object::NAMED_OBJECT_UNKNOWN: - { - 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. - 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); - } -} - -// Expression = UnaryExpr { binary_op Expression } . - -// PRECEDENCE is the precedence of the current operator. - -// If MAY_BE_SINK is true, this expression may be "_". - -// If MAY_BE_COMPOSITE_LIT is true, this expression may be a composite -// literal. - -// If IS_TYPE_SWITCH is not NULL, this will recognize a type switch -// guard (var := expr.("type") using the literal keyword "type"). - -// If IS_PARENTHESIZED is not NULL, *IS_PARENTHESIZED is set to true -// if the entire expression is in parentheses. - -Expression* -Parse::expression(Precedence precedence, bool may_be_sink, - bool may_be_composite_lit, bool* is_type_switch, - bool *is_parenthesized) -{ - Expression* left = this->unary_expr(may_be_sink, may_be_composite_lit, - is_type_switch, is_parenthesized); - - while (true) - { - if (is_type_switch != NULL && *is_type_switch) - return left; - - const Token* token = this->peek_token(); - if (token->classification() != Token::TOKEN_OPERATOR) - { - // Not a binary_op. - return left; - } - - Precedence right_precedence; - switch (token->op()) - { - case OPERATOR_OROR: - right_precedence = PRECEDENCE_OROR; - break; - case OPERATOR_ANDAND: - right_precedence = PRECEDENCE_ANDAND; - break; - case OPERATOR_EQEQ: - case OPERATOR_NOTEQ: - case OPERATOR_LT: - case OPERATOR_LE: - case OPERATOR_GT: - case OPERATOR_GE: - right_precedence = PRECEDENCE_RELOP; - break; - case OPERATOR_PLUS: - case OPERATOR_MINUS: - case OPERATOR_OR: - case OPERATOR_XOR: - right_precedence = PRECEDENCE_ADDOP; - break; - case OPERATOR_MULT: - case OPERATOR_DIV: - case OPERATOR_MOD: - case OPERATOR_LSHIFT: - case OPERATOR_RSHIFT: - case OPERATOR_AND: - case OPERATOR_BITCLEAR: - right_precedence = PRECEDENCE_MULOP; - break; - default: - right_precedence = PRECEDENCE_INVALID; - break; - } - - if (right_precedence == PRECEDENCE_INVALID) - { - // Not a binary_op. - return left; - } - - if (is_parenthesized != NULL) - *is_parenthesized = false; - - Operator op = token->op(); - Location binop_location = token->location(); - - if (precedence >= right_precedence) - { - // We've already seen A * B, and we see + C. We want to - // return so that A * B becomes a group. - return left; - } - - this->advance_token(); - - left = this->verify_not_sink(left); - Expression* right = this->expression(right_precedence, false, - may_be_composite_lit, - NULL, NULL); - left = Expression::make_binary(op, left, right, binop_location); - } -} - -bool -Parse::expression_may_start_here() -{ - const Token* token = this->peek_token(); - switch (token->classification()) - { - case Token::TOKEN_INVALID: - case Token::TOKEN_EOF: - return false; - case Token::TOKEN_KEYWORD: - switch (token->keyword()) - { - case KEYWORD_CHAN: - case KEYWORD_FUNC: - case KEYWORD_MAP: - case KEYWORD_STRUCT: - case KEYWORD_INTERFACE: - return true; - default: - return false; - } - case Token::TOKEN_IDENTIFIER: - return true; - case Token::TOKEN_STRING: - return true; - case Token::TOKEN_OPERATOR: - switch (token->op()) - { - case OPERATOR_PLUS: - case OPERATOR_MINUS: - case OPERATOR_NOT: - case OPERATOR_XOR: - case OPERATOR_MULT: - case OPERATOR_CHANOP: - case OPERATOR_AND: - case OPERATOR_LPAREN: - case OPERATOR_LSQUARE: - return true; - default: - return false; - } - case Token::TOKEN_CHARACTER: - case Token::TOKEN_INTEGER: - case Token::TOKEN_FLOAT: - case Token::TOKEN_IMAGINARY: - return true; - default: - go_unreachable(); - } -} - -// UnaryExpr = unary_op UnaryExpr | PrimaryExpr . - -// If MAY_BE_SINK is true, this expression may be "_". - -// If MAY_BE_COMPOSITE_LIT is true, this expression may be a composite -// literal. - -// If IS_TYPE_SWITCH is not NULL, this will recognize a type switch -// guard (var := expr.("type") using the literal keyword "type"). - -// If IS_PARENTHESIZED is not NULL, *IS_PARENTHESIZED is set to true -// if the entire expression is in parentheses. - -Expression* -Parse::unary_expr(bool may_be_sink, bool may_be_composite_lit, - bool* is_type_switch, bool* is_parenthesized) -{ - 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, 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) - || token->is_op(OPERATOR_XOR) - || token->is_op(OPERATOR_CHANOP) - || token->is_op(OPERATOR_MULT) - || token->is_op(OPERATOR_AND)) - { - Location location = token->location(); - Operator op = token->op(); - this->advance_token(); - - Expression* expr = this->unary_expr(false, may_be_composite_lit, NULL, - NULL); - if (expr->is_error_expression()) - ; - else if (op == OPERATOR_MULT && expr->is_type_expression()) - expr = Expression::make_type(Type::make_pointer_type(expr->type()), - location); - else if (op == OPERATOR_AND && expr->is_composite_literal()) - expr = Expression::make_heap_composite(expr, location); - else if (op != OPERATOR_CHANOP) - expr = Expression::make_unary(op, expr, location); - else - expr = Expression::make_receive(expr, location); - return expr; - } - else - return this->primary_expr(may_be_sink, may_be_composite_lit, - is_type_switch, is_parenthesized); -} - -// 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 | -// FallthroughStmt | Block | IfStmt | SwitchStmt | SelectStmt | ForStmt | -// DeferStmt . - -// LABEL is the label of this statement if it has one. - -void -Parse::statement(Label* label) -{ - const Token* token = this->peek_token(); - switch (token->classification()) - { - case Token::TOKEN_KEYWORD: - { - switch (token->keyword()) - { - case KEYWORD_CONST: - case KEYWORD_TYPE: - case KEYWORD_VAR: - this->declaration(); - break; - case KEYWORD_FUNC: - case KEYWORD_MAP: - case KEYWORD_STRUCT: - case KEYWORD_INTERFACE: - this->simple_stat(true, NULL, NULL, NULL); - break; - case KEYWORD_GO: - case KEYWORD_DEFER: - this->go_or_defer_stat(); - break; - case KEYWORD_RETURN: - this->return_stat(); - break; - case KEYWORD_BREAK: - this->break_stat(); - break; - case KEYWORD_CONTINUE: - this->continue_stat(); - break; - case KEYWORD_GOTO: - this->goto_stat(); - break; - case KEYWORD_IF: - this->if_stat(); - break; - case KEYWORD_SWITCH: - this->switch_stat(label); - break; - case KEYWORD_SELECT: - this->select_stat(label); - break; - case KEYWORD_FOR: - this->for_stat(label); - break; - default: - error_at(this->location(), "expected statement"); - this->advance_token(); - break; - } - } - break; - - case Token::TOKEN_IDENTIFIER: - { - std::string identifier = token->identifier(); - bool is_exported = token->is_identifier_exported(); - Location location = token->location(); - if (this->advance_token()->is_op(OPERATOR_COLON)) - { - this->advance_token(); - this->labeled_stmt(identifier, location); - } - else - { - this->unget_token(Token::make_identifier_token(identifier, - is_exported, - location)); - this->simple_stat(true, NULL, NULL, NULL); - } - } - break; - - case Token::TOKEN_OPERATOR: - if (token->is_op(OPERATOR_LCURLY)) - { - Location location = token->location(); - this->gogo_->start_block(location); - Location end_loc = this->block(); - this->gogo_->add_block(this->gogo_->finish_block(end_loc), - location); - } - else if (!token->is_op(OPERATOR_SEMICOLON)) - this->simple_stat(true, NULL, NULL, NULL); - break; - - case Token::TOKEN_STRING: - case Token::TOKEN_CHARACTER: - case Token::TOKEN_INTEGER: - case Token::TOKEN_FLOAT: - case Token::TOKEN_IMAGINARY: - this->simple_stat(true, NULL, NULL, NULL); - break; - - default: - error_at(this->location(), "expected statement"); - this->advance_token(); - break; - } -} - -bool -Parse::statement_may_start_here() -{ - const Token* token = this->peek_token(); - switch (token->classification()) - { - case Token::TOKEN_KEYWORD: - { - switch (token->keyword()) - { - case KEYWORD_CONST: - case KEYWORD_TYPE: - case KEYWORD_VAR: - case KEYWORD_FUNC: - case KEYWORD_MAP: - case KEYWORD_STRUCT: - case KEYWORD_INTERFACE: - case KEYWORD_GO: - case KEYWORD_DEFER: - case KEYWORD_RETURN: - case KEYWORD_BREAK: - case KEYWORD_CONTINUE: - case KEYWORD_GOTO: - case KEYWORD_IF: - case KEYWORD_SWITCH: - case KEYWORD_SELECT: - case KEYWORD_FOR: - return true; - - default: - return false; - } - } - break; - - case Token::TOKEN_IDENTIFIER: - return true; - - case Token::TOKEN_OPERATOR: - if (token->is_op(OPERATOR_LCURLY) - || token->is_op(OPERATOR_SEMICOLON)) - return true; - else - return this->expression_may_start_here(); - - case Token::TOKEN_STRING: - case Token::TOKEN_CHARACTER: - case Token::TOKEN_INTEGER: - case Token::TOKEN_FLOAT: - case Token::TOKEN_IMAGINARY: - return true; - - default: - return false; - } -} - -// LabeledStmt = Label ":" Statement . -// Label = identifier . - -void -Parse::labeled_stmt(const std::string& label_name, Location location) -{ - Label* label = this->gogo_->add_label_definition(label_name, location); - - if (this->peek_token()->is_op(OPERATOR_RCURLY)) - { - // This is a label at the end of a block. A program is - // permitted to omit a semicolon here. - return; - } - - 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)); - return; - } - - this->statement(label); -} - -// 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 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, 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. - -// If P_TYPE_SWITCH is not NULL, this will recognize a type switch -// guard (var := expr.("type") using the literal keyword "type"). - -Expression* -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(); - - // An identifier follow by := is a SimpleVarDecl. - if (token->is_identifier()) - { - std::string identifier = token->identifier(); - bool is_exported = token->is_identifier_exported(); - Location location = token->location(); - - token = this->advance_token(); - if (token->is_op(OPERATOR_COLONEQ) - || token->is_op(OPERATOR_COMMA)) - { - identifier = this->gogo_->pack_hidden_name(identifier, is_exported); - this->simple_var_decl_or_assignment(identifier, location, - may_be_composite_lit, - p_range_clause, - (token->is_op(OPERATOR_COLONEQ) - ? p_type_switch - : NULL)); - return NULL; - } - - this->unget_token(Token::make_identifier_token(identifier, is_exported, - location)); - } - - Expression* exp = this->expression(PRECEDENCE_NORMAL, true, - may_be_composite_lit, - (p_type_switch == NULL - ? NULL - : &p_type_switch->found), - NULL); - if (p_type_switch != NULL && p_type_switch->found) - { - p_type_switch->name.clear(); - p_type_switch->location = exp->location(); - p_type_switch->expr = this->verify_not_sink(exp); - return NULL; - } - token = this->peek_token(); - 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)) - this->assignment(exp, may_be_composite_lit, p_range_clause); - else if (token->is_op(OPERATOR_PLUSEQ) - || token->is_op(OPERATOR_MINUSEQ) - || token->is_op(OPERATOR_OREQ) - || token->is_op(OPERATOR_XOREQ) - || token->is_op(OPERATOR_MULTEQ) - || token->is_op(OPERATOR_DIVEQ) - || token->is_op(OPERATOR_MODEQ) - || token->is_op(OPERATOR_LSHIFTEQ) - || token->is_op(OPERATOR_RSHIFTEQ) - || token->is_op(OPERATOR_ANDEQ) - || token->is_op(OPERATOR_BITCLEAREQ)) - this->assignment(this->verify_not_sink(exp), may_be_composite_lit, - p_range_clause); - else if (return_exp != NULL) - return this->verify_not_sink(exp); - else - { - exp = this->verify_not_sink(exp); - - if (token->is_op(OPERATOR_COLONEQ)) - { - if (!exp->is_error_expression()) - error_at(token->location(), "non-name on left side of %<:=%>"); - this->gogo_->mark_locals_used(); - while (!token->is_op(OPERATOR_SEMICOLON) - && !token->is_eof()) - token = this->advance_token(); - return NULL; - } - - this->expression_stat(exp); - } - - return NULL; -} - -bool -Parse::simple_stat_may_start_here() -{ - return this->expression_may_start_here(); -} - -// Parse { Statement ";" } which is used in a few places. The list of -// statements may end with a right curly brace, in which case the -// semicolon may be omitted. - -void -Parse::statement_list() -{ - while (this->statement_may_start_here()) - { - this->statement(NULL); - if (this->peek_token()->is_op(OPERATOR_SEMICOLON)) - this->advance_token(); - else if (this->peek_token()->is_op(OPERATOR_RCURLY)) - break; - else - { - if (!this->peek_token()->is_eof() || !saw_errors()) - error_at(this->location(), "expected %<;%> or %<}%> or newline"); - if (!this->skip_past_error(OPERATOR_RCURLY)) - return; - } - } -} - -bool -Parse::statement_list_may_start_here() -{ - return this->statement_may_start_here(); -} - -// ExpressionStat = Expression . - -void -Parse::expression_stat(Expression* 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)); - Location loc = this->location(); - this->advance_token(); - Expression* val = this->expression(PRECEDENCE_NORMAL, false, true, NULL, - NULL); - Statement* s = Statement::make_send_statement(channel, val, loc); - this->gogo_->add_statement(s); -} - -// IncDecStat = Expression ( "++" | "--" ) . - -void -Parse::inc_dec_stat(Expression* exp) -{ - const Token* token = this->peek_token(); - - // Lvalue maps require special handling. - if (exp->index_expression() != NULL) - exp->index_expression()->set_is_lvalue(); - - if (token->is_op(OPERATOR_PLUSPLUS)) - this->gogo_->add_statement(Statement::make_inc_statement(exp)); - else if (token->is_op(OPERATOR_MINUSMINUS)) - this->gogo_->add_statement(Statement::make_dec_statement(exp)); - else - go_unreachable(); - this->advance_token(); -} - -// Assignment = ExpressionList assign_op ExpressionList . - -// EXP is an expression that we have already parsed. - -// If MAY_BE_COMPOSITE_LIT is true, an expression on the right hand -// side may be a composite literal. - -// If RANGE_CLAUSE is not NULL, then this will recognize a -// RangeClause. - -void -Parse::assignment(Expression* expr, bool may_be_composite_lit, - Range_clause* p_range_clause) -{ - Expression_list* vars; - if (!this->peek_token()->is_op(OPERATOR_COMMA)) - { - vars = new Expression_list(); - vars->push_back(expr); - } - else - { - this->advance_token(); - vars = this->expression_list(expr, true, may_be_composite_lit); - } - - this->tuple_assignment(vars, may_be_composite_lit, p_range_clause); -} - -// An assignment statement. LHS is the list of expressions which -// appear on the left hand side. - -// If MAY_BE_COMPOSITE_LIT is true, an expression on the right hand -// side may be a composite literal. - -// If RANGE_CLAUSE is not NULL, then this will recognize a -// RangeClause. - -void -Parse::tuple_assignment(Expression_list* lhs, bool may_be_composite_lit, - Range_clause* p_range_clause) -{ - const Token* token = this->peek_token(); - if (!token->is_op(OPERATOR_EQ) - && !token->is_op(OPERATOR_PLUSEQ) - && !token->is_op(OPERATOR_MINUSEQ) - && !token->is_op(OPERATOR_OREQ) - && !token->is_op(OPERATOR_XOREQ) - && !token->is_op(OPERATOR_MULTEQ) - && !token->is_op(OPERATOR_DIVEQ) - && !token->is_op(OPERATOR_MODEQ) - && !token->is_op(OPERATOR_LSHIFTEQ) - && !token->is_op(OPERATOR_RSHIFTEQ) - && !token->is_op(OPERATOR_ANDEQ) - && !token->is_op(OPERATOR_BITCLEAREQ)) - { - error_at(this->location(), "expected assignment operator"); - return; - } - Operator op = token->op(); - Location location = token->location(); - - token = this->advance_token(); - - if (p_range_clause != NULL && token->is_keyword(KEYWORD_RANGE)) - { - if (op != OPERATOR_EQ) - error_at(this->location(), "range clause requires %<=%>"); - this->range_clause_expr(lhs, p_range_clause); - return; - } - - Expression_list* vals = this->expression_list(NULL, false, - may_be_composite_lit); - - // We've parsed everything; check for errors. - if (lhs == NULL || vals == NULL) - return; - for (Expression_list::const_iterator pe = lhs->begin(); - pe != lhs->end(); - ++pe) - { - if ((*pe)->is_error_expression()) - return; - if (op != OPERATOR_EQ && (*pe)->is_sink_expression()) - error_at((*pe)->location(), "cannot use _ as value"); - } - for (Expression_list::const_iterator pe = vals->begin(); - pe != vals->end(); - ++pe) - { - if ((*pe)->is_error_expression()) - return; - } - - // Map expressions act differently when they are lvalues. - for (Expression_list::iterator plv = lhs->begin(); - plv != lhs->end(); - ++plv) - if ((*plv)->index_expression() != NULL) - (*plv)->index_expression()->set_is_lvalue(); - - Call_expression* call; - Index_expression* map_index; - Receive_expression* receive; - Type_guard_expression* type_guard; - if (lhs->size() == vals->size()) - { - Statement* s; - if (lhs->size() > 1) - { - if (op != OPERATOR_EQ) - error_at(location, "multiple values only permitted with %<=%>"); - s = Statement::make_tuple_assignment(lhs, vals, location); - } - else - { - if (op == OPERATOR_EQ) - s = Statement::make_assignment(lhs->front(), vals->front(), - location); - else - s = Statement::make_assignment_operation(op, lhs->front(), - vals->front(), location); - delete lhs; - delete vals; - } - this->gogo_->add_statement(s); - } - else if (vals->size() == 1 - && (call = (*vals->begin())->call_expression()) != NULL) - { - if (op != OPERATOR_EQ) - error_at(location, "multiple results only permitted with %<=%>"); - delete vals; - vals = new Expression_list; - for (unsigned int i = 0; i < lhs->size(); ++i) - vals->push_back(Expression::make_call_result(call, i)); - Statement* s = Statement::make_tuple_assignment(lhs, vals, location); - this->gogo_->add_statement(s); - } - else if (lhs->size() == 2 - && vals->size() == 1 - && (map_index = (*vals->begin())->index_expression()) != NULL) - { - if (op != OPERATOR_EQ) - error_at(location, "two values from map requires %<=%>"); - Expression* val = lhs->front(); - Expression* present = lhs->back(); - Statement* s = Statement::make_tuple_map_assignment(val, present, - map_index, location); - this->gogo_->add_statement(s); - } - else if (lhs->size() == 1 - && vals->size() == 2 - && (map_index = lhs->front()->index_expression()) != NULL) - { - if (op != OPERATOR_EQ) - error_at(location, "assigning tuple to map index requires %<=%>"); - Expression* val = vals->front(); - Expression* should_set = vals->back(); - Statement* s = Statement::make_map_assignment(map_index, val, should_set, - location); - this->gogo_->add_statement(s); - } - else if (lhs->size() == 2 - && vals->size() == 1 - && (receive = (*vals->begin())->receive_expression()) != NULL) - { - if (op != OPERATOR_EQ) - error_at(location, "two values from receive requires %<=%>"); - Expression* val = lhs->front(); - Expression* success = lhs->back(); - Expression* channel = receive->channel(); - Statement* s = Statement::make_tuple_receive_assignment(val, success, - channel, - location); - this->gogo_->add_statement(s); - } - else if (lhs->size() == 2 - && vals->size() == 1 - && (type_guard = (*vals->begin())->type_guard_expression()) != NULL) - { - if (op != OPERATOR_EQ) - error_at(location, "two values from type guard requires %<=%>"); - Expression* val = lhs->front(); - Expression* ok = lhs->back(); - Expression* expr = type_guard->expr(); - Type* type = type_guard->type(); - Statement* s = Statement::make_tuple_type_guard_assignment(val, ok, - expr, type, - location); - this->gogo_->add_statement(s); - } - else - { - error_at(location, "number of variables does not match number of values"); - } -} - -// GoStat = "go" Expression . -// DeferStat = "defer" Expression . - -void -Parse::go_or_defer_stat() -{ - 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); - Location stat_location = this->location(); - - this->advance_token(); - Location expr_location = this->location(); - - bool is_parenthesized = false; - Expression* expr = this->expression(PRECEDENCE_NORMAL, false, true, NULL, - &is_parenthesized); - Call_expression* call_expr = expr->call_expression(); - if (is_parenthesized || call_expr == NULL) - { - error_at(expr_location, "argument to go/defer must be function call"); - return; - } - - // Make it easier to simplify go/defer statements by putting every - // statement in its own block. - this->gogo_->start_block(stat_location); - Statement* stat; - if (is_go) - stat = Statement::make_go_statement(call_expr, stat_location); - else - stat = Statement::make_defer_statement(call_expr, stat_location); - this->gogo_->add_statement(stat); - this->gogo_->add_block(this->gogo_->finish_block(stat_location), - stat_location); -} - -// ReturnStat = "return" [ ExpressionList ] . - -void -Parse::return_stat() -{ - go_assert(this->peek_token()->is_keyword(KEYWORD_RETURN)); - Location location = this->location(); - this->advance_token(); - Expression_list* vals = NULL; - if (this->expression_may_start_here()) - vals = this->expression_list(NULL, false, true); - this->gogo_->add_statement(Statement::make_return_statement(vals, location)); - - if (vals == NULL - && this->gogo_->current_function()->func_value()->results_are_named()) - { - Named_object* function = this->gogo_->current_function(); - Function::Results* results = function->func_value()->result_variables(); - for (Function::Results::const_iterator p = results->begin(); - p != results->end(); - ++p) - { - Named_object* no = this->gogo_->lookup((*p)->name(), NULL); - if (no == NULL) - go_assert(saw_errors()); - else if (!no->is_result_variable()) - error_at(location, "%qs is shadowed during return", - (*p)->message_name().c_str()); - } - } -} - -// IfStmt = "if" [ SimpleStmt ";" ] Expression Block -// [ "else" ( IfStmt | Block ) ] . - -void -Parse::if_stat() -{ - go_assert(this->peek_token()->is_keyword(KEYWORD_IF)); - Location location = this->location(); - this->advance_token(); - - this->gogo_->start_block(location); - - bool saw_simple_stat = false; - Expression* cond = NULL; - bool saw_send_stmt = false; - if (this->simple_stat_may_start_here()) - { - 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. - this->expression_stat(cond); - cond = NULL; - } - if (cond == NULL) - { - 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 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, NULL); - } - - this->gogo_->start_block(this->location()); - Location end_loc = this->block(); - Block* then_block = this->gogo_->finish_block(end_loc); - - // Check for the easy error of a newline before "else". - if (this->peek_token()->is_op(OPERATOR_SEMICOLON)) - { - Location semi_loc = this->location(); - if (this->advance_token()->is_keyword(KEYWORD_ELSE)) - error_at(this->location(), - "unexpected semicolon or newline before %<else%>"); - else - this->unget_token(Token::make_operator_token(OPERATOR_SEMICOLON, - semi_loc)); - } - - Block* else_block = NULL; - if (this->peek_token()->is_keyword(KEYWORD_ELSE)) - { - this->gogo_->start_block(this->location()); - 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()); - } - - this->gogo_->add_statement(Statement::make_if_statement(cond, then_block, - else_block, - location)); - - this->gogo_->add_block(this->gogo_->finish_block(this->location()), - location); -} - -// SwitchStmt = ExprSwitchStmt | TypeSwitchStmt . -// ExprSwitchStmt = "switch" [ [ SimpleStat ] ";" ] [ Expression ] -// "{" { ExprCaseClause } "}" . -// TypeSwitchStmt = "switch" [ [ SimpleStat ] ";" ] TypeSwitchGuard -// "{" { TypeCaseClause } "}" . -// TypeSwitchGuard = [ identifier ":=" ] Expression "." "(" "type" ")" . - -void -Parse::switch_stat(Label* label) -{ - go_assert(this->peek_token()->is_keyword(KEYWORD_SWITCH)); - 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; - bool have_type_switch_block = false; - if (this->simple_stat_may_start_here()) - { - 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. - this->expression_stat(switch_val); - switch_val = NULL; - } - if (switch_val == NULL && !type_switch.found) - { - 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()) - { - const Token* token = this->peek_token(); - std::string identifier = token->identifier(); - bool is_exported = token->is_identifier_exported(); - Location id_loc = token->location(); - - token = this->advance_token(); - bool is_coloneq = token->is_op(OPERATOR_COLONEQ); - this->unget_token(Token::make_identifier_token(identifier, - is_exported, - id_loc)); - if (is_coloneq) - { - // This must be a TypeSwitchGuard. It is in a - // different block from any initial SimpleStat. - if (saw_simple_stat) - { - this->gogo_->start_block(id_loc); - have_type_switch_block = true; - } - - switch_val = this->simple_stat(false, &saw_send_stmt, NULL, - &type_switch); - if (!type_switch.found) - { - 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 (switch_val == NULL && !type_switch.found) - { - switch_val = this->expression(PRECEDENCE_NORMAL, false, false, - &type_switch.found, NULL); - if (type_switch.found) - { - type_switch.name.clear(); - type_switch.expr = switch_val; - type_switch.location = switch_val->location(); - } - } - } - } - - if (!this->peek_token()->is_op(OPERATOR_LCURLY)) - { - Location token_loc = this->location(); - 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, NULL); - if (this->peek_token()->is_op(OPERATOR_SEMICOLON)) - this->advance_token(); - if (!this->peek_token()->is_op(OPERATOR_LCURLY)) - { - if (have_type_switch_block) - this->gogo_->add_block(this->gogo_->finish_block(location), - location); - this->gogo_->add_block(this->gogo_->finish_block(location), - location); - return; - } - if (type_switch.found) - type_switch.expr = Expression::make_error(location); - } - else - { - error_at(this->location(), "expected %<{%>"); - if (have_type_switch_block) - this->gogo_->add_block(this->gogo_->finish_block(this->location()), - location); - this->gogo_->add_block(this->gogo_->finish_block(this->location()), - location); - return; - } - } - this->advance_token(); - - Statement* statement; - if (type_switch.found) - statement = this->type_switch_body(label, type_switch, location); - else - statement = this->expr_switch_body(label, switch_val, location); - - if (statement != NULL) - this->gogo_->add_statement(statement); - - if (have_type_switch_block) - this->gogo_->add_block(this->gogo_->finish_block(this->location()), - location); - - this->gogo_->add_block(this->gogo_->finish_block(this->location()), - location); -} - -// The body of an expression switch. -// "{" { ExprCaseClause } "}" - -Statement* -Parse::expr_switch_body(Label* label, Expression* switch_val, - Location location) -{ - Switch_statement* statement = Statement::make_switch_statement(switch_val, - location); - - this->push_break_statement(statement, label); - - Case_clauses* case_clauses = new Case_clauses(); - bool saw_default = false; - while (!this->peek_token()->is_op(OPERATOR_RCURLY)) - { - if (this->peek_token()->is_eof()) - { - if (!saw_errors()) - error_at(this->location(), "missing %<}%>"); - return NULL; - } - this->expr_case_clause(case_clauses, &saw_default); - } - this->advance_token(); - - statement->add_clauses(case_clauses); - - this->pop_break_statement(); - - return statement; -} - -// ExprCaseClause = ExprSwitchCase ":" [ StatementList ] . -// FallthroughStat = "fallthrough" . - -void -Parse::expr_case_clause(Case_clauses* clauses, bool* saw_default) -{ - Location location = this->location(); - - bool is_default = false; - Expression_list* vals = this->expr_switch_case(&is_default); - - if (!this->peek_token()->is_op(OPERATOR_COLON)) - { - if (!saw_errors()) - error_at(this->location(), "expected %<:%>"); - return; - } - else - this->advance_token(); - - Block* statements = NULL; - if (this->statement_list_may_start_here()) - { - this->gogo_->start_block(this->location()); - this->statement_list(); - statements = this->gogo_->finish_block(this->location()); - } - - bool is_fallthrough = false; - if (this->peek_token()->is_keyword(KEYWORD_FALLTHROUGH)) - { - is_fallthrough = true; - if (this->advance_token()->is_op(OPERATOR_SEMICOLON)) - this->advance_token(); - } - - if (is_default) - { - if (*saw_default) - { - error_at(location, "multiple defaults in switch"); - return; - } - *saw_default = true; - } - - if (is_default || vals != NULL) - clauses->add(vals, is_default, statements, is_fallthrough, location); -} - -// ExprSwitchCase = "case" ExpressionList | "default" . - -Expression_list* -Parse::expr_switch_case(bool* is_default) -{ - const Token* token = this->peek_token(); - if (token->is_keyword(KEYWORD_CASE)) - { - this->advance_token(); - return this->expression_list(NULL, false, true); - } - else if (token->is_keyword(KEYWORD_DEFAULT)) - { - this->advance_token(); - *is_default = true; - return NULL; - } - else - { - if (!saw_errors()) - error_at(this->location(), "expected %<case%> or %<default%>"); - if (!token->is_op(OPERATOR_RCURLY)) - this->advance_token(); - return NULL; - } -} - -// The body of a type switch. -// "{" { TypeCaseClause } "}" . - -Statement* -Parse::type_switch_body(Label* label, const Type_switch& type_switch, - Location location) -{ - Named_object* switch_no = NULL; - if (!type_switch.name.empty()) - { - if (Gogo::is_sink_name(type_switch.name)) - error_at(type_switch.location, - "no new variables on left side of %<:=%>"); - else - { - Variable* switch_var = new Variable(NULL, type_switch.expr, false, - false, false, - type_switch.location); - switch_no = this->gogo_->add_variable(type_switch.name, switch_var); - } - } - - Type_switch_statement* statement = - Statement::make_type_switch_statement(switch_no, - (switch_no == NULL - ? type_switch.expr - : NULL), - location); - - this->push_break_statement(statement, label); - - Type_case_clauses* case_clauses = new Type_case_clauses(); - bool saw_default = false; - while (!this->peek_token()->is_op(OPERATOR_RCURLY)) - { - if (this->peek_token()->is_eof()) - { - error_at(this->location(), "missing %<}%>"); - return NULL; - } - this->type_case_clause(switch_no, case_clauses, &saw_default); - } - this->advance_token(); - - statement->add_clauses(case_clauses); - - this->pop_break_statement(); - - return statement; -} - -// TypeCaseClause = TypeSwitchCase ":" [ StatementList ] . - -void -Parse::type_case_clause(Named_object* switch_no, Type_case_clauses* clauses, - bool* saw_default) -{ - Location location = this->location(); - - std::vector<Type*> types; - bool is_default = false; - this->type_switch_case(&types, &is_default); - - if (!this->peek_token()->is_op(OPERATOR_COLON)) - error_at(this->location(), "expected %<:%>"); - else - this->advance_token(); - - Block* statements = NULL; - if (this->statement_list_may_start_here()) - { - this->gogo_->start_block(this->location()); - if (switch_no != NULL && types.size() == 1) - { - Type* type = types.front(); - Expression* init = Expression::make_var_reference(switch_no, - location); - init = Expression::make_type_guard(init, type, location); - Variable* v = new Variable(type, init, false, false, false, - location); - v->set_is_type_switch_var(); - Named_object* no = this->gogo_->add_variable(switch_no->name(), v); - - // We don't want to issue an error if the compiler - // introduced special variable is not used. Instead we want - // to issue an error if the variable defined by the switch - // is not used. That is handled via type_switch_vars_ and - // Parse::mark_var_used. - v->set_is_used(); - this->type_switch_vars_[no] = switch_no; - } - this->statement_list(); - statements = this->gogo_->finish_block(this->location()); - } - - if (this->peek_token()->is_keyword(KEYWORD_FALLTHROUGH)) - { - error_at(this->location(), - "fallthrough is not permitted in a type switch"); - if (this->advance_token()->is_op(OPERATOR_SEMICOLON)) - this->advance_token(); - } - - if (is_default) - { - go_assert(types.empty()); - if (*saw_default) - { - error_at(location, "multiple defaults in type switch"); - return; - } - *saw_default = true; - clauses->add(NULL, false, true, statements, location); - } - else if (!types.empty()) - { - for (std::vector<Type*>::const_iterator p = types.begin(); - p + 1 != types.end(); - ++p) - clauses->add(*p, true, false, NULL, location); - clauses->add(types.back(), false, false, statements, location); - } - else - clauses->add(Type::make_error_type(), false, false, statements, location); -} - -// TypeSwitchCase = "case" type | "default" - -// We accept a comma separated list of types. - -void -Parse::type_switch_case(std::vector<Type*>* types, bool* is_default) -{ - const Token* token = this->peek_token(); - if (token->is_keyword(KEYWORD_CASE)) - { - this->advance_token(); - while (true) - { - Type* t = this->type(); - - if (!t->is_error_type()) - types->push_back(t); - else - { - this->gogo_->mark_locals_used(); - token = this->peek_token(); - while (!token->is_op(OPERATOR_COLON) - && !token->is_op(OPERATOR_COMMA) - && !token->is_op(OPERATOR_RCURLY) - && !token->is_eof()) - token = this->advance_token(); - } - - if (!this->peek_token()->is_op(OPERATOR_COMMA)) - break; - this->advance_token(); - } - } - else if (token->is_keyword(KEYWORD_DEFAULT)) - { - this->advance_token(); - *is_default = true; - } - else - { - error_at(this->location(), "expected %<case%> or %<default%>"); - if (!token->is_op(OPERATOR_RCURLY)) - this->advance_token(); - } -} - -// SelectStat = "select" "{" { CommClause } "}" . - -void -Parse::select_stat(Label* label) -{ - go_assert(this->peek_token()->is_keyword(KEYWORD_SELECT)); - Location location = this->location(); - const Token* token = this->advance_token(); - - if (!token->is_op(OPERATOR_LCURLY)) - { - Location token_loc = token->location(); - if (token->is_op(OPERATOR_SEMICOLON) - && this->advance_token()->is_op(OPERATOR_LCURLY)) - error_at(token_loc, "unexpected semicolon or newline before %<{%>"); - else - { - error_at(this->location(), "expected %<{%>"); - return; - } - } - this->advance_token(); - - Select_statement* statement = Statement::make_select_statement(location); - - this->push_break_statement(statement, label); - - Select_clauses* select_clauses = new Select_clauses(); - bool saw_default = false; - while (!this->peek_token()->is_op(OPERATOR_RCURLY)) - { - if (this->peek_token()->is_eof()) - { - error_at(this->location(), "expected %<}%>"); - return; - } - this->comm_clause(select_clauses, &saw_default); - } - - this->advance_token(); - - statement->add_clauses(select_clauses); - - this->pop_break_statement(); - - this->gogo_->add_statement(statement); -} - -// CommClause = CommCase ":" { Statement ";" } . - -void -Parse::comm_clause(Select_clauses* clauses, bool* saw_default) -{ - Location location = this->location(); - 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, &closed, - &varname, &closedname, &is_default); - - 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 - error_at(this->location(), "expected colon"); - - this->gogo_->start_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) - { - error_at(location, "multiple defaults in select"); - return; - } - *saw_default = true; - } - - if (got_case) - 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 = "case" ( SendStmt | RecvStmt ) | "default" . - -bool -Parse::comm_case(bool* is_send, Expression** channel, Expression** val, - Expression** closed, std::string* varname, - std::string* closedname, bool* is_default) -{ - const Token* token = this->peek_token(); - if (token->is_keyword(KEYWORD_DEFAULT)) - { - this->advance_token(); - *is_default = true; - } - else if (token->is_keyword(KEYWORD_CASE)) - { - this->advance_token(); - if (!this->send_or_recv_stmt(is_send, channel, val, closed, varname, - closedname)) - return false; - } - else - { - error_at(this->location(), "expected %<case%> or %<default%>"); - if (!token->is_op(OPERATOR_RCURLY)) - this->advance_token(); - return false; - } - - return true; -} - -// RecvStmt = [ Expression [ "," Expression ] ( "=" | ":=" ) ] RecvExpr . -// RecvExpr = Expression . - -bool -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(); - 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_rv_exported = token->is_identifier_exported(); - Location recv_var_loc = token->location(); - token = this->advance_token(); - if (token->is_op(OPERATOR_COLONEQ)) - { - // case rv := <-c: - this->advance_token(); - Expression* e = this->expression(PRECEDENCE_NORMAL, false, false, - NULL, NULL); - Receive_expression* re = e->receive_expression(); - if (re == NULL) - { - if (!e->is_error_expression()) - error_at(this->location(), "expected receive expression"); - return false; - } - if (recv_var == "_") - { - error_at(recv_var_loc, - "no new variables on left side of %<:=%>"); - recv_var = "blank"; - } - *is_send = false; - *varname = gogo->pack_hidden_name(recv_var, is_rv_exported); - *channel = re->channel(); - 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(); - Location recv_closed_loc = token->location(); - closed_is_id = true; - - token = this->advance_token(); - if (token->is_op(OPERATOR_COLONEQ)) - { - // case rv, rc := <-c: - this->advance_token(); - Expression* e = this->expression(PRECEDENCE_NORMAL, false, - false, NULL, NULL); - Receive_expression* re = e->receive_expression(); - if (re == NULL) - { - if (!e->is_error_expression()) - error_at(this->location(), - "expected receive expression"); - 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); - *channel = re->channel(); - 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 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, NULL); - else - { - // case <-c: - *is_send = false; - this->advance_token(); - *channel = this->expression(PRECEDENCE_NORMAL, false, true, NULL, 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()); - } - - if (this->peek_token()->is_op(OPERATOR_EQ)) - { - if (!this->advance_token()->is_op(OPERATOR_CHANOP)) - { - error_at(this->location(), "missing %<<-%>"); - return false; - } - *is_send = false; - this->advance_token(); - *channel = this->expression(PRECEDENCE_NORMAL, false, true, NULL, NULL); - if (saw_comma) - { - // case v, e = <-c: - // *VAL is already set. - if (!e->is_sink_expression()) - *closed = e; - } - else - { - // case v = <-c: - if (!e->is_sink_expression()) - *val = e; - } - 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, NULL); - return true; - } - - error_at(this->location(), "expected %<<-%> or %<=%>"); - return false; -} - -// ForStat = "for" [ Condition | ForClause | RangeClause ] Block . -// Condition = Expression . - -void -Parse::for_stat(Label* label) -{ - go_assert(this->peek_token()->is_keyword(KEYWORD_FOR)); - Location location = this->location(); - const Token* token = this->advance_token(); - - // Open a block to hold any variables defined in the init statement - // of the for statement. - this->gogo_->start_block(location); - - Block* init = NULL; - Expression* cond = NULL; - Block* post = NULL; - Range_clause range_clause; - - if (!token->is_op(OPERATOR_LCURLY)) - { - if (token->is_keyword(KEYWORD_VAR)) - { - error_at(this->location(), - "var declaration not allowed in for initializer"); - this->var_decl(); - } - - if (token->is_op(OPERATOR_SEMICOLON)) - this->for_clause(&cond, &post); - else - { - // We might be looking at a Condition, an InitStat, or a - // RangeClause. - 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) - { - 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 - { - if (range_clause.found) - error_at(this->location(), "parse error after range clause"); - - if (cond != NULL) - { - // COND is actually an expression statement for - // InitStat at the start of a ForClause. - this->expression_stat(cond); - cond = NULL; - } - - this->for_clause(&cond, &post); - } - } - } - - // Build the For_statement and note that it is the current target - // for break and continue statements. - - For_statement* sfor; - For_range_statement* srange; - Statement* s; - if (!range_clause.found) - { - sfor = Statement::make_for_statement(init, cond, post, location); - s = sfor; - srange = NULL; - } - else - { - srange = Statement::make_for_range_statement(range_clause.index, - range_clause.value, - range_clause.range, - location); - s = srange; - sfor = NULL; - } - - this->push_break_statement(s, label); - this->push_continue_statement(s, label); - - // Gather the block of statements in the loop and add them to the - // For_statement. - - this->gogo_->start_block(this->location()); - Location end_loc = this->block(); - Block* statements = this->gogo_->finish_block(end_loc); - - if (sfor != NULL) - sfor->add_statements(statements); - else - srange->add_statements(statements); - - // This is no longer the break/continue target. - this->pop_break_statement(); - this->pop_continue_statement(); - - // Add the For_statement to the list of statements, and close out - // the block we started to hold any variables defined in the for - // statement. - - this->gogo_->add_statement(s); - - this->gogo_->add_block(this->gogo_->finish_block(this->location()), - location); -} - -// ForClause = [ InitStat ] ";" [ Condition ] ";" [ PostStat ] . -// InitStat = SimpleStat . -// PostStat = SimpleStat . - -// We have already read InitStat at this point. - -void -Parse::for_clause(Expression** cond, Block** post) -{ - go_assert(this->peek_token()->is_op(OPERATOR_SEMICOLON)); - this->advance_token(); - if (this->peek_token()->is_op(OPERATOR_SEMICOLON)) - *cond = NULL; - else if (this->peek_token()->is_op(OPERATOR_LCURLY)) - { - error_at(this->location(), - "unexpected semicolon or newline before %<{%>"); - *cond = NULL; - *post = NULL; - return; - } - else - *cond = this->expression(PRECEDENCE_NORMAL, false, true, NULL, NULL); - if (!this->peek_token()->is_op(OPERATOR_SEMICOLON)) - error_at(this->location(), "expected semicolon"); - else - this->advance_token(); - - if (this->peek_token()->is_op(OPERATOR_LCURLY)) - *post = NULL; - else - { - this->gogo_->start_block(this->location()); - this->simple_stat(false, NULL, NULL, NULL); - *post = this->gogo_->finish_block(this->location()); - } -} - -// RangeClause = IdentifierList ( "=" | ":=" ) "range" Expression . - -// This is the := version. It is called with a list of identifiers. - -void -Parse::range_clause_decl(const Typed_identifier_list* til, - Range_clause* p_range_clause) -{ - go_assert(this->peek_token()->is_keyword(KEYWORD_RANGE)); - Location location = this->location(); - - p_range_clause->found = true; - - go_assert(til->size() >= 1); - if (til->size() > 2) - error_at(this->location(), "too many variables for range clause"); - - this->advance_token(); - Expression* expr = this->expression(PRECEDENCE_NORMAL, false, false, NULL, - NULL); - p_range_clause->range = expr; - - bool any_new = false; - - const Typed_identifier* pti = &til->front(); - Named_object* no = this->init_var(*pti, NULL, expr, true, true, &any_new, - NULL, NULL); - if (any_new && no->is_variable()) - no->var_value()->set_type_from_range_index(); - p_range_clause->index = Expression::make_var_reference(no, location); - - if (til->size() == 1) - p_range_clause->value = NULL; - else - { - pti = &til->back(); - bool is_new = false; - no = this->init_var(*pti, NULL, expr, true, true, &is_new, NULL, NULL); - if (is_new && no->is_variable()) - no->var_value()->set_type_from_range_value(); - if (is_new) - any_new = true; - p_range_clause->value = Expression::make_var_reference(no, location); - } - - if (!any_new) - error_at(location, "variables redeclared but no variable is new"); -} - -// The = version of RangeClause. This is called with a list of -// expressions. - -void -Parse::range_clause_expr(const Expression_list* vals, - Range_clause* p_range_clause) -{ - go_assert(this->peek_token()->is_keyword(KEYWORD_RANGE)); - - p_range_clause->found = true; - - go_assert(vals->size() >= 1); - if (vals->size() > 2) - error_at(this->location(), "too many variables for range clause"); - - this->advance_token(); - p_range_clause->range = this->expression(PRECEDENCE_NORMAL, false, false, - NULL, NULL); - - p_range_clause->index = vals->front(); - if (vals->size() == 1) - p_range_clause->value = NULL; - else - p_range_clause->value = vals->back(); -} - -// Push a statement on the break stack. - -void -Parse::push_break_statement(Statement* enclosing, Label* 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, Label* 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(); -} - -// Pop the continue stack. - -void -Parse::pop_continue_statement() -{ - 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) - { - p->second->set_is_used(); - return p->first; - } - } - return NULL; -} - -// BreakStat = "break" [ identifier ] . - -void -Parse::break_stat() -{ - go_assert(this->peek_token()->is_keyword(KEYWORD_BREAK)); - Location location = this->location(); - - const Token* token = this->advance_token(); - Statement* enclosing; - if (!token->is_identifier()) - { - 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; - } - else - { - enclosing = this->find_bc_statement(this->break_stack_, - token->identifier()); - if (enclosing == NULL) - { - // 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(), - Linemap::unknown_location(), false); - - error_at(token->location(), "invalid break label %qs", - Gogo::message_name(token->identifier()).c_str()); - this->advance_token(); - return; - } - this->advance_token(); - } - - Unnamed_label* label; - if (enclosing->classification() == Statement::STATEMENT_FOR) - label = enclosing->for_statement()->break_label(); - else if (enclosing->classification() == Statement::STATEMENT_FOR_RANGE) - label = enclosing->for_range_statement()->break_label(); - else if (enclosing->classification() == Statement::STATEMENT_SWITCH) - label = enclosing->switch_statement()->break_label(); - else if (enclosing->classification() == Statement::STATEMENT_TYPE_SWITCH) - label = enclosing->type_switch_statement()->break_label(); - else if (enclosing->classification() == Statement::STATEMENT_SELECT) - label = enclosing->select_statement()->break_label(); - else - go_unreachable(); - - this->gogo_->add_statement(Statement::make_break_statement(label, - location)); -} - -// ContinueStat = "continue" [ identifier ] . - -void -Parse::continue_stat() -{ - go_assert(this->peek_token()->is_keyword(KEYWORD_CONTINUE)); - Location location = this->location(); - - const Token* token = this->advance_token(); - Statement* enclosing; - if (!token->is_identifier()) - { - 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; - } - else - { - enclosing = this->find_bc_statement(this->continue_stack_, - token->identifier()); - if (enclosing == NULL) - { - // 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(), - Linemap::unknown_location(), false); - - error_at(token->location(), "invalid continue label %qs", - Gogo::message_name(token->identifier()).c_str()); - this->advance_token(); - return; - } - this->advance_token(); - } - - Unnamed_label* label; - if (enclosing->classification() == Statement::STATEMENT_FOR) - label = enclosing->for_statement()->continue_label(); - else if (enclosing->classification() == Statement::STATEMENT_FOR_RANGE) - label = enclosing->for_range_statement()->continue_label(); - else - go_unreachable(); - - this->gogo_->add_statement(Statement::make_continue_statement(label, - location)); -} - -// GotoStat = "goto" identifier . - -void -Parse::goto_stat() -{ - go_assert(this->peek_token()->is_keyword(KEYWORD_GOTO)); - 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(), - location, true); - Statement* s = Statement::make_goto_statement(label, location); - this->gogo_->add_statement(s); - this->advance_token(); - } -} - -// PackageClause = "package" PackageName . - -void -Parse::package_clause() -{ - const Token* token = this->peek_token(); - Location location = token->location(); - std::string name; - if (!token->is_keyword(KEYWORD_PACKAGE)) - { - error_at(this->location(), "program must start with package clause"); - name = "ERROR"; - } - else - { - token = this->advance_token(); - if (token->is_identifier()) - { - name = token->identifier(); - if (name == "_") - { - error_at(this->location(), "invalid package name _"); - name = "blank"; - } - this->advance_token(); - } - else - { - error_at(this->location(), "package name must be an identifier"); - name = "ERROR"; - } - } - this->gogo_->set_package_name(name, location); -} - -// ImportDecl = "import" Decl<ImportSpec> . - -void -Parse::import_decl() -{ - go_assert(this->peek_token()->is_keyword(KEYWORD_IMPORT)); - this->advance_token(); - this->decl(&Parse::import_spec, NULL); -} - -// ImportSpec = [ "." | PackageName ] PackageFileName . - -void -Parse::import_spec(void*) -{ - const Token* token = this->peek_token(); - Location location = token->location(); - - std::string local_name; - bool is_local_name_exported = false; - if (token->is_op(OPERATOR_DOT)) - { - local_name = "."; - token = this->advance_token(); - } - else if (token->is_identifier()) - { - local_name = token->identifier(); - is_local_name_exported = token->is_identifier_exported(); - token = this->advance_token(); - } - - if (!token->is_string()) - { - error_at(this->location(), "import statement not a string"); - this->advance_token(); - return; - } - - this->gogo_->import_package(token->string_value(), local_name, - is_local_name_exported, location); - - this->advance_token(); -} - -// SourceFile = PackageClause ";" { ImportDecl ";" } -// { TopLevelDecl ";" } . - -void -Parse::program() -{ - this->package_clause(); - - const Token* token = this->peek_token(); - if (token->is_op(OPERATOR_SEMICOLON)) - token = this->advance_token(); - else - error_at(this->location(), - "expected %<;%> or newline after package clause"); - - while (token->is_keyword(KEYWORD_IMPORT)) - { - this->import_decl(); - token = this->peek_token(); - if (token->is_op(OPERATOR_SEMICOLON)) - token = this->advance_token(); - else - error_at(this->location(), - "expected %<;%> or newline after import declaration"); - } - - while (!token->is_eof()) - { - if (this->declaration_may_start_here()) - this->declaration(); - else - { - error_at(this->location(), "expected declaration"); - this->gogo_->mark_locals_used(); - do - this->advance_token(); - while (!this->peek_token()->is_eof() - && !this->peek_token()->is_op(OPERATOR_SEMICOLON) - && !this->peek_token()->is_op(OPERATOR_RCURLY)); - if (!this->peek_token()->is_eof() - && !this->peek_token()->is_op(OPERATOR_SEMICOLON)) - this->advance_token(); - } - token = this->peek_token(); - if (token->is_op(OPERATOR_SEMICOLON)) - token = this->advance_token(); - else if (!token->is_eof() || !saw_errors()) - { - 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); - } - } -} - -// Reset the current iota value. - -void -Parse::reset_iota() -{ - this->iota_ = 0; -} - -// Return the current iota value. - -int -Parse::iota_value() -{ - return this->iota_; -} - -// Increment the current iota value. - -void -Parse::increment_iota() -{ - ++this->iota_; -} - -// Skip forward to a semicolon or OP. OP will normally be -// OPERATOR_RPAREN or OPERATOR_RCURLY. If we find a semicolon, move -// past it and return. If we find OP, it will be the next token to -// read. Return true if we are OK, false if we found EOF. - -bool -Parse::skip_past_error(Operator op) -{ - this->gogo_->mark_locals_used(); - const Token* token = this->peek_token(); - while (!token->is_op(op)) - { - if (token->is_eof()) - return false; - if (token->is_op(OPERATOR_SEMICOLON)) - { - this->advance_token(); - return true; - } - token = this->advance_token(); - } - return true; -} - -// Check that an expression is not a sink. - -Expression* -Parse::verify_not_sink(Expression* expr) -{ - if (expr->is_sink_expression()) - { - error_at(expr->location(), "cannot use _ as value"); - expr = Expression::make_error(expr->location()); - } - return expr; -} - -// Mark a variable as used. - -void -Parse::mark_var_used(Named_object* no) -{ - if (no->is_variable()) - { - no->var_value()->set_is_used(); - - // When a type switch uses := to define a variable, then for - // each case with a single type we introduce a new variable with - // the appropriate type. When we do, if the newly introduced - // variable is used, then the type switch variable is used. - Type_switch_vars::iterator p = this->type_switch_vars_.find(no); - if (p != this->type_switch_vars_.end()) - p->second->var_value()->set_is_used(); - } -} |