diff options
author | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
---|---|---|
committer | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
commit | 554fd8c5195424bdbcabf5de30fdc183aba391bd (patch) | |
tree | 976dc5ab7fddf506dadce60ae936f43f58787092 /gcc/go/gofrontend/gogo.cc | |
download | cbb-gcc-4.6.4-upstream.tar.bz2 cbb-gcc-4.6.4-upstream.tar.xz |
obtained gcc-4.6.4.tar.bz2 from upstream website;upstream
verified gcc-4.6.4.tar.bz2.sig;
imported gcc-4.6.4 source tree from verified upstream tarball.
downloading a git-generated archive based on the 'upstream' tag
should provide you with a source tree that is binary identical
to the one extracted from the above tarball.
if you have obtained the source via the command 'git clone',
however, do note that line-endings of files in your working
directory might differ from line-endings of the respective
files in the upstream repository.
Diffstat (limited to 'gcc/go/gofrontend/gogo.cc')
-rw-r--r-- | gcc/go/gofrontend/gogo.cc | 4519 |
1 files changed, 4519 insertions, 0 deletions
diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc new file mode 100644 index 000000000..f8c143c9c --- /dev/null +++ b/gcc/go/gofrontend/gogo.cc @@ -0,0 +1,4519 @@ +// gogo.cc -- Go frontend parsed representation. + +// 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 "go-c.h" +#include "go-dump.h" +#include "lex.h" +#include "types.h" +#include "statements.h" +#include "expressions.h" +#include "dataflow.h" +#include "import.h" +#include "export.h" +#include "gogo.h" + +// Class Gogo. + +Gogo::Gogo(int int_type_size, int pointer_size) + : package_(NULL), + functions_(), + globals_(new Bindings(NULL)), + imports_(), + imported_unsafe_(false), + packages_(), + map_descriptors_(NULL), + type_descriptor_decls_(NULL), + init_functions_(), + need_init_fn_(false), + init_fn_name_(), + imported_init_fns_(), + unique_prefix_(), + unique_prefix_specified_(false), + interface_types_(), + named_types_are_converted_(false) +{ + const source_location loc = BUILTINS_LOCATION; + + Named_type* uint8_type = Type::make_integer_type("uint8", true, 8, + RUNTIME_TYPE_KIND_UINT8); + this->add_named_type(uint8_type); + this->add_named_type(Type::make_integer_type("uint16", true, 16, + RUNTIME_TYPE_KIND_UINT16)); + this->add_named_type(Type::make_integer_type("uint32", true, 32, + RUNTIME_TYPE_KIND_UINT32)); + this->add_named_type(Type::make_integer_type("uint64", true, 64, + RUNTIME_TYPE_KIND_UINT64)); + + this->add_named_type(Type::make_integer_type("int8", false, 8, + RUNTIME_TYPE_KIND_INT8)); + this->add_named_type(Type::make_integer_type("int16", false, 16, + RUNTIME_TYPE_KIND_INT16)); + this->add_named_type(Type::make_integer_type("int32", false, 32, + RUNTIME_TYPE_KIND_INT32)); + this->add_named_type(Type::make_integer_type("int64", false, 64, + RUNTIME_TYPE_KIND_INT64)); + + this->add_named_type(Type::make_float_type("float32", 32, + RUNTIME_TYPE_KIND_FLOAT32)); + this->add_named_type(Type::make_float_type("float64", 64, + RUNTIME_TYPE_KIND_FLOAT64)); + + this->add_named_type(Type::make_complex_type("complex64", 64, + RUNTIME_TYPE_KIND_COMPLEX64)); + this->add_named_type(Type::make_complex_type("complex128", 128, + RUNTIME_TYPE_KIND_COMPLEX128)); + + if (int_type_size < 32) + int_type_size = 32; + this->add_named_type(Type::make_integer_type("uint", true, + int_type_size, + RUNTIME_TYPE_KIND_UINT)); + Named_type* int_type = Type::make_integer_type("int", false, int_type_size, + RUNTIME_TYPE_KIND_INT); + this->add_named_type(int_type); + + // "byte" is an alias for "uint8". Construct a Named_object which + // points to UINT8_TYPE. Note that this breaks the normal pairing + // in which a Named_object points to a Named_type which points back + // to the same Named_object. + Named_object* byte_type = this->declare_type("byte", loc); + byte_type->set_type_value(uint8_type); + + this->add_named_type(Type::make_integer_type("uintptr", true, + pointer_size, + RUNTIME_TYPE_KIND_UINTPTR)); + + this->add_named_type(Type::make_named_bool_type()); + + this->add_named_type(Type::make_named_string_type()); + + this->globals_->add_constant(Typed_identifier("true", + Type::make_boolean_type(), + loc), + NULL, + Expression::make_boolean(true, loc), + 0); + this->globals_->add_constant(Typed_identifier("false", + Type::make_boolean_type(), + loc), + NULL, + Expression::make_boolean(false, loc), + 0); + + this->globals_->add_constant(Typed_identifier("nil", Type::make_nil_type(), + loc), + NULL, + Expression::make_nil(loc), + 0); + + Type* abstract_int_type = Type::make_abstract_integer_type(); + this->globals_->add_constant(Typed_identifier("iota", abstract_int_type, + loc), + NULL, + Expression::make_iota(), + 0); + + Function_type* new_type = Type::make_function_type(NULL, NULL, NULL, loc); + new_type->set_is_varargs(); + new_type->set_is_builtin(); + this->globals_->add_function_declaration("new", NULL, new_type, loc); + + Function_type* make_type = Type::make_function_type(NULL, NULL, NULL, loc); + make_type->set_is_varargs(); + make_type->set_is_builtin(); + this->globals_->add_function_declaration("make", NULL, make_type, loc); + + Typed_identifier_list* len_result = new Typed_identifier_list(); + len_result->push_back(Typed_identifier("", int_type, loc)); + Function_type* len_type = Type::make_function_type(NULL, NULL, len_result, + loc); + len_type->set_is_builtin(); + this->globals_->add_function_declaration("len", NULL, len_type, loc); + + Typed_identifier_list* cap_result = new Typed_identifier_list(); + cap_result->push_back(Typed_identifier("", int_type, loc)); + Function_type* cap_type = Type::make_function_type(NULL, NULL, len_result, + loc); + cap_type->set_is_builtin(); + this->globals_->add_function_declaration("cap", NULL, cap_type, loc); + + Function_type* print_type = Type::make_function_type(NULL, NULL, NULL, loc); + print_type->set_is_varargs(); + print_type->set_is_builtin(); + this->globals_->add_function_declaration("print", NULL, print_type, loc); + + print_type = Type::make_function_type(NULL, NULL, NULL, loc); + print_type->set_is_varargs(); + print_type->set_is_builtin(); + this->globals_->add_function_declaration("println", NULL, print_type, loc); + + Type *empty = Type::make_interface_type(NULL, loc); + Typed_identifier_list* panic_parms = new Typed_identifier_list(); + panic_parms->push_back(Typed_identifier("e", empty, loc)); + Function_type *panic_type = Type::make_function_type(NULL, panic_parms, + NULL, loc); + panic_type->set_is_builtin(); + this->globals_->add_function_declaration("panic", NULL, panic_type, loc); + + Typed_identifier_list* recover_result = new Typed_identifier_list(); + recover_result->push_back(Typed_identifier("", empty, loc)); + Function_type* recover_type = Type::make_function_type(NULL, NULL, + recover_result, + loc); + recover_type->set_is_builtin(); + this->globals_->add_function_declaration("recover", NULL, recover_type, loc); + + Function_type* close_type = Type::make_function_type(NULL, NULL, NULL, loc); + close_type->set_is_varargs(); + close_type->set_is_builtin(); + this->globals_->add_function_declaration("close", NULL, close_type, loc); + + Typed_identifier_list* closed_result = new Typed_identifier_list(); + closed_result->push_back(Typed_identifier("", Type::lookup_bool_type(), + loc)); + Function_type* closed_type = Type::make_function_type(NULL, NULL, + closed_result, loc); + closed_type->set_is_varargs(); + closed_type->set_is_builtin(); + this->globals_->add_function_declaration("closed", NULL, closed_type, loc); + + Typed_identifier_list* copy_result = new Typed_identifier_list(); + copy_result->push_back(Typed_identifier("", int_type, loc)); + Function_type* copy_type = Type::make_function_type(NULL, NULL, + copy_result, loc); + copy_type->set_is_varargs(); + copy_type->set_is_builtin(); + this->globals_->add_function_declaration("copy", NULL, copy_type, loc); + + Function_type* append_type = Type::make_function_type(NULL, NULL, NULL, loc); + append_type->set_is_varargs(); + append_type->set_is_builtin(); + this->globals_->add_function_declaration("append", NULL, append_type, loc); + + Function_type* complex_type = Type::make_function_type(NULL, NULL, NULL, loc); + complex_type->set_is_varargs(); + complex_type->set_is_builtin(); + this->globals_->add_function_declaration("complex", NULL, complex_type, loc); + + Function_type* real_type = Type::make_function_type(NULL, NULL, NULL, loc); + real_type->set_is_varargs(); + real_type->set_is_builtin(); + this->globals_->add_function_declaration("real", NULL, real_type, loc); + + Function_type* imag_type = Type::make_function_type(NULL, NULL, NULL, loc); + imag_type->set_is_varargs(); + imag_type->set_is_builtin(); + this->globals_->add_function_declaration("imag", NULL, imag_type, loc); + + this->define_builtin_function_trees(); +} + +// Munge name for use in an error message. + +std::string +Gogo::message_name(const std::string& name) +{ + return go_localize_identifier(Gogo::unpack_hidden_name(name).c_str()); +} + +// Get the package name. + +const std::string& +Gogo::package_name() const +{ + gcc_assert(this->package_ != NULL); + return this->package_->name(); +} + +// Set the package name. + +void +Gogo::set_package_name(const std::string& package_name, + source_location location) +{ + if (this->package_ != NULL && this->package_->name() != package_name) + { + error_at(location, "expected package %<%s%>", + Gogo::message_name(this->package_->name()).c_str()); + return; + } + + // If the user did not specify a unique prefix, we always use "go". + // This in effect requires that the package name be unique. + if (this->unique_prefix_.empty()) + this->unique_prefix_ = "go"; + + this->package_ = this->register_package(package_name, this->unique_prefix_, + location); + + // We used to permit people to qualify symbols with the current + // package name (e.g., P.x), but we no longer do. + // this->globals_->add_package(package_name, this->package_); + + if (this->is_main_package()) + { + // Declare "main" as a function which takes no parameters and + // returns no value. + this->declare_function("main", + Type::make_function_type(NULL, NULL, NULL, + BUILTINS_LOCATION), + BUILTINS_LOCATION); + } +} + +// Return whether this is the "main" package. This is not true if +// -fgo-prefix was used. + +bool +Gogo::is_main_package() const +{ + return this->package_name() == "main" && !this->unique_prefix_specified_; +} + +// Import a package. + +void +Gogo::import_package(const std::string& filename, + const std::string& local_name, + bool is_local_name_exported, + source_location location) +{ + if (filename == "unsafe") + { + this->import_unsafe(local_name, is_local_name_exported, location); + return; + } + + Imports::const_iterator p = this->imports_.find(filename); + if (p != this->imports_.end()) + { + Package* package = p->second; + package->set_location(location); + package->set_is_imported(); + std::string ln = local_name; + bool is_ln_exported = is_local_name_exported; + if (ln.empty()) + { + ln = package->name(); + is_ln_exported = Lex::is_exported_name(ln); + } + if (ln == ".") + { + Bindings* bindings = package->bindings(); + for (Bindings::const_declarations_iterator p = + bindings->begin_declarations(); + p != bindings->end_declarations(); + ++p) + this->add_named_object(p->second); + } + else if (ln == "_") + package->set_uses_sink_alias(); + else + { + ln = this->pack_hidden_name(ln, is_ln_exported); + this->package_->bindings()->add_package(ln, package); + } + return; + } + + Import::Stream* stream = Import::open_package(filename, location); + if (stream == NULL) + { + error_at(location, "import file %qs not found", filename.c_str()); + return; + } + + Import imp(stream, location); + imp.register_builtin_types(this); + Package* package = imp.import(this, local_name, is_local_name_exported); + if (package != NULL) + { + if (package->name() == this->package_name() + && package->unique_prefix() == this->unique_prefix()) + error_at(location, + ("imported package uses same package name and prefix " + "as package being compiled (see -fgo-prefix option)")); + + this->imports_.insert(std::make_pair(filename, package)); + package->set_is_imported(); + } + + delete stream; +} + +// Add an import control function for an imported package to the list. + +void +Gogo::add_import_init_fn(const std::string& package_name, + const std::string& init_name, int prio) +{ + for (std::set<Import_init>::const_iterator p = + this->imported_init_fns_.begin(); + p != this->imported_init_fns_.end(); + ++p) + { + if (p->init_name() == init_name + && (p->package_name() != package_name || p->priority() != prio)) + { + error("duplicate package initialization name %qs", + Gogo::message_name(init_name).c_str()); + inform(UNKNOWN_LOCATION, "used by package %qs at priority %d", + Gogo::message_name(p->package_name()).c_str(), + p->priority()); + inform(UNKNOWN_LOCATION, " and by package %qs at priority %d", + Gogo::message_name(package_name).c_str(), prio); + return; + } + } + + this->imported_init_fns_.insert(Import_init(package_name, init_name, + prio)); +} + +// Return whether we are at the global binding level. + +bool +Gogo::in_global_scope() const +{ + return this->functions_.empty(); +} + +// Return the current binding contour. + +Bindings* +Gogo::current_bindings() +{ + if (!this->functions_.empty()) + return this->functions_.back().blocks.back()->bindings(); + else if (this->package_ != NULL) + return this->package_->bindings(); + else + return this->globals_; +} + +const Bindings* +Gogo::current_bindings() const +{ + if (!this->functions_.empty()) + return this->functions_.back().blocks.back()->bindings(); + else if (this->package_ != NULL) + return this->package_->bindings(); + else + return this->globals_; +} + +// Return the current block. + +Block* +Gogo::current_block() +{ + if (this->functions_.empty()) + return NULL; + else + return this->functions_.back().blocks.back(); +} + +// Look up a name in the current binding contour. If PFUNCTION is not +// NULL, set it to the function in which the name is defined, or NULL +// if the name is defined in global scope. + +Named_object* +Gogo::lookup(const std::string& name, Named_object** pfunction) const +{ + if (pfunction != NULL) + *pfunction = NULL; + + if (Gogo::is_sink_name(name)) + return Named_object::make_sink(); + + for (Open_functions::const_reverse_iterator p = this->functions_.rbegin(); + p != this->functions_.rend(); + ++p) + { + Named_object* ret = p->blocks.back()->bindings()->lookup(name); + if (ret != NULL) + { + if (pfunction != NULL) + *pfunction = p->function; + return ret; + } + } + + if (this->package_ != NULL) + { + Named_object* ret = this->package_->bindings()->lookup(name); + if (ret != NULL) + { + if (ret->package() != NULL) + ret->package()->set_used(); + return ret; + } + } + + // We do not look in the global namespace. If we did, the global + // namespace would effectively hide names which were defined in + // package scope which we have not yet seen. Instead, + // define_global_names is called after parsing is over to connect + // undefined names at package scope with names defined at global + // scope. + + return NULL; +} + +// Look up a name in the current block, without searching enclosing +// blocks. + +Named_object* +Gogo::lookup_in_block(const std::string& name) const +{ + gcc_assert(!this->functions_.empty()); + gcc_assert(!this->functions_.back().blocks.empty()); + return this->functions_.back().blocks.back()->bindings()->lookup_local(name); +} + +// Look up a name in the global namespace. + +Named_object* +Gogo::lookup_global(const char* name) const +{ + return this->globals_->lookup(name); +} + +// Add an imported package. + +Package* +Gogo::add_imported_package(const std::string& real_name, + const std::string& alias_arg, + bool is_alias_exported, + const std::string& unique_prefix, + source_location location, + bool* padd_to_globals) +{ + // FIXME: Now that we compile packages as a whole, should we permit + // importing the current package? + if (this->package_name() == real_name + && this->unique_prefix() == unique_prefix) + { + *padd_to_globals = false; + if (!alias_arg.empty() && alias_arg != ".") + { + std::string alias = this->pack_hidden_name(alias_arg, + is_alias_exported); + this->package_->bindings()->add_package(alias, this->package_); + } + return this->package_; + } + else if (alias_arg == ".") + { + *padd_to_globals = true; + return this->register_package(real_name, unique_prefix, location); + } + else if (alias_arg == "_") + { + Package* ret = this->register_package(real_name, unique_prefix, location); + ret->set_uses_sink_alias(); + return ret; + } + else + { + *padd_to_globals = false; + std::string alias = alias_arg; + if (alias.empty()) + { + alias = real_name; + is_alias_exported = Lex::is_exported_name(alias); + } + alias = this->pack_hidden_name(alias, is_alias_exported); + Named_object* no = this->add_package(real_name, alias, unique_prefix, + location); + if (!no->is_package()) + return NULL; + return no->package_value(); + } +} + +// Add a package. + +Named_object* +Gogo::add_package(const std::string& real_name, const std::string& alias, + const std::string& unique_prefix, source_location location) +{ + gcc_assert(this->in_global_scope()); + + // Register the package. Note that we might have already seen it in + // an earlier import. + Package* package = this->register_package(real_name, unique_prefix, location); + + return this->package_->bindings()->add_package(alias, package); +} + +// Register a package. This package may or may not be imported. This +// returns the Package structure for the package, creating if it +// necessary. + +Package* +Gogo::register_package(const std::string& package_name, + const std::string& unique_prefix, + source_location location) +{ + gcc_assert(!unique_prefix.empty() && !package_name.empty()); + std::string name = unique_prefix + '.' + package_name; + Package* package = NULL; + std::pair<Packages::iterator, bool> ins = + this->packages_.insert(std::make_pair(name, package)); + if (!ins.second) + { + // We have seen this package name before. + package = ins.first->second; + gcc_assert(package != NULL); + gcc_assert(package->name() == package_name + && package->unique_prefix() == unique_prefix); + if (package->location() == UNKNOWN_LOCATION) + package->set_location(location); + } + else + { + // First time we have seen this package name. + package = new Package(package_name, unique_prefix, location); + gcc_assert(ins.first->second == NULL); + ins.first->second = package; + } + + return package; +} + +// Start compiling a function. + +Named_object* +Gogo::start_function(const std::string& name, Function_type* type, + bool add_method_to_type, source_location location) +{ + bool at_top_level = this->functions_.empty(); + + Block* block = new Block(NULL, location); + + Function* enclosing = (at_top_level + ? NULL + : this->functions_.back().function->func_value()); + + Function* function = new Function(type, enclosing, block, location); + + if (type->is_method()) + { + const Typed_identifier* receiver = type->receiver(); + Variable* this_param = new Variable(receiver->type(), NULL, false, + true, true, location); + std::string name = receiver->name(); + if (name.empty()) + { + // We need to give receivers a name since they wind up in + // DECL_ARGUMENTS. FIXME. + static unsigned int count; + char buf[50]; + snprintf(buf, sizeof buf, "r.%u", count); + ++count; + name = buf; + } + block->bindings()->add_variable(name, NULL, this_param); + } + + const Typed_identifier_list* parameters = type->parameters(); + bool is_varargs = type->is_varargs(); + if (parameters != NULL) + { + for (Typed_identifier_list::const_iterator p = parameters->begin(); + p != parameters->end(); + ++p) + { + Variable* param = new Variable(p->type(), NULL, false, true, false, + location); + if (is_varargs && p + 1 == parameters->end()) + param->set_is_varargs_parameter(); + + std::string name = p->name(); + if (name.empty() || Gogo::is_sink_name(name)) + { + // We need to give parameters a name since they wind up + // in DECL_ARGUMENTS. FIXME. + static unsigned int count; + char buf[50]; + snprintf(buf, sizeof buf, "p.%u", count); + ++count; + name = buf; + } + block->bindings()->add_variable(name, NULL, param); + } + } + + function->create_named_result_variables(this); + + const std::string* pname; + std::string nested_name; + bool is_init = false; + if (Gogo::unpack_hidden_name(name) == "init" && !type->is_method()) + { + if ((type->parameters() != NULL && !type->parameters()->empty()) + || (type->results() != NULL && !type->results()->empty())) + error_at(location, + "func init must have no arguments and no return values"); + // There can be multiple "init" functions, so give them each a + // different name. + static int init_count; + char buf[30]; + snprintf(buf, sizeof buf, ".$init%d", init_count); + ++init_count; + nested_name = buf; + pname = &nested_name; + is_init = true; + } + else if (!name.empty()) + pname = &name; + else + { + // Invent a name for a nested function. + static int nested_count; + char buf[30]; + snprintf(buf, sizeof buf, ".$nested%d", nested_count); + ++nested_count; + nested_name = buf; + pname = &nested_name; + } + + Named_object* ret; + if (Gogo::is_sink_name(*pname)) + { + static int sink_count; + char buf[30]; + snprintf(buf, sizeof buf, ".$sink%d", sink_count); + ++sink_count; + ret = Named_object::make_function(buf, NULL, function); + } + else if (!type->is_method()) + { + ret = this->package_->bindings()->add_function(*pname, NULL, function); + if (!ret->is_function() || ret->func_value() != function) + { + // Redefinition error. Invent a name to avoid knockon + // errors. + static int redefinition_count; + char buf[30]; + snprintf(buf, sizeof buf, ".$redefined%d", redefinition_count); + ++redefinition_count; + ret = this->package_->bindings()->add_function(buf, NULL, function); + } + } + else + { + if (!add_method_to_type) + ret = Named_object::make_function(name, NULL, function); + else + { + gcc_assert(at_top_level); + Type* rtype = type->receiver()->type(); + + // We want to look through the pointer created by the + // parser, without getting an error if the type is not yet + // defined. + if (rtype->classification() == Type::TYPE_POINTER) + rtype = rtype->points_to(); + + if (rtype->is_error_type()) + ret = Named_object::make_function(name, NULL, function); + else if (rtype->named_type() != NULL) + { + ret = rtype->named_type()->add_method(name, function); + if (!ret->is_function()) + { + // Redefinition error. + ret = Named_object::make_function(name, NULL, function); + } + } + else if (rtype->forward_declaration_type() != NULL) + { + Named_object* type_no = + rtype->forward_declaration_type()->named_object(); + if (type_no->is_unknown()) + { + // If we are seeing methods it really must be a + // type. Declare it as such. An alternative would + // be to support lists of methods for unknown + // expressions. Either way the error messages if + // this is not a type are going to get confusing. + Named_object* declared = + this->declare_package_type(type_no->name(), + type_no->location()); + gcc_assert(declared + == type_no->unknown_value()->real_named_object()); + } + ret = rtype->forward_declaration_type()->add_method(name, + function); + } + else + gcc_unreachable(); + } + this->package_->bindings()->add_method(ret); + } + + this->functions_.resize(this->functions_.size() + 1); + Open_function& of(this->functions_.back()); + of.function = ret; + of.blocks.push_back(block); + + if (is_init) + { + this->init_functions_.push_back(ret); + this->need_init_fn_ = true; + } + + return ret; +} + +// Finish compiling a function. + +void +Gogo::finish_function(source_location location) +{ + this->finish_block(location); + gcc_assert(this->functions_.back().blocks.empty()); + this->functions_.pop_back(); +} + +// Return the current function. + +Named_object* +Gogo::current_function() const +{ + gcc_assert(!this->functions_.empty()); + return this->functions_.back().function; +} + +// Start a new block. + +void +Gogo::start_block(source_location location) +{ + gcc_assert(!this->functions_.empty()); + Block* block = new Block(this->current_block(), location); + this->functions_.back().blocks.push_back(block); +} + +// Finish a block. + +Block* +Gogo::finish_block(source_location location) +{ + gcc_assert(!this->functions_.empty()); + gcc_assert(!this->functions_.back().blocks.empty()); + Block* block = this->functions_.back().blocks.back(); + this->functions_.back().blocks.pop_back(); + block->set_end_location(location); + return block; +} + +// Add an unknown name. + +Named_object* +Gogo::add_unknown_name(const std::string& name, source_location location) +{ + return this->package_->bindings()->add_unknown_name(name, location); +} + +// Declare a function. + +Named_object* +Gogo::declare_function(const std::string& name, Function_type* type, + source_location location) +{ + if (!type->is_method()) + return this->current_bindings()->add_function_declaration(name, NULL, type, + location); + else + { + // We don't bother to add this to the list of global + // declarations. + Type* rtype = type->receiver()->type(); + + // We want to look through the pointer created by the + // parser, without getting an error if the type is not yet + // defined. + if (rtype->classification() == Type::TYPE_POINTER) + rtype = rtype->points_to(); + + if (rtype->is_error_type()) + return NULL; + else if (rtype->named_type() != NULL) + return rtype->named_type()->add_method_declaration(name, NULL, type, + location); + else if (rtype->forward_declaration_type() != NULL) + { + Forward_declaration_type* ftype = rtype->forward_declaration_type(); + return ftype->add_method_declaration(name, type, location); + } + else + gcc_unreachable(); + } +} + +// Add a label definition. + +Label* +Gogo::add_label_definition(const std::string& label_name, + source_location location) +{ + gcc_assert(!this->functions_.empty()); + Function* func = this->functions_.back().function->func_value(); + Label* label = func->add_label_definition(label_name, location); + this->add_statement(Statement::make_label_statement(label, location)); + return label; +} + +// Add a label reference. + +Label* +Gogo::add_label_reference(const std::string& label_name) +{ + gcc_assert(!this->functions_.empty()); + Function* func = this->functions_.back().function->func_value(); + return func->add_label_reference(label_name); +} + +// Add a statement. + +void +Gogo::add_statement(Statement* statement) +{ + gcc_assert(!this->functions_.empty() + && !this->functions_.back().blocks.empty()); + this->functions_.back().blocks.back()->add_statement(statement); +} + +// Add a block. + +void +Gogo::add_block(Block* block, source_location location) +{ + gcc_assert(!this->functions_.empty() + && !this->functions_.back().blocks.empty()); + Statement* statement = Statement::make_block_statement(block, location); + this->functions_.back().blocks.back()->add_statement(statement); +} + +// Add a constant. + +Named_object* +Gogo::add_constant(const Typed_identifier& tid, Expression* expr, + int iota_value) +{ + return this->current_bindings()->add_constant(tid, NULL, expr, iota_value); +} + +// Add a type. + +void +Gogo::add_type(const std::string& name, Type* type, source_location location) +{ + Named_object* no = this->current_bindings()->add_type(name, NULL, type, + location); + if (!this->in_global_scope() && no->is_type()) + no->type_value()->set_in_function(this->functions_.back().function); +} + +// Add a named type. + +void +Gogo::add_named_type(Named_type* type) +{ + gcc_assert(this->in_global_scope()); + this->current_bindings()->add_named_type(type); +} + +// Declare a type. + +Named_object* +Gogo::declare_type(const std::string& name, source_location location) +{ + Bindings* bindings = this->current_bindings(); + Named_object* no = bindings->add_type_declaration(name, NULL, location); + if (!this->in_global_scope() && no->is_type_declaration()) + { + Named_object* f = this->functions_.back().function; + no->type_declaration_value()->set_in_function(f); + } + return no; +} + +// Declare a type at the package level. + +Named_object* +Gogo::declare_package_type(const std::string& name, source_location location) +{ + return this->package_->bindings()->add_type_declaration(name, NULL, location); +} + +// Define a type which was already declared. + +void +Gogo::define_type(Named_object* no, Named_type* type) +{ + this->current_bindings()->define_type(no, type); +} + +// Add a variable. + +Named_object* +Gogo::add_variable(const std::string& name, Variable* variable) +{ + Named_object* no = this->current_bindings()->add_variable(name, NULL, + variable); + + // In a function the middle-end wants to see a DECL_EXPR node. + if (no != NULL + && no->is_variable() + && !no->var_value()->is_parameter() + && !this->functions_.empty()) + this->add_statement(Statement::make_variable_declaration(no)); + + return no; +} + +// Add a sink--a reference to the blank identifier _. + +Named_object* +Gogo::add_sink() +{ + return Named_object::make_sink(); +} + +// Add a named object. + +void +Gogo::add_named_object(Named_object* no) +{ + this->current_bindings()->add_named_object(no); +} + +// Record that we've seen an interface type. + +void +Gogo::record_interface_type(Interface_type* itype) +{ + this->interface_types_.push_back(itype); +} + +// Return a name for a thunk object. + +std::string +Gogo::thunk_name() +{ + static int thunk_count; + char thunk_name[50]; + snprintf(thunk_name, sizeof thunk_name, "$thunk%d", thunk_count); + ++thunk_count; + return thunk_name; +} + +// Return whether a function is a thunk. + +bool +Gogo::is_thunk(const Named_object* no) +{ + return no->name().compare(0, 6, "$thunk") == 0; +} + +// Define the global names. We do this only after parsing all the +// input files, because the program might define the global names +// itself. + +void +Gogo::define_global_names() +{ + for (Bindings::const_declarations_iterator p = + this->globals_->begin_declarations(); + p != this->globals_->end_declarations(); + ++p) + { + Named_object* global_no = p->second; + std::string name(Gogo::pack_hidden_name(global_no->name(), false)); + Named_object* no = this->package_->bindings()->lookup(name); + if (no == NULL) + continue; + no = no->resolve(); + if (no->is_type_declaration()) + { + if (global_no->is_type()) + { + if (no->type_declaration_value()->has_methods()) + error_at(no->location(), + "may not define methods for global type"); + no->set_type_value(global_no->type_value()); + } + else + { + error_at(no->location(), "expected type"); + Type* errtype = Type::make_error_type(); + Named_object* err = Named_object::make_type("error", NULL, + errtype, + BUILTINS_LOCATION); + no->set_type_value(err->type_value()); + } + } + else if (no->is_unknown()) + no->unknown_value()->set_real_named_object(global_no); + } +} + +// Clear out names in file scope. + +void +Gogo::clear_file_scope() +{ + this->package_->bindings()->clear_file_scope(); + + // Warn about packages which were imported but not used. + for (Packages::iterator p = this->packages_.begin(); + p != this->packages_.end(); + ++p) + { + Package* package = p->second; + if (package != this->package_ + && package->is_imported() + && !package->used() + && !package->uses_sink_alias() + && !saw_errors()) + error_at(package->location(), "imported and not used: %s", + Gogo::message_name(package->name()).c_str()); + package->clear_is_imported(); + package->clear_uses_sink_alias(); + package->clear_used(); + } +} + +// Traverse the tree. + +void +Gogo::traverse(Traverse* traverse) +{ + // Traverse the current package first for consistency. The other + // packages will only contain imported types, constants, and + // declarations. + if (this->package_->bindings()->traverse(traverse, true) == TRAVERSE_EXIT) + return; + for (Packages::const_iterator p = this->packages_.begin(); + p != this->packages_.end(); + ++p) + { + if (p->second != this->package_) + { + if (p->second->bindings()->traverse(traverse, true) == TRAVERSE_EXIT) + break; + } + } +} + +// Traversal class used to verify types. + +class Verify_types : public Traverse +{ + public: + Verify_types() + : Traverse(traverse_types) + { } + + int + type(Type*); +}; + +// Verify that a type is correct. + +int +Verify_types::type(Type* t) +{ + if (!t->verify()) + return TRAVERSE_SKIP_COMPONENTS; + return TRAVERSE_CONTINUE; +} + +// Verify that all types are correct. + +void +Gogo::verify_types() +{ + Verify_types traverse; + this->traverse(&traverse); +} + +// Traversal class used to lower parse tree. + +class Lower_parse_tree : public Traverse +{ + public: + Lower_parse_tree(Gogo* gogo, Named_object* function) + : Traverse(traverse_variables + | traverse_constants + | traverse_functions + | traverse_statements + | traverse_expressions), + gogo_(gogo), function_(function), iota_value_(-1) + { } + + int + variable(Named_object*); + + int + constant(Named_object*, bool); + + int + function(Named_object*); + + int + statement(Block*, size_t* pindex, Statement*); + + int + expression(Expression**); + + private: + // General IR. + Gogo* gogo_; + // The function we are traversing. + Named_object* function_; + // Value to use for the predeclared constant iota. + int iota_value_; +}; + +// Lower variables. We handle variables specially to break loops in +// which a variable initialization expression refers to itself. The +// loop breaking is in lower_init_expression. + +int +Lower_parse_tree::variable(Named_object* no) +{ + if (no->is_variable()) + no->var_value()->lower_init_expression(this->gogo_, this->function_); + return TRAVERSE_CONTINUE; +} + +// Lower constants. We handle constants specially so that we can set +// the right value for the predeclared constant iota. This works in +// conjunction with the way we lower Const_expression objects. + +int +Lower_parse_tree::constant(Named_object* no, bool) +{ + Named_constant* nc = no->const_value(); + + // Don't get into trouble if the constant's initializer expression + // refers to the constant itself. + if (nc->lowering()) + return TRAVERSE_CONTINUE; + nc->set_lowering(); + + gcc_assert(this->iota_value_ == -1); + this->iota_value_ = nc->iota_value(); + nc->traverse_expression(this); + this->iota_value_ = -1; + + nc->clear_lowering(); + + // We will traverse the expression a second time, but that will be + // fast. + + return TRAVERSE_CONTINUE; +} + +// Lower function closure types. Record the function while lowering +// it, so that we can pass it down when lowering an expression. + +int +Lower_parse_tree::function(Named_object* no) +{ + no->func_value()->set_closure_type(); + + gcc_assert(this->function_ == NULL); + this->function_ = no; + int t = no->func_value()->traverse(this); + this->function_ = NULL; + + if (t == TRAVERSE_EXIT) + return t; + return TRAVERSE_SKIP_COMPONENTS; +} + +// Lower statement parse trees. + +int +Lower_parse_tree::statement(Block* block, size_t* pindex, Statement* sorig) +{ + // Lower the expressions first. + int t = sorig->traverse_contents(this); + if (t == TRAVERSE_EXIT) + return t; + + // Keep lowering until nothing changes. + Statement* s = sorig; + while (true) + { + Statement* snew = s->lower(this->gogo_, block); + if (snew == s) + break; + s = snew; + t = s->traverse_contents(this); + if (t == TRAVERSE_EXIT) + return t; + } + + if (s != sorig) + block->replace_statement(*pindex, s); + + return TRAVERSE_SKIP_COMPONENTS; +} + +// Lower expression parse trees. + +int +Lower_parse_tree::expression(Expression** pexpr) +{ + // We have to lower all subexpressions first, so that we can get + // their type if necessary. This is awkward, because we don't have + // a postorder traversal pass. + if ((*pexpr)->traverse_subexpressions(this) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + // Keep lowering until nothing changes. + while (true) + { + Expression* e = *pexpr; + Expression* enew = e->lower(this->gogo_, this->function_, + this->iota_value_); + if (enew == e) + break; + *pexpr = enew; + } + return TRAVERSE_SKIP_COMPONENTS; +} + +// Lower the parse tree. This is called after the parse is complete, +// when all names should be resolved. + +void +Gogo::lower_parse_tree() +{ + Lower_parse_tree lower_parse_tree(this, NULL); + this->traverse(&lower_parse_tree); +} + +// Lower an expression. + +void +Gogo::lower_expression(Named_object* function, Expression** pexpr) +{ + Lower_parse_tree lower_parse_tree(this, function); + lower_parse_tree.expression(pexpr); +} + +// Lower a constant. This is called when lowering a reference to a +// constant. We have to make sure that the constant has already been +// lowered. + +void +Gogo::lower_constant(Named_object* no) +{ + gcc_assert(no->is_const()); + Lower_parse_tree lower(this, NULL); + lower.constant(no, false); +} + +// Look for interface types to finalize methods of inherited +// interfaces. + +class Finalize_methods : public Traverse +{ + public: + Finalize_methods(Gogo* gogo) + : Traverse(traverse_types), + gogo_(gogo) + { } + + int + type(Type*); + + private: + Gogo* gogo_; +}; + +// Finalize the methods of an interface type. + +int +Finalize_methods::type(Type* t) +{ + // Check the classification so that we don't finalize the methods + // twice for a named interface type. + switch (t->classification()) + { + case Type::TYPE_INTERFACE: + t->interface_type()->finalize_methods(); + break; + + case Type::TYPE_NAMED: + { + // We have to finalize the methods of the real type first. + // But if the real type is a struct type, then we only want to + // finalize the methods of the field types, not of the struct + // type itself. We don't want to add methods to the struct, + // since it has a name. + Type* rt = t->named_type()->real_type(); + if (rt->classification() != Type::TYPE_STRUCT) + { + if (Type::traverse(rt, this) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + else + { + if (rt->struct_type()->traverse_field_types(this) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + + t->named_type()->finalize_methods(this->gogo_); + + return TRAVERSE_SKIP_COMPONENTS; + } + + case Type::TYPE_STRUCT: + t->struct_type()->finalize_methods(this->gogo_); + break; + + default: + break; + } + + return TRAVERSE_CONTINUE; +} + +// Finalize method lists and build stub methods for types. + +void +Gogo::finalize_methods() +{ + Finalize_methods finalize(this); + this->traverse(&finalize); +} + +// Set types for unspecified variables and constants. + +void +Gogo::determine_types() +{ + Bindings* bindings = this->current_bindings(); + for (Bindings::const_definitions_iterator p = bindings->begin_definitions(); + p != bindings->end_definitions(); + ++p) + { + if ((*p)->is_function()) + (*p)->func_value()->determine_types(); + else if ((*p)->is_variable()) + (*p)->var_value()->determine_type(); + else if ((*p)->is_const()) + (*p)->const_value()->determine_type(); + + // See if a variable requires us to build an initialization + // function. We know that we will see all global variables + // here. + if (!this->need_init_fn_ && (*p)->is_variable()) + { + Variable* variable = (*p)->var_value(); + + // If this is a global variable which requires runtime + // initialization, we need an initialization function. + if (!variable->is_global()) + ; + else if (variable->init() == NULL) + ; + else if (variable->type()->interface_type() != NULL) + this->need_init_fn_ = true; + else if (variable->init()->is_constant()) + ; + else if (!variable->init()->is_composite_literal()) + this->need_init_fn_ = true; + else if (variable->init()->is_nonconstant_composite_literal()) + this->need_init_fn_ = true; + + // If this is a global variable which holds a pointer value, + // then we need an initialization function to register it as a + // GC root. + if (variable->is_global() && variable->type()->has_pointer()) + this->need_init_fn_ = true; + } + } + + // Determine the types of constants in packages. + for (Packages::const_iterator p = this->packages_.begin(); + p != this->packages_.end(); + ++p) + p->second->determine_types(); +} + +// Traversal class used for type checking. + +class Check_types_traverse : public Traverse +{ + public: + Check_types_traverse(Gogo* gogo) + : Traverse(traverse_variables + | traverse_constants + | traverse_statements + | traverse_expressions), + gogo_(gogo) + { } + + int + variable(Named_object*); + + int + constant(Named_object*, bool); + + int + statement(Block*, size_t* pindex, Statement*); + + int + expression(Expression**); + + private: + // General IR. + Gogo* gogo_; +}; + +// Check that a variable initializer has the right type. + +int +Check_types_traverse::variable(Named_object* named_object) +{ + if (named_object->is_variable()) + { + Variable* var = named_object->var_value(); + Expression* init = var->init(); + std::string reason; + if (init != NULL + && !Type::are_assignable(var->type(), init->type(), &reason)) + { + if (reason.empty()) + error_at(var->location(), "incompatible type in initialization"); + else + error_at(var->location(), + "incompatible type in initialization (%s)", + reason.c_str()); + var->clear_init(); + } + } + return TRAVERSE_CONTINUE; +} + +// Check that a constant initializer has the right type. + +int +Check_types_traverse::constant(Named_object* named_object, bool) +{ + Named_constant* constant = named_object->const_value(); + Type* ctype = constant->type(); + if (ctype->integer_type() == NULL + && ctype->float_type() == NULL + && ctype->complex_type() == NULL + && !ctype->is_boolean_type() + && !ctype->is_string_type()) + { + if (!ctype->is_error_type()) + error_at(constant->location(), "invalid constant type"); + constant->set_error(); + } + else if (!constant->expr()->is_constant()) + { + error_at(constant->expr()->location(), "expression is not constant"); + constant->set_error(); + } + else if (!Type::are_assignable(constant->type(), constant->expr()->type(), + NULL)) + { + error_at(constant->location(), + "initialization expression has wrong type"); + constant->set_error(); + } + return TRAVERSE_CONTINUE; +} + +// Check that types are valid in a statement. + +int +Check_types_traverse::statement(Block*, size_t*, Statement* s) +{ + s->check_types(this->gogo_); + return TRAVERSE_CONTINUE; +} + +// Check that types are valid in an expression. + +int +Check_types_traverse::expression(Expression** expr) +{ + (*expr)->check_types(this->gogo_); + return TRAVERSE_CONTINUE; +} + +// Check that types are valid. + +void +Gogo::check_types() +{ + Check_types_traverse traverse(this); + this->traverse(&traverse); +} + +// Check the types in a single block. + +void +Gogo::check_types_in_block(Block* block) +{ + Check_types_traverse traverse(this); + block->traverse(&traverse); +} + +// A traversal class used to find a single shortcut operator within an +// expression. + +class Find_shortcut : public Traverse +{ + public: + Find_shortcut() + : Traverse(traverse_blocks + | traverse_statements + | traverse_expressions), + found_(NULL) + { } + + // A pointer to the expression which was found, or NULL if none was + // found. + Expression** + found() const + { return this->found_; } + + protected: + int + block(Block*) + { return TRAVERSE_SKIP_COMPONENTS; } + + int + statement(Block*, size_t*, Statement*) + { return TRAVERSE_SKIP_COMPONENTS; } + + int + expression(Expression**); + + private: + Expression** found_; +}; + +// Find a shortcut expression. + +int +Find_shortcut::expression(Expression** pexpr) +{ + Expression* expr = *pexpr; + Binary_expression* be = expr->binary_expression(); + if (be == NULL) + return TRAVERSE_CONTINUE; + Operator op = be->op(); + if (op != OPERATOR_OROR && op != OPERATOR_ANDAND) + return TRAVERSE_CONTINUE; + gcc_assert(this->found_ == NULL); + this->found_ = pexpr; + return TRAVERSE_EXIT; +} + +// A traversal class used to turn shortcut operators into explicit if +// statements. + +class Shortcuts : public Traverse +{ + public: + Shortcuts(Gogo* gogo) + : Traverse(traverse_variables + | traverse_statements), + gogo_(gogo) + { } + + protected: + int + variable(Named_object*); + + int + statement(Block*, size_t*, Statement*); + + private: + // Convert a shortcut operator. + Statement* + convert_shortcut(Block* enclosing, Expression** pshortcut); + + // The IR. + Gogo* gogo_; +}; + +// Remove shortcut operators in a single statement. + +int +Shortcuts::statement(Block* block, size_t* pindex, Statement* s) +{ + // FIXME: This approach doesn't work for switch statements, because + // we add the new statements before the whole switch when we need to + // instead add them just before the switch expression. The right + // fix is probably to lower switch statements with nonconstant cases + // to a series of conditionals. + if (s->switch_statement() != NULL) + return TRAVERSE_CONTINUE; + + while (true) + { + Find_shortcut find_shortcut; + + // If S is a variable declaration, then ordinary traversal won't + // do anything. We want to explicitly traverse the + // initialization expression if there is one. + Variable_declaration_statement* vds = s->variable_declaration_statement(); + Expression* init = NULL; + if (vds == NULL) + s->traverse_contents(&find_shortcut); + else + { + init = vds->var()->var_value()->init(); + if (init == NULL) + return TRAVERSE_CONTINUE; + init->traverse(&init, &find_shortcut); + } + Expression** pshortcut = find_shortcut.found(); + if (pshortcut == NULL) + return TRAVERSE_CONTINUE; + + Statement* snew = this->convert_shortcut(block, pshortcut); + block->insert_statement_before(*pindex, snew); + ++*pindex; + + if (pshortcut == &init) + vds->var()->var_value()->set_init(init); + } +} + +// Remove shortcut operators in the initializer of a global variable. + +int +Shortcuts::variable(Named_object* no) +{ + if (no->is_result_variable()) + return TRAVERSE_CONTINUE; + Variable* var = no->var_value(); + Expression* init = var->init(); + if (!var->is_global() || init == NULL) + return TRAVERSE_CONTINUE; + + while (true) + { + Find_shortcut find_shortcut; + init->traverse(&init, &find_shortcut); + Expression** pshortcut = find_shortcut.found(); + if (pshortcut == NULL) + return TRAVERSE_CONTINUE; + + Statement* snew = this->convert_shortcut(NULL, pshortcut); + var->add_preinit_statement(this->gogo_, snew); + if (pshortcut == &init) + var->set_init(init); + } +} + +// Given an expression which uses a shortcut operator, return a +// statement which implements it, and update *PSHORTCUT accordingly. + +Statement* +Shortcuts::convert_shortcut(Block* enclosing, Expression** pshortcut) +{ + Binary_expression* shortcut = (*pshortcut)->binary_expression(); + Expression* left = shortcut->left(); + Expression* right = shortcut->right(); + source_location loc = shortcut->location(); + + Block* retblock = new Block(enclosing, loc); + retblock->set_end_location(loc); + + Temporary_statement* ts = Statement::make_temporary(Type::lookup_bool_type(), + left, loc); + retblock->add_statement(ts); + + Block* block = new Block(retblock, loc); + block->set_end_location(loc); + Expression* tmpref = Expression::make_temporary_reference(ts, loc); + Statement* assign = Statement::make_assignment(tmpref, right, loc); + block->add_statement(assign); + + Expression* cond = Expression::make_temporary_reference(ts, loc); + if (shortcut->binary_expression()->op() == OPERATOR_OROR) + cond = Expression::make_unary(OPERATOR_NOT, cond, loc); + + Statement* if_statement = Statement::make_if_statement(cond, block, NULL, + loc); + retblock->add_statement(if_statement); + + *pshortcut = Expression::make_temporary_reference(ts, loc); + + delete shortcut; + + // Now convert any shortcut operators in LEFT and RIGHT. + Shortcuts shortcuts(this->gogo_); + retblock->traverse(&shortcuts); + + return Statement::make_block_statement(retblock, loc); +} + +// Turn shortcut operators into explicit if statements. Doing this +// considerably simplifies the order of evaluation rules. + +void +Gogo::remove_shortcuts() +{ + Shortcuts shortcuts(this); + this->traverse(&shortcuts); +} + +// A traversal class which finds all the expressions which must be +// evaluated in order within a statement or larger expression. This +// is used to implement the rules about order of evaluation. + +class Find_eval_ordering : public Traverse +{ + private: + typedef std::vector<Expression**> Expression_pointers; + + public: + Find_eval_ordering() + : Traverse(traverse_blocks + | traverse_statements + | traverse_expressions), + exprs_() + { } + + size_t + size() const + { return this->exprs_.size(); } + + typedef Expression_pointers::const_iterator const_iterator; + + const_iterator + begin() const + { return this->exprs_.begin(); } + + const_iterator + end() const + { return this->exprs_.end(); } + + protected: + int + block(Block*) + { return TRAVERSE_SKIP_COMPONENTS; } + + int + statement(Block*, size_t*, Statement*) + { return TRAVERSE_SKIP_COMPONENTS; } + + int + expression(Expression**); + + private: + // A list of pointers to expressions with side-effects. + Expression_pointers exprs_; +}; + +// If an expression must be evaluated in order, put it on the list. + +int +Find_eval_ordering::expression(Expression** expression_pointer) +{ + // We have to look at subexpressions before this one. + if ((*expression_pointer)->traverse_subexpressions(this) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + if ((*expression_pointer)->must_eval_in_order()) + this->exprs_.push_back(expression_pointer); + return TRAVERSE_SKIP_COMPONENTS; +} + +// A traversal class for ordering evaluations. + +class Order_eval : public Traverse +{ + public: + Order_eval(Gogo* gogo) + : Traverse(traverse_variables + | traverse_statements), + gogo_(gogo) + { } + + int + variable(Named_object*); + + int + statement(Block*, size_t*, Statement*); + + private: + // The IR. + Gogo* gogo_; +}; + +// Implement the order of evaluation rules for a statement. + +int +Order_eval::statement(Block* block, size_t* pindex, Statement* s) +{ + // FIXME: This approach doesn't work for switch statements, because + // we add the new statements before the whole switch when we need to + // instead add them just before the switch expression. The right + // fix is probably to lower switch statements with nonconstant cases + // to a series of conditionals. + if (s->switch_statement() != NULL) + return TRAVERSE_CONTINUE; + + Find_eval_ordering find_eval_ordering; + + // If S is a variable declaration, then ordinary traversal won't do + // anything. We want to explicitly traverse the initialization + // expression if there is one. + Variable_declaration_statement* vds = s->variable_declaration_statement(); + Expression* init = NULL; + Expression* orig_init = NULL; + if (vds == NULL) + s->traverse_contents(&find_eval_ordering); + else + { + init = vds->var()->var_value()->init(); + if (init == NULL) + return TRAVERSE_CONTINUE; + orig_init = init; + + // It might seem that this could be + // init->traverse_subexpressions. Unfortunately that can fail + // in a case like + // var err os.Error + // newvar, err := call(arg()) + // Here newvar will have an init of call result 0 of + // call(arg()). If we only traverse subexpressions, we will + // only find arg(), and we won't bother to move anything out. + // Then we get to the assignment to err, we will traverse the + // whole statement, and this time we will find both call() and + // arg(), and so we will move them out. This will cause them to + // be put into temporary variables before the assignment to err + // but after the declaration of newvar. To avoid that problem, + // we traverse the entire expression here. + Expression::traverse(&init, &find_eval_ordering); + } + + if (find_eval_ordering.size() <= 1) + { + // If there is only one expression with a side-effect, we can + // leave it in place. + return TRAVERSE_CONTINUE; + } + + bool is_thunk = s->thunk_statement() != NULL; + for (Find_eval_ordering::const_iterator p = find_eval_ordering.begin(); + p != find_eval_ordering.end(); + ++p) + { + Expression** pexpr = *p; + + // If the last expression is a send or receive expression, we + // may be ignoring the value; we don't want to evaluate it + // early. + if (p + 1 == find_eval_ordering.end() + && ((*pexpr)->classification() == Expression::EXPRESSION_SEND + || (*pexpr)->classification() == Expression::EXPRESSION_RECEIVE)) + break; + + // The last expression in a thunk will be the call passed to go + // or defer, which we must not evaluate early. + if (is_thunk && p + 1 == find_eval_ordering.end()) + break; + + source_location loc = (*pexpr)->location(); + Temporary_statement* ts = Statement::make_temporary(NULL, *pexpr, loc); + block->insert_statement_before(*pindex, ts); + ++*pindex; + + *pexpr = Expression::make_temporary_reference(ts, loc); + } + + if (init != orig_init) + vds->var()->var_value()->set_init(init); + + return TRAVERSE_CONTINUE; +} + +// Implement the order of evaluation rules for the initializer of a +// global variable. + +int +Order_eval::variable(Named_object* no) +{ + if (no->is_result_variable()) + return TRAVERSE_CONTINUE; + Variable* var = no->var_value(); + Expression* init = var->init(); + if (!var->is_global() || init == NULL) + return TRAVERSE_CONTINUE; + + Find_eval_ordering find_eval_ordering; + init->traverse_subexpressions(&find_eval_ordering); + + if (find_eval_ordering.size() <= 1) + { + // If there is only one expression with a side-effect, we can + // leave it in place. + return TRAVERSE_SKIP_COMPONENTS; + } + + for (Find_eval_ordering::const_iterator p = find_eval_ordering.begin(); + p != find_eval_ordering.end(); + ++p) + { + Expression** pexpr = *p; + source_location loc = (*pexpr)->location(); + Temporary_statement* ts = Statement::make_temporary(NULL, *pexpr, loc); + var->add_preinit_statement(this->gogo_, ts); + *pexpr = Expression::make_temporary_reference(ts, loc); + } + + return TRAVERSE_SKIP_COMPONENTS; +} + +// Use temporary variables to implement the order of evaluation rules. + +void +Gogo::order_evaluations() +{ + Order_eval order_eval(this); + this->traverse(&order_eval); +} + +// Traversal to convert calls to the predeclared recover function to +// pass in an argument indicating whether it can recover from a panic +// or not. + +class Convert_recover : public Traverse +{ + public: + Convert_recover(Named_object* arg) + : Traverse(traverse_expressions), + arg_(arg) + { } + + protected: + int + expression(Expression**); + + private: + // The argument to pass to the function. + Named_object* arg_; +}; + +// Convert calls to recover. + +int +Convert_recover::expression(Expression** pp) +{ + Call_expression* ce = (*pp)->call_expression(); + if (ce != NULL && ce->is_recover_call()) + ce->set_recover_arg(Expression::make_var_reference(this->arg_, + ce->location())); + return TRAVERSE_CONTINUE; +} + +// Traversal for build_recover_thunks. + +class Build_recover_thunks : public Traverse +{ + public: + Build_recover_thunks(Gogo* gogo) + : Traverse(traverse_functions), + gogo_(gogo) + { } + + int + function(Named_object*); + + private: + Expression* + can_recover_arg(source_location); + + // General IR. + Gogo* gogo_; +}; + +// If this function calls recover, turn it into a thunk. + +int +Build_recover_thunks::function(Named_object* orig_no) +{ + Function* orig_func = orig_no->func_value(); + if (!orig_func->calls_recover() + || orig_func->is_recover_thunk() + || orig_func->has_recover_thunk()) + return TRAVERSE_CONTINUE; + + Gogo* gogo = this->gogo_; + source_location location = orig_func->location(); + + static int count; + char buf[50]; + + Function_type* orig_fntype = orig_func->type(); + Typed_identifier_list* new_params = new Typed_identifier_list(); + std::string receiver_name; + if (orig_fntype->is_method()) + { + const Typed_identifier* receiver = orig_fntype->receiver(); + snprintf(buf, sizeof buf, "rt.%u", count); + ++count; + receiver_name = buf; + new_params->push_back(Typed_identifier(receiver_name, receiver->type(), + receiver->location())); + } + const Typed_identifier_list* orig_params = orig_fntype->parameters(); + if (orig_params != NULL && !orig_params->empty()) + { + for (Typed_identifier_list::const_iterator p = orig_params->begin(); + p != orig_params->end(); + ++p) + { + snprintf(buf, sizeof buf, "pt.%u", count); + ++count; + new_params->push_back(Typed_identifier(buf, p->type(), + p->location())); + } + } + snprintf(buf, sizeof buf, "pr.%u", count); + ++count; + std::string can_recover_name = buf; + new_params->push_back(Typed_identifier(can_recover_name, + Type::lookup_bool_type(), + orig_fntype->location())); + + const Typed_identifier_list* orig_results = orig_fntype->results(); + Typed_identifier_list* new_results; + if (orig_results == NULL || orig_results->empty()) + new_results = NULL; + else + { + new_results = new Typed_identifier_list(); + for (Typed_identifier_list::const_iterator p = orig_results->begin(); + p != orig_results->end(); + ++p) + new_results->push_back(Typed_identifier("", p->type(), p->location())); + } + + Function_type *new_fntype = Type::make_function_type(NULL, new_params, + new_results, + orig_fntype->location()); + if (orig_fntype->is_varargs()) + new_fntype->set_is_varargs(); + + std::string name = orig_no->name() + "$recover"; + Named_object *new_no = gogo->start_function(name, new_fntype, false, + location); + Function *new_func = new_no->func_value(); + if (orig_func->enclosing() != NULL) + new_func->set_enclosing(orig_func->enclosing()); + + // We build the code for the original function attached to the new + // function, and then swap the original and new function bodies. + // This means that existing references to the original function will + // then refer to the new function. That makes this code a little + // confusing, in that the reference to NEW_NO really refers to the + // other function, not the one we are building. + + Expression* closure = NULL; + if (orig_func->needs_closure()) + { + Named_object* orig_closure_no = orig_func->closure_var(); + Variable* orig_closure_var = orig_closure_no->var_value(); + Variable* new_var = new Variable(orig_closure_var->type(), NULL, false, + true, false, location); + snprintf(buf, sizeof buf, "closure.%u", count); + ++count; + Named_object* new_closure_no = Named_object::make_variable(buf, NULL, + new_var); + new_func->set_closure_var(new_closure_no); + closure = Expression::make_var_reference(new_closure_no, location); + } + + Expression* fn = Expression::make_func_reference(new_no, closure, location); + + Expression_list* args = new Expression_list(); + if (new_params != NULL) + { + // Note that we skip the last parameter, which is the boolean + // indicating whether recover can succed. + for (Typed_identifier_list::const_iterator p = new_params->begin(); + p + 1 != new_params->end(); + ++p) + { + Named_object* p_no = gogo->lookup(p->name(), NULL); + gcc_assert(p_no != NULL + && p_no->is_variable() + && p_no->var_value()->is_parameter()); + args->push_back(Expression::make_var_reference(p_no, location)); + } + } + args->push_back(this->can_recover_arg(location)); + + Call_expression* call = Expression::make_call(fn, args, false, location); + + Statement* s; + if (orig_fntype->results() == NULL || orig_fntype->results()->empty()) + s = Statement::make_statement(call); + else + { + Expression_list* vals = new Expression_list(); + size_t rc = orig_fntype->results()->size(); + if (rc == 1) + vals->push_back(call); + else + { + for (size_t i = 0; i < rc; ++i) + vals->push_back(Expression::make_call_result(call, i)); + } + s = Statement::make_return_statement(new_func->type()->results(), + vals, location); + } + s->determine_types(); + gogo->add_statement(s); + + gogo->finish_function(location); + + // Swap the function bodies and types. + new_func->swap_for_recover(orig_func); + orig_func->set_is_recover_thunk(); + new_func->set_calls_recover(); + new_func->set_has_recover_thunk(); + + Bindings* orig_bindings = orig_func->block()->bindings(); + Bindings* new_bindings = new_func->block()->bindings(); + if (orig_fntype->is_method()) + { + // We changed the receiver to be a regular parameter. We have + // to update the binding accordingly in both functions. + Named_object* orig_rec_no = orig_bindings->lookup_local(receiver_name); + gcc_assert(orig_rec_no != NULL + && orig_rec_no->is_variable() + && !orig_rec_no->var_value()->is_receiver()); + orig_rec_no->var_value()->set_is_receiver(); + + const std::string& new_receiver_name(orig_fntype->receiver()->name()); + Named_object* new_rec_no = new_bindings->lookup_local(new_receiver_name); + if (new_rec_no == NULL) + gcc_assert(saw_errors()); + else + { + gcc_assert(new_rec_no->is_variable() + && new_rec_no->var_value()->is_receiver()); + new_rec_no->var_value()->set_is_not_receiver(); + } + } + + // Because we flipped blocks but not types, the can_recover + // parameter appears in the (now) old bindings as a parameter. + // Change it to a local variable, whereupon it will be discarded. + Named_object* can_recover_no = orig_bindings->lookup_local(can_recover_name); + gcc_assert(can_recover_no != NULL + && can_recover_no->is_variable() + && can_recover_no->var_value()->is_parameter()); + orig_bindings->remove_binding(can_recover_no); + + // Add the can_recover argument to the (now) new bindings, and + // attach it to any recover statements. + Variable* can_recover_var = new Variable(Type::lookup_bool_type(), NULL, + false, true, false, location); + can_recover_no = new_bindings->add_variable(can_recover_name, NULL, + can_recover_var); + Convert_recover convert_recover(can_recover_no); + new_func->traverse(&convert_recover); + + // Update the function pointers in any named results. + new_func->update_named_result_variables(); + orig_func->update_named_result_variables(); + + return TRAVERSE_CONTINUE; +} + +// Return the expression to pass for the .can_recover parameter to the +// new function. This indicates whether a call to recover may return +// non-nil. The expression is +// __go_can_recover(__builtin_return_address()). + +Expression* +Build_recover_thunks::can_recover_arg(source_location location) +{ + static Named_object* builtin_return_address; + if (builtin_return_address == NULL) + { + const source_location bloc = BUILTINS_LOCATION; + + Typed_identifier_list* param_types = new Typed_identifier_list(); + Type* uint_type = Type::lookup_integer_type("uint"); + param_types->push_back(Typed_identifier("l", uint_type, bloc)); + + Typed_identifier_list* return_types = new Typed_identifier_list(); + Type* voidptr_type = Type::make_pointer_type(Type::make_void_type()); + return_types->push_back(Typed_identifier("", voidptr_type, bloc)); + + Function_type* fntype = Type::make_function_type(NULL, param_types, + return_types, bloc); + builtin_return_address = + Named_object::make_function_declaration("__builtin_return_address", + NULL, fntype, bloc); + const char* n = "__builtin_return_address"; + builtin_return_address->func_declaration_value()->set_asm_name(n); + } + + static Named_object* can_recover; + if (can_recover == NULL) + { + const source_location bloc = BUILTINS_LOCATION; + Typed_identifier_list* param_types = new Typed_identifier_list(); + Type* voidptr_type = Type::make_pointer_type(Type::make_void_type()); + param_types->push_back(Typed_identifier("a", voidptr_type, bloc)); + Type* boolean_type = Type::lookup_bool_type(); + Typed_identifier_list* results = new Typed_identifier_list(); + results->push_back(Typed_identifier("", boolean_type, bloc)); + Function_type* fntype = Type::make_function_type(NULL, param_types, + results, bloc); + can_recover = Named_object::make_function_declaration("__go_can_recover", + NULL, fntype, + bloc); + can_recover->func_declaration_value()->set_asm_name("__go_can_recover"); + } + + Expression* fn = Expression::make_func_reference(builtin_return_address, + NULL, location); + + mpz_t zval; + mpz_init_set_ui(zval, 0UL); + Expression* zexpr = Expression::make_integer(&zval, NULL, location); + mpz_clear(zval); + Expression_list *args = new Expression_list(); + args->push_back(zexpr); + + Expression* call = Expression::make_call(fn, args, false, location); + + args = new Expression_list(); + args->push_back(call); + + fn = Expression::make_func_reference(can_recover, NULL, location); + return Expression::make_call(fn, args, false, location); +} + +// Build thunks for functions which call recover. We build a new +// function with an extra parameter, which is whether a call to +// recover can succeed. We then move the body of this function to +// that one. We then turn this function into a thunk which calls the +// new one, passing the value of +// __go_can_recover(__builtin_return_address()). The function will be +// marked as not splitting the stack. This will cooperate with the +// implementation of defer to make recover do the right thing. + +void +Gogo::build_recover_thunks() +{ + Build_recover_thunks build_recover_thunks(this); + this->traverse(&build_recover_thunks); +} + +// Look for named types to see whether we need to create an interface +// method table. + +class Build_method_tables : public Traverse +{ + public: + Build_method_tables(Gogo* gogo, + const std::vector<Interface_type*>& interfaces) + : Traverse(traverse_types), + gogo_(gogo), interfaces_(interfaces) + { } + + int + type(Type*); + + private: + // The IR. + Gogo* gogo_; + // A list of locally defined interfaces which have hidden methods. + const std::vector<Interface_type*>& interfaces_; +}; + +// Build all required interface method tables for types. We need to +// ensure that we have an interface method table for every interface +// which has a hidden method, for every named type which implements +// that interface. Normally we can just build interface method tables +// as we need them. However, in some cases we can require an +// interface method table for an interface defined in a different +// package for a type defined in that package. If that interface and +// type both use a hidden method, that is OK. However, we will not be +// able to build that interface method table when we need it, because +// the type's hidden method will be static. So we have to build it +// here, and just refer it from other packages as needed. + +void +Gogo::build_interface_method_tables() +{ + std::vector<Interface_type*> hidden_interfaces; + hidden_interfaces.reserve(this->interface_types_.size()); + for (std::vector<Interface_type*>::const_iterator pi = + this->interface_types_.begin(); + pi != this->interface_types_.end(); + ++pi) + { + const Typed_identifier_list* methods = (*pi)->methods(); + if (methods == NULL) + continue; + for (Typed_identifier_list::const_iterator pm = methods->begin(); + pm != methods->end(); + ++pm) + { + if (Gogo::is_hidden_name(pm->name())) + { + hidden_interfaces.push_back(*pi); + break; + } + } + } + + if (!hidden_interfaces.empty()) + { + // Now traverse the tree looking for all named types. + Build_method_tables bmt(this, hidden_interfaces); + this->traverse(&bmt); + } + + // We no longer need the list of interfaces. + + this->interface_types_.clear(); +} + +// This is called for each type. For a named type, for each of the +// interfaces with hidden methods that it implements, create the +// method table. + +int +Build_method_tables::type(Type* type) +{ + Named_type* nt = type->named_type(); + if (nt != NULL) + { + for (std::vector<Interface_type*>::const_iterator p = + this->interfaces_.begin(); + p != this->interfaces_.end(); + ++p) + { + // We ask whether a pointer to the named type implements the + // interface, because a pointer can implement more methods + // than a value. + if ((*p)->implements_interface(Type::make_pointer_type(nt), NULL)) + { + nt->interface_method_table(this->gogo_, *p, false); + nt->interface_method_table(this->gogo_, *p, true); + } + } + } + return TRAVERSE_CONTINUE; +} + +// Traversal class used to check for return statements. + +class Check_return_statements_traverse : public Traverse +{ + public: + Check_return_statements_traverse() + : Traverse(traverse_functions) + { } + + int + function(Named_object*); +}; + +// Check that a function has a return statement if it needs one. + +int +Check_return_statements_traverse::function(Named_object* no) +{ + Function* func = no->func_value(); + const Function_type* fntype = func->type(); + const Typed_identifier_list* results = fntype->results(); + + // We only need a return statement if there is a return value. + if (results == NULL || results->empty()) + return TRAVERSE_CONTINUE; + + if (func->block()->may_fall_through()) + error_at(func->location(), "control reaches end of non-void function"); + + return TRAVERSE_CONTINUE; +} + +// Check return statements. + +void +Gogo::check_return_statements() +{ + Check_return_statements_traverse traverse; + this->traverse(&traverse); +} + +// Get the unique prefix to use before all exported symbols. This +// must be unique across the entire link. + +const std::string& +Gogo::unique_prefix() const +{ + gcc_assert(!this->unique_prefix_.empty()); + return this->unique_prefix_; +} + +// Set the unique prefix to use before all exported symbols. This +// comes from the command line option -fgo-prefix=XXX. + +void +Gogo::set_unique_prefix(const std::string& arg) +{ + gcc_assert(this->unique_prefix_.empty()); + this->unique_prefix_ = arg; + this->unique_prefix_specified_ = true; +} + +// Work out the package priority. It is one more than the maximum +// priority of an imported package. + +int +Gogo::package_priority() const +{ + int priority = 0; + for (Packages::const_iterator p = this->packages_.begin(); + p != this->packages_.end(); + ++p) + if (p->second->priority() > priority) + priority = p->second->priority(); + return priority + 1; +} + +// Export identifiers as requested. + +void +Gogo::do_exports() +{ + // For now we always stream to a section. Later we may want to + // support streaming to a separate file. + Stream_to_section stream; + + Export exp(&stream); + exp.register_builtin_types(this); + exp.export_globals(this->package_name(), + this->unique_prefix(), + this->package_priority(), + (this->need_init_fn_ && !this->is_main_package() + ? this->get_init_fn_name() + : ""), + this->imported_init_fns_, + this->package_->bindings()); +} + +// Find the blocks in order to convert named types defined in blocks. + +class Convert_named_types : public Traverse +{ + public: + Convert_named_types(Gogo* gogo) + : Traverse(traverse_blocks), + gogo_(gogo) + { } + + protected: + int + block(Block* block); + + private: + Gogo* gogo_; +}; + +int +Convert_named_types::block(Block* block) +{ + this->gogo_->convert_named_types_in_bindings(block->bindings()); + return TRAVERSE_CONTINUE; +} + +// Convert all named types to the backend representation. Since named +// types can refer to other types, this needs to be done in the right +// sequence, which is handled by Named_type::convert. Here we arrange +// to call that for each named type. + +void +Gogo::convert_named_types() +{ + this->convert_named_types_in_bindings(this->globals_); + for (Packages::iterator p = this->packages_.begin(); + p != this->packages_.end(); + ++p) + { + Package* package = p->second; + this->convert_named_types_in_bindings(package->bindings()); + } + + Convert_named_types cnt(this); + this->traverse(&cnt); + + // Make all the builtin named types used for type descriptors, and + // then convert them. They will only be written out if they are + // needed. + Type::make_type_descriptor_type(); + Type::make_type_descriptor_ptr_type(); + Function_type::make_function_type_descriptor_type(); + Pointer_type::make_pointer_type_descriptor_type(); + Struct_type::make_struct_type_descriptor_type(); + Array_type::make_array_type_descriptor_type(); + Array_type::make_slice_type_descriptor_type(); + Map_type::make_map_type_descriptor_type(); + Channel_type::make_chan_type_descriptor_type(); + Interface_type::make_interface_type_descriptor_type(); + Type::convert_builtin_named_types(this); + + this->named_types_are_converted_ = true; +} + +// Convert all names types in a set of bindings. + +void +Gogo::convert_named_types_in_bindings(Bindings* bindings) +{ + for (Bindings::const_definitions_iterator p = bindings->begin_definitions(); + p != bindings->end_definitions(); + ++p) + { + if ((*p)->is_type()) + (*p)->type_value()->convert(this); + } +} + +// Class Function. + +Function::Function(Function_type* type, Function* enclosing, Block* block, + source_location location) + : type_(type), enclosing_(enclosing), named_results_(NULL), + closure_var_(NULL), block_(block), location_(location), fndecl_(NULL), + defer_stack_(NULL), calls_recover_(false), is_recover_thunk_(false), + has_recover_thunk_(false) +{ +} + +// Create the named result variables. + +void +Function::create_named_result_variables(Gogo* gogo) +{ + const Typed_identifier_list* results = this->type_->results(); + if (results == NULL + || results->empty() + || results->front().name().empty()) + return; + + this->named_results_ = new Named_results(); + this->named_results_->reserve(results->size()); + + Block* block = this->block_; + int index = 0; + for (Typed_identifier_list::const_iterator p = results->begin(); + p != results->end(); + ++p, ++index) + { + std::string name = p->name(); + if (Gogo::is_sink_name(name)) + { + static int unnamed_result_counter; + char buf[100]; + snprintf(buf, sizeof buf, "_$%d", unnamed_result_counter); + ++unnamed_result_counter; + name = gogo->pack_hidden_name(buf, false); + } + Result_variable* result = new Result_variable(p->type(), this, index); + Named_object* no = block->bindings()->add_result_variable(name, result); + if (no->is_result_variable()) + this->named_results_->push_back(no); + } +} + +// Update the named result variables when cloning a function which +// calls recover. + +void +Function::update_named_result_variables() +{ + if (this->named_results_ == NULL) + return; + + for (Named_results::iterator p = this->named_results_->begin(); + p != this->named_results_->end(); + ++p) + (*p)->result_var_value()->set_function(this); +} + +// Return the closure variable, creating it if necessary. + +Named_object* +Function::closure_var() +{ + if (this->closure_var_ == NULL) + { + // We don't know the type of the variable yet. We add fields as + // we find them. + source_location loc = this->type_->location(); + Struct_field_list* sfl = new Struct_field_list; + Type* struct_type = Type::make_struct_type(sfl, loc); + Variable* var = new Variable(Type::make_pointer_type(struct_type), + NULL, false, true, false, loc); + this->closure_var_ = Named_object::make_variable("closure", NULL, var); + // Note that the new variable is not in any binding contour. + } + return this->closure_var_; +} + +// Set the type of the closure variable. + +void +Function::set_closure_type() +{ + if (this->closure_var_ == NULL) + return; + Named_object* closure = this->closure_var_; + Struct_type* st = closure->var_value()->type()->deref()->struct_type(); + unsigned int index = 0; + for (Closure_fields::const_iterator p = this->closure_fields_.begin(); + p != this->closure_fields_.end(); + ++p, ++index) + { + Named_object* no = p->first; + char buf[20]; + snprintf(buf, sizeof buf, "%u", index); + std::string n = no->name() + buf; + Type* var_type; + if (no->is_variable()) + var_type = no->var_value()->type(); + else + var_type = no->result_var_value()->type(); + Type* field_type = Type::make_pointer_type(var_type); + st->push_field(Struct_field(Typed_identifier(n, field_type, p->second))); + } +} + +// Return whether this function is a method. + +bool +Function::is_method() const +{ + return this->type_->is_method(); +} + +// Add a label definition. + +Label* +Function::add_label_definition(const std::string& label_name, + source_location location) +{ + Label* lnull = NULL; + std::pair<Labels::iterator, bool> ins = + this->labels_.insert(std::make_pair(label_name, lnull)); + if (ins.second) + { + // This is a new label. + Label* label = new Label(label_name); + label->define(location); + ins.first->second = label; + return label; + } + else + { + // The label was already in the hash table. + Label* label = ins.first->second; + if (!label->is_defined()) + { + label->define(location); + return label; + } + else + { + error_at(location, "redefinition of label %qs", + Gogo::message_name(label_name).c_str()); + inform(label->location(), "previous definition of %qs was here", + Gogo::message_name(label_name).c_str()); + return new Label(label_name); + } + } +} + +// Add a reference to a label. + +Label* +Function::add_label_reference(const std::string& label_name) +{ + Label* lnull = NULL; + std::pair<Labels::iterator, bool> ins = + this->labels_.insert(std::make_pair(label_name, lnull)); + if (!ins.second) + { + // The label was already in the hash table. + return ins.first->second; + } + else + { + gcc_assert(ins.first->second == NULL); + Label* label = new Label(label_name); + ins.first->second = label; + return label; + } +} + +// Swap one function with another. This is used when building the +// thunk we use to call a function which calls recover. It may not +// work for any other case. + +void +Function::swap_for_recover(Function *x) +{ + gcc_assert(this->enclosing_ == x->enclosing_); + std::swap(this->named_results_, x->named_results_); + std::swap(this->closure_var_, x->closure_var_); + std::swap(this->block_, x->block_); + gcc_assert(this->location_ == x->location_); + gcc_assert(this->fndecl_ == NULL && x->fndecl_ == NULL); + gcc_assert(this->defer_stack_ == NULL && x->defer_stack_ == NULL); +} + +// Traverse the tree. + +int +Function::traverse(Traverse* traverse) +{ + unsigned int traverse_mask = traverse->traverse_mask(); + + if ((traverse_mask + & (Traverse::traverse_types | Traverse::traverse_expressions)) + != 0) + { + if (Type::traverse(this->type_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + + // FIXME: We should check traverse_functions here if nested + // functions are stored in block bindings. + if (this->block_ != NULL + && (traverse_mask + & (Traverse::traverse_variables + | Traverse::traverse_constants + | Traverse::traverse_blocks + | Traverse::traverse_statements + | Traverse::traverse_expressions + | Traverse::traverse_types)) != 0) + { + if (this->block_->traverse(traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + + return TRAVERSE_CONTINUE; +} + +// Work out types for unspecified variables and constants. + +void +Function::determine_types() +{ + if (this->block_ != NULL) + this->block_->determine_types(); +} + +// Export the function. + +void +Function::export_func(Export* exp, const std::string& name) const +{ + Function::export_func_with_type(exp, name, this->type_); +} + +// Export a function with a type. + +void +Function::export_func_with_type(Export* exp, const std::string& name, + const Function_type* fntype) +{ + exp->write_c_string("func "); + + if (fntype->is_method()) + { + exp->write_c_string("("); + exp->write_type(fntype->receiver()->type()); + exp->write_c_string(") "); + } + + exp->write_string(name); + + exp->write_c_string(" ("); + const Typed_identifier_list* parameters = fntype->parameters(); + if (parameters != NULL) + { + bool is_varargs = fntype->is_varargs(); + bool first = true; + for (Typed_identifier_list::const_iterator p = parameters->begin(); + p != parameters->end(); + ++p) + { + if (first) + first = false; + else + exp->write_c_string(", "); + if (!is_varargs || p + 1 != parameters->end()) + exp->write_type(p->type()); + else + { + exp->write_c_string("..."); + exp->write_type(p->type()->array_type()->element_type()); + } + } + } + exp->write_c_string(")"); + + const Typed_identifier_list* results = fntype->results(); + if (results != NULL) + { + if (results->size() == 1) + { + exp->write_c_string(" "); + exp->write_type(results->begin()->type()); + } + else + { + exp->write_c_string(" ("); + bool first = true; + for (Typed_identifier_list::const_iterator p = results->begin(); + p != results->end(); + ++p) + { + if (first) + first = false; + else + exp->write_c_string(", "); + exp->write_type(p->type()); + } + exp->write_c_string(")"); + } + } + exp->write_c_string(";\n"); +} + +// Import a function. + +void +Function::import_func(Import* imp, std::string* pname, + Typed_identifier** preceiver, + Typed_identifier_list** pparameters, + Typed_identifier_list** presults, + bool* is_varargs) +{ + imp->require_c_string("func "); + + *preceiver = NULL; + if (imp->peek_char() == '(') + { + imp->require_c_string("("); + Type* rtype = imp->read_type(); + *preceiver = new Typed_identifier(Import::import_marker, rtype, + imp->location()); + imp->require_c_string(") "); + } + + *pname = imp->read_identifier(); + + Typed_identifier_list* parameters; + *is_varargs = false; + imp->require_c_string(" ("); + if (imp->peek_char() == ')') + parameters = NULL; + else + { + parameters = new Typed_identifier_list(); + while (true) + { + if (imp->match_c_string("...")) + { + imp->advance(3); + *is_varargs = true; + } + + Type* ptype = imp->read_type(); + if (*is_varargs) + ptype = Type::make_array_type(ptype, NULL); + parameters->push_back(Typed_identifier(Import::import_marker, + ptype, imp->location())); + if (imp->peek_char() != ',') + break; + gcc_assert(!*is_varargs); + imp->require_c_string(", "); + } + } + imp->require_c_string(")"); + *pparameters = parameters; + + Typed_identifier_list* results; + if (imp->peek_char() != ' ') + results = NULL; + else + { + results = new Typed_identifier_list(); + imp->require_c_string(" "); + if (imp->peek_char() != '(') + { + Type* rtype = imp->read_type(); + results->push_back(Typed_identifier(Import::import_marker, rtype, + imp->location())); + } + else + { + imp->require_c_string("("); + while (true) + { + Type* rtype = imp->read_type(); + results->push_back(Typed_identifier(Import::import_marker, + rtype, imp->location())); + if (imp->peek_char() != ',') + break; + imp->require_c_string(", "); + } + imp->require_c_string(")"); + } + } + imp->require_c_string(";\n"); + *presults = results; +} + +// Class Block. + +Block::Block(Block* enclosing, source_location location) + : enclosing_(enclosing), statements_(), + bindings_(new Bindings(enclosing == NULL + ? NULL + : enclosing->bindings())), + start_location_(location), + end_location_(UNKNOWN_LOCATION) +{ +} + +// Add a statement to a block. + +void +Block::add_statement(Statement* statement) +{ + this->statements_.push_back(statement); +} + +// Add a statement to the front of a block. This is slow but is only +// used for reference counts of parameters. + +void +Block::add_statement_at_front(Statement* statement) +{ + this->statements_.insert(this->statements_.begin(), statement); +} + +// Replace a statement in a block. + +void +Block::replace_statement(size_t index, Statement* s) +{ + gcc_assert(index < this->statements_.size()); + this->statements_[index] = s; +} + +// Add a statement before another statement. + +void +Block::insert_statement_before(size_t index, Statement* s) +{ + gcc_assert(index < this->statements_.size()); + this->statements_.insert(this->statements_.begin() + index, s); +} + +// Add a statement after another statement. + +void +Block::insert_statement_after(size_t index, Statement* s) +{ + gcc_assert(index < this->statements_.size()); + this->statements_.insert(this->statements_.begin() + index + 1, s); +} + +// Traverse the tree. + +int +Block::traverse(Traverse* traverse) +{ + unsigned int traverse_mask = traverse->traverse_mask(); + + if ((traverse_mask & Traverse::traverse_blocks) != 0) + { + int t = traverse->block(this); + if (t == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + else if (t == TRAVERSE_SKIP_COMPONENTS) + return TRAVERSE_CONTINUE; + } + + if ((traverse_mask + & (Traverse::traverse_variables + | Traverse::traverse_constants + | Traverse::traverse_expressions + | Traverse::traverse_types)) != 0) + { + for (Bindings::const_definitions_iterator pb = + this->bindings_->begin_definitions(); + pb != this->bindings_->end_definitions(); + ++pb) + { + switch ((*pb)->classification()) + { + case Named_object::NAMED_OBJECT_CONST: + if ((traverse_mask & Traverse::traverse_constants) != 0) + { + if (traverse->constant(*pb, false) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + if ((traverse_mask & Traverse::traverse_types) != 0 + || (traverse_mask & Traverse::traverse_expressions) != 0) + { + Type* t = (*pb)->const_value()->type(); + if (t != NULL + && Type::traverse(t, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + if ((traverse_mask & Traverse::traverse_expressions) != 0 + || (traverse_mask & Traverse::traverse_types) != 0) + { + if ((*pb)->const_value()->traverse_expression(traverse) + == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + break; + + case Named_object::NAMED_OBJECT_VAR: + case Named_object::NAMED_OBJECT_RESULT_VAR: + if ((traverse_mask & Traverse::traverse_variables) != 0) + { + if (traverse->variable(*pb) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + if (((traverse_mask & Traverse::traverse_types) != 0 + || (traverse_mask & Traverse::traverse_expressions) != 0) + && ((*pb)->is_result_variable() + || (*pb)->var_value()->has_type())) + { + Type* t = ((*pb)->is_variable() + ? (*pb)->var_value()->type() + : (*pb)->result_var_value()->type()); + if (t != NULL + && Type::traverse(t, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + if ((*pb)->is_variable() + && ((traverse_mask & Traverse::traverse_expressions) != 0 + || (traverse_mask & Traverse::traverse_types) != 0)) + { + if ((*pb)->var_value()->traverse_expression(traverse) + == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + break; + + case Named_object::NAMED_OBJECT_FUNC: + case Named_object::NAMED_OBJECT_FUNC_DECLARATION: + // FIXME: Where will nested functions be found? + gcc_unreachable(); + + case Named_object::NAMED_OBJECT_TYPE: + if ((traverse_mask & Traverse::traverse_types) != 0 + || (traverse_mask & Traverse::traverse_expressions) != 0) + { + if (Type::traverse((*pb)->type_value(), traverse) + == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + break; + + case Named_object::NAMED_OBJECT_TYPE_DECLARATION: + case Named_object::NAMED_OBJECT_UNKNOWN: + break; + + case Named_object::NAMED_OBJECT_PACKAGE: + case Named_object::NAMED_OBJECT_SINK: + gcc_unreachable(); + + default: + gcc_unreachable(); + } + } + } + + // No point in checking traverse_mask here--if we got here we always + // want to walk the statements. The traversal can insert new + // statements before or after the current statement. Inserting + // statements before the current statement requires updating I via + // the pointer; those statements will not be traversed. Any new + // statements inserted after the current statement will be traversed + // in their turn. + for (size_t i = 0; i < this->statements_.size(); ++i) + { + if (this->statements_[i]->traverse(this, &i, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + + return TRAVERSE_CONTINUE; +} + +// Work out types for unspecified variables and constants. + +void +Block::determine_types() +{ + for (Bindings::const_definitions_iterator pb = + this->bindings_->begin_definitions(); + pb != this->bindings_->end_definitions(); + ++pb) + { + if ((*pb)->is_variable()) + (*pb)->var_value()->determine_type(); + else if ((*pb)->is_const()) + (*pb)->const_value()->determine_type(); + } + + for (std::vector<Statement*>::const_iterator ps = this->statements_.begin(); + ps != this->statements_.end(); + ++ps) + (*ps)->determine_types(); +} + +// Return true if the statements in this block may fall through. + +bool +Block::may_fall_through() const +{ + if (this->statements_.empty()) + return true; + return this->statements_.back()->may_fall_through(); +} + +// Class Variable. + +Variable::Variable(Type* type, Expression* init, bool is_global, + bool is_parameter, bool is_receiver, + source_location location) + : type_(type), init_(init), preinit_(NULL), location_(location), + is_global_(is_global), is_parameter_(is_parameter), + is_receiver_(is_receiver), is_varargs_parameter_(false), + is_address_taken_(false), seen_(false), init_is_lowered_(false), + type_from_init_tuple_(false), type_from_range_index_(false), + type_from_range_value_(false), type_from_chan_element_(false), + is_type_switch_var_(false), determined_type_(false) +{ + gcc_assert(type != NULL || init != NULL); + gcc_assert(!is_parameter || init == NULL); +} + +// Traverse the initializer expression. + +int +Variable::traverse_expression(Traverse* traverse) +{ + if (this->preinit_ != NULL) + { + if (this->preinit_->traverse(traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + if (this->init_ != NULL) + { + if (Expression::traverse(&this->init_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + return TRAVERSE_CONTINUE; +} + +// Lower the initialization expression after parsing is complete. + +void +Variable::lower_init_expression(Gogo* gogo, Named_object* function) +{ + if (this->init_ != NULL && !this->init_is_lowered_) + { + if (this->seen_) + { + // We will give an error elsewhere, this is just to prevent + // an infinite loop. + return; + } + this->seen_ = true; + + gogo->lower_expression(function, &this->init_); + + this->seen_ = false; + + this->init_is_lowered_ = true; + } +} + +// Get the preinit block. + +Block* +Variable::preinit_block(Gogo* gogo) +{ + gcc_assert(this->is_global_); + if (this->preinit_ == NULL) + this->preinit_ = new Block(NULL, this->location()); + + // If a global variable has a preinitialization statement, then we + // need to have an initialization function. + gogo->set_need_init_fn(); + + return this->preinit_; +} + +// Add a statement to be run before the initialization expression. + +void +Variable::add_preinit_statement(Gogo* gogo, Statement* s) +{ + Block* b = this->preinit_block(gogo); + b->add_statement(s); + b->set_end_location(s->location()); +} + +// In an assignment which sets a variable to a tuple of EXPR, return +// the type of the first element of the tuple. + +Type* +Variable::type_from_tuple(Expression* expr, bool report_error) const +{ + if (expr->map_index_expression() != NULL) + { + Map_type* mt = expr->map_index_expression()->get_map_type(); + if (mt == NULL) + return Type::make_error_type(); + return mt->val_type(); + } + else if (expr->receive_expression() != NULL) + { + Expression* channel = expr->receive_expression()->channel(); + Type* channel_type = channel->type(); + if (channel_type->channel_type() == NULL) + return Type::make_error_type(); + return channel_type->channel_type()->element_type(); + } + else + { + if (report_error) + error_at(this->location(), "invalid tuple definition"); + return Type::make_error_type(); + } +} + +// Given EXPR used in a range clause, return either the index type or +// the value type of the range, depending upon GET_INDEX_TYPE. + +Type* +Variable::type_from_range(Expression* expr, bool get_index_type, + bool report_error) const +{ + Type* t = expr->type(); + if (t->array_type() != NULL + || (t->points_to() != NULL + && t->points_to()->array_type() != NULL + && !t->points_to()->is_open_array_type())) + { + if (get_index_type) + return Type::lookup_integer_type("int"); + else + return t->deref()->array_type()->element_type(); + } + else if (t->is_string_type()) + return Type::lookup_integer_type("int"); + else if (t->map_type() != NULL) + { + if (get_index_type) + return t->map_type()->key_type(); + else + return t->map_type()->val_type(); + } + else if (t->channel_type() != NULL) + { + if (get_index_type) + return t->channel_type()->element_type(); + else + { + if (report_error) + error_at(this->location(), + "invalid definition of value variable for channel range"); + return Type::make_error_type(); + } + } + else + { + if (report_error) + error_at(this->location(), "invalid type for range clause"); + return Type::make_error_type(); + } +} + +// EXPR should be a channel. Return the channel's element type. + +Type* +Variable::type_from_chan_element(Expression* expr, bool report_error) const +{ + Type* t = expr->type(); + if (t->channel_type() != NULL) + return t->channel_type()->element_type(); + else + { + if (report_error) + error_at(this->location(), "expected channel"); + return Type::make_error_type(); + } +} + +// Return the type of the Variable. This may be called before +// Variable::determine_type is called, which means that we may need to +// get the type from the initializer. FIXME: If we combine lowering +// with type determination, then this should be unnecessary. + +Type* +Variable::type() +{ + // A variable in a type switch with a nil case will have the wrong + // type here. This gets fixed up in determine_type, below. + Type* type = this->type_; + Expression* init = this->init_; + if (this->is_type_switch_var_ + && this->type_->is_nil_constant_as_type()) + { + Type_guard_expression* tge = this->init_->type_guard_expression(); + gcc_assert(tge != NULL); + init = tge->expr(); + type = NULL; + } + + if (this->seen_) + { + if (this->type_ == NULL || !this->type_->is_error_type()) + { + error_at(this->location_, "variable initializer refers to itself"); + this->type_ = Type::make_error_type(); + } + return this->type_; + } + + this->seen_ = true; + + if (type != NULL) + ; + else if (this->type_from_init_tuple_) + type = this->type_from_tuple(init, false); + else if (this->type_from_range_index_ || this->type_from_range_value_) + type = this->type_from_range(init, this->type_from_range_index_, false); + else if (this->type_from_chan_element_) + type = this->type_from_chan_element(init, false); + else + { + gcc_assert(init != NULL); + type = init->type(); + gcc_assert(type != NULL); + + // Variables should not have abstract types. + if (type->is_abstract()) + type = type->make_non_abstract_type(); + + if (type->is_void_type()) + type = Type::make_error_type(); + } + + this->seen_ = false; + + return type; +} + +// Fetch the type from a const pointer, in which case it should have +// been set already. + +Type* +Variable::type() const +{ + gcc_assert(this->type_ != NULL); + return this->type_; +} + +// Set the type if necessary. + +void +Variable::determine_type() +{ + if (this->determined_type_) + return; + this->determined_type_ = true; + + if (this->preinit_ != NULL) + this->preinit_->determine_types(); + + // A variable in a type switch with a nil case will have the wrong + // type here. It will have an initializer which is a type guard. + // We want to initialize it to the value without the type guard, and + // use the type of that value as well. + if (this->is_type_switch_var_ && this->type_->is_nil_constant_as_type()) + { + Type_guard_expression* tge = this->init_->type_guard_expression(); + gcc_assert(tge != NULL); + this->type_ = NULL; + this->init_ = tge->expr(); + } + + if (this->init_ == NULL) + gcc_assert(this->type_ != NULL && !this->type_->is_abstract()); + else if (this->type_from_init_tuple_) + { + Expression *init = this->init_; + init->determine_type_no_context(); + this->type_ = this->type_from_tuple(init, true); + this->init_ = NULL; + } + else if (this->type_from_range_index_ || this->type_from_range_value_) + { + Expression* init = this->init_; + init->determine_type_no_context(); + this->type_ = this->type_from_range(init, this->type_from_range_index_, + true); + this->init_ = NULL; + } + else + { + // type_from_chan_element_ should have been cleared during + // lowering. + gcc_assert(!this->type_from_chan_element_); + + Type_context context(this->type_, false); + this->init_->determine_type(&context); + if (this->type_ == NULL) + { + Type* type = this->init_->type(); + gcc_assert(type != NULL); + if (type->is_abstract()) + type = type->make_non_abstract_type(); + + if (type->is_void_type()) + { + error_at(this->location_, "variable has no type"); + type = Type::make_error_type(); + } + else if (type->is_nil_type()) + { + error_at(this->location_, "variable defined to nil type"); + type = Type::make_error_type(); + } + else if (type->is_call_multiple_result_type()) + { + error_at(this->location_, + "single variable set to multiple value function call"); + type = Type::make_error_type(); + } + + this->type_ = type; + } + } +} + +// Export the variable + +void +Variable::export_var(Export* exp, const std::string& name) const +{ + gcc_assert(this->is_global_); + exp->write_c_string("var "); + exp->write_string(name); + exp->write_c_string(" "); + exp->write_type(this->type()); + exp->write_c_string(";\n"); +} + +// Import a variable. + +void +Variable::import_var(Import* imp, std::string* pname, Type** ptype) +{ + imp->require_c_string("var "); + *pname = imp->read_identifier(); + imp->require_c_string(" "); + *ptype = imp->read_type(); + imp->require_c_string(";\n"); +} + +// Class Named_constant. + +// Traverse the initializer expression. + +int +Named_constant::traverse_expression(Traverse* traverse) +{ + return Expression::traverse(&this->expr_, traverse); +} + +// Determine the type of the constant. + +void +Named_constant::determine_type() +{ + if (this->type_ != NULL) + { + Type_context context(this->type_, false); + this->expr_->determine_type(&context); + } + else + { + // A constant may have an abstract type. + Type_context context(NULL, true); + this->expr_->determine_type(&context); + this->type_ = this->expr_->type(); + gcc_assert(this->type_ != NULL); + } +} + +// Indicate that we found and reported an error for this constant. + +void +Named_constant::set_error() +{ + this->type_ = Type::make_error_type(); + this->expr_ = Expression::make_error(this->location_); +} + +// Export a constant. + +void +Named_constant::export_const(Export* exp, const std::string& name) const +{ + exp->write_c_string("const "); + exp->write_string(name); + exp->write_c_string(" "); + if (!this->type_->is_abstract()) + { + exp->write_type(this->type_); + exp->write_c_string(" "); + } + exp->write_c_string("= "); + this->expr()->export_expression(exp); + exp->write_c_string(";\n"); +} + +// Import a constant. + +void +Named_constant::import_const(Import* imp, std::string* pname, Type** ptype, + Expression** pexpr) +{ + imp->require_c_string("const "); + *pname = imp->read_identifier(); + imp->require_c_string(" "); + if (imp->peek_char() == '=') + *ptype = NULL; + else + { + *ptype = imp->read_type(); + imp->require_c_string(" "); + } + imp->require_c_string("= "); + *pexpr = Expression::import_expression(imp); + imp->require_c_string(";\n"); +} + +// Add a method. + +Named_object* +Type_declaration::add_method(const std::string& name, Function* function) +{ + Named_object* ret = Named_object::make_function(name, NULL, function); + this->methods_.push_back(ret); + return ret; +} + +// Add a method declaration. + +Named_object* +Type_declaration::add_method_declaration(const std::string& name, + Function_type* type, + source_location location) +{ + Named_object* ret = Named_object::make_function_declaration(name, NULL, type, + location); + this->methods_.push_back(ret); + return ret; +} + +// Return whether any methods ere defined. + +bool +Type_declaration::has_methods() const +{ + return !this->methods_.empty(); +} + +// Define methods for the real type. + +void +Type_declaration::define_methods(Named_type* nt) +{ + for (Methods::const_iterator p = this->methods_.begin(); + p != this->methods_.end(); + ++p) + nt->add_existing_method(*p); +} + +// We are using the type. Return true if we should issue a warning. + +bool +Type_declaration::using_type() +{ + bool ret = !this->issued_warning_; + this->issued_warning_ = true; + return ret; +} + +// Class Unknown_name. + +// Set the real named object. + +void +Unknown_name::set_real_named_object(Named_object* no) +{ + gcc_assert(this->real_named_object_ == NULL); + gcc_assert(!no->is_unknown()); + this->real_named_object_ = no; +} + +// Class Named_object. + +Named_object::Named_object(const std::string& name, + const Package* package, + Classification classification) + : name_(name), package_(package), classification_(classification), + tree_(NULL) +{ + if (Gogo::is_sink_name(name)) + gcc_assert(classification == NAMED_OBJECT_SINK); +} + +// Make an unknown name. This is used by the parser. The name must +// be resolved later. Unknown names are only added in the current +// package. + +Named_object* +Named_object::make_unknown_name(const std::string& name, + source_location location) +{ + Named_object* named_object = new Named_object(name, NULL, + NAMED_OBJECT_UNKNOWN); + Unknown_name* value = new Unknown_name(location); + named_object->u_.unknown_value = value; + return named_object; +} + +// Make a constant. + +Named_object* +Named_object::make_constant(const Typed_identifier& tid, + const Package* package, Expression* expr, + int iota_value) +{ + Named_object* named_object = new Named_object(tid.name(), package, + NAMED_OBJECT_CONST); + Named_constant* named_constant = new Named_constant(tid.type(), expr, + iota_value, + tid.location()); + named_object->u_.const_value = named_constant; + return named_object; +} + +// Make a named type. + +Named_object* +Named_object::make_type(const std::string& name, const Package* package, + Type* type, source_location location) +{ + Named_object* named_object = new Named_object(name, package, + NAMED_OBJECT_TYPE); + Named_type* named_type = Type::make_named_type(named_object, type, location); + named_object->u_.type_value = named_type; + return named_object; +} + +// Make a type declaration. + +Named_object* +Named_object::make_type_declaration(const std::string& name, + const Package* package, + source_location location) +{ + Named_object* named_object = new Named_object(name, package, + NAMED_OBJECT_TYPE_DECLARATION); + Type_declaration* type_declaration = new Type_declaration(location); + named_object->u_.type_declaration = type_declaration; + return named_object; +} + +// Make a variable. + +Named_object* +Named_object::make_variable(const std::string& name, const Package* package, + Variable* variable) +{ + Named_object* named_object = new Named_object(name, package, + NAMED_OBJECT_VAR); + named_object->u_.var_value = variable; + return named_object; +} + +// Make a result variable. + +Named_object* +Named_object::make_result_variable(const std::string& name, + Result_variable* result) +{ + Named_object* named_object = new Named_object(name, NULL, + NAMED_OBJECT_RESULT_VAR); + named_object->u_.result_var_value = result; + return named_object; +} + +// Make a sink. This is used for the special blank identifier _. + +Named_object* +Named_object::make_sink() +{ + return new Named_object("_", NULL, NAMED_OBJECT_SINK); +} + +// Make a named function. + +Named_object* +Named_object::make_function(const std::string& name, const Package* package, + Function* function) +{ + Named_object* named_object = new Named_object(name, package, + NAMED_OBJECT_FUNC); + named_object->u_.func_value = function; + return named_object; +} + +// Make a function declaration. + +Named_object* +Named_object::make_function_declaration(const std::string& name, + const Package* package, + Function_type* fntype, + source_location location) +{ + Named_object* named_object = new Named_object(name, package, + NAMED_OBJECT_FUNC_DECLARATION); + Function_declaration *func_decl = new Function_declaration(fntype, location); + named_object->u_.func_declaration_value = func_decl; + return named_object; +} + +// Make a package. + +Named_object* +Named_object::make_package(const std::string& alias, Package* package) +{ + Named_object* named_object = new Named_object(alias, NULL, + NAMED_OBJECT_PACKAGE); + named_object->u_.package_value = package; + return named_object; +} + +// Return the name to use in an error message. + +std::string +Named_object::message_name() const +{ + if (this->package_ == NULL) + return Gogo::message_name(this->name_); + std::string ret = Gogo::message_name(this->package_->name()); + ret += '.'; + ret += Gogo::message_name(this->name_); + return ret; +} + +// Set the type when a declaration is defined. + +void +Named_object::set_type_value(Named_type* named_type) +{ + gcc_assert(this->classification_ == NAMED_OBJECT_TYPE_DECLARATION); + Type_declaration* td = this->u_.type_declaration; + td->define_methods(named_type); + Named_object* in_function = td->in_function(); + if (in_function != NULL) + named_type->set_in_function(in_function); + delete td; + this->classification_ = NAMED_OBJECT_TYPE; + this->u_.type_value = named_type; +} + +// Define a function which was previously declared. + +void +Named_object::set_function_value(Function* function) +{ + gcc_assert(this->classification_ == NAMED_OBJECT_FUNC_DECLARATION); + this->classification_ = NAMED_OBJECT_FUNC; + // FIXME: We should free the old value. + this->u_.func_value = function; +} + +// Declare an unknown object as a type declaration. + +void +Named_object::declare_as_type() +{ + gcc_assert(this->classification_ == NAMED_OBJECT_UNKNOWN); + Unknown_name* unk = this->u_.unknown_value; + this->classification_ = NAMED_OBJECT_TYPE_DECLARATION; + this->u_.type_declaration = new Type_declaration(unk->location()); + delete unk; +} + +// Return the location of a named object. + +source_location +Named_object::location() const +{ + switch (this->classification_) + { + default: + case NAMED_OBJECT_UNINITIALIZED: + gcc_unreachable(); + + case NAMED_OBJECT_UNKNOWN: + return this->unknown_value()->location(); + + case NAMED_OBJECT_CONST: + return this->const_value()->location(); + + case NAMED_OBJECT_TYPE: + return this->type_value()->location(); + + case NAMED_OBJECT_TYPE_DECLARATION: + return this->type_declaration_value()->location(); + + case NAMED_OBJECT_VAR: + return this->var_value()->location(); + + case NAMED_OBJECT_RESULT_VAR: + return this->result_var_value()->function()->location(); + + case NAMED_OBJECT_SINK: + gcc_unreachable(); + + case NAMED_OBJECT_FUNC: + return this->func_value()->location(); + + case NAMED_OBJECT_FUNC_DECLARATION: + return this->func_declaration_value()->location(); + + case NAMED_OBJECT_PACKAGE: + return this->package_value()->location(); + } +} + +// Export a named object. + +void +Named_object::export_named_object(Export* exp) const +{ + switch (this->classification_) + { + default: + case NAMED_OBJECT_UNINITIALIZED: + case NAMED_OBJECT_UNKNOWN: + gcc_unreachable(); + + case NAMED_OBJECT_CONST: + this->const_value()->export_const(exp, this->name_); + break; + + case NAMED_OBJECT_TYPE: + this->type_value()->export_named_type(exp, this->name_); + break; + + case NAMED_OBJECT_TYPE_DECLARATION: + error_at(this->type_declaration_value()->location(), + "attempt to export %<%s%> which was declared but not defined", + this->message_name().c_str()); + break; + + case NAMED_OBJECT_FUNC_DECLARATION: + this->func_declaration_value()->export_func(exp, this->name_); + break; + + case NAMED_OBJECT_VAR: + this->var_value()->export_var(exp, this->name_); + break; + + case NAMED_OBJECT_RESULT_VAR: + case NAMED_OBJECT_SINK: + gcc_unreachable(); + + case NAMED_OBJECT_FUNC: + this->func_value()->export_func(exp, this->name_); + break; + } +} + +// Class Bindings. + +Bindings::Bindings(Bindings* enclosing) + : enclosing_(enclosing), named_objects_(), bindings_() +{ +} + +// Clear imports. + +void +Bindings::clear_file_scope() +{ + Contour::iterator p = this->bindings_.begin(); + while (p != this->bindings_.end()) + { + bool keep; + if (p->second->package() != NULL) + keep = false; + else if (p->second->is_package()) + keep = false; + else if (p->second->is_function() + && !p->second->func_value()->type()->is_method() + && Gogo::unpack_hidden_name(p->second->name()) == "init") + keep = false; + else + keep = true; + + if (keep) + ++p; + else + p = this->bindings_.erase(p); + } +} + +// Look up a symbol. + +Named_object* +Bindings::lookup(const std::string& name) const +{ + Contour::const_iterator p = this->bindings_.find(name); + if (p != this->bindings_.end()) + return p->second->resolve(); + else if (this->enclosing_ != NULL) + return this->enclosing_->lookup(name); + else + return NULL; +} + +// Look up a symbol locally. + +Named_object* +Bindings::lookup_local(const std::string& name) const +{ + Contour::const_iterator p = this->bindings_.find(name); + if (p == this->bindings_.end()) + return NULL; + return p->second; +} + +// Remove an object from a set of bindings. This is used for a +// special case in thunks for functions which call recover. + +void +Bindings::remove_binding(Named_object* no) +{ + Contour::iterator pb = this->bindings_.find(no->name()); + gcc_assert(pb != this->bindings_.end()); + this->bindings_.erase(pb); + for (std::vector<Named_object*>::iterator pn = this->named_objects_.begin(); + pn != this->named_objects_.end(); + ++pn) + { + if (*pn == no) + { + this->named_objects_.erase(pn); + return; + } + } + gcc_unreachable(); +} + +// Add a method to the list of objects. This is not added to the +// lookup table. This is so that we have a single list of objects +// declared at the top level, which we walk through when it's time to +// convert to trees. + +void +Bindings::add_method(Named_object* method) +{ + this->named_objects_.push_back(method); +} + +// Add a generic Named_object to a Contour. + +Named_object* +Bindings::add_named_object_to_contour(Contour* contour, + Named_object* named_object) +{ + gcc_assert(named_object == named_object->resolve()); + const std::string& name(named_object->name()); + gcc_assert(!Gogo::is_sink_name(name)); + + std::pair<Contour::iterator, bool> ins = + contour->insert(std::make_pair(name, named_object)); + if (!ins.second) + { + // The name was already there. + if (named_object->package() != NULL + && ins.first->second->package() == named_object->package() + && (ins.first->second->classification() + == named_object->classification())) + { + // This is a second import of the same object. + return ins.first->second; + } + ins.first->second = this->new_definition(ins.first->second, + named_object); + return ins.first->second; + } + else + { + // Don't push declarations on the list. We push them on when + // and if we find the definitions. That way we genericize the + // functions in order. + if (!named_object->is_type_declaration() + && !named_object->is_function_declaration() + && !named_object->is_unknown()) + this->named_objects_.push_back(named_object); + return named_object; + } +} + +// We had an existing named object OLD_OBJECT, and we've seen a new +// one NEW_OBJECT with the same name. FIXME: This does not free the +// new object when we don't need it. + +Named_object* +Bindings::new_definition(Named_object* old_object, Named_object* new_object) +{ + std::string reason; + switch (old_object->classification()) + { + default: + case Named_object::NAMED_OBJECT_UNINITIALIZED: + gcc_unreachable(); + + case Named_object::NAMED_OBJECT_UNKNOWN: + { + Named_object* real = old_object->unknown_value()->real_named_object(); + if (real != NULL) + return this->new_definition(real, new_object); + gcc_assert(!new_object->is_unknown()); + old_object->unknown_value()->set_real_named_object(new_object); + if (!new_object->is_type_declaration() + && !new_object->is_function_declaration()) + this->named_objects_.push_back(new_object); + return new_object; + } + + case Named_object::NAMED_OBJECT_CONST: + break; + + case Named_object::NAMED_OBJECT_TYPE: + if (new_object->is_type_declaration()) + return old_object; + break; + + case Named_object::NAMED_OBJECT_TYPE_DECLARATION: + if (new_object->is_type_declaration()) + return old_object; + if (new_object->is_type()) + { + old_object->set_type_value(new_object->type_value()); + new_object->type_value()->set_named_object(old_object); + this->named_objects_.push_back(old_object); + return old_object; + } + break; + + case Named_object::NAMED_OBJECT_VAR: + case Named_object::NAMED_OBJECT_RESULT_VAR: + break; + + case Named_object::NAMED_OBJECT_SINK: + gcc_unreachable(); + + case Named_object::NAMED_OBJECT_FUNC: + if (new_object->is_function_declaration()) + { + if (!new_object->func_declaration_value()->asm_name().empty()) + sorry("__asm__ for function definitions"); + Function_type* old_type = old_object->func_value()->type(); + Function_type* new_type = + new_object->func_declaration_value()->type(); + if (old_type->is_valid_redeclaration(new_type, &reason)) + return old_object; + } + break; + + case Named_object::NAMED_OBJECT_FUNC_DECLARATION: + { + Function_type* old_type = old_object->func_declaration_value()->type(); + if (new_object->is_function_declaration()) + { + Function_type* new_type = + new_object->func_declaration_value()->type(); + if (old_type->is_valid_redeclaration(new_type, &reason)) + return old_object; + } + if (new_object->is_function()) + { + Function_type* new_type = new_object->func_value()->type(); + if (old_type->is_valid_redeclaration(new_type, &reason)) + { + if (!old_object->func_declaration_value()->asm_name().empty()) + sorry("__asm__ for function definitions"); + old_object->set_function_value(new_object->func_value()); + this->named_objects_.push_back(old_object); + return old_object; + } + } + } + break; + + case Named_object::NAMED_OBJECT_PACKAGE: + if (new_object->is_package() + && (old_object->package_value()->name() + == new_object->package_value()->name())) + return old_object; + + break; + } + + std::string n = old_object->message_name(); + if (reason.empty()) + error_at(new_object->location(), "redefinition of %qs", n.c_str()); + else + error_at(new_object->location(), "redefinition of %qs: %s", n.c_str(), + reason.c_str()); + + inform(old_object->location(), "previous definition of %qs was here", + n.c_str()); + + return old_object; +} + +// Add a named type. + +Named_object* +Bindings::add_named_type(Named_type* named_type) +{ + return this->add_named_object(named_type->named_object()); +} + +// Add a function. + +Named_object* +Bindings::add_function(const std::string& name, const Package* package, + Function* function) +{ + return this->add_named_object(Named_object::make_function(name, package, + function)); +} + +// Add a function declaration. + +Named_object* +Bindings::add_function_declaration(const std::string& name, + const Package* package, + Function_type* type, + source_location location) +{ + Named_object* no = Named_object::make_function_declaration(name, package, + type, location); + return this->add_named_object(no); +} + +// Define a type which was previously declared. + +void +Bindings::define_type(Named_object* no, Named_type* type) +{ + no->set_type_value(type); + this->named_objects_.push_back(no); +} + +// Traverse bindings. + +int +Bindings::traverse(Traverse* traverse, bool is_global) +{ + unsigned int traverse_mask = traverse->traverse_mask(); + + // We don't use an iterator because we permit the traversal to add + // new global objects. + for (size_t i = 0; i < this->named_objects_.size(); ++i) + { + Named_object* p = this->named_objects_[i]; + switch (p->classification()) + { + case Named_object::NAMED_OBJECT_CONST: + if ((traverse_mask & Traverse::traverse_constants) != 0) + { + if (traverse->constant(p, is_global) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + if ((traverse_mask & Traverse::traverse_types) != 0 + || (traverse_mask & Traverse::traverse_expressions) != 0) + { + Type* t = p->const_value()->type(); + if (t != NULL + && Type::traverse(t, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + if (p->const_value()->traverse_expression(traverse) + == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + break; + + case Named_object::NAMED_OBJECT_VAR: + case Named_object::NAMED_OBJECT_RESULT_VAR: + if ((traverse_mask & Traverse::traverse_variables) != 0) + { + if (traverse->variable(p) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + if (((traverse_mask & Traverse::traverse_types) != 0 + || (traverse_mask & Traverse::traverse_expressions) != 0) + && (p->is_result_variable() + || p->var_value()->has_type())) + { + Type* t = (p->is_variable() + ? p->var_value()->type() + : p->result_var_value()->type()); + if (t != NULL + && Type::traverse(t, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + if (p->is_variable() + && ((traverse_mask & Traverse::traverse_types) != 0 + || (traverse_mask & Traverse::traverse_expressions) != 0)) + { + if (p->var_value()->traverse_expression(traverse) + == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + break; + + case Named_object::NAMED_OBJECT_FUNC: + if ((traverse_mask & Traverse::traverse_functions) != 0) + { + int t = traverse->function(p); + if (t == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + else if (t == TRAVERSE_SKIP_COMPONENTS) + break; + } + + if ((traverse_mask + & (Traverse::traverse_variables + | Traverse::traverse_constants + | Traverse::traverse_functions + | Traverse::traverse_blocks + | Traverse::traverse_statements + | Traverse::traverse_expressions + | Traverse::traverse_types)) != 0) + { + if (p->func_value()->traverse(traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + break; + + case Named_object::NAMED_OBJECT_PACKAGE: + // These are traversed in Gogo::traverse. + gcc_assert(is_global); + break; + + case Named_object::NAMED_OBJECT_TYPE: + if ((traverse_mask & Traverse::traverse_types) != 0 + || (traverse_mask & Traverse::traverse_expressions) != 0) + { + if (Type::traverse(p->type_value(), traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + break; + + case Named_object::NAMED_OBJECT_TYPE_DECLARATION: + case Named_object::NAMED_OBJECT_FUNC_DECLARATION: + case Named_object::NAMED_OBJECT_UNKNOWN: + break; + + case Named_object::NAMED_OBJECT_SINK: + default: + gcc_unreachable(); + } + } + + return TRAVERSE_CONTINUE; +} + +// Class Package. + +Package::Package(const std::string& name, const std::string& unique_prefix, + source_location location) + : name_(name), unique_prefix_(unique_prefix), bindings_(new Bindings(NULL)), + priority_(0), location_(location), used_(false), is_imported_(false), + uses_sink_alias_(false) +{ + gcc_assert(!name.empty() && !unique_prefix.empty()); +} + +// Set the priority. We may see multiple priorities for an imported +// package; we want to use the largest one. + +void +Package::set_priority(int priority) +{ + if (priority > this->priority_) + this->priority_ = priority; +} + +// Determine types of constants. Everything else in a package +// (variables, function declarations) should already have a fixed +// type. Constants may have abstract types. + +void +Package::determine_types() +{ + Bindings* bindings = this->bindings_; + for (Bindings::const_definitions_iterator p = bindings->begin_definitions(); + p != bindings->end_definitions(); + ++p) + { + if ((*p)->is_const()) + (*p)->const_value()->determine_type(); + } +} + +// Class Traverse. + +// Destructor. + +Traverse::~Traverse() +{ + if (this->types_seen_ != NULL) + delete this->types_seen_; + if (this->expressions_seen_ != NULL) + delete this->expressions_seen_; +} + +// Record that we are looking at a type, and return true if we have +// already seen it. + +bool +Traverse::remember_type(const Type* type) +{ + if (type->is_error_type()) + return true; + gcc_assert((this->traverse_mask() & traverse_types) != 0 + || (this->traverse_mask() & traverse_expressions) != 0); + // We only have to remember named types, as they are the only ones + // we can see multiple times in a traversal. + if (type->classification() != Type::TYPE_NAMED) + return false; + if (this->types_seen_ == NULL) + this->types_seen_ = new Types_seen(); + std::pair<Types_seen::iterator, bool> ins = this->types_seen_->insert(type); + return !ins.second; +} + +// Record that we are looking at an expression, and return true if we +// have already seen it. + +bool +Traverse::remember_expression(const Expression* expression) +{ + gcc_assert((this->traverse_mask() & traverse_types) != 0 + || (this->traverse_mask() & traverse_expressions) != 0); + if (this->expressions_seen_ == NULL) + this->expressions_seen_ = new Expressions_seen(); + std::pair<Expressions_seen::iterator, bool> ins = + this->expressions_seen_->insert(expression); + return !ins.second; +} + +// The default versions of these functions should never be called: the +// traversal mask indicates which functions may be called. + +int +Traverse::variable(Named_object*) +{ + gcc_unreachable(); +} + +int +Traverse::constant(Named_object*, bool) +{ + gcc_unreachable(); +} + +int +Traverse::function(Named_object*) +{ + gcc_unreachable(); +} + +int +Traverse::block(Block*) +{ + gcc_unreachable(); +} + +int +Traverse::statement(Block*, size_t*, Statement*) +{ + gcc_unreachable(); +} + +int +Traverse::expression(Expression**) +{ + gcc_unreachable(); +} + +int +Traverse::type(Type*) +{ + gcc_unreachable(); +} |