| // 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 "go-diagnostics.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) const |
| { |
| 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), |
| enclosing_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()) |
| { |
| go_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()) |
| { |
| go_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()) |
| { |
| go_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()->note_usage(Gogo::unpack_hidden_name(name)); |
| |
| token = this->advance_token(); |
| if (!token->is_identifier()) |
| { |
| go_error_at(this->location(), "expected identifier"); |
| return false; |
| } |
| |
| name = token->identifier(); |
| |
| if (name == "_") |
| { |
| go_error_at(this->location(), "invalid use of %<_%>"); |
| name = Gogo::erroneous_name(); |
| } |
| |
| 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(true); |
| 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()) |
| go_error_at(this->location(), "expected %<)%>"); |
| } |
| return ret; |
| } |
| else |
| { |
| go_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()); |
| go_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()); |
| go_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) |
| go_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 |
| { |
| go_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)) |
| { |
| go_error_at(this->location(), "expected %<]%>"); |
| return Type::make_error_type(); |
| } |
| this->advance_token(); |
| } |
| |
| Type* element_type = this->type(); |
| if (element_type->is_error_type()) |
| return Type::make_error_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)) |
| { |
| go_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)) |
| { |
| go_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)) |
| go_error_at(token_loc, "unexpected semicolon or newline before %<{%>"); |
| else |
| { |
| go_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)) |
| { |
| go_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())) |
| go_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 |
| { |
| go_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()) |
| { |
| go_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()) |
| { |
| go_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)) |
| { |
| go_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)) |
| go_error_at(this->location(), "unexpected %<}%> in channel type"); |
| else if (token->is_op(OPERATOR_RPAREN)) |
| go_error_at(this->location(), "unexpected %<)%> in channel type"); |
| else if (token->is_op(OPERATOR_COMMA)) |
| go_error_at(this->location(), "unexpected comma in channel type"); |
| else |
| go_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) |
| { |
| go_error_at(p->location(), "redefinition of %qs", |
| Gogo::message_name(p->name()).c_str()); |
| go_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 (receiver != NULL) |
| names[receiver->name()] = receiver; |
| 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)) |
| { |
| go_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)) |
| { |
| go_error_at(this->location(), "expected %<)%>"); |
| return false; |
| } |
| 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 id_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, |
| id_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, id_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(); |
| id_location = this->peek_token()->location(); |
| id_name = this->gogo_->pack_hidden_name(name, is_exported); |
| ret->push_back(Typed_identifier(id_name, NULL, id_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, |
| id_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 |
| { |
| go_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 |
| { |
| go_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, |
| &saw_error); |
| while (this->peek_token()->is_op(OPERATOR_COMMA)) |
| { |
| if (this->advance_token()->is_op(OPERATOR_RPAREN)) |
| break; |
| if (is_varargs != NULL && *is_varargs) |
| { |
| go_error_at(this->location(), "%<...%> must be last parameter"); |
| saw_error = true; |
| } |
| this->parameter_decl(parameters_have_names, ret, is_varargs, &mix_error, |
| &saw_error); |
| } |
| if (mix_error) |
| { |
| go_error_at(location, "mixed named and unnamed function parameters"); |
| 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, |
| bool* saw_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) |
| go_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->peek_token()->is_eof()) |
| this->advance_token(); |
| } |
| } |
| if (!type->is_error_type()) |
| til->push_back(Typed_identifier("", type, location)); |
| else |
| *saw_error = true; |
| } |
| 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) |
| { |
| go_error_at(this->location(), "invalid use of %<...%>"); |
| *saw_error = true; |
| } |
| else if (new_count > orig_count + 1) |
| { |
| go_error_at(this->location(), "%<...%> only permits one name"); |
| *saw_error = true; |
| } |
| 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)) |
| go_error_at(loc, "unexpected semicolon or newline before %<{%>"); |
| else |
| { |
| go_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()) |
| go_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(bool record) |
| { |
| 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)) |
| go_error_at(token_loc, "unexpected semicolon or newline before %<{%>"); |
| else |
| { |
| go_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)) |
| { |
| go_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; |
| if (methods == NULL) |
| ret = Type::make_empty_interface_type(location); |
| else |
| ret = Type::make_interface_type(methods, location); |
| if (record) |
| 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()) |
| { |
| go_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. |
| if (name == "_") |
| go_error_at(this->location(), |
| "methods must have a unique non-blank name"); |
| 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)) |
| go_error_at(this->location(), |
| "name list not allowed in interface type"); |
| else |
| go_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(); |
| |
| unsigned int pragmas = this->lex_->get_and_clear_pragmas(); |
| if (pragmas != 0 |
| && !token->is_keyword(KEYWORD_FUNC) |
| && !token->is_keyword(KEYWORD_TYPE)) |
| go_warning_at(token->location(), 0, |
| "ignoring magic comment before non-function"); |
| |
| std::vector<std::string>* embeds = NULL; |
| if (this->lex_->has_embeds()) |
| { |
| embeds = new(std::vector<std::string>); |
| this->lex_->get_and_clear_embeds(embeds); |
| |
| if (!this->gogo_->current_file_imported_embed()) |
| { |
| go_error_at(token->location(), |
| "invalid go:embed: missing import %<embed%>"); |
| delete embeds; |
| embeds = NULL; |
| } |
| if (!token->is_keyword(KEYWORD_VAR)) |
| { |
| go_error_at(token->location(), "misplaced go:embed directive"); |
| if (embeds != NULL) |
| { |
| delete embeds; |
| embeds = NULL; |
| } |
| } |
| } |
| |
| if (token->is_keyword(KEYWORD_CONST)) |
| this->const_decl(); |
| else if (token->is_keyword(KEYWORD_TYPE)) |
| this->type_decl(pragmas); |
| else if (token->is_keyword(KEYWORD_VAR)) |
| this->var_decl(embeds); |
| else if (token->is_keyword(KEYWORD_FUNC)) |
| this->function_decl(pragmas); |
| else |
| { |
| go_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)(unsigned int, std::vector<std::string>*), |
| unsigned int pragmas, std::vector<std::string>* embeds) |
| { |
| if (this->peek_token()->is_eof()) |
| { |
| if (!saw_errors()) |
| go_error_at(this->location(), "unexpected end of file"); |
| return; |
| } |
| |
| if (!this->peek_token()->is_op(OPERATOR_LPAREN)) |
| (this->*pfn)(pragmas, embeds); |
| else |
| { |
| if (pragmas != 0) |
| go_warning_at(this->location(), 0, |
| "ignoring magic %<//go:...%> comment before group"); |
| if (embeds != NULL) |
| go_error_at(this->location(), |
| "ignoring %<//go:embed%> comment before group"); |
| if (!this->advance_token()->is_op(OPERATOR_RPAREN)) |
| { |
| this->list(pfn, true); |
| if (!this->peek_token()->is_op(OPERATOR_RPAREN)) |
| { |
| go_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)(unsigned int, std::vector<std::string>*), |
| bool follow_is_paren) |
| { |
| (this->*pfn)(0, NULL); |
| 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)) |
| go_error_at(this->location(), "unexpected comma"); |
| if (this->advance_token()->is_op(follow)) |
| break; |
| (this->*pfn)(0, NULL); |
| } |
| } |
| |
| // ConstDecl = "const" ( ConstSpec | "(" { ConstSpec ";" } ")" ) . |
| |
| void |
| Parse::const_decl() |
| { |
| go_assert(this->peek_token()->is_keyword(KEYWORD_CONST)); |
| this->advance_token(); |
| |
| int iota = 0; |
| Type* last_type = NULL; |
| Expression_list* last_expr_list = NULL; |
| |
| if (!this->peek_token()->is_op(OPERATOR_LPAREN)) |
| this->const_spec(iota, &last_type, &last_expr_list); |
| else |
| { |
| this->advance_token(); |
| while (!this->peek_token()->is_op(OPERATOR_RPAREN)) |
| { |
| this->const_spec(iota, &last_type, &last_expr_list); |
| ++iota; |
| if (this->peek_token()->is_op(OPERATOR_SEMICOLON)) |
| this->advance_token(); |
| else if (!this->peek_token()->is_op(OPERATOR_RPAREN)) |
| { |
| go_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(int iota, Type** last_type, Expression_list** last_expr_list) |
| { |
| Location loc = this->location(); |
| 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) |
| { |
| go_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) |
| { |
| Expression* copy = (*p)->copy(); |
| copy->set_location(loc); |
| expr_list->push_back(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()) |
| { |
| go_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, iota); |
| else |
| { |
| static int count; |
| char buf[30]; |
| snprintf(buf, sizeof buf, ".$sinkconst%d", count); |
| ++count; |
| Typed_identifier ti(std::string(buf), type, pi->location()); |
| Named_object* no = this->gogo_->add_constant(ti, *pe, iota); |
| no->const_value()->set_is_sink(); |
| } |
| } |
| if (pe != expr_list->end()) |
| go_error_at(this->location(), "too many initializers"); |
| |
| return; |
| } |
| |
| // TypeDecl = "type" Decl<TypeSpec> . |
| |
| void |
| Parse::type_decl(unsigned int pragmas) |
| { |
| go_assert(this->peek_token()->is_keyword(KEYWORD_TYPE)); |
| this->advance_token(); |
| this->decl(&Parse::type_spec, pragmas, NULL); |
| } |
| |
| // TypeSpec = identifier ["="] Type . |
| |
| void |
| Parse::type_spec(unsigned int pragmas, std::vector<std::string>*) |
| { |
| const Token* token = this->peek_token(); |
| if (!token->is_identifier()) |
| { |
| go_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(); |
| |
| bool is_alias = false; |
| if (token->is_op(OPERATOR_EQ)) |
| { |
| is_alias = true; |
| 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 (name == "_" && token->is_keyword(KEYWORD_INTERFACE)) |
| { |
| // We call Parse::interface_type explicity here because we do not want |
| // to record an interface with a blank type name. |
| type = this->interface_type(false); |
| } |
| else if (!token->is_op(OPERATOR_SEMICOLON)) |
| type = this->type(); |
| else |
| { |
| go_error_at(this->location(), |
| "unexpected semicolon or newline in type declaration"); |
| type = Type::make_error_type(); |
| } |
| |
| 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)) |
| { |
| go_error_at(location, "invalid recursive type"); |
| type = Type::make_error_type(); |
| } |
| |
| Named_type* nt = Type::make_named_type(named_type, type, location); |
| if (is_alias) |
| nt->set_is_alias(); |
| |
| this->gogo_->define_type(named_type, nt); |
| go_assert(named_type->package() == NULL); |
| |
| if ((pragmas & GOPRAGMA_NOTINHEAP) != 0) |
| { |
| nt->set_not_in_heap(); |
| pragmas &= ~GOPRAGMA_NOTINHEAP; |
| } |
| if (pragmas != 0) |
| go_warning_at(location, 0, |
| "ignoring magic %<//go:...%> comment before type"); |
| } |
| else |
| { |
| // This will probably give a redefinition error. |
| this->gogo_->add_type(name, type, location); |
| } |
| } |
| } |
| |
| // VarDecl = "var" Decl<VarSpec> . |
| |
| void |
| Parse::var_decl(std::vector<std::string>* embeds) |
| { |
| go_assert(this->peek_token()->is_keyword(KEYWORD_VAR)); |
| this->advance_token(); |
| this->decl(&Parse::var_spec, 0, embeds); |
| } |
| |
| // VarSpec = IdentifierList |
| // ( CompleteType [ "=" ExpressionList ] | "=" ExpressionList ) . |
| |
| void |
| Parse::var_spec(unsigned int pragmas, std::vector<std::string>* embeds) |
| { |
| Location loc = this->location(); |
| |
| if (pragmas != 0) |
| go_warning_at(loc, 0, "ignoring magic %<//go:...%> comment before var"); |
| |
| // Get the variable names. |
| Typed_identifier_list til; |
| this->identifier_list(&til); |
| |
| if (embeds != NULL) |
| { |
| if (!this->gogo_->in_global_scope()) |
| { |
| go_error_at(loc, "go:embed only permitted at package scope"); |
| embeds = NULL; |
| } |
| if (til.size() > 1) |
| { |
| go_error_at(loc, "go:embed cannot apply to multiple vars"); |
| embeds = NULL; |
| } |
| } |
| |
| 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); |
| } |
| |
| if (embeds != NULL && init != NULL) |
| { |
| go_error_at(loc, "go:embed cannot apply to var with initializer"); |
| embeds = NULL; |
| } |
| |
| this->init_vars(&til, type, init, false, embeds, 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, |
| std::vector<std::string>* embeds, Location location) |
| { |
| // Check for an initialization which can yield multiple values. |
| if (init != NULL && init->size() == 1 && til->size() > 1) |
| { |
| go_assert(embeds == NULL); |
| 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()) |
| go_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()); |
| Named_object* no = this->init_var(*p, type, |
| init == NULL ? NULL : *pexpr, |
| is_coloneq, false, &any_new, |
| vars, vals); |
| if (embeds != NULL && no->is_variable()) |
| no->var_value()->set_embeds(embeds); |
| if (init != NULL) |
| ++pexpr; |
| } |
| if (init != NULL) |
| go_assert(pexpr == init->end()); |
| if (is_coloneq && !any_new) |
| go_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. |
| |
| call->set_expected_result_count(vars->size()); |
| |
| 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 |
| { |
| // If the current object is a redefinition of another object, we |
| // might have already recorded the dependency relationship between |
| // it and the first variable. Either way, an error will be |
| // reported for the redefinition and we don't need to properly |
| // record dependency information for an invalid program. |
| if (no->is_redefinition()) |
| continue; |
| |
| // 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) |
| go_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) |
| go_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) |
| go_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) |
| go_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 |
| { |
| // 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); |
| var->set_is_global_sink(); |
| 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)); |
| |
| std::set<std::string> uniq_idents; |
| uniq_idents.insert(name); |
| std::string dup_name; |
| Location dup_loc; |
| |
| // 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(); |
| std::pair<std::set<std::string>::iterator, bool> ins; |
| |
| 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); |
| ins = uniq_idents.insert(id); |
| if (!ins.second && !Gogo::is_sink_name(id)) |
| { |
| // Use %s to print := to avoid -Wformat-diag warning. |
| go_error_at(id_location, |
| "%qs repeated on left side of %s", |
| Gogo::message_name(id).c_str(), ":="); |
| } |
| 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); |
| ins = uniq_idents.insert(id); |
| if (!ins.second && !Gogo::is_sink_name(id)) |
| { |
| dup_name = Gogo::message_name(id); |
| dup_loc = id_location; |
| } |
| 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(), |
| true, false)); |
| |
| 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 (!dup_name.empty()) |
| { |
| // Use %s to print := to avoid -Wformat-diag warning. |
| go_error_at(dup_loc, "%qs repeated on left side of %s", |
| dup_name.c_str(), ":="); |
| } |
| |
| 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, NULL, 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 or //go:linkname comments. |
| // |
| // PRAGMAS is a bitset of magic comments. |
| |
| void |
| Parse::function_decl(unsigned int pragmas) |
| { |
| 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(); |
| |
| bool expected_receiver = false; |
| Typed_identifier* rec = NULL; |
| if (token->is_op(OPERATOR_LPAREN)) |
| { |
| expected_receiver = true; |
| rec = this->receiver(); |
| token = this->peek_token(); |
| } |
| |
| if (!token->is_identifier()) |
| { |
| go_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)) |
| { |
| go_error_at(this->location(), "expected %<(%>"); |
| return; |
| } |
| token = this->advance_token(); |
| if (!token->is_string()) |
| { |
| go_error_at(this->location(), "expected string"); |
| return; |
| } |
| std::string asm_name = token->string_value(); |
| if (!this->advance_token()->is_op(OPERATOR_RPAREN)) |
| { |
| go_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)) |
| go_error_at(this->location(), |
| "unexpected semicolon or newline before %<{%>"); |
| else |
| this->unget_token(Token::make_operator_token(OPERATOR_SEMICOLON, |
| semi_loc)); |
| } |
| |
| static struct { |
| unsigned int bit; |
| const char* name; |
| bool decl_ok; |
| bool func_ok; |
| bool method_ok; |
| } pragma_check[] = |
| { |
| { GOPRAGMA_NOINTERFACE, "nointerface", false, false, true }, |
| { GOPRAGMA_NOESCAPE, "noescape", true, false, false }, |
| { GOPRAGMA_NORACE, "norace", false, true, true }, |
| { GOPRAGMA_NOSPLIT, "nosplit", false, true, true }, |
| { GOPRAGMA_NOINLINE, "noinline", false, true, true }, |
| { GOPRAGMA_SYSTEMSTACK, "systemstack", false, true, true }, |
| { GOPRAGMA_NOWRITEBARRIER, "nowritebarrier", false, true, true }, |
| { GOPRAGMA_NOWRITEBARRIERREC, "nowritebarrierrec", false, true, |
| true }, |
| { GOPRAGMA_YESWRITEBARRIERREC, "yeswritebarrierrec", false, true, |
| true }, |
| { GOPRAGMA_CGOUNSAFEARGS, "cgo_unsafe_args", false, true, true }, |
| { GOPRAGMA_UINTPTRESCAPES, "uintptrescapes", true, true, true }, |
| }; |
| |
| bool is_decl = !this->peek_token()->is_op(OPERATOR_LCURLY); |
| if (pragmas != 0) |
| { |
| for (size_t i = 0; |
| i < sizeof(pragma_check) / sizeof(pragma_check[0]); |
| ++i) |
| { |
| if ((pragmas & pragma_check[i].bit) == 0) |
| continue; |
| |
| if (is_decl) |
| { |
| if (pragma_check[i].decl_ok) |
| continue; |
| go_warning_at(location, 0, |
| ("ignoring magic %<//go:%s%> comment " |
| "before declaration"), |
| pragma_check[i].name); |
| } |
| else if (rec == NULL) |
| { |
| if (pragma_check[i].func_ok) |
| continue; |
| go_warning_at(location, 0, |
| ("ignoring magic %<//go:%s%> comment " |
| "before function definition"), |
| pragma_check[i].name); |
| } |
| else |
| { |
| if (pragma_check[i].method_ok) |
| continue; |
| go_warning_at(location, 0, |
| ("ignoring magic %<//go:%s%> comment " |
| "before method definition"), |
| pragma_check[i].name); |
| } |
| |
| pragmas &= ~ pragma_check[i].bit; |
| } |
| } |
| |
| if (is_decl) |
| { |
| if (named_object == NULL) |
| { |
| // Function declarations with the blank identifier as a name are |
| // mostly ignored since they cannot be called. We make an object |
| // for this declaration for type-checking purposes. |
| if (Gogo::is_sink_name(name)) |
| { |
| static int count; |
| char buf[30]; |
| snprintf(buf, sizeof buf, ".$sinkfndecl%d", count); |
| ++count; |
| name = std::string(buf); |
| } |
| |
| if (fntype == NULL |
| || (expected_receiver && rec == 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 (pragmas != 0 && named_object->is_function_declaration()) |
| named_object->func_declaration_value()->set_pragmas(pragmas); |
| } |
| 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 (pragmas != 0 |
| && !this->is_erroneous_function_ |
| && named_object->is_function()) |
| named_object->func_value()->set_pragmas(pragmas); |
| this->is_erroneous_function_ = hold_is_erroneous_function; |
| } |
| } |
| |
| // Receiver = Parameters . |
| |
| Typed_identifier* |
| Parse::receiver() |
| { |
| Location location = this->location(); |
| Typed_identifier_list* til; |
| if (!this->parameters(&til, NULL)) |
| return NULL; |
| else if (til == NULL || til->empty()) |
| { |
| go_error_at(location, "method has no receiver"); |
| return NULL; |
| } |
| else if (til->size() > 1) |
| { |
| go_error_at(location, "method has multiple receivers"); |
| return NULL; |
| } |
| else |
| return &til->front(); |
| } |
| |
| // 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()) |
| { |
| go_error_at(location, "unexpected reference to package"); |
| return Expression::make_error(location); |
| } |
| package = named_object->package_value(); |
| package->note_usage(id); |
| 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); |
| go_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) |
| go_error_at(location, |
| ("invalid reference to unexported identifier " |
| "%<%s.%s%>"), |
| n1.c_str(), n2.c_str()); |
| else |
| go_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, |
| may_be_sink, 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: |
| // Any left-hand-side can be a sink, so if this can not be |
| // a sink, then it must be a use of the variable. |
| if (!may_be_sink) |
| 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 |
| { |
| go_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_z(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, MPFR_RNDN); |
| mpc_t val; |
| mpc_init2(val, mpc_precision); |
| mpc_set_fr_fr(val, zero, *token->imaginary_value(), MPC_RNDNN); |
| mpfr_clear(zero); |
| ret = Expression::make_complex(&val, NULL, token->location()); |
| mpc_clear(val); |
| 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)) |
| go_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; |
| } |
| |
| go_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, |
| bool may_be_sink, Location location) |
| { |
| go_assert(var->is_variable() || var->is_result_variable()); |
| |
| // Any left-hand-side can be a sink, so if this can not be |
| // a sink, then it must be a use of the variable. |
| if (!may_be_sink) |
| this->mark_var_used(var); |
| |
| Named_object* this_function = this->gogo_->current_function(); |
| Named_object* closure = this_function->func_value()->closure_var(); |
| |
| // The last argument to the Enclosing_var constructor is the index |
| // of this variable in the closure. We add 1 to the current number |
| // of enclosed variables, because the first field in the closure |
| // points to the function code. |
| Enclosing_var ev(var, in_function, this->enclosing_vars_.size() + 1); |
| 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_dereference(closure_ref, |
| Expression::NIL_CHECK_NOT_NEEDED, |
| 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_dereference(e, Expression::NIL_CHECK_NOT_NEEDED, |
| location); |
| return Expression::make_enclosing_var_reference(e, var, location); |
| } |
| |
| // 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, |
| false, location); |
| } |
| |
| bool has_keys = false; |
| bool all_are_names = true; |
| Expression_list* vals = new Expression_list; |
| while (true) |
| { |
| Expression* val; |
| bool is_type_omitted = false; |
| bool is_name = false; |
| |
| const Token* token = this->peek_token(); |
| |
| if (token->is_identifier()) |
| { |
| std::string identifier = token->identifier(); |
| bool is_exported = token->is_identifier_exported(); |
| Location id_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), |
| id_location, false, true); |
| is_name = true; |
| } |
| else |
| { |
| this->unget_token(Token::make_identifier_token(identifier, |
| is_exported, |
| id_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); |
| is_name = false; |
| } |
| else |
| { |
| if (is_type_omitted) |
| { |
| // VAL is a nested composite literal with an omitted type being |
| // used a key. Record this information in VAL so that the correct |
| // type is associated with the literal value if VAL is a |
| // map literal. |
| val->complit()->update_key_path(depth); |
| } |
| |
| 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; |
| |
| 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 (!is_name) |
| all_are_names = false; |
| |
| 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)) |
| go_error_at(this->location(), |
| ("need trailing comma before newline " |
| "in composite literal")); |
| else |
| go_error_at(this->location(), "expected %<,%> or %<}%>"); |
| |
| this->gogo_->mark_locals_used(); |
| int edepth = 0; |
| while (!token->is_eof() |
| && (edepth > 0 || !token->is_op(OPERATOR_RCURLY))) |
| { |
| if (token->is_op(OPERATOR_LCURLY)) |
| ++edepth; |
|