| // 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 <fstream> |
| |
| #include "filenames.h" |
| |
| #include "go-c.h" |
| #include "go-diagnostics.h" |
| #include "go-encode-id.h" |
| #include "go-dump.h" |
| #include "go-optimize.h" |
| #include "lex.h" |
| #include "types.h" |
| #include "statements.h" |
| #include "expressions.h" |
| #include "runtime.h" |
| #include "import.h" |
| #include "export.h" |
| #include "backend.h" |
| #include "gogo.h" |
| |
| // Class Gogo. |
| |
| Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size) |
| : backend_(backend), |
| linemap_(linemap), |
| package_(NULL), |
| functions_(), |
| globals_(new Bindings(NULL)), |
| file_block_names_(), |
| imports_(), |
| imported_unsafe_(false), |
| current_file_imported_unsafe_(false), |
| packages_(), |
| init_functions_(), |
| var_deps_(), |
| need_init_fn_(false), |
| init_fn_name_(), |
| imported_init_fns_(), |
| pkgpath_(), |
| pkgpath_symbol_(), |
| prefix_(), |
| pkgpath_set_(false), |
| pkgpath_from_option_(false), |
| prefix_from_option_(false), |
| relative_import_path_(), |
| c_header_(), |
| check_divide_by_zero_(true), |
| check_divide_overflow_(true), |
| compiling_runtime_(false), |
| debug_escape_level_(0), |
| debug_optimization_(false), |
| nil_check_size_threshold_(4096), |
| need_eqtype_(false), |
| verify_types_(), |
| interface_types_(), |
| specific_type_functions_(), |
| specific_type_functions_are_written_(false), |
| named_types_are_converted_(false), |
| analysis_sets_(), |
| gc_roots_(), |
| type_descriptors_(), |
| imported_inlinable_functions_(), |
| imported_inline_functions_() |
| { |
| const Location loc = Linemap::predeclared_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)); |
| Named_type* int32_type = Type::make_integer_type("int32", false, 32, |
| RUNTIME_TYPE_KIND_INT32); |
| this->add_named_type(int32_type); |
| 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)); |
| |
| int int_type_size = pointer_size; |
| 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); |
| |
| this->add_named_type(Type::make_integer_type("uintptr", true, |
| pointer_size, |
| RUNTIME_TYPE_KIND_UINTPTR)); |
| |
| // "byte" is an alias for "uint8". |
| uint8_type->integer_type()->set_is_byte(); |
| Named_object* byte_type = Named_object::make_type("byte", NULL, uint8_type, |
| loc); |
| byte_type->type_value()->set_is_alias(); |
| this->add_named_type(byte_type->type_value()); |
| |
| // "rune" is an alias for "int32". |
| int32_type->integer_type()->set_is_rune(); |
| Named_object* rune_type = Named_object::make_type("rune", NULL, int32_type, |
| loc); |
| rune_type->type_value()->set_is_alias(); |
| this->add_named_type(rune_type->type_value()); |
| |
| this->add_named_type(Type::make_named_bool_type()); |
| |
| this->add_named_type(Type::make_named_string_type()); |
| |
| // "error" is interface { Error() string }. |
| { |
| Typed_identifier_list *methods = new Typed_identifier_list; |
| Typed_identifier_list *results = new Typed_identifier_list; |
| results->push_back(Typed_identifier("", Type::lookup_string_type(), loc)); |
| Type *method_type = Type::make_function_type(NULL, NULL, results, loc); |
| methods->push_back(Typed_identifier("Error", method_type, loc)); |
| Interface_type *error_iface = Type::make_interface_type(methods, loc); |
| error_iface->finalize_methods(); |
| Named_type *error_type = Named_object::make_type("error", NULL, error_iface, loc)->type_value(); |
| this->add_named_type(error_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_empty_interface_type(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* 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); |
| |
| Function_type* delete_type = Type::make_function_type(NULL, NULL, NULL, loc); |
| delete_type->set_is_varargs(); |
| delete_type->set_is_builtin(); |
| this->globals_->add_function_declaration("delete", NULL, delete_type, loc); |
| } |
| |
| std::string |
| Gogo::pkgpath_for_symbol(const std::string& pkgpath) |
| { |
| go_assert(!pkgpath.empty()); |
| return go_encode_id(pkgpath); |
| } |
| |
| // Return a hash code for a string, given a starting hash. |
| |
| unsigned int |
| Gogo::hash_string(const std::string& s, unsigned int h) |
| { |
| const char* p = s.data(); |
| size_t len = s.length(); |
| for (; len > 0; --len) |
| { |
| h ^= *p++; |
| h*= 16777619; |
| } |
| return h; |
| } |
| |
| // Get the package path to use for type reflection data. This should |
| // ideally be unique across the entire link. |
| |
| const std::string& |
| Gogo::pkgpath() const |
| { |
| go_assert(this->pkgpath_set_); |
| return this->pkgpath_; |
| } |
| |
| // Set the package path from the -fgo-pkgpath command line option. |
| |
| void |
| Gogo::set_pkgpath(const std::string& arg) |
| { |
| go_assert(!this->pkgpath_set_); |
| this->pkgpath_ = arg; |
| this->pkgpath_set_ = true; |
| this->pkgpath_from_option_ = true; |
| } |
| |
| // Get the package path to use for symbol names. |
| |
| const std::string& |
| Gogo::pkgpath_symbol() const |
| { |
| go_assert(this->pkgpath_set_); |
| return this->pkgpath_symbol_; |
| } |
| |
| // Set the unique prefix to use to determine the package path, from |
| // the -fgo-prefix command line option. |
| |
| void |
| Gogo::set_prefix(const std::string& arg) |
| { |
| go_assert(!this->prefix_from_option_); |
| this->prefix_ = arg; |
| this->prefix_from_option_ = true; |
| } |
| |
| // 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 |
| { |
| go_assert(this->package_ != NULL); |
| return this->package_->package_name(); |
| } |
| |
| // Set the package name. |
| |
| void |
| Gogo::set_package_name(const std::string& package_name, |
| Location location) |
| { |
| if (this->package_ != NULL) |
| { |
| if (this->package_->package_name() != package_name) |
| go_error_at(location, "expected package %<%s%>", |
| Gogo::message_name(this->package_->package_name()).c_str()); |
| return; |
| } |
| |
| // Now that we know the name of the package we are compiling, set |
| // the package path to use for reflect.Type.PkgPath and global |
| // symbol names. |
| if (this->pkgpath_set_) |
| this->pkgpath_symbol_ = Gogo::pkgpath_for_symbol(this->pkgpath_); |
| else |
| { |
| if (!this->prefix_from_option_ && package_name == "main") |
| { |
| this->pkgpath_ = package_name; |
| this->pkgpath_symbol_ = Gogo::pkgpath_for_symbol(package_name); |
| } |
| else |
| { |
| if (!this->prefix_from_option_) |
| this->prefix_ = "go"; |
| this->pkgpath_ = this->prefix_ + '.' + package_name; |
| this->pkgpath_symbol_ = (Gogo::pkgpath_for_symbol(this->prefix_) + '.' |
| + Gogo::pkgpath_for_symbol(package_name)); |
| } |
| this->pkgpath_set_ = true; |
| } |
| |
| this->package_ = this->register_package(this->pkgpath_, |
| this->pkgpath_symbol_, location); |
| this->package_->set_package_name(package_name, location); |
| |
| if (this->is_main_package()) |
| { |
| // Declare "main" as a function which takes no parameters and |
| // returns no value. |
| Location uloc = Linemap::unknown_location(); |
| this->declare_function(Gogo::pack_hidden_name("main", false), |
| Type::make_function_type (NULL, NULL, NULL, uloc), |
| uloc); |
| } |
| } |
| |
| // Return whether this is the "main" package. This is not true if |
| // -fgo-pkgpath or -fgo-prefix was used. |
| |
| bool |
| Gogo::is_main_package() const |
| { |
| return (this->package_name() == "main" |
| && !this->pkgpath_from_option_ |
| && !this->prefix_from_option_); |
| } |
| |
| // Import a package. |
| |
| void |
| Gogo::import_package(const std::string& filename, |
| const std::string& local_name, |
| bool is_local_name_exported, |
| bool must_exist, |
| Location location) |
| { |
| if (filename.empty()) |
| { |
| go_error_at(location, "import path is empty"); |
| return; |
| } |
| |
| const char *pf = filename.data(); |
| const char *pend = pf + filename.length(); |
| while (pf < pend) |
| { |
| unsigned int c; |
| int adv = Lex::fetch_char(pf, &c); |
| if (adv == 0) |
| { |
| go_error_at(location, "import path contains invalid UTF-8 sequence"); |
| return; |
| } |
| if (c == '\0') |
| { |
| go_error_at(location, "import path contains NUL"); |
| return; |
| } |
| if (c < 0x20 || c == 0x7f) |
| { |
| go_error_at(location, "import path contains control character"); |
| return; |
| } |
| if (c == '\\') |
| { |
| go_error_at(location, "import path contains backslash; use slash"); |
| return; |
| } |
| if (Lex::is_unicode_space(c)) |
| { |
| go_error_at(location, "import path contains space character"); |
| return; |
| } |
| if (c < 0x7f && strchr("!\"#$%&'()*,:;<=>?[]^`{|}", c) != NULL) |
| { |
| go_error_at(location, |
| "import path contains invalid character '%c'", c); |
| return; |
| } |
| pf += adv; |
| } |
| |
| if (IS_ABSOLUTE_PATH(filename.c_str())) |
| { |
| go_error_at(location, "import path cannot be absolute path"); |
| return; |
| } |
| |
| if (local_name == "init") |
| go_error_at(location, "cannot import package as init"); |
| |
| if (filename == "unsafe") |
| { |
| this->import_unsafe(local_name, is_local_name_exported, location); |
| this->current_file_imported_unsafe_ = true; |
| return; |
| } |
| |
| Imports::const_iterator p = this->imports_.find(filename); |
| if (p != this->imports_.end()) |
| { |
| Package* package = p->second; |
| package->set_location(location); |
| std::string ln = local_name; |
| bool is_ln_exported = is_local_name_exported; |
| if (ln.empty()) |
| { |
| ln = package->package_name(); |
| go_assert(!ln.empty()); |
| is_ln_exported = Lex::is_exported_name(ln); |
| } |
| if (ln == "_") |
| ; |
| else if (ln == ".") |
| { |
| Bindings* bindings = package->bindings(); |
| for (Bindings::const_declarations_iterator pd = |
| bindings->begin_declarations(); |
| pd != bindings->end_declarations(); |
| ++pd) |
| this->add_dot_import_object(pd->second); |
| std::string dot_alias = "." + package->package_name(); |
| package->add_alias(dot_alias, location); |
| } |
| else |
| { |
| package->add_alias(ln, location); |
| 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, |
| this->relative_import_path_); |
| if (stream == NULL) |
| { |
| if (must_exist) |
| go_error_at(location, "import file %qs not found", filename.c_str()); |
| return; |
| } |
| |
| Import* imp = new Import(stream, location); |
| imp->register_builtin_types(this); |
| Package* package = imp->import(this, local_name, is_local_name_exported); |
| if (package != NULL) |
| { |
| if (package->pkgpath() == this->pkgpath()) |
| go_error_at(location, |
| ("imported package uses same package path as package " |
| "being compiled (see %<-fgo-pkgpath%> option)")); |
| |
| this->imports_.insert(std::make_pair(filename, package)); |
| } |
| |
| imp->clear_stream(); |
| delete stream; |
| |
| // FIXME: we never delete imp; we may need it for inlinable functions. |
| } |
| |
| Import_init * |
| Gogo::lookup_init(const std::string& init_name) |
| { |
| Import_init tmp("", init_name, -1); |
| Import_init_set::iterator it = this->imported_init_fns_.find(&tmp); |
| return (it != this->imported_init_fns_.end()) ? *it : NULL; |
| } |
| |
| // 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 (Import_init_set::iterator p = |
| this->imported_init_fns_.begin(); |
| p != this->imported_init_fns_.end(); |
| ++p) |
| { |
| Import_init *ii = (*p); |
| if (ii->init_name() == init_name) |
| { |
| // If a test of package P1, built as part of package P1, |
| // imports package P2, and P2 imports P1 (perhaps |
| // indirectly), then we will see the same import name with |
| // different import priorities. That is OK, so don't give |
| // an error about it. |
| if (ii->package_name() != package_name) |
| { |
| go_error_at(Linemap::unknown_location(), |
| "duplicate package initialization name %qs", |
| Gogo::message_name(init_name).c_str()); |
| go_inform(Linemap::unknown_location(), "used by package %qs", |
| Gogo::message_name(ii->package_name()).c_str()); |
| go_inform(Linemap::unknown_location(), " and by package %qs", |
| Gogo::message_name(package_name).c_str()); |
| } |
| ii->set_priority(prio); |
| return; |
| } |
| } |
| |
| Import_init* nii = new Import_init(package_name, init_name, prio); |
| this->imported_init_fns_.insert(nii); |
| } |
| |
| // 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_; |
| } |
| |
| void |
| Gogo::update_init_priority(Import_init* ii, |
| std::set<const Import_init *>* visited) |
| { |
| visited->insert(ii); |
| int succ_prior = -1; |
| |
| for (std::set<std::string>::const_iterator pci = |
| ii->precursors().begin(); |
| pci != ii->precursors().end(); |
| ++pci) |
| { |
| Import_init* succ = this->lookup_init(*pci); |
| if (visited->find(succ) == visited->end()) |
| update_init_priority(succ, visited); |
| succ_prior = std::max(succ_prior, succ->priority()); |
| } |
| if (ii->priority() <= succ_prior) |
| ii->set_priority(succ_prior + 1); |
| } |
| |
| void |
| Gogo::recompute_init_priorities() |
| { |
| std::set<Import_init *> nonroots; |
| |
| for (Import_init_set::const_iterator p = |
| this->imported_init_fns_.begin(); |
| p != this->imported_init_fns_.end(); |
| ++p) |
| { |
| const Import_init *ii = *p; |
| for (std::set<std::string>::const_iterator pci = |
| ii->precursors().begin(); |
| pci != ii->precursors().end(); |
| ++pci) |
| { |
| Import_init* ii_init = this->lookup_init(*pci); |
| nonroots.insert(ii_init); |
| } |
| } |
| |
| // Recursively update priorities starting at roots. |
| std::set<const Import_init*> visited; |
| for (Import_init_set::iterator p = |
| this->imported_init_fns_.begin(); |
| p != this->imported_init_fns_.end(); |
| ++p) |
| { |
| Import_init* ii = *p; |
| if (nonroots.find(ii) != nonroots.end()) |
| continue; |
| update_init_priority(ii, &visited); |
| } |
| } |
| |
| // Add statements to INIT_STMTS which run the initialization |
| // functions for imported packages. This is only used for the "main" |
| // package. |
| |
| void |
| Gogo::init_imports(std::vector<Bstatement*>& init_stmts, Bfunction *bfunction) |
| { |
| go_assert(this->is_main_package()); |
| |
| if (this->imported_init_fns_.empty()) |
| return; |
| |
| Location unknown_loc = Linemap::unknown_location(); |
| Function_type* func_type = |
| Type::make_function_type(NULL, NULL, NULL, unknown_loc); |
| Btype* fntype = func_type->get_backend_fntype(this); |
| |
| // Recompute init priorities based on a walk of the init graph. |
| recompute_init_priorities(); |
| |
| // We must call them in increasing priority order. |
| std::vector<const Import_init*> v; |
| for (Import_init_set::const_iterator p = |
| this->imported_init_fns_.begin(); |
| p != this->imported_init_fns_.end(); |
| ++p) |
| { |
| // Don't include dummy inits. They are not real functions. |
| if ((*p)->is_dummy()) |
| continue; |
| if ((*p)->priority() < 0) |
| go_error_at(Linemap::unknown_location(), |
| "internal error: failed to set init priority for %s", |
| (*p)->package_name().c_str()); |
| v.push_back(*p); |
| } |
| std::sort(v.begin(), v.end(), priority_compare); |
| |
| // We build calls to the init functions, which take no arguments. |
| std::vector<Bexpression*> empty_args; |
| for (std::vector<const Import_init*>::const_iterator p = v.begin(); |
| p != v.end(); |
| ++p) |
| { |
| const Import_init* ii = *p; |
| std::string user_name = ii->package_name() + ".init"; |
| const std::string& init_name(ii->init_name()); |
| const unsigned int flags = |
| (Backend::function_is_visible |
| | Backend::function_is_declaration |
| | Backend::function_is_inlinable); |
| Bfunction* pfunc = this->backend()->function(fntype, user_name, init_name, |
| flags, unknown_loc); |
| Bexpression* pfunc_code = |
| this->backend()->function_code_expression(pfunc, unknown_loc); |
| Bexpression* pfunc_call = |
| this->backend()->call_expression(bfunction, pfunc_code, empty_args, |
| NULL, unknown_loc); |
| init_stmts.push_back(this->backend()->expression_statement(bfunction, |
| pfunc_call)); |
| } |
| } |
| |
| // Register global variables with the garbage collector. We need to |
| // register all variables which can hold a pointer value. They become |
| // roots during the mark phase. We build a struct that is easy to |
| // hook into a list of roots. |
| |
| // type gcRoot struct { |
| // decl unsafe.Pointer // Pointer to variable. |
| // size uintptr // Total size of variable. |
| // ptrdata uintptr // Length of variable's gcdata. |
| // gcdata *byte // Pointer mask. |
| // } |
| // |
| // type gcRootList struct { |
| // next *gcRootList |
| // count int |
| // roots [...]gcRoot |
| // } |
| |
| // The last entry in the roots array has a NULL decl field. |
| |
| void |
| Gogo::register_gc_vars(const std::vector<Named_object*>& var_gc, |
| std::vector<Bstatement*>& init_stmts, |
| Bfunction* init_bfn) |
| { |
| if (var_gc.empty() && this->gc_roots_.empty()) |
| return; |
| |
| Type* pvt = Type::make_pointer_type(Type::make_void_type()); |
| Type* uintptr_type = Type::lookup_integer_type("uintptr"); |
| Type* byte_type = this->lookup_global("byte")->type_value(); |
| Type* pointer_byte_type = Type::make_pointer_type(byte_type); |
| Struct_type* root_type = |
| Type::make_builtin_struct_type(4, |
| "decl", pvt, |
| "size", uintptr_type, |
| "ptrdata", uintptr_type, |
| "gcdata", pointer_byte_type); |
| |
| Location builtin_loc = Linemap::predeclared_location(); |
| unsigned long roots_len = var_gc.size() + this->gc_roots_.size(); |
| Expression* length = Expression::make_integer_ul(roots_len, NULL, |
| builtin_loc); |
| Array_type* root_array_type = Type::make_array_type(root_type, length); |
| root_array_type->set_is_array_incomparable(); |
| |
| Type* int_type = Type::lookup_integer_type("int"); |
| Struct_type* root_list_type = |
| Type::make_builtin_struct_type(3, |
| "next", pvt, |
| "count", int_type, |
| "roots", root_array_type); |
| |
| // Build an initializer for the roots array. |
| |
| Expression_list* roots_init = new Expression_list(); |
| |
| for (std::vector<Named_object*>::const_iterator p = var_gc.begin(); |
| p != var_gc.end(); |
| ++p) |
| { |
| Expression_list* init = new Expression_list(); |
| |
| Location no_loc = (*p)->location(); |
| Expression* decl = Expression::make_var_reference(*p, no_loc); |
| Expression* decl_addr = |
| Expression::make_unary(OPERATOR_AND, decl, no_loc); |
| decl_addr->unary_expression()->set_does_not_escape(); |
| decl_addr = Expression::make_cast(pvt, decl_addr, no_loc); |
| init->push_back(decl_addr); |
| |
| Expression* size = |
| Expression::make_type_info(decl->type(), |
| Expression::TYPE_INFO_SIZE); |
| init->push_back(size); |
| |
| Expression* ptrdata = |
| Expression::make_type_info(decl->type(), |
| Expression::TYPE_INFO_BACKEND_PTRDATA); |
| init->push_back(ptrdata); |
| |
| Expression* gcdata = Expression::make_ptrmask_symbol(decl->type()); |
| init->push_back(gcdata); |
| |
| Expression* root_ctor = |
| Expression::make_struct_composite_literal(root_type, init, no_loc); |
| roots_init->push_back(root_ctor); |
| } |
| |
| for (std::vector<Expression*>::const_iterator p = this->gc_roots_.begin(); |
| p != this->gc_roots_.end(); |
| ++p) |
| { |
| Expression_list *init = new Expression_list(); |
| |
| Expression* expr = *p; |
| Location eloc = expr->location(); |
| init->push_back(Expression::make_cast(pvt, expr, eloc)); |
| |
| Type* type = expr->type()->points_to(); |
| go_assert(type != NULL); |
| |
| Expression* size = |
| Expression::make_type_info(type, |
| Expression::TYPE_INFO_SIZE); |
| init->push_back(size); |
| |
| Expression* ptrdata = |
| Expression::make_type_info(type, |
| Expression::TYPE_INFO_BACKEND_PTRDATA); |
| init->push_back(ptrdata); |
| |
| Expression* gcdata = Expression::make_ptrmask_symbol(type); |
| init->push_back(gcdata); |
| |
| Expression* root_ctor = |
| Expression::make_struct_composite_literal(root_type, init, eloc); |
| roots_init->push_back(root_ctor); |
| } |
| |
| // Build a constructor for the struct. |
| |
| Expression_list* root_list_init = new Expression_list(); |
| root_list_init->push_back(Expression::make_nil(builtin_loc)); |
| root_list_init->push_back(Expression::make_integer_ul(roots_len, int_type, |
| builtin_loc)); |
| |
| Expression* roots_ctor = |
| Expression::make_array_composite_literal(root_array_type, roots_init, |
| builtin_loc); |
| root_list_init->push_back(roots_ctor); |
| |
| Expression* root_list_ctor = |
| Expression::make_struct_composite_literal(root_list_type, root_list_init, |
| builtin_loc); |
| |
| Expression* root_addr = Expression::make_unary(OPERATOR_AND, root_list_ctor, |
| builtin_loc); |
| root_addr->unary_expression()->set_is_gc_root(); |
| Expression* register_roots = Runtime::make_call(Runtime::REGISTER_GC_ROOTS, |
| builtin_loc, 1, root_addr); |
| |
| Translate_context context(this, NULL, NULL, NULL); |
| Bexpression* bcall = register_roots->get_backend(&context); |
| init_stmts.push_back(this->backend()->expression_statement(init_bfn, bcall)); |
| } |
| |
| // Build the list of type descriptors defined in this package. This is to help |
| // the reflect package to find compiler-generated types. |
| |
| // type typeDescriptorList struct { |
| // count int |
| // types [...]unsafe.Pointer |
| // } |
| |
| static Struct_type* |
| type_descriptor_list_type(unsigned long len) |
| { |
| Location builtin_loc = Linemap::predeclared_location(); |
| Type* int_type = Type::lookup_integer_type("int"); |
| Type* ptr_type = Type::make_pointer_type(Type::make_void_type()); |
| // Avoid creating zero-length type. |
| unsigned long nelems = (len != 0 ? len : 1); |
| Expression* len_expr = Expression::make_integer_ul(nelems, NULL, |
| builtin_loc); |
| Array_type* array_type = Type::make_array_type(ptr_type, len_expr); |
| array_type->set_is_array_incomparable(); |
| Struct_type* list_type = |
| Type::make_builtin_struct_type(2, "count", int_type, |
| "types", array_type); |
| return list_type; |
| } |
| |
| void |
| Gogo::build_type_descriptor_list() |
| { |
| // Create the list type |
| Location builtin_loc = Linemap::predeclared_location(); |
| unsigned long len = this->type_descriptors_.size(); |
| Struct_type* list_type = type_descriptor_list_type(len); |
| Btype* bt = list_type->get_backend(this); |
| Btype* bat = list_type->field(1)->type()->get_backend(this); |
| |
| // Create the variable |
| std::string name = this->type_descriptor_list_symbol(this->pkgpath_symbol()); |
| Bvariable* bv = this->backend()->implicit_variable(name, name, bt, |
| false, true, false, |
| 0); |
| |
| // Build the initializer |
| std::vector<unsigned long> indexes; |
| std::vector<Bexpression*> vals; |
| std::vector<Type*>::iterator p = this->type_descriptors_.begin(); |
| for (unsigned long i = 0; i < len; ++i, ++p) |
| { |
| Bexpression* bexpr = (*p)->type_descriptor_pointer(this, |
| builtin_loc); |
| indexes.push_back(i); |
| vals.push_back(bexpr); |
| } |
| Bexpression* barray = |
| this->backend()->array_constructor_expression(bat, indexes, vals, |
| builtin_loc); |
| |
| Translate_context context(this, NULL, NULL, NULL); |
| std::vector<Bexpression*> fields; |
| Expression* len_expr = Expression::make_integer_ul(len, NULL, |
| builtin_loc); |
| fields.push_back(len_expr->get_backend(&context)); |
| fields.push_back(barray); |
| Bexpression* binit = |
| this->backend()->constructor_expression(bt, fields, builtin_loc); |
| |
| this->backend()->implicit_variable_set_init(bv, name, bt, false, |
| true, false, binit); |
| } |
| |
| // Register the type descriptors with the runtime. This is to help |
| // the reflect package to find compiler-generated types. |
| |
| void |
| Gogo::register_type_descriptors(std::vector<Bstatement*>& init_stmts, |
| Bfunction* init_bfn) |
| { |
| // Create the list type |
| Location builtin_loc = Linemap::predeclared_location(); |
| Struct_type* list_type = type_descriptor_list_type(1); |
| Btype* bt = list_type->get_backend(this); |
| |
| // Collect type lists from transitive imports. |
| std::vector<std::string> list_names; |
| for (Import_init_set::iterator it = this->imported_init_fns_.begin(); |
| it != this->imported_init_fns_.end(); |
| ++it) |
| { |
| std::string pkgpath_symbol = |
| this->pkgpath_symbol_from_init_fn_name((*it)->init_name()); |
| list_names.push_back(this->type_descriptor_list_symbol(pkgpath_symbol)); |
| } |
| // Add the main package itself. |
| list_names.push_back(this->type_descriptor_list_symbol("main")); |
| |
| // Build a list of lists. |
| std::vector<unsigned long> indexes; |
| std::vector<Bexpression*> vals; |
| unsigned long i = 0; |
| for (std::vector<std::string>::iterator p = list_names.begin(); |
| p != list_names.end(); |
| ++p) |
| { |
| Bvariable* bv = |
| this->backend()->implicit_variable_reference(*p, *p, bt); |
| Bexpression* bexpr = this->backend()->var_expression(bv, builtin_loc); |
| bexpr = this->backend()->address_expression(bexpr, builtin_loc); |
| |
| indexes.push_back(i); |
| vals.push_back(bexpr); |
| i++; |
| } |
| Expression* len_expr = Expression::make_integer_ul(i, NULL, builtin_loc); |
| Type* list_ptr_type = Type::make_pointer_type(list_type); |
| Type* list_array_type = Type::make_array_type(list_ptr_type, len_expr); |
| Btype* bat = list_array_type->get_backend(this); |
| Bexpression* barray = |
| this->backend()->array_constructor_expression(bat, indexes, vals, |
| builtin_loc); |
| |
| // Create a variable holding the list. |
| std::string name = this->typelists_symbol(); |
| Bvariable* bv = this->backend()->implicit_variable(name, name, bat, |
| true, true, false, |
| 0); |
| this->backend()->implicit_variable_set_init(bv, name, bat, true, true, |
| false, barray); |
| |
| // Build the call in main package's init function. |
| Translate_context context(this, NULL, NULL, NULL); |
| Bexpression* bexpr = this->backend()->var_expression(bv, builtin_loc); |
| bexpr = this->backend()->address_expression(bexpr, builtin_loc); |
| Type* array_ptr_type = Type::make_pointer_type(list_array_type); |
| Expression* expr = Expression::make_backend(bexpr, array_ptr_type, |
| builtin_loc); |
| expr = Runtime::make_call(Runtime::REGISTER_TYPE_DESCRIPTORS, |
| builtin_loc, 2, len_expr->copy(), expr); |
| Bexpression* bcall = expr->get_backend(&context); |
| init_stmts.push_back(this->backend()->expression_statement(init_bfn, |
| bcall)); |
| } |
| |
| // Build the decl for the initialization function. |
| |
| Named_object* |
| Gogo::initialization_function_decl() |
| { |
| std::string name = this->get_init_fn_name(); |
| Location loc = this->package_->location(); |
| |
| Function_type* fntype = Type::make_function_type(NULL, NULL, NULL, loc); |
| Function* initfn = new Function(fntype, NULL, NULL, loc); |
| return Named_object::make_function(name, NULL, initfn); |
| } |
| |
| // Create the magic initialization function. CODE_STMT is the |
| // code that it needs to run. |
| |
| Named_object* |
| Gogo::create_initialization_function(Named_object* initfn, |
| Bstatement* code_stmt) |
| { |
| // Make sure that we thought we needed an initialization function, |
| // as otherwise we will not have reported it in the export data. |
| go_assert(this->is_main_package() || this->need_init_fn_); |
| |
| if (initfn == NULL) |
| initfn = this->initialization_function_decl(); |
| |
| // Bind the initialization function code to a block. |
| Bfunction* fndecl = initfn->func_value()->get_or_make_decl(this, initfn); |
| Location pkg_loc = this->package_->location(); |
| std::vector<Bvariable*> vars; |
| this->backend()->block(fndecl, NULL, vars, pkg_loc, pkg_loc); |
| |
| if (!this->backend()->function_set_body(fndecl, code_stmt)) |
| { |
| go_assert(saw_errors()); |
| return NULL; |
| } |
| return initfn; |
| } |
| |
| // Given an expression, collect all the global variables defined in |
| // this package that it references. |
| |
| class Find_vars : public Traverse |
| { |
| private: |
| // The list of variables we accumulate. |
| typedef Unordered_set(Named_object*) Vars; |
| |
| // A hash table we use to avoid looping. The index is a |
| // Named_object* or a Temporary_statement*. We only look through |
| // objects defined in this package. |
| typedef Unordered_set(const void*) Seen_objects; |
| |
| public: |
| Find_vars() |
| : Traverse(traverse_expressions), |
| vars_(), seen_objects_() |
| { } |
| |
| // An iterator through the variables found, after the traversal. |
| typedef Vars::const_iterator const_iterator; |
| |
| const_iterator |
| begin() const |
| { return this->vars_.begin(); } |
| |
| const_iterator |
| end() const |
| { return this->vars_.end(); } |
| |
| int |
| expression(Expression**); |
| |
| private: |
| // Accumulated variables. |
| Vars vars_; |
| // Objects we have already seen. |
| Seen_objects seen_objects_; |
| }; |
| |
| // Collect global variables referenced by EXPR. Look through function |
| // calls and variable initializations. |
| |
| int |
| Find_vars::expression(Expression** pexpr) |
| { |
| Expression* e = *pexpr; |
| |
| Var_expression* ve = e->var_expression(); |
| if (ve != NULL) |
| { |
| Named_object* v = ve->named_object(); |
| if (!v->is_variable() || v->package() != NULL) |
| { |
| // This is a result parameter or a variable defined in a |
| // different package. Either way we don't care about it. |
| return TRAVERSE_CONTINUE; |
| } |
| |
| std::pair<Seen_objects::iterator, bool> ins = |
| this->seen_objects_.insert(v); |
| if (!ins.second) |
| { |
| // We've seen this variable before. |
| return TRAVERSE_CONTINUE; |
| } |
| |
| if (v->var_value()->is_global()) |
| this->vars_.insert(v); |
| |
| Expression* init = v->var_value()->init(); |
| if (init != NULL) |
| { |
| if (Expression::traverse(&init, this) == TRAVERSE_EXIT) |
| return TRAVERSE_EXIT; |
| } |
| } |
| |
| // We traverse the code of any function or bound method we see. Note that |
| // this means that we will traverse the code of a function or bound method |
| // whose address is taken even if it is not called. |
| Func_expression* fe = e->func_expression(); |
| Bound_method_expression* bme = e->bound_method_expression(); |
| if (fe != NULL || bme != NULL) |
| { |
| const Named_object* f = fe != NULL ? fe->named_object() : bme->function(); |
| if (f->is_function() && f->package() == NULL) |
| { |
| std::pair<Seen_objects::iterator, bool> ins = |
| this->seen_objects_.insert(f); |
| if (ins.second) |
| { |
| // This is the first time we have seen this name. |
| if (f->func_value()->block()->traverse(this) == TRAVERSE_EXIT) |
| return TRAVERSE_EXIT; |
| } |
| } |
| } |
| |
| Temporary_reference_expression* tre = e->temporary_reference_expression(); |
| if (tre != NULL) |
| { |
| Temporary_statement* ts = tre->statement(); |
| Expression* init = ts->init(); |
| if (init != NULL) |
| { |
| std::pair<Seen_objects::iterator, bool> ins = |
| this->seen_objects_.insert(ts); |
| if (ins.second) |
| { |
| // This is the first time we have seen this temporary |
| // statement. |
| if (Expression::traverse(&init, this) == TRAVERSE_EXIT) |
| return TRAVERSE_EXIT; |
| } |
| } |
| } |
| |
| return TRAVERSE_CONTINUE; |
| } |
| |
| // Return true if EXPR, PREINIT, or DEP refers to VAR. |
| |
| static bool |
| expression_requires(Expression* expr, Block* preinit, Named_object* dep, |
| Named_object* var) |
| { |
| Find_vars find_vars; |
| if (expr != NULL) |
| Expression::traverse(&expr, &find_vars); |
| if (preinit != NULL) |
| preinit->traverse(&find_vars); |
| if (dep != NULL) |
| { |
| Expression* init = dep->var_value()->init(); |
| if (init != NULL) |
| Expression::traverse(&init, &find_vars); |
| if (dep->var_value()->has_pre_init()) |
| dep->var_value()->preinit()->traverse(&find_vars); |
| } |
| |
| for (Find_vars::const_iterator p = find_vars.begin(); |
| p != find_vars.end(); |
| ++p) |
| { |
| if (*p == var) |
| return true; |
| } |
| return false; |
| } |
| |
| // Sort variable initializations. If the initialization expression |
| // for variable A refers directly or indirectly to the initialization |
| // expression for variable B, then we must initialize B before A. |
| |
| class Var_init |
| { |
| public: |
| Var_init() |
| : var_(NULL), init_(NULL), refs_(NULL), dep_count_(0) |
| { } |
| |
| Var_init(Named_object* var, Bstatement* init) |
| : var_(var), init_(init), refs_(NULL), dep_count_(0) |
| { } |
| |
| // Return the variable. |
| Named_object* |
| var() const |
| { return this->var_; } |
| |
| // Return the initialization expression. |
| Bstatement* |
| init() const |
| { return this->init_; } |
| |
| // Add a reference. |
| void |
| add_ref(Named_object* var); |
| |
| // The variables which this variable's initializers refer to. |
| const std::vector<Named_object*>* |
| refs() |
| { return this->refs_; } |
| |
| // Clear the references, if any. |
| void |
| clear_refs(); |
| |
| // Return the number of remaining dependencies. |
| size_t |
| dep_count() const |
| { return this->dep_count_; } |
| |
| // Increment the number of dependencies. |
| void |
| add_dependency() |
| { ++this->dep_count_; } |
| |
| // Decrement the number of dependencies. |
| void |
| remove_dependency() |
| { --this->dep_count_; } |
| |
| private: |
| // The variable being initialized. |
| Named_object* var_; |
| // The backend initialization statement. |
| Bstatement* init_; |
| // Variables this refers to. |
| std::vector<Named_object*>* refs_; |
| // The number of initializations this is dependent on. A variable |
| // initialization should not be emitted if any of its dependencies |
| // have not yet been resolved. |
| size_t dep_count_; |
| }; |
| |
| // Add a reference. |
| |
| void |
| Var_init::add_ref(Named_object* var) |
| { |
| if (this->refs_ == NULL) |
| this->refs_ = new std::vector<Named_object*>; |
| this->refs_->push_back(var); |
| } |
| |
| // Clear the references, if any. |
| |
| void |
| Var_init::clear_refs() |
| { |
| if (this->refs_ != NULL) |
| { |
| delete this->refs_; |
| this->refs_ = NULL; |
| } |
| } |
| |
| // For comparing Var_init keys in a map. |
| |
| inline bool |
| operator<(const Var_init& v1, const Var_init& v2) |
| { return v1.var()->name() < v2.var()->name(); } |
| |
| typedef std::list<Var_init> Var_inits; |
| |
| // Sort the variable initializations. The rule we follow is that we |
| // emit them in the order they appear in the array, except that if the |
| // initialization expression for a variable V1 depends upon another |
| // variable V2 then we initialize V1 after V2. |
| |
| static void |
| sort_var_inits(Gogo* gogo, Var_inits* var_inits) |
| { |
| if (var_inits->empty()) |
| return; |
| |
| std::map<Named_object*, Var_init*> var_to_init; |
| |
| // A mapping from a variable initialization to a set of |
| // variable initializations that depend on it. |
| typedef std::map<Var_init, std::set<Var_init*> > Init_deps; |
| Init_deps init_deps; |
| bool init_loop = false; |
| |
| // Compute all variable references. |
| for (Var_inits::iterator pvar = var_inits->begin(); |
| pvar != var_inits->end(); |
| ++pvar) |
| { |
| Named_object* var = pvar->var(); |
| var_to_init[var] = &*pvar; |
| |
| Find_vars find_vars; |
| Expression* init = var->var_value()->init(); |
| if (init != NULL) |
| Expression::traverse(&init, &find_vars); |
| if (var->var_value()->has_pre_init()) |
| var->var_value()->preinit()->traverse(&find_vars); |
| Named_object* dep = gogo->var_depends_on(var->var_value()); |
| if (dep != NULL) |
| { |
| Expression* dinit = dep->var_value()->init(); |
| if (dinit != NULL) |
| Expression::traverse(&dinit, &find_vars); |
| if (dep->var_value()->has_pre_init()) |
| dep->var_value()->preinit()->traverse(&find_vars); |
| } |
| for (Find_vars::const_iterator p = find_vars.begin(); |
| p != find_vars.end(); |
| ++p) |
| pvar->add_ref(*p); |
| } |
| |
| // Add dependencies to init_deps, and check for cycles. |
| for (Var_inits::iterator pvar = var_inits->begin(); |
| pvar != var_inits->end(); |
| ++pvar) |
| { |
| Named_object* var = pvar->var(); |
| |
| const std::vector<Named_object*>* refs = pvar->refs(); |
| if (refs == NULL) |
| continue; |
| for (std::vector<Named_object*>::const_iterator pdep = refs->begin(); |
| pdep != refs->end(); |
| ++pdep) |
| { |
| Named_object* dep = *pdep; |
| if (var == dep) |
| { |
| // This is a reference from a variable to itself, which |
| // may indicate a loop. We only report an error if |
| // there is an initializer and there is no dependency. |
| // When there is no initializer, it means that the |
| // preinitializer sets the variable, which will appear |
| // to be a loop here. |
| if (var->var_value()->init() != NULL |
| && gogo->var_depends_on(var->var_value()) == NULL) |
| go_error_at(var->location(), |
| ("initialization expression for %qs " |
| "depends upon itself"), |
| var->message_name().c_str()); |
| |
| continue; |
| } |
| |
| Var_init* dep_init = var_to_init[dep]; |
| if (dep_init == NULL) |
| { |
| // This is a dependency on some variable that doesn't |
| // have an initializer, so for purposes of |
| // initialization ordering this is irrelevant. |
| continue; |
| } |
| |
| init_deps[*dep_init].insert(&(*pvar)); |
| pvar->add_dependency(); |
| |
| // Check for cycles. |
| const std::vector<Named_object*>* deprefs = dep_init->refs(); |
| if (deprefs == NULL) |
| continue; |
| for (std::vector<Named_object*>::const_iterator pdepdep = |
| deprefs->begin(); |
| pdepdep != deprefs->end(); |
| ++pdepdep) |
| { |
| if (*pdepdep == var) |
| { |
| go_error_at(var->location(), |
| ("initialization expressions for %qs and " |
| "%qs depend upon each other"), |
| var->message_name().c_str(), |
| dep->message_name().c_str()); |
| go_inform(dep->location(), "%qs defined here", |
| dep->message_name().c_str()); |
| init_loop = true; |
| break; |
| } |
| } |
| } |
| } |
| |
| var_to_init.clear(); |
| for (Var_inits::iterator pvar = var_inits->begin(); |
| pvar != var_inits->end(); |
| ++pvar) |
| pvar->clear_refs(); |
| |
| // If there are no dependencies then the declaration order is sorted. |
| if (!init_deps.empty() && !init_loop) |
| { |
| // Otherwise, sort variable initializations by emitting all variables with |
| // no dependencies in declaration order. VAR_INITS is already in |
| // declaration order. |
| Var_inits ready; |
| while (!var_inits->empty()) |
| { |
| Var_inits::iterator v1;; |
| for (v1 = var_inits->begin(); v1 != var_inits->end(); ++v1) |
| { |
| if (v1->dep_count() == 0) |
| break; |
| } |
| go_assert(v1 != var_inits->end()); |
| |
| // V1 either has no dependencies or its dependencies have already |
| // been emitted, add it to READY next. When V1 is emitted, remove |
| // a dependency from each V that depends on V1. |
| ready.splice(ready.end(), *var_inits, v1); |
| |
| Init_deps::iterator p1 = init_deps.find(*v1); |
| if (p1 != init_deps.end()) |
| { |
| std::set<Var_init*> resolved = p1->second; |
| for (std::set<Var_init*>::iterator pv = resolved.begin(); |
| pv != resolved.end(); |
| ++pv) |
| (*pv)->remove_dependency(); |
| init_deps.erase(p1); |
| } |
| } |
| var_inits->swap(ready); |
| go_assert(init_deps.empty()); |
| } |
| } |
| |
| // Give an error if the initialization expression for VAR depends on |
| // itself. We only check if INIT is not NULL and there is no |
| // dependency; when INIT is NULL, it means that PREINIT sets VAR, |
| // which we will interpret as a loop. |
| |
| void |
| Gogo::check_self_dep(Named_object* var) |
| { |
| Expression* init = var->var_value()->init(); |
| Block* preinit = var->var_value()->preinit(); |
| Named_object* dep = this->var_depends_on(var->var_value()); |
| if (init != NULL |
| && dep == NULL |
| && expression_requires(init, preinit, NULL, var)) |
| go_error_at(var->location(), |
| "initialization expression for %qs depends upon itself", |
| var->message_name().c_str()); |
| } |
| |
| // Write out the global definitions. |
| |
| void |
| Gogo::write_globals() |
| { |
| this->build_interface_method_tables(); |
| |
| Bindings* bindings = this->current_bindings(); |
| |
| for (Bindings::const_declarations_iterator p = bindings->begin_declarations(); |
| p != bindings->end_declarations(); |
| ++p) |
| { |
| // If any function declarations needed a descriptor, make sure |
| // we build it. |
| Named_object* no = p->second; |
| if (no->is_function_declaration()) |
| no->func_declaration_value()->build_backend_descriptor(this); |
| } |
| |
| // Lists of globally declared types, variables, constants, and functions |
| // that must be defined. |
| std::vector<Btype*> type_decls; |
| std::vector<Bvariable*> var_decls; |
| std::vector<Bexpression*> const_decls; |
| std::vector<Bfunction*> func_decls; |
| |
| // The init function declaration and associated Bfunction, if necessary. |
| Named_object* init_fndecl = NULL; |
| Bfunction* init_bfn = NULL; |
| |
| std::vector<Bstatement*> init_stmts; |
| std::vector<Bstatement*> var_init_stmts; |
| |
| if (this->is_main_package()) |
| { |
| init_fndecl = this->initialization_function_decl(); |
| init_bfn = init_fndecl->func_value()->get_or_make_decl(this, init_fndecl); |
| } |
| |
| // A list of variable initializations. |
| Var_inits var_inits; |
| |
| // A list of variables which need to be registered with the garbage |
| // collector. |
| size_t count_definitions = bindings->size_definitions(); |
| std::vector<Named_object*> var_gc; |
| var_gc.reserve(count_definitions); |
| |
| for (Bindings::const_definitions_iterator p = bindings->begin_definitions(); |
| p != bindings->end_definitions(); |
| ++p) |
| { |
| Named_object* no = *p; |
| go_assert(!no->is_type_declaration() && !no->is_function_declaration()); |
| |
| // There is nothing to do for a package. |
| if (no->is_package()) |
| continue; |
| |
| // There is nothing to do for an object which was imported from |
| // a different package into the global scope. |
| if (no->package() != NULL) |
| continue; |
| |
| // Skip blank named functions and constants. |
| if ((no->is_function() && no->func_value()->is_sink()) |
| || (no->is_const() && no->const_value()->is_sink())) |
| continue; |
| |
| // There is nothing useful we can output for constants which |
| // have ideal or non-integral type. |
| if (no->is_const()) |
| { |
| Type* type = no->const_value()->type(); |
| if (type == NULL) |
| type = no->const_value()->expr()->type(); |
| if (type->is_abstract() || !type->is_numeric_type()) |
| continue; |
| } |
| |
| if (!no->is_variable()) |
| no->get_backend(this, const_decls, type_decls, func_decls); |
| else |
| { |
| Variable* var = no->var_value(); |
| Bvariable* bvar = no->get_backend_variable(this, NULL); |
| var_decls.push_back(bvar); |
| |
| // Check for a sink variable, which may be used to run an |
| // initializer purely for its side effects. |
| bool is_sink = no->name()[0] == '_' && no->name()[1] == '.'; |
| |
| Bstatement* var_init_stmt = NULL; |
| if (!var->has_pre_init()) |
| { |
| // If the backend representation of the variable initializer is |
| // constant, we can just set the initial value using |
| // global_var_set_init instead of during the init() function. |
| // The initializer is constant if it is the zero-value of the |
| // variable's type or if the initial value is an immutable value |
| // that is not copied to the heap. |
| Expression* init = var->init(); |
| |
| // If we see "a = b; b = x", and x is a static |
| // initializer, just set a to x. |
| while (init != NULL && init->var_expression() != NULL) |
| { |
| Named_object* ino = init->var_expression()->named_object(); |
| if (!ino->is_variable() || ino->package() != NULL) |
| break; |
| Expression* ino_init = ino->var_value()->init(); |
| if (ino->var_value()->has_pre_init() |
| || ino_init == NULL |
| || !ino_init->is_static_initializer()) |
| break; |
| init = ino_init; |
| } |
| |
| bool is_static_initializer; |
| if (init == NULL) |
| is_static_initializer = true; |
| else |
| { |
| Type* var_type = var->type(); |
| init = Expression::make_cast(var_type, init, var->location()); |
| is_static_initializer = init->is_static_initializer(); |
| } |
| |
| // Non-constant variable initializations might need to create |
| // temporary variables, which will need the initialization |
| // function as context. |
| Named_object* var_init_fn; |
| if (is_static_initializer) |
| var_init_fn = NULL; |
| else |
| { |
| if (init_fndecl == NULL) |
| { |
| init_fndecl = this->initialization_function_decl(); |
| Function* func = init_fndecl->func_value(); |
| init_bfn = func->get_or_make_decl(this, init_fndecl); |
| } |
| var_init_fn = init_fndecl; |
| } |
| |
| Bexpression* var_binit; |
| if (init == NULL) |
| var_binit = NULL; |
| else |
| { |
| Translate_context context(this, var_init_fn, NULL, NULL); |
| var_binit = init->get_backend(&context); |
| } |
| |
| if (var_binit == NULL) |
| ; |
| else if (is_static_initializer) |
| { |
| if (expression_requires(var->init(), NULL, |
| this->var_depends_on(var), no)) |
| go_error_at(no->location(), |
| "initialization expression for %qs depends " |
| "upon itself", |
| no->message_name().c_str()); |
| this->backend()->global_variable_set_init(bvar, var_binit); |
| } |
| else if (is_sink) |
| var_init_stmt = |
| this->backend()->expression_statement(init_bfn, var_binit); |
| else |
| { |
| Location loc = var->location(); |
| Bexpression* var_expr = |
| this->backend()->var_expression(bvar, loc); |
| var_init_stmt = |
| this->backend()->assignment_statement(init_bfn, var_expr, |
| var_binit, loc); |
| } |
| } |
| else |
| { |
| // We are going to create temporary variables which |
| // means that we need an fndecl. |
| if (init_fndecl == NULL) |
| init_fndecl = this->initialization_function_decl(); |
| |
| Bvariable* var_decl = is_sink ? NULL : bvar; |
| var_init_stmt = var->get_init_block(this, init_fndecl, var_decl); |
| } |
| |
| if (var_init_stmt != NULL) |
| { |
| if (var->init() == NULL && !var->has_pre_init()) |
| var_init_stmts.push_back(var_init_stmt); |
| else |
| var_inits.push_back(Var_init(no, var_init_stmt)); |
| } |
| else if (this->var_depends_on(var) != NULL) |
| { |
| // This variable is initialized from something that is |
| // not in its init or preinit. This variable needs to |
| // participate in dependency analysis sorting, in case |
| // some other variable depends on this one. |
| Btype* btype = no->var_value()->type()->get_backend(this); |
| Bexpression* zero = this->backend()->zero_expression(btype); |
| Bstatement* zero_stmt = |
| this->backend()->expression_statement(init_bfn, zero); |
| var_inits.push_back(Var_init(no, zero_stmt)); |
| } |
| |
| // Collect a list of all global variables with pointers, |
| // to register them for the garbage collector. |
| if (!is_sink && var->type()->has_pointer()) |
| { |
| // Avoid putting runtime.gcRoots itself on the list. |
| if (this->compiling_runtime() |
| && this->package_name() == "runtime" |
| && (Gogo::unpack_hidden_name(no->name()) == "gcRoots" |
| || Gogo::unpack_hidden_name(no->name()) == "gcRootsIndex")) |
| ; |
| else |
| var_gc.push_back(no); |
| } |
| } |
| } |
| |
| // Output inline functions, which are in different packages. |
| for (std::vector<Named_object*>::const_iterator p = |
| this->imported_inline_functions_.begin(); |
| p != this->imported_inline_functions_.end(); |
| ++p) |
| (*p)->get_backend(this, const_decls, type_decls, func_decls); |
| |
| // Build the list of type descriptors. |
| this->build_type_descriptor_list(); |
| |
| if (this->is_main_package()) |
| { |
| // Register the type descriptor lists, so that at run time |
| // the reflect package can find compiler-created types, and |
| // deduplicate if the same type is created with reflection. |
| // This needs to be done before calling any package's init |
| // function, as it may create type through reflection. |
| this->register_type_descriptors(init_stmts, init_bfn); |
| |
| // Initialize imported packages. |
| this->init_imports(init_stmts, init_bfn); |
| } |
| |
| // Register global variables with the garbage collector. |
| this->register_gc_vars(var_gc, init_stmts, init_bfn); |
| |
| // Simple variable initializations, after all variables are |
| // registered. |
| init_stmts.push_back(this->backend()->statement_list(var_init_stmts)); |
| |
| // Complete variable initializations, first sorting them into a |
| // workable order. |
| if (!var_inits.empty()) |
| { |
| sort_var_inits(this, &var_inits); |
| for (Var_inits::const_iterator p = var_inits.begin(); |
| p != var_inits.end(); |
| ++p) |
| init_stmts.push_back(p->init()); |
| } |
| |
| // After all the variables are initialized, call the init |
| // functions if there are any. Init functions take no arguments, so |
| // we pass in EMPTY_ARGS to call them. |
| std::vector<Bexpression*> empty_args; |
| for (std::vector<Named_object*>::const_iterator p = |
| this->init_functions_.begin(); |
| p != this->init_functions_.end(); |
| ++p) |
| { |
| Location func_loc = (*p)->location(); |
| Function* func = (*p)->func_value(); |
| Bfunction* initfn = func->get_or_make_decl(this, *p); |
| Bexpression* func_code = |
| this->backend()->function_code_expression(initfn, func_loc); |
| Bexpression* call = this->backend()->call_expression(init_bfn, func_code, |
| empty_args, |
| NULL, func_loc); |
| Bstatement* ist = this->backend()->expression_statement(init_bfn, call); |
| init_stmts.push_back(ist); |
| } |
| |
| // Set up a magic function to do all the initialization actions. |
| // This will be called if this package is imported. |
| Bstatement* init_fncode = this->backend()->statement_list(init_stmts); |
| if (this->need_init_fn_ || this->is_main_package()) |
| { |
| init_fndecl = |
| this->create_initialization_function(init_fndecl, init_fncode); |
| if (init_fndecl != NULL) |
| func_decls.push_back(init_fndecl->func_value()->get_decl()); |
| } |
| |
| // We should not have seen any new bindings created during the conversion. |
| go_assert(count_definitions == this->current_bindings()->size_definitions()); |
| |
| // Define all globally declared values. |
| if (!saw_errors()) |
| this->backend()->write_global_definitions(type_decls, const_decls, |
| func_decls, var_decls); |
| } |
| |
| // 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) |
| { |
| std::string dot_alias = "." + ret->package()->package_name(); |
| ret->package()->note_usage(dot_alias); |
| } |
| 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 |
| { |
| go_assert(!this->functions_.empty()); |
| go_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& pkgpath, |
| const std::string& pkgpath_symbol, |
| Location location, |
| bool* padd_to_globals) |
| { |
| Package* ret = this->register_package(pkgpath, pkgpath_symbol, location); |
| ret->set_package_name(real_name, location); |
| |
| *padd_to_globals = false; |
| |
| if (alias_arg == "_") |
| ; |
| else if (alias_arg == ".") |
| { |
| *padd_to_globals = true; |
| std::string dot_alias = "." + real_name; |
| ret->add_alias(dot_alias, location); |
| } |
| else |
| { |
| std::string alias = alias_arg; |
| if (alias.empty()) |
| { |
| alias = real_name; |
| is_alias_exported = Lex::is_exported_name(alias); |
| } |
| ret->add_alias(alias, location); |
| alias = this->pack_hidden_name(alias, is_alias_exported); |
| Named_object* no = this->package_->bindings()->add_package(alias, ret); |
| if (!no->is_package()) |
| return NULL; |
| } |
| |
| return ret; |
| } |
| |
| // Register a package. This package may or may not be imported. This |
| // returns the Package structure for the package, creating if it |
| // necessary. LOCATION is the location of the import statement that |
| // led us to see this package. PKGPATH_SYMBOL is the symbol to use |
| // for names in the package; it may be the empty string, in which case |
| // we either get it later or make a guess when we need it. |
| |
| Package* |
| Gogo::register_package(const std::string& pkgpath, |
| const std::string& pkgpath_symbol, Location location) |
| { |
| Package* package = NULL; |
| std::pair<Packages::iterator, bool> ins = |
| this->packages_.insert(std::make_pair(pkgpath, package)); |
| if (!ins.second) |
| { |
| // We have seen this package name before. |
| package = ins.first->second; |
| go_assert(package != NULL && package->pkgpath() == pkgpath); |
| if (!pkgpath_symbol.empty()) |
| package->set_pkgpath_symbol(pkgpath_symbol); |
| if (Linemap::is_unknown_location(package->location())) |
| package->set_location(location); |
| } |
| else |
| { |
| // First time we have seen this package name. |
| package = new Package(pkgpath, pkgpath_symbol, location); |
| go_assert(ins.first->second == NULL); |
| ins.first->second = package; |
| } |
| |
| return package; |
| } |
| |
| // Return the pkgpath symbol for a package, given the pkgpath. |
| |
| std::string |
| Gogo::pkgpath_symbol_for_package(const std::string& pkgpath) |
| { |
| Packages::iterator p = this->packages_.find(pkgpath); |
| go_assert(p != this->packages_.end()); |
| return p->second->pkgpath_symbol(); |
| } |
| |
| // Start compiling a function. |
| |
| Named_object* |
| Gogo::start_function(const std::string& name, Function_type* type, |
| bool add_method_to_type, Location location) |
| { |
| bool at_top_level = this->functions_.empty(); |
| |
| Block* block = new Block(NULL, location); |
| |
| Named_object* enclosing = (at_top_level |
| ? NULL |
| : this->functions_.back().function); |
| |
| 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 rname = receiver->name(); |
| unsigned rcounter = 0; |
| |
| // We need to give a nameless receiver parameter a synthesized name to |
| // avoid having it clash with some other nameless param. FIXME. |
| Gogo::rename_if_empty(&rname, "r", &rcounter); |
| |
| block->bindings()->add_variable(rname, NULL, this_param); |
| } |
| |
| const Typed_identifier_list* parameters = type->parameters(); |
| bool is_varargs = type->is_varargs(); |
| unsigned pcounter = 0; |
| 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, |
| p->location()); |
| if (is_varargs && p + 1 == parameters->end()) |
| param->set_is_varargs_parameter(); |
| |
| std::string pname = p->name(); |
| |
| // We need to give each nameless parameter a non-empty name to avoid |
| // having it clash with some other nameless param. FIXME. |
| Gogo::rename_if_empty(&pname, "p", &pcounter); |
| |
| block->bindings()->add_variable(pname, NULL, param); |
| } |
| } |
| |
| function->create_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())) |
| go_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. |
| nested_name = this->init_function_name(); |
| pname = &nested_name; |
| is_init = true; |
| } |
| else if (!name.empty()) |
| pname = &name; |
| else |
| { |
| // Invent a name for a nested function. |
| nested_name = this->nested_function_name(enclosing); |
| pname = &nested_name; |
| } |
| |
| Named_object* ret; |
| if (Gogo::is_sink_name(*pname)) |
| { |
| std::string sname(this->sink_function_name()); |
| ret = Named_object::make_function(sname, NULL, function); |
| ret->func_value()->set_is_sink(); |
| |
| if (!type->is_method()) |
| ret = this->package_->bindings()->add_named_object(ret); |
| else if (add_method_to_type) |
| { |
| // We should report errors even for sink methods. |
| Type* rtype = type->receiver()->type(); |
| // Avoid points_to and deref to avoid getting an error if |
| // the type is not yet defined. |
| if (rtype->classification() == Type::TYPE_POINTER) |
| rtype = rtype->points_to(); |
| while (rtype->named_type() != NULL |
| && rtype->named_type()->is_alias()) |
| rtype = rtype->named_type()->real_type()->forwarded(); |
| if (rtype->is_error_type()) |
| ; |
| else if (rtype->named_type() != NULL) |
| { |
| if (rtype->named_type()->named_object()->package() != NULL) |
| go_error_at(type->receiver()->location(), |
| "may not define methods on non-local type"); |
| } |
| else if (rtype->forward_declaration_type() != NULL) |
| { |
| // Go ahead and add the method in case we need to report |
| // an error when we see the definition. |
| rtype->forward_declaration_type()->add_existing_method(ret); |
| } |
| else |
| go_error_at(type->receiver()->location(), |
| ("invalid receiver type " |
| "(receiver must be a named type)")); |
| } |
| } |
| 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. |
| std::string rname(this->redefined_function_name()); |
| ret = this->package_->bindings()->add_function(rname, NULL, function); |
| } |
| } |
| else |
| { |
| if (!add_method_to_type) |
| ret = Named_object::make_function(name, NULL, function); |
| else |
| { |
| go_assert(at_top_level); |
| Type* rtype = type->receiver()->type(); |
| |
| while (rtype->named_type() != NULL |
| && rtype->named_type()->is_alias()) |
| rtype = rtype->named_type()->real_type()->forwarded(); |
| |
| // 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(); |
| |
| while (rtype->named_type() != NULL |
| && rtype->named_type()->is_alias()) |
| rtype = rtype->named_type()->real_type()->forwarded(); |
| |
| if (rtype->is_error_type()) |
| ret = Named_object::make_function(name, NULL, function); |
| else if (rtype->named_type() != NULL) |
| { |
| if (rtype->named_type()->named_object()->package() != NULL) |
| { |
| go_error_at(type->receiver()->location(), |
| "may not define methods on non-local type"); |
| ret = Named_object::make_function(name, NULL, function); |
| } |
| else |
| { |
| 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()); |
| go_assert(declared |
| == type_no->unknown_value()->real_named_object()); |
| } |
| ret = rtype->forward_declaration_type()->add_method(name, |
| function); |
| } |
| else |
| { |
| go_error_at(type->receiver()->location(), |
| ("invalid receiver type (receiver must " |
| "be a named type)")); |
| ret = Named_object::make_function(name, NULL, function); |
| } |
| } |
| 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(Location location) |
| { |
| this->finish_block(location); |
| go_assert(this->functions_.back().blocks.empty()); |
| this->functions_.pop_back(); |
| } |
| |
| // Return the current function. |
| |
| Named_object* |
| Gogo::current_function() const |
| { |
| go_assert(!this->functions_.empty()); |
| return this->functions_.back().function; |
| } |
| |
| // Start a new block. |
| |
| void |
| Gogo::start_block(Location location) |
| { |
| go_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(Location location) |
| { |
| go_assert(!this->functions_.empty()); |
| go_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 erroneous name. |
| |
| Named_object* |
| Gogo::add_erroneous_name(const std::string& name) |
| { |
| return this->package_->bindings()->add_erroneous_name(name); |
| } |
| |
| // Add an unknown name. |
| |
| Named_object* |
| Gogo::add_unknown_name(const std::string& name, 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, |
| 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(); |
| |
| while (rtype->named_type() != NULL |
| && rtype->named_type()->is_alias()) |
| rtype = rtype->named_type()->real_type()->forwarded(); |
| |
| // 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(); |
| |
| while (rtype->named_type() != NULL |
| && rtype->named_type()->is_alias()) |
| rtype = rtype->named_type()->real_type()->forwarded(); |
| |
| 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, NULL, type, location); |
| } |
| else |
| { |
| go_error_at(type->receiver()->location(), |
| "invalid receiver type (receiver must be a named type)"); |
| return Named_object::make_erroneous_name(name); |
| } |
| } |
| } |
| |
| // Add a label definition. |
| |
| Label* |
| Gogo::add_label_definition(const std::string& label_name, |
| Location location) |
| { |
| go_assert(!this->functions_.empty()); |
| Function* func = this->functions_.back().function->func_value(); |
| Label* label = func->add_label_definition(this, 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, |
| Location location, bool issue_goto_errors) |
| { |
| go_assert(!this->functions_.empty()); |
| Function* func = this->functions_.back().function->func_value(); |
| return func->add_label_reference(this, label_name, location, |
| issue_goto_errors); |
| } |
| |
| // Return the current binding state. |
| |
| Bindings_snapshot* |
| Gogo::bindings_snapshot(Location location) |
| { |
| return new Bindings_snapshot(this->current_block(), location); |
| } |
| |
| // Add a statement. |
| |
| void |
| Gogo::add_statement(Statement* statement) |
| { |
| go_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, Location location) |
| { |
| go_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, Location location) |
| { |
| Named_object* no = this->current_bindings()->add_type(name, NULL, type, |
| location); |
| if (!this->in_global_scope() && no->is_type()) |
| { |
| Named_object* f = this->functions_.back().function; |
| unsigned int index; |
| if (f->is_function()) |
| index = f->func_value()->new_local_type_index(); |
| else |
| index = 0; |
| no->type_value()->set_in_function(f, index); |
| } |
| } |
| |
| // Add a named type. |
| |
| void |
| Gogo::add_named_type(Named_type* type) |
| { |
| go_assert(this->in_global_scope()); |
| this->current_bindings()->add_named_type(type); |
| } |
| |
| // Declare a type. |
| |
| Named_object* |
| Gogo::declare_type(const std::string& name, 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; |
| unsigned int index; |
| if (f->is_function()) |
| index = f->func_value()->new_local_type_index(); |
| else |
| index = 0; |
| no->type_declaration_value()->set_in_function(f, index); |
| } |
| return no; |
| } |
| |
| // Declare a type at the package level. |
| |
| Named_object* |
| Gogo::declare_package_type(const std::string& name, Location location) |
| { |
| return this->package_->bindings()->add_type_declaration(name, NULL, location); |
| } |
| |
| // Declare a function at the package level. |
| |
| Named_object* |
| Gogo::declare_package_function(const std::string& name, Function_type* type, |
| Location location) |
| { |
| return this->package_->bindings()->add_function_declaration(name, NULL, type, |
| location); |
| } |
| |
| // Add a function declaration to the list of functions we may want to |
| // inline. |
| |
| void |
| Gogo::add_imported_inlinable_function(Named_object* no) |
| { |
| go_assert(no->is_function_declaration()); |
| Function_declaration* fd = no->func_declaration_value(); |
| if (fd->is_on_inlinable_list()) |
| return; |
| this->imported_inlinable_functions_.push_back(no); |
| fd->set_is_on_inlinable_list(); |
| } |
| |
| // 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; |
| } |
| |
| void |
| Gogo::rename_if_empty(std::string* pname, const char* tag, unsigned* count) |
| { |
| if (pname->empty() || Gogo::is_sink_name(*pname)) |
| { |
| char buf[50]; |
| go_assert(strlen(tag) < 10); |
| snprintf(buf, sizeof buf, "%s.%u", tag, *count); |
| ++(*count); |
| *pname = buf; |
| } |
| } |
| |
| |
| // Add a sink--a reference to the blank identifier _. |
| |
| Named_object* |
| Gogo::add_sink() |
| { |
| return Named_object::make_sink(); |
| } |
| |
| // Add a named object for a dot import. |
| |
| void |
| Gogo::add_dot_import_object(Named_object* no) |
| { |
| // If the name already exists, then it was defined in some file seen |
| // earlier. If the earlier name is just a declaration, don't add |
| // this name, because that will cause the previous declaration to |
| // merge to this imported name, which should not happen. Just add |
| // this name to the list of file block names to get appropriate |
| // errors if we see a later definition. |
| Named_object* e = this->package_->bindings()->lookup(no->name()); |
| if (e != NULL && e->package() == NULL) |
| { |
| if (e->is_unknown()) |
| e = e->resolve(); |
| if (e->package() == NULL |
| && (e->is_type_declaration() |
| || e->is_function_declaration() |
| || e->is_unknown())) |
| { |
| this->add_file_block_name(no->name(), no->location()); |
| return; |
| } |
| } |
| |
| this->current_bindings()->add_named_object(no); |
| } |
| |
| // Add a linkname. This implements the go:linkname compiler directive. |
| // We only support this for functions and function declarations. |
| |
| void |
| Gogo::add_linkname(const std::string& go_name, bool is_exported, |
| const std::string& ext_name, Location loc) |
| { |
| Named_object* no = |
| this->package_->bindings()->lookup(this->pack_hidden_name(go_name, |
| is_exported)); |
| if (no == NULL) |
| go_error_at(loc, "%s is not defined", go_name.c_str()); |
| else if (no->is_function()) |
| { |
| if (ext_name.empty()) |
| no->func_value()->set_is_exported_by_linkname(); |
| else |
| no->func_value()->set_asm_name(ext_name); |
| } |
| else if (no->is_function_declaration()) |
| { |
| if (ext_name.empty()) |
| go_error_at(loc, |
| ("%<//go:linkname%> missing external name " |
| "for declaration of %s"), |
| go_name.c_str()); |
| else |
| no->func_declaration_value()->set_asm_name(ext_name); |
| } |
| else |
| go_error_at(loc, |
| ("%s is not a function; " |
| "%<//go:linkname%> is only supported for functions"), |
| go_name.c_str()); |
| } |
| |
| // Mark all local variables used. This is used when some types of |
| // parse error occur. |
| |
| void |
| Gogo::mark_locals_used() |
| { |
| for (Open_functions::iterator pf = this->functions_.begin(); |
| pf != this->functions_.end(); |
| ++pf) |
| { |
| for (std::vector<Block*>::iterator pb = pf->blocks.begin(); |
| pb != pf->blocks.end(); |
| ++pb) |
| (*pb)->bindings()->mark_locals_used(); |
| } |
| } |
| |
| // Record that we've seen an interface type. |
| |
| void |
| Gogo::record_interface_type(Interface_type* itype) |
| { |
| this->interface_types_.push_back(itype); |
| } |
| |
| // 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() |
| { |
| if (this->is_main_package()) |
| { |
| // Every Go program has to import the runtime package, so that |
| // it is properly initialized. We can't use |
| // predeclared_location here as it will cause runtime functions |
| // to appear to be builtin functions. |
| this->import_package("runtime", "_", false, false, |
| this->package_->location()); |
| } |
| |
| 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()) |
| { |
| for (std::vector<Named_object*>::const_iterator pm = |
| no->type_declaration_value()->methods()->begin(); |
| pm != no->type_declaration_value()->methods()->end(); |
| pm++) |
| go_error_at((*pm)->location(), |
| "may not define methods on non-local type"); |
| } |
| no->set_type_value(global_no->type_value()); |
| } |
| else |
| { |
| go_error_at(no->location(), "expected type"); |
| Type* errtype = Type::make_error_type(); |
| Named_object* err = |
| Named_object::make_type("erroneous_type", NULL, errtype, |
| Linemap::predeclared_location()); |
| no->set_type_value(err->type_value()); |
| } |
| } |
| else if (no->is_unknown()) |
| no->unknown_value()->set_real_named_object(global_no); |
| } |
| |
| // Give an error if any name is defined in both the package block |
| // and the file block. For example, this can happen if one file |
| // imports "fmt" and another file defines a global variable fmt. |
| for (Bindings::const_declarations_iterator p = |
| this->package_->bindings()->begin_declarations(); |
| p != this->package_->bindings()->end_declarations(); |
| ++p) |
| { |
| if (p->second->is_unknown() |
| && p->second->unknown_value()->real_named_object() == NULL) |
| { |
| // No point in warning about an undefined name, as we will |
| // get other errors later anyhow. |
| continue; |
| } |
| File_block_names::const_iterator pf = |
| this->file_block_names_.find(p->second->name()); |
| if (pf != this->file_block_names_.end()) |
| { |
| std::string n = p->second->message_name(); |
| go_error_at(p->second->location(), |
| "%qs defined as both imported name and global name", |
| n.c_str()); |
| go_inform(pf->second, "%qs imported here", n.c_str()); |
| } |
| |
| // No package scope identifier may be named "init". |
| if (!p->second->is_function() |
| && Gogo::unpack_hidden_name(p->second->name()) == "init") |
| { |
| go_error_at(p->second->location(), |
| "cannot declare init - must be func"); |
| } |
| } |
| } |
| |
| // Clear out names in file scope. |
| |
| void |
| Gogo::clear_file_scope() |
| { |
| this->package_->bindings()->clear_file_scope(this); |
| |
| // Warn about packages which were imported but not used. |
| bool quiet = saw_errors(); |
| for (Packages::iterator p = this->packages_.begin(); |
| p != this->packages_.end(); |
| ++p) |
| { |
| Package* package = p->second; |
| if (package != this->package_ && !quiet) |
| { |
| for (Package::Aliases::const_iterator p1 = package->aliases().begin(); |
| p1 != package->aliases().end(); |
| ++p1) |
| { |
| if (!p1->second->used()) |
| { |
| // Give a more refined error message if the alias name is known. |
| std::string pkg_name = package->package_name(); |
| if (p1->first != pkg_name && p1->first[0] != '.') |
| { |
| go_error_at(p1->second->location(), |
| "imported and not used: %s as %s", |
| Gogo::message_name(pkg_name).c_str(), |
| Gogo::message_name(p1->first).c_str()); |
| } |
| else |
| go_error_at(p1->second->location(), |
| "imported and not used: %s", |
| Gogo::message_name(pkg_name).c_str()); |
| } |
| } |
| } |
| package->clear_used(); |
| } |
| |
| this->current_file_imported_unsafe_ = false; |
| } |
| |
| // Queue up a type-specific hash function for later writing. These |
| // are written out in write_specific_type_functions, called after the |
| // parse tree is lowered. |
| |
| void |
| Gogo::queue_hash_function(Type* type, int64_t size, Backend_name* bname, |
| Function_type* hash_fntype) |
| { |
| go_assert(!this->specific_type_functions_are_written_); |
| go_assert(!this->in_global_scope()); |
| Specific_type_function::Specific_type_function_kind kind = |
| Specific_type_function::SPECIFIC_HASH; |
| Specific_type_function* tsf = new Specific_type_function(type, NULL, size, |
| kind, bname, |
| hash_fntype); |
| this->specific_type_functions_.push_back(tsf); |
| } |
| |
| // Queue up a type-specific equal function for later writing. These |
| // are written out in write_specific_type_functions, called after the |
| // parse tree is lowered. |
| |
| void |
| Gogo::queue_equal_function(Type* type, Named_type* name, int64_t size, |
| Backend_name* bname, Function_type* equal_fntype) |
| { |
| go_assert(!this->specific_type_functions_are_written_); |
| go_assert(!this->in_global_scope()); |
| Specific_type_function::Specific_type_function_kind kind = |
| Specific_type_function::SPECIFIC_EQUAL; |
| Specific_type_function* tsf = new Specific_type_function(type, name, size, |
| kind, bname, |
| equal_fntype); |
| this->specific_type_functions_.push_back(tsf); |
| } |
| |
| // Look for types which need specific hash or equality functions. |
| |
| class Specific_type_functions : public Traverse |
| { |
| public: |
| Specific_type_functions(Gogo* gogo) |
| : Traverse(traverse_types), |
| gogo_(gogo) |
| { } |
| |
| int |
| type(Type*); |
| |
| private: |
| Gogo* gogo_; |
| }; |
| |
| int |
| Specific_type_functions::type(Type* t) |
| { |
| switch (t->classification()) |
| { |
| case Type::TYPE_NAMED: |
| { |
| Named_type* nt = t->named_type(); |
| if (nt->is_alias()) |
| return TRAVERSE_CONTINUE; |
| if (t->needs_specific_type_functions(this->gogo_)) |
| t->equal_function(this->gogo_, nt, NULL); |
| |
| // If this is a struct type, we don't want to make functions |
| // for the unnamed struct. |
| Type* rt = nt->real_type(); |
| if (rt->struct_type() == NULL) |
| { |
| if (Type::traverse(rt, this) == TRAVERSE_EXIT) |
| return TRAVERSE_EXIT; |
| } |
| else |
| { |
| // If this type is defined in another package, then we don't |
| // need to worry about the unexported fields. |
| bool is_defined_elsewhere = nt->named_object()->package() != NULL; |
| const Struct_field_list* fields = rt->struct_type()->fields(); |
| for (Struct_field_list::const_iterator p = fields->begin(); |
| p != fields->end(); |
| ++p) |
| { |
| if (is_defined_elsewhere |
| && Gogo::is_hidden_name(p->field_name())) |
| continue; |
| if (Type::traverse(p->type(), this) == TRAVERSE_EXIT) |
| return TRAVERSE_EXIT; |
| } |
| } |
| |
| return TRAVERSE_SKIP_COMPONENTS; |
| } |
| |
| case Type::TYPE_STRUCT: |
| case Type::TYPE_ARRAY: |
| if (t->needs_specific_type_functions(this->gogo_)) |
| t->equal_function(this->gogo_, NULL, NULL); |
| break; |
| |
| case Type::TYPE_MAP: |
| { |
| Type* key_type = t->map_type()->key_type(); |
| if (key_type->needs_specific_type_functions(this->gogo_)) |
| key_type->hash_function(this->gogo_, NULL); |
| } |
| break; |
| |
| default: |
| break; |
| } |
| |
| return TRAVERSE_CONTINUE; |
| } |
| |
| // Write out type specific functions. |
| |
| void |
| Gogo::write_specific_type_functions() |
| { |
| Specific_type_functions stf(this); |
| this->traverse(&stf); |
| |
| while (!this->specific_type_functions_.empty()) |
| { |
| Specific_type_function* tsf = this->specific_type_functions_.back(); |
| this->specific_type_functions_.pop_back(); |
| if (tsf->kind == Specific_type_function::SPECIFIC_HASH) |
| tsf->type->write_hash_function(this, tsf->size, &tsf->bname, |
| tsf->fntype); |
| else |
| tsf->type->write_equal_function(this, tsf->name, tsf->size, |
| &tsf->bname, tsf->fntype); |
| delete tsf; |
| } |
| this->specific_type_functions_are_written_ = true; |
| } |
| |
| // 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; |
| } |
| } |
| } |
| |
| // Add a type to verify. This is used for types of sink variables, in |
| // order to give appropriate error messages. |
| |
| void |
| Gogo::add_type_to_verify(Type* type) |
| { |
| this->verify_types_.push_back(type); |
| } |
| |
| // 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); |
| |
| for (std::vector<Type*>::iterator p = this->verify_types_.begin(); |
| p != this->verify_types_.end(); |
| ++p) |
| (*p)->verify(); |
| this->verify_types_.clear(); |
| } |
| |
| // 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), inserter_() |
| { } |
| |
| void |
| set_inserter(const Statement_inserter* inserter) |
| { this->inserter_ = *inserter; } |
| |
| 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_; |
| // Current statement inserter for use by expressions. |
| Statement_inserter inserter_; |
| }; |
| |
| // Lower variables. |
| |
| int |
| Lower_parse_tree::variable(Named_object* no) |
| { |
| if (!no->is_variable()) |
| return TRAVERSE_CONTINUE; |
| |
| if (no->is_variable() && no->var_value()->is_global()) |
| { |
| // Global variables can have loops in their initialization |
| // expressions. This is handled in lower_init_expression. |
| no->var_value()->lower_init_expression(this->gogo_, this->function_, |
| &this->inserter_); |
| return TRAVERSE_CONTINUE; |
| } |
| |
| // This is a local variable. We are going to return |
| // TRAVERSE_SKIP_COMPONENTS here because we want to traverse the |
| // initialization expression when we reach the variable declaration |
| // statement. However, that means that we need to traverse the type |
| // ourselves. |
| if (no->var_value()->has_type()) |
| { |
| Type* type = no->var_value()->type(); |
| if (type != NULL) |
| { |
| if (Type::traverse(type, this) == TRAVERSE_EXIT) |
| return TRAVERSE_EXIT; |
| } |
| } |
| go_assert(!no->var_value()->has_pre_init()); |
| |
| return TRAVERSE_SKIP_COMPONENTS; |
| } |
| |
| // 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(); |
| |
| go_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 the body of a function, and set the closure type. 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(); |
| |
| go_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) |
| { |
| // Because we explicitly traverse the statement's contents |
| // ourselves, we want to skip block statements here. There is |
| // nothing to lower in a block statement. |
| if (sorig->is_block_statement()) |
| return TRAVERSE_CONTINUE; |
| |
| Statement_inserter hold_inserter(this->inserter_); |
| this->inserter_ = Statement_inserter(block, pindex); |
| |
| // Lower the expressions first. |
| int t = sorig->traverse_contents(this); |
| if (t == TRAVERSE_EXIT) |
| { |
| this->inserter_ = hold_inserter; |
| return t; |
| } |
| |
| // Keep lowering until nothing changes. |
| Statement* s = sorig; |
| while (true) |
| { |
| Statement* snew = s->lower(this->gogo_, this->function_, block, |
| &this->inserter_); |
| if (snew == s) |
| break; |
| s = snew; |
| t = s->traverse_contents(this); |
| if (t == TRAVERSE_EXIT) |
| { |
| this->inserter_ = hold_inserter; |
| return t; |
| } |
| } |
| |
| if (s != sorig) |
| block->replace_statement(*pindex, s); |
| |
| this->inserter_ = hold_inserter; |
| 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->inserter_, this->iota_value_); |
| if (enew == e) |
| break; |
| if (enew->traverse_subexpressions(this) == TRAVERSE_EXIT) |
| return TRAVERSE_EXIT; |
| *pexpr = enew; |
| } |
| |
| // Lower the type of this expression before the parent looks at it, |
| // in case the type contains an array that has expressions in its |
| // length. Skip an Unknown_expression, as at this point that means |
| // a composite literal key that does not have a type. |
| if ((*pexpr)->unknown_expression() == NULL) |
| Type::traverse((*pexpr)->type(), this); |
| |
| 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); |
| |
| // If we found any functions defined in other packages that are |
| // inlinables, import their bodies and turn them into functions. |
| // |
| // Note that as we import inlinable functions we may find more |
| // inlinable functions, so don't use an iterator. |
| for (size_t i = 0; i < this->imported_inlinable_functions_.size(); i++) |
| { |
| Named_object* no = this->imported_inlinable_functions_[i]; |
| no->func_declaration_value()->import_function_body(this, no); |
| } |
| |
| // There might be type definitions that involve expressions such as the |
| // array length. Make sure to lower these expressions as well. Otherwise, |
| // errors hidden within a type can introduce unexpected errors into later |
| // passes. |
| for (std::vector<Type*>::iterator p = this->verify_types_.begin(); |
| p != this->verify_types_.end(); |
| ++p) |
| Type::traverse(*p, &lower_parse_tree); |
| } |
| |
| // Lower a block. |
| |
| void |
| Gogo::lower_block(Named_object* function, Block* block) |
| { |
| Lower_parse_tree lower_parse_tree(this, function); |
| block->traverse(&lower_parse_tree); |
| } |
| |
| // Lower an expression. INSERTER may be NULL, in which case the |
| // expression had better not need to create any temporaries. |
| |
| void |
| Gogo::lower_expression(Named_object* function, Statement_inserter* inserter, |
| Expression** pexpr) |
| { |
| Lower_parse_tree lower_parse_tree(this, function); |
| if (inserter != NULL) |
| lower_parse_tree.set_inserter(inserter); |
| 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) |
| { |
| go_assert(no->is_const()); |
| Lower_parse_tree lower(this, NULL); |
| lower.constant(no, false); |
| } |
| |
| // Make implicit type conversions explicit. Currently only does for |
| // interface conversions, so the escape analysis can see them and |
| // optimize. |
| |
| class Add_conversions : public Traverse |
| { |
| public: |
| Add_conversions() |
| : Traverse(traverse_statements |
| | traverse_expressions) |
| { } |
| |
| int |
| statement(Block*, size_t* pindex, Statement*); |
| |
| int |
| expression(Expression**); |
| }; |
| |
| // Add explicit conversions in a statement. |
| |
| int |
| Add_conversions::statement(Block*, size_t*, Statement* sorig) |
| { |
| sorig->add_conversions(); |
| return TRAVERSE_CONTINUE; |
| } |
| |
| // Add explicit conversions in an expression. |
| |
| int |
| Add_conversions::expression(Expression** pexpr) |
| { |
| (*pexpr)->add_conversions(); |
| return TRAVERSE_CONTINUE; |
| } |
| |
| void |
| Gogo::add_conversions() |
| { |
| Add_conversions add_conversions; |
| this->traverse(&add_conversions); |
| } |
| |
| void |
| Gogo::add_conversions_in_block(Block *b) |
| { |
| Add_conversions add_conversions; |
| b->traverse(&add_conversions); |
| } |
| |
| // Traversal class for simple deadcode elimination. |
| |
| class Remove_deadcode : public Traverse |
| { |
| public: |
| Remove_deadcode() |
| : Traverse(traverse_statements |
| | traverse_expressions) |
| { } |
| |
| int |
| statement(Block*, size_t* pindex, Statement*); |
| |
| int |
| expression(Expression**); |
| }; |
| |
| // Remove deadcode in a statement. |
| |
| int |
| Remove_deadcode::statement(Block* block, size_t* pindex, Statement* sorig) |
| { |
| Location loc = sorig->location(); |
| If_statement* ifs = sorig->if_statement(); |
| if (ifs != NULL) |
| { |
| // Remove the dead branch of an if statement. |
| bool bval; |
| if (ifs->condition()->boolean_constant_value(&bval)) |
| { |
| Statement* s; |
| if (bval) |
| s = Statement::make_block_statement(ifs->then_block(), |
| loc); |
| else |
| if (ifs->else_block() != NULL) |
| s = Statement::make_block_statement(ifs->else_block(), |
| loc); |
| else |
| // Make a dummy statement. |
| s = Statement::make_statement(Expression::make_boolean(false, loc), |
| true); |
| |
| block->replace_statement(*pindex, s); |
| } |
| } |
| return TRAVERSE_CONTINUE; |
| } |
| |
| // Remove deadcode in an expression. |
| |
| int |
| Remove_deadcode::expression(Expression** pexpr) |
| { |
| // Discard the right arm of a shortcut expression of constant value. |
| Binary_expression* be = (*pexpr)->binary_expression(); |
| bool bval; |
| if (be != NULL |
| && be->boolean_constant_value(&bval) |
| && (be->op() == OPERATOR_ANDAND |
| || be->op() == OPERATOR_OROR)) |
| { |
| *pexpr = Expression::make_boolean(bval, be->location()); |
| Type_context context(NULL, false); |
| (*pexpr)->determine_type(&context); |
| } |
| return TRAVERSE_CONTINUE; |
| } |
| |
| // Remove deadcode. |
| |
| void |
| Gogo::remove_deadcode() |
| { |
| Remove_deadcode remove_deadcode; |
| this->traverse(&remove_deadcode); |
| } |
| |
| // Traverse the tree to create function descriptors as needed. |
| |
| class Create_function_descriptors : public Traverse |
| { |
| public: |
| Create_function_descriptors(Gogo* gogo) |
| : Traverse(traverse_functions | traverse_expressions), |
| gogo_(gogo) |
| { } |
| |
| int |
| function(Named_object*); |
| |
| int |
| expression(Expression**); |
| |
| private: |
| Gogo* gogo_; |
| }; |
| |
| // Create a descriptor for every top-level exported function and every |
| // function referenced by an inline function. |
| |
| int |
| Create_function_descriptors::function(Named_object* no) |
| { |
| if (no->is_function() |
| && no->func_value()->enclosing() == NULL |
| && !no->func_value()->is_method() |
| && ((!Gogo::is_hidden_name(no->name()) |
| && !Gogo::is_thunk(no)) |
| || no->func_value()->is_referenced_by_inline())) |
| no->func_value()->descriptor(this->gogo_, no); |
| |
| return TRAVERSE_CONTINUE; |
| } |
| |
| // If we see a function referenced in any way other than calling it, |
| // create a descriptor for it. |
| |
| int |
| Create_function_descriptors::expression(Expression** pexpr) |
| { |
| Expression* expr = *pexpr; |
| |
| Func_expression* fe = expr->func_expression(); |
| if (fe != NULL) |
| { |
| // We would not get here for a call to this function, so this is |
| // a reference to a function other than calling it. We need a |
| // descriptor. |
| if (fe->closure() != NULL) |
| return TRAVERSE_CONTINUE; |
| Named_object* no = fe->named_object(); |
| if (no->is_function() && !no->func_value()->is_method()) |
| no->func_value()->descriptor(this->gogo_, no); |
| else if (no->is_function_declaration() |
| && !no->func_declaration_value()->type()->is_method() |
| && !Linemap::is_predeclared_location(no->location())) |
| no->func_declaration_value()->descriptor(this->gogo_, no); |
| return TRAVERSE_CONTINUE; |
| } |
| |
| Bound_method_expression* bme = expr->bound_method_expression(); |
| if (bme != NULL) |
| { |
| // We would not get here for a call to this method, so this is a |
| // method value. We need to create a thunk. |
| Bound_method_expression::create_thunk(this->gogo_, bme->method(), |
| bme->function()); |
| return TRAVERSE_CONTINUE; |
| } |
| |
| Interface_field_reference_expression* ifre = |
| expr->interface_field_reference_expression(); |
| if (ifre != NULL) |
| { |
| // We would not get here for a call to this interface method, so |
| // this is a method value. We need to create a thunk. |
| Interface_type* type = ifre->expr()->type()->interface_type(); |
| if (type != NULL) |
| Interface_field_reference_expression::create_thunk(this->gogo_, type, |
| ifre->name()); |
| return TRAVERSE_CONTINUE; |
| } |
| |
| Call_expression* ce = expr->call_expression(); |
| if (ce != NULL) |
| { |
| Expression* fn = ce->fn(); |
| if (fn->func_expression() != NULL |
| || fn->bound_method_expression() != NULL |
| || fn->interface_field_reference_expression() != NULL) |
| { |
| // Traverse the arguments but not the function. |
| Expression_list* args = ce->args(); |
| if (args != NULL) |
| { |
| if (args->traverse(this) == TRAVERSE_EXIT) |
| return TRAVERSE_EXIT; |
| } |
| return TRAVERSE_SKIP_COMPONENTS; |
| } |
| } |
| |
| return TRAVERSE_CONTINUE; |
| } |
| |
| // Create function descriptors as needed. We need a function |
| // descriptor for all exported functions and for all functions that |
| // are referenced without being called. |
| |
| void |
| Gogo::create_function_descriptors() |
| { |
| // Create a function descriptor for any exported function that is |
| // declared in this package. This is so that we have a descriptor |
| // for functions written in assembly. Gather the descriptors first |
| // so that we don't add declarations while looping over them. |
| std::vector<Named_object*> fndecls; |
| Bindings* b = this->package_->bindings(); |
| for (Bindings::const_declarations_iterator p = b->begin_declarations(); |
| p != b->end_declarations(); |
| ++p) |
| { |
| Named_object* no = p->second; |
| if (no->is_function_declaration() |
| && !no->func_declaration_value()->type()->is_method() |
| && !Linemap::is_predeclared_location(no->location()) |
| && !Gogo::is_hidden_name(no->name())) |
| fndecls.push_back(no); |
| } |
| for (std::vector<Named_object*>::const_iterator p = fndecls.begin(); |
| p != fndecls.end(); |
| ++p) |
| (*p)->func_declaration_value()->descriptor(this, *p); |
| fndecls.clear(); |
| |
| Create_function_descriptors cfd(this); |
| this->traverse(&cfd); |
| } |
| |
| // 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: |
| { |
| Named_type* nt = t->named_type(); |
| |
| if (nt->is_alias()) |
| return TRAVERSE_CONTINUE; |
| |
| Type* rt = nt->real_type(); |
| if (rt->classification() != Type::TYPE_STRUCT) |
| { |
| // Finalize the methods of the real type first. |
| if (Type::traverse(rt, this) == TRAVERSE_EXIT) |
| return TRAVERSE_EXIT; |
| |
| // Finalize the methods of this type. |
| nt->finalize_methods(this->gogo_); |
| } |
| else |
| { |
| // We don't want to finalize the methods of a named struct |
| // type, as the methods should be attached to the named |
| // type, not the struct type. We just want to finalize |
| // the field types. |
| // |
| // It is possible that a field type refers indirectly to |
| // this type, such as via a field with function type with |
| // an argument or result whose type is this type. To |
| // avoid the cycle, first finalize the methods of any |
| // embedded types, which are the only types we need to |
| // know to finalize the methods of this type. |
| const Struct_field_list* fields = rt->struct_type()->fields(); |
| if (fields != NULL) |
| { |
| for (Struct_field_list::const_iterator pf = fields->begin(); |
| pf != fields->end(); |
| ++pf) |
| { |
| if (pf->is_anonymous()) |
| { |
| if (Type::traverse(pf->type(), this) == TRAVERSE_EXIT) |
| return TRAVERSE_EXIT; |
| } |
| } |
| } |
| |
| // Finalize the methods of this type. |
| nt->finalize_methods(this->gogo_); |
| |
| // Finalize all the struct fields. |
| if (rt->struct_type()->traverse_field_types(this) == TRAVERSE_EXIT) |
| return TRAVERSE_EXIT; |
| } |
| |
| // If this type is defined in a different package, then finalize the |
| // types of all the methods, since we won't see them otherwise. |
| if (nt->named_object()->package() != NULL && nt->has_any_methods()) |
| { |
| const Methods* methods = nt->methods(); |
| for (Methods::const_iterator p = methods->begin(); |
| p != methods->end(); |
| ++p) |
| { |
| if (Type::traverse(p->second->type(), this) == TRAVERSE_EXIT) |
| return TRAVERSE_EXIT; |
| } |
| } |
| |
| // Finalize the types of all methods that are declared but not |
| // defined, since we won't see the declarations otherwise. |
| if (nt->named_object()->package() == NULL |
| && nt->local_methods() != NULL) |
| { |
| const Bindings* methods = nt->local_methods(); |
| for (Bindings::const_declarations_iterator p = |
| methods->begin_declarations(); |
| p != methods->end_declarations(); |
| p++) |
| { |
| if (p->second->is_function_declaration()) |
| { |
| Type* mt = p->second->func_declaration_value()->type(); |
| if (Type::traverse(mt, this) == TRAVERSE_EXIT) |
| return TRAVERSE_EXIT; |
| } |
| } |
| } |
| |
| return TRAVERSE_SKIP_COMPONENTS; |
| } |
| |
| case Type::TYPE_STRUCT: |
| // Traverse the field types first in case there is an embedded |
| // field with methods that the struct should inherit. |
| if (t->struct_type()->traverse_field_types(this) == TRAVERSE_EXIT) |
| return TRAVERSE_EXIT; |
| t->struct_type()->finalize_methods(this->gogo_); |
| return TRAVERSE_SKIP_COMPONENTS; |
| |
| 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); |
| } |
| |
| // Finalize the method list for a type. This is called when a type is |
| // parsed for an inlined function body, which happens after the |
| // finalize_methods pass. |
| |
| void |
| Gogo::finalize_methods_for_type(Type* type) |
| { |
| Finalize_methods finalize(this); |
| Type::traverse(type, &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_functions |
| | traverse_statements |
| | traverse_expressions), |
| gogo_(gogo) |
| { } |
| |
| 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_; |
| }; |
| |
| // 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(); |
| |
| // Give error if variable type is not defined. |
| var->type()->base(); |
| |
| Expression* init = var->init(); |
| std::string reason; |
| if (init != NULL |
| && !Type::are_assignable(var->type(), init->type(), &reason)) |
| { |
| if (reason.empty()) |
| go_error_at(var->location(), "incompatible type in initialization"); |
| else |
| go_error_at(var->location(), |
| "incompatible type in initialization (%s)", |
| reason.c_str()); |
| init = Expression::make_error(named_object->location()); |
| var->clear_init(); |
| } |
| else if (init != NULL |
| && init->func_expression() != NULL) |
| { |
| Named_object* no = init->func_expression()->named_object(); |
| Function_type* fntype; |
| if (no->is_function()) |
| fntype = no->func_value()->type(); |
| else if (no->is_function_declaration()) |
| fntype = no->func_declaration_value()->type(); |
| else |
| go_unreachable(); |
| |
| // Builtin functions cannot be used as function values for variable |
| // initialization. |
| if (fntype->is_builtin()) |
| { |
| go_error_at(init->location(), |
| "invalid use of special built-in function %qs; " |
| "must be called", |
| no->message_name().c_str()); |
| } |
| } |
| if (!var->is_used() |
| && !var->is_global() |
| && !var->is_parameter() |
| && !var->is_receiver() |
| && !var->type()->is_error() |
| && (init == NULL || !init->is_error_expression()) |
| && !Lex::is_invalid_identifier(named_object->name())) |
| go_error_at(var->location(), "%qs declared and not used", |
| named_object->message_name().c_str()); |
| } |
| 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_nil_type()) |
| go_error_at(constant->location(), "const initializer cannot be nil"); |
| else if (!ctype->is_error()) |
| go_error_at(constant->location(), "invalid constant type"); |
| constant->set_error(); |
| } |
| else if (!constant->expr()->is_constant()) |
| { |
| go_error_at(constant->expr()->location(), "expression is not constant"); |
| constant->set_error(); |
| } |
| else if (!Type::are_assignable(constant->type(), constant->expr()->type(), |
| NULL)) |
| { |
| go_error_at(constant->location(), |
| "initialization expression has wrong type"); |
| constant->set_error(); |
| } |
| return TRAVERSE_CONTINUE; |
| } |
| |
| // There are no types to check in a function, but this is where we |
| // issue warnings about labels which are defined but not referenced. |
| |
| int |
| Check_types_traverse::function(Named_object* no) |
| { |
| no->func_value()->check_labels(); |
| 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); |
| |
| Bindings* bindings = this->current_bindings(); |
| for (Bindings::const_declarations_iterator p = bindings->begin_declarations(); |
| p != bindings->end_declarations(); |
| ++p) |
| { |
| // Also check the types in a function declaration's signature. |
| Named_object* no = p->second; |
| if (no->is_function_declaration()) |
| no->func_declaration_value()->check_types(); |
| } |
| } |
| |
| // 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 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) |
| { |
| Binary_expression* binexp = (*expression_pointer)->binary_expression(); |
| if (binexp != NULL |
| && (binexp->op() == OPERATOR_ANDAND || binexp->op() == OPERATOR_OROR)) |
| { |
| // Shortcut expressions may potentially have side effects which need |
| // to be ordered, so add them to the list. |
| // We don't order its subexpressions here since they may be evaluated |
| // conditionally. This is handled in remove_shortcuts. |
| this->exprs_.push_back(expression_pointer); |
| return TRAVERSE_SKIP_COMPONENTS; |
| } |
| |
| // 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* stmt) |
| { |
| // 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 (stmt->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 = stmt->variable_declaration_statement(); |
| Expression* init = NULL; |
| Expression* orig_init = NULL; |
| if (vds == NULL) |
| stmt->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); |
| } |
| |
| size_t c = find_eval_ordering.size(); |
| if (c == 0) |
| return TRAVERSE_CONTINUE; |
| |
| // If there is only one expression with a side-effect, we can |
| // usually leave it in place. |
| if (c == 1) |
| { |
| switch (stmt->classification()) |
| { |
| case Statement::STATEMENT_ASSIGNMENT: |
| // For an assignment statement, we need to evaluate an |
| // expression on the right hand side before we evaluate any |
| // index expression on the left hand side, so for that case |
| // we always move the expression. Otherwise we mishandle |
| // m[0] = len(m) where m is a map. |
| break; |
| |
| case Statement::STATEMENT_EXPRESSION: |
| { |
| // If this is a call statement that doesn't return any |
| // values, it will not have been counted as a value to |
| // move. We need to move any subexpressions in case they |
| // are themselves call statements that require passing a |
| // closure. |
| Expression* expr = stmt->expression_statement()->expr(); |
| if (expr->call_expression() != NULL |
| && expr->call_expression()->result_count() == 0) |
| break; |
| return TRAVERSE_CONTINUE; |
| } |
| |
| default: |
| // We can leave the expression in place. |
| return TRAVERSE_CONTINUE; |
| } |
| } |
| |
| bool is_thunk = stmt->thunk_statement() != NULL; |
| Expression_statement* es = stmt->expression_statement(); |
| for (Find_eval_ordering::const_iterator p = find_eval_ordering.begin(); |
| p != find_eval_ordering.end(); |
| ++p) |
| { |
| Expression** pexpr = *p; |
| |
| // 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; |
| |
| Location loc = (*pexpr)->location(); |
| Statement* s; |
| if ((*pexpr)->call_expression() == NULL |
| || (*pexpr)->call_expression()->result_count() < 2) |
| { |
| Temporary_statement* ts = Statement::make_temporary(NULL, *pexpr, |
| loc); |
| s = ts; |
| *pexpr = Expression::make_temporary_reference(ts, loc); |
| } |
| else |
| { |
| // A call expression which returns multiple results needs to |
| // be handled specially. We can't create a temporary |
| // because there is no type to give it. Any actual uses of |
| // the values will be done via Call_result_expressions. |
| // |
| // Since a given call expression can be shared by multiple |
| // Call_result_expressions, avoid hoisting the call the |
| // second time we see it here. In addition, don't try to |
| // hoist the top-level multi-return call in the statement, |
| // since doing this would result a tree with more than one copy |
| // of the call. |
| if (this->remember_expression(*pexpr)) |
| s = NULL; |
| else if (es != NULL && *pexpr == es->expr()) |
| s = NULL; |
| else |
| s = Statement::make_statement(*pexpr, true); |
| } |
| |
| if (s != NULL) |
| { |
| block->insert_statement_before(*pindex, s); |
| ++*pindex; |
| } |
| } |
| |
| 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; |
| 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_SKIP_COMPONENTS; |
| } |
| |
| Expression* orig_init = init; |
| |
| for (Find_eval_ordering::const_iterator p = find_eval_ordering.begin(); |
| p != find_eval_ordering.end(); |
| ++p) |
| { |
| Expression** pexpr = *p; |
| Location loc = (*pexpr)->location(); |
| Statement* s; |
| if ((*pexpr)->call_expression() == NULL |
| || (*pexpr)->call_expression()->result_count() < 2) |
| { |
| Temporary_statement* ts = Statement::make_temporary(NULL, *pexpr, |
| loc); |
| s = ts; |
| *pexpr = Expression::make_temporary_reference(ts, loc); |
| } |
| else |
| { |
| // A call expression which returns multiple results needs to |
| // be handled specially. |
| s = Statement::make_statement(*pexpr, true); |
| } |
| var->add_preinit_statement(this->gogo_, s); |
| } |
| |
| if (init != orig_init) |
| var->set_init(init); |
| |
| 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); |
| } |
| |
| // Order evaluations in a block. |
| |
| void |
| Gogo::order_block(Block* block) |
| { |
| Order_eval order_eval(this); |
| block->traverse(&order_eval); |
| } |
| |
| // 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; |
| go_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(); |
| Location loc = shortcut->location(); |
| |
| Block* retblock = new Block(enclosing, loc); |
| retblock->set_end_location(loc); |
| |
| Temporary_statement* ts = Statement::make_temporary(shortcut->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. |
| // LEFT and RIGHT were skipped in the top level |
| // Gogo::order_evaluations. We need to order their |
| // components first. |
| Order_eval order_eval(this->gogo_); |
| retblock->traverse(&order_eval); |
| 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); |
| } |
| |
| // Turn shortcut operators into explicit if statements in a block. |
| |
| void |
| Gogo::remove_shortcuts_in_block(Block* block) |
| { |
| Shortcuts shortcuts(this); |
| block->traverse(&shortcuts); |
| } |
| |
| // Traversal to flatten parse tree after order of evaluation rules are applied. |
| |
| class Flatten : public Traverse |
| { |
| public: |
| Flatten(Gogo* gogo, Named_object* function) |
| : Traverse(traverse_variables |
| | traverse_functions |
| | traverse_statements |
| | traverse_expressions), |
| gogo_(gogo), function_(function), inserter_() |
| { } |
| |
| void |
| set_inserter(const Statement_inserter* inserter) |
| { this->inserter_ = *inserter; } |
| |
| int |
| variable(Named_object*); |
| |
| 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_; |
| // Current statement inserter for use by expressions. |
| Statement_inserter inserter_; |
| }; |
| |
| // Flatten variables. |
| |
| int |
| Flatten::variable(Named_object* no) |
| { |
| if (!no->is_variable()) |
| return TRAVERSE_CONTINUE; |
| |
| if (no->is_variable() && no->var_value()->is_global()) |
| { |
| // Global variables can have loops in their initialization |
| // expressions. This is handled in flatten_init_expression. |
| no->var_value()->flatten_init_expression(this->gogo_, this->function_, |
| &this->inserter_); |
| return TRAVERSE_CONTINUE; |
| } |
| |
| if (!no->var_value()->is_parameter() |
| && !no->var_value()->is_receiver() |
| && !no->var_value()->is_closure() |
| && no->var_value()->is_non_escaping_address_taken() |
| && !no->var_value()->is_in_heap() |
| && no->var_value()->toplevel_decl() == NULL) |
| { |
| // Local variable that has address taken but not escape. |
| // It needs to be live beyond its lexical scope. So we |
| // create a top-level declaration for it. |
| // No need to do it if it is already in the top level. |
| Block* top_block = function_->func_value()->block(); |
| if (top_block->bindings()->lookup_local(no->name()) != no) |
| { |
| Variable* var = no->var_value(); |
| Temporary_statement* ts = |
| Statement::make_temporary(var->type(), NULL, var->location()); |
| ts->set_is_address_taken(); |
| top_block->add_statement_at_front(ts); |
| var->set_toplevel_decl(ts); |
| } |
| } |
| |
| go_assert(!no->var_value()->has_pre_init()); |
| |
| return TRAVERSE_SKIP_COMPONENTS; |
| } |
| |
| // Flatten the body of a function. Record the function while flattening it, |
| // so that we can pass it down when flattening an expression. |
| |
| int |
| Flatten::function(Named_object* no) |
| { |
| go_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; |
| } |
| |
| // Flatten statement parse trees. |
| |
| int |
| Flatten::statement(Block* block, size_t* pindex, Statement* sorig) |
| { |
| // Because we explicitly traverse the statement's contents |
| // ourselves, we want to skip block statements here. There is |
| // nothing to flatten in a block statement. |
| if (sorig->is_block_statement()) |
| return TRAVERSE_CONTINUE; |
| |
| Statement_inserter hold_inserter(this->inserter_); |
| this->inserter_ = Statement_inserter(block, pindex); |
| |
| // Flatten the expressions first. |
| int t = sorig->traverse_contents(this); |
| if (t == TRAVERSE_EXIT) |
| { |
| this->inserter_ = hold_inserter; |
| return t; |
| } |
| |
| // Keep flattening until nothing changes. |
| Statement* s = sorig; |
| while (true) |
| { |
| Statement* snew = s->flatten(this->gogo_, this->function_, block, |
| &this->inserter_); |
| if (snew == s) |
| break; |
| s = snew; |
| t = s->traverse_contents(this); |
| if (t == TRAVERSE_EXIT) |
| { |
| this->inserter_ = hold_inserter; |
| return t; |
| } |
| } |
| |
| if (s != sorig) |
| block->replace_statement(*pindex, s); |
| |
| this->inserter_ = hold_inserter; |
| return TRAVERSE_SKIP_COMPONENTS; |
| } |
| |
| // Flatten expression parse trees. |
| |
| int |
| Flatten::expression(Expression** pexpr) |
| { |
| // Keep flattening until nothing changes. |
| while (true) |
| { |
| Expression* e = *pexpr; |
| if (e->traverse_subexpressions(this) == TRAVERSE_EXIT) |
| return TRAVERSE_EXIT; |
| |
| Expression* enew = e->flatten(this->gogo_, this->function_, |
| &this->inserter_); |
| if (enew == e) |
| break; |
| *pexpr = enew; |
| } |
| return TRAVERSE_SKIP_COMPONENTS; |
| } |
| |
| // Flatten a block. |
| |
| void |
| Gogo::flatten_block(Named_object* function, Block* block) |
| { |
| Flatten flatten(this, function); |
| block->traverse(&flatten); |
| } |
| |
| // Flatten an expression. INSERTER may be NULL, in which case the |
| // expression had better not need to create any temporaries. |
| |
| void |
| Gogo::flatten_expression(Named_object* function, Statement_inserter* inserter, |
| Expression** pexpr) |
| { |
| Flatten flatten(this, function); |
| if (inserter != NULL) |
| flatten.set_inserter(inserter); |
| flatten.expression(pexpr); |
| } |
| |
| void |
| Gogo::flatten() |
| { |
| Flatten flatten(this, NULL); |
| this->traverse(&flatten); |
| } |
| |
| // 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(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_; |
| 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(); |
| |
| Type* rtype = NULL; |
| if (orig_fntype->is_method()) |
| rtype = orig_fntype->receiver()->type(); |
| std::string name(gogo->recover_thunk_name(orig_no->name(), rtype)); |
| 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()) |
| { |
| // For the new function we are creating, declare a new parameter |
| // variable NEW_CLOSURE_NO and set it to be the closure variable |
| // of the function. This will be set to the closure value |
| // passed in by the caller. Then pass a reference to this |
| // variable as the closure value when calling the original |
| // function. In other words, simply pass the closure value |
| // through the thunk we are creating. |
| 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, |
| false, false, location); |
| new_var->set_is_closure(); |
| 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); |
| go_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)); |
| |
| gogo->start_block(location); |
| |
| Call_expression* call = Expression::make_call(fn, args, false, location); |
| |
| // Any varargs call has already been lowered. |
| call->set_varargs_are_lowered(); |
| |
| Statement* s = Statement::make_return_from_call(call, location); |
| s->determine_types(); |
| gogo->add_statement(s); |
| |
| Block* b = gogo->finish_block(location); |
| |
| gogo->add_block(b, location); |
| |
| // Lower the call in case it returns multiple results. |
| gogo->lower_block(new_no, b); |
| |
| 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); |
| go_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(); |
| |
| std::string new_receiver_name(orig_fntype->receiver()->name()); |
| if (new_receiver_name.empty()) |
| { |
| // Find the receiver. It was named "r.NNN" in |
| // Gogo::start_function. |
| for (Bindings::const_definitions_iterator p = |
| new_bindings->begin_definitions(); |
| p != new_bindings->end_definitions(); |
| ++p) |
| { |
| const std::string& pname((*p)->name()); |
| if (pname[0] == 'r' && pname[1] == '.') |
| { |
| new_receiver_name = pname; |
| break; |
| } |
| } |
| go_assert(!new_receiver_name.empty()); |
| } |
| Named_object* new_rec_no = new_bindings->lookup_local(new_receiver_name); |
| if (new_rec_no == NULL) |
| go_assert(saw_errors()); |
| else |
| { |
| go_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); |
| go_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_result_variables(); |
| orig_func->update_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 runtime.canrecover(__builtin_return_address()). |
| |
| Expression* |
| Build_recover_thunks::can_recover_arg(Location location) |
| { |
| Type* uintptr_type = Type::lookup_integer_type("uintptr"); |
| static Named_object* can_recover; |
| if (can_recover == NULL) |
| { |
| const Location bloc = Linemap::predeclared_location(); |
| Typed_identifier_list* param_types = new Typed_identifier_list(); |
| param_types->push_back(Typed_identifier("a", uintptr_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("runtime_canrecover", |
| NULL, fntype, bloc); |
| can_recover->func_declaration_value()->set_asm_name("runtime.canrecover"); |
| } |
| |
| Expression* zexpr = Expression::make_integer_ul(0, NULL, location); |
| Expression* call = Runtime::make_call(Runtime::BUILTIN_RETURN_ADDRESS, |
| location, 1, zexpr); |
| call = Expression::make_unsafe_cast(uintptr_type, call, location); |
| |
| Expression_list* args = new Expression_list(); |
| args->push_back(call); |
| |
| Expression* 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 runtime.canrecover(__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() |
| { |
| if (saw_errors()) |
| return; |
| |
| 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(); |
| Struct_type* st = type->struct_type(); |
| if (nt != NULL || st != NULL) |
| { |
| Translate_context context(this->gogo_, NULL, NULL, 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 (nt != NULL) |
| { |
| if ((*p)->implements_interface(Type::make_pointer_type(nt), |
| NULL)) |
| { |
| nt->interface_method_table(*p, false)->get_backend(&context); |
| nt->interface_method_table(*p, true)->get_backend(&context); |
| } |
| } |
| else |
| { |
| if ((*p)->implements_interface(Type::make_pointer_type(st), |
| NULL)) |
| { |
| st->interface_method_table(*p, false)->get_backend(&context); |
| st->interface_method_table(*p, true)->get_backend(&context); |
| } |
| } |
| } |
| } |
| return TRAVERSE_CONTINUE; |
| } |
| |
| // Return an expression which allocates memory to hold values of type TYPE. |
| |
| Expression* |
| Gogo::allocate_memory(Type* type, Location location) |
| { |
| Expression* td = Expression::make_type_descriptor(type, location); |
| return Runtime::make_call(Runtime::NEW, location, 1, td); |
| } |
| |
| // 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()) |
| go_error_at(func->block()->end_location(), |
| "missing return at end of function"); |
| |
| return TRAVERSE_CONTINUE; |
| } |
| |
| // Check return statements. |
| |
| void |
| Gogo::check_return_statements() |
| { |
| Check_return_statements_traverse traverse; |
| this->traverse(&traverse); |
| } |
| |
| // Traversal class to decide whether a function body is less than the |
| // inlining budget. This adjusts *available as it goes, and stops the |
| // traversal if it goes negative. |
| |
| class Inline_within_budget : public Traverse |
| { |
| public: |
| Inline_within_budget(int* available) |
| : Traverse(traverse_statements |
| | traverse_expressions), |
| available_(available) |
| { } |
| |
| int |
| statement(Block*, size_t*, Statement*); |
| |
| int |
| expression(Expression**); |
| |
| private: |
| // Pointer to remaining budget. |
| int* available_; |
| }; |
| |
| // Adjust the budget for the inlining cost of a statement. |
| |
| int |
| Inline_within_budget::statement(Block*, size_t*, Statement* s) |
| { |
| if (*this->available_ < 0) |
| return TRAVERSE_EXIT; |
| *this->available_ -= s->inlining_cost(); |
| return TRAVERSE_CONTINUE; |
| } |
| |
| // Adjust the budget for the inlining cost of an expression. |
| |
| int |
| Inline_within_budget::expression(Expression** pexpr) |
| { |
| if (*this->available_ < 0) |
| return TRAVERSE_EXIT; |
| *this->available_ -= (*pexpr)->inlining_cost(); |
| return TRAVERSE_CONTINUE; |
| } |
| |
| // Traversal class to find functions whose body should be exported for |
| // inlining by other packages. |
| |
| class Mark_inline_candidates : public Traverse |
| { |
| public: |
| Mark_inline_candidates(Unordered_set(Named_object*)* marked) |
| : Traverse(traverse_functions |
| | traverse_types), |
| marked_functions_(marked) |
| { } |
| |
| int |
| function(Named_object*); |
| |
| int |
| type(Type*); |
| |
| private: |
| // We traverse the function body trying to determine how expensive |
| // it is for inlining. We start with a budget, and decrease that |
| // budget for each statement and expression. If the budget goes |
| // negative, we do not export the function body. The value of this |
| // budget is a heuristic. In the usual GCC spirit, we could |
| // consider setting this via a command line option. |
| const int budget_heuristic = 80; |
| |
| // Set of named objects that are marked as inline candidates. |
| Unordered_set(Named_object*)* marked_functions_; |
| }; |
| |
| // Mark a function if it is an inline candidate. |
| |
| int |
| Mark_inline_candidates::function(Named_object* no) |
| { |
| Function* func = no->func_value(); |
| if ((func->pragmas() & GOPRAGMA_NOINLINE) != 0) |
| return TRAVERSE_CONTINUE; |
| int budget = budget_heuristic; |
| Inline_within_budget iwb(&budget); |
| func->block()->traverse(&iwb); |
| if (budget >= 0) |
| { |
| func->set_export_for_inlining(); |
| this->marked_functions_->insert(no); |
| } |
| return TRAVERSE_CONTINUE; |
| } |
| |
| // Mark methods if they are inline candidates. |
| |
| int |
| Mark_inline_candidates::type(Type* t) |
| { |
| Named_type* nt = t->named_type(); |
| if (nt == NULL || nt->is_alias()) |
| return TRAVERSE_CONTINUE; |
| const Bindings* methods = nt->local_methods(); |
| if (methods == NULL) |
| return TRAVERSE_CONTINUE; |
| for (Bindings::const_definitions_iterator p = methods->begin_definitions(); |
| p != methods->end_definitions(); |
| ++p) |
| { |
| Named_object* no = *p; |
| go_assert(no->is_function()); |
| Function *func = no->func_value(); |
| if ((func->pragmas() & GOPRAGMA_NOINLINE) != 0) |
| continue; |
| int budget = budget_heuristic; |
| Inline_within_budget iwb(&budget); |
| func->block()->traverse(&iwb); |
| if (budget >= 0) |
| { |
| func->set_export_for_inlining(); |
| this->marked_functions_->insert(no); |
| } |
| } |
| return TRAVERSE_CONTINUE; |
| } |
| |
| // Export identifiers as requested. |
| |
| void |
| Gogo::do_exports() |
| { |
| if (saw_errors()) |
| return; |
| |
| // Mark any functions whose body should be exported for inlining by |
| // other packages. |
| Unordered_set(Named_object*) marked_functions; |
| Mark_inline_candidates mic(&marked_functions); |
| this->traverse(&mic); |
| |
| // For now we always stream to a section. Later we may want to |
| // support streaming to a separate file. |
| Stream_to_section stream(this->backend()); |
| |
| // Write out either the prefix or pkgpath depending on how we were |
| // invoked. |
| std::string prefix; |
| std::string pkgpath; |
| if (this->pkgpath_from_option_) |
| pkgpath = this->pkgpath_; |
| else if (this->prefix_from_option_) |
| prefix = this->prefix_; |
| else if (this->is_main_package()) |
| pkgpath = "main"; |
| else |
| prefix = "go"; |
| |
| std::string init_fn_name; |
| if (this->is_main_package()) |
| init_fn_name = ""; |
| else if (this->need_init_fn_) |
| init_fn_name = this->get_init_fn_name(); |
| else |
| init_fn_name = this->dummy_init_fn_name(); |
| |
| Export exp(&stream); |
| exp.register_builtin_types(this); |
| exp.export_globals(this->package_name(), |
| prefix, |
| pkgpath, |
| this->packages_, |
| this->imports_, |
| init_fn_name, |
| this->imported_init_fns_, |
| this->package_->bindings(), |
| &marked_functions); |
| |
| if (!this->c_header_.empty() && !saw_errors()) |
| this->write_c_header(); |
| } |
| |
| // Write the top level named struct types in C format to a C header |
| // file. This is used when building the runtime package, to share |
| // struct definitions between C and Go. |
| |
| void |
| Gogo::write_c_header() |
| { |
| std::ofstream out; |
| out.open(this->c_header_.c_str()); |
| if (out.fail()) |
| { |
| go_error_at(Linemap::unknown_location(), |
| "cannot open %s: %m", this->c_header_.c_str()); |
| return; |
| } |
| |
| std::list<Named_object*> types; |
| Bindings* top = this->package_->bindings(); |
| for (Bindings::const_definitions_iterator p = top->begin_definitions(); |
| p != top->end_definitions(); |
| ++p) |
| { |
| Named_object* no = *p; |
| |
| // Skip names that start with underscore followed by something |
| // other than an uppercase letter, as when compiling the runtime |
| // package they are mostly types defined by mkrsysinfo.sh based |
| // on the C system header files. We don't need to translate |
| // types to C and back to Go. But do accept the special cases |
| // _defer, _panic, and _type. |
| std::string name = Gogo::unpack_hidden_name(no->name()); |
| if (name[0] == '_' |
| && (name[1] < 'A' || name[1] > 'Z') |
| && (name != "_defer" && name != "_panic" && name != "_type")) |
| continue; |
| |
| if (no->is_type() && no->type_value()->struct_type() != NULL) |
| types.push_back(no); |
| if (no->is_const() |
| && no->const_value()->type()->integer_type() != NULL |
| && !no->const_value()->is_sink()) |
| { |
| Numeric_constant nc; |
| unsigned long val; |
| if (no->const_value()->expr()->numeric_constant_value(&nc) |
| && nc.to_unsigned_long(&val) == Numeric_constant::NC_UL_VALID) |
| { |
| out << "#define " << no->message_name() << ' ' << val |
| << std::endl; |
| } |
| } |
| } |
| |
| std::vector<const Named_object*> written; |
| int loop = 0; |
| while (!types.empty()) |
| { |
| Named_object* no = types.front(); |
| types.pop_front(); |
| |
| std::vector<const Named_object*> requires; |
| std::vector<const Named_object*> declare; |
| if (!no->type_value()->struct_type()->can_write_to_c_header(&requires, |
| &declare)) |
| continue; |
| |
| bool ok = true; |
| for (std::vector<const Named_object*>::const_iterator pr |
| = requires.begin(); |
| pr != requires.end() && ok; |
| ++pr) |
| { |
| for (std::list<Named_object*>::const_iterator pt = types.begin(); |
| pt != types.end() && ok; |
| ++pt) |
| if (*pr == *pt) |
| ok = false; |
| } |
| if (!ok) |
| { |
| ++loop; |
| if (loop > 10000) |
| { |
| // This should be impossible since the code parsed and |
| // type checked. |
| go_unreachable(); |
| } |
| |
| types.push_back(no); |
| continue; |
| } |
| |
| for (std::vector<const Named_object*>::const_iterator pd |
| = declare.begin(); |
| pd != declare.end(); |
| ++pd) |
| { |
| if (*pd == no) |
| continue; |
| |
| std::vector<const Named_object*> drequires; |
| std::vector<const Named_object*> ddeclare; |
| if (!(*pd)->type_value()->struct_type()-> |
| can_write_to_c_header(&drequires, &ddeclare)) |
| continue; |
| |
| bool done = false; |
| for (std::vector<const Named_object*>::const_iterator pw |
| = written.begin(); |
| pw != written.end(); |
| ++pw) |
| { |
| if (*pw == *pd) |
| { |
| done = true; |
| break; |
| } |
| } |
| if (!done) |
| { |
| out << std::endl; |
| out << "struct " << (*pd)->message_name() << ";" << std::endl; |
| written.push_back(*pd); |
| } |
| } |
| |
| out << std::endl; |
| out << "struct " << no->message_name() << " {" << std::endl; |
| no->type_value()->struct_type()->write_to_c_header(out); |
| out << "};" << std::endl; |
| written.push_back(no); |
| } |
| |
| out.close(); |
| if (out.fail()) |
| go_error_at(Linemap::unknown_location(), |
| "error writing to %s: %m", this->c_header_.c_str()); |
| } |
| |
| // 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(); |
| Expression::make_func_descriptor_type(); |
| Type::convert_builtin_named_types(this); |
| |
| Runtime::convert_types(this); |
| |
| this->named_types_are_converted_ = true; |
| |
| Type::finish_pointer_types(this); |
| } |
| |
| // 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); |
| } |
| } |
| |
| void |
| debug_go_gogo(Gogo* gogo) |
| { |
| if (gogo != NULL) |
| gogo->debug_dump(); |
| } |
| |
| void |
| Gogo::debug_dump() |
| { |
| std::cerr << "Packages:\n"; |
| for (Packages::const_iterator p = this->packages_.begin(); |
| p != this->packages_.end(); |
| ++p) |
| { |
| const char *tag = " "; |
| if (p->second == this->package_) |
| tag = "* "; |
| std::cerr << tag << "'" << p->first << "' " |
| << p->second->pkgpath() << " " << ((void*)p->second) << "\n"; |
| } |
| } |
| |
| // Class Function. |
| |
| Function::Function(Function_type* type, Named_object* enclosing, Block* block, |
| Location location) |
| : type_(type), enclosing_(enclosing), results_(NULL), |
| closure_var_(NULL), block_(block), location_(location), labels_(), |
| local_type_count_(0), descriptor_(NULL), fndecl_(NULL), defer_stack_(NULL), |
| pragmas_(0), nested_functions_(0), is_sink_(false), |
| results_are_named_(false), is_unnamed_type_stub_method_(false), |
| calls_recover_(false), is_recover_thunk_(false), has_recover_thunk_(false), |
| calls_defer_retaddr_(false), is_type_specific_function_(false), |
| in_unique_section_(false), export_for_inlining_(false), |
| is_inline_only_(false), is_referenced_by_inline_(false), |
| is_exported_by_linkname_(false) |
| { |
| } |
| |
| // Create the named result variables. |
| |
| void |
| Function::create_result_variables(Gogo* gogo) |
| { |
| const Typed_identifier_list* results = this->type_->results(); |
| if (results == NULL || results->empty()) |
| return; |
| |
| if (!results->front().name().empty()) |
| this->results_are_named_ = true; |
| |
| this->results_ = new Results(); |
| this->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 (name.empty() || Gogo::is_sink_name(name)) |
| { |
| static int result_counter; |
| char buf[100]; |
| snprintf(buf, sizeof buf, "$ret%d", result_counter); |
| ++result_counter; |
| name = gogo->pack_hidden_name(buf, false); |
| } |
| Result_variable* result = new Result_variable(p->type(), this, index, |
| p->location()); |
| Named_object* no = block->bindings()->add_result_variable(name, result); |
| if (no->is_result_variable()) |
| this->results_->push_back(no); |
| else |
| { |
| static int dummy_result_count; |
| char buf[100]; |
| snprintf(buf, sizeof buf, "$dret%d", dummy_result_count); |
| ++dummy_result_count; |
| name = gogo->pack_hidden_name(buf, false); |
| no = block->bindings()->add_result_variable(name, result); |
| go_assert(no->is_result_variable()); |
| this->results_->push_back(no); |
| } |
| } |
| } |
| |
| // Update the named result variables when cloning a function which |
| // calls recover. |
| |
| void |
| Function::update_result_variables() |
| { |
| if (this->results_ == NULL) |
| return; |
| |
| for (Results::iterator p = this->results_->begin(); |
| p != this->results_->end(); |
| ++p) |
| (*p)->result_var_value()->set_function(this); |
| } |
| |
| // Whether this method should not be included in the type descriptor. |
| |
| bool |
| Function::nointerface() const |
| { |
| go_assert(this->is_method()); |
| return (this->pragmas_ & GOPRAGMA_NOINTERFACE) != 0; |
| } |
| |
| // Record that this method should not be included in the type |
| // descriptor. |
| |
| void |
| Function::set_nointerface() |
| { |
| this->pragmas_ |= GOPRAGMA_NOINTERFACE; |
| } |
| |
| // Return the closure variable, creating it if necessary. |
| |
| Named_object* |
| Function::closure_var() |
| { |
| if (this->closure_var_ == NULL) |
| { |
| go_assert(this->descriptor_ == NULL); |
| // We don't know the type of the variable yet. We add fields as |
| // we find them. |
| Location loc = this->type_->location(); |
| Struct_field_list* sfl = new Struct_field_list; |
| Struct_type* struct_type = Type::make_struct_type(sfl, loc); |
| struct_type->set_is_struct_incomparable(); |
| Variable* var = new Variable(Type::make_pointer_type(struct_type), |
| NULL, false, false, false, loc); |
| var->set_is_used(); |
| var->set_is_closure(); |
| 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(); |
| |
| // The first field of a closure is always a pointer to the function |
| // code. |
| Type* voidptr_type = Type::make_pointer_type(Type::make_void_type()); |
| st->push_field(Struct_field(Typed_identifier(".f", voidptr_type, |
| this->location_))); |
| |
| unsigned int index = 1; |
| 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(Gogo* gogo, const std::string& label_name, |
| Location location) |
| { |
| Label* lnull = NULL; |
| std::pair<Labels::iterator, bool> ins = |
| this->labels_.insert(std::make_pair(label_name, lnull)); |
| Label* label; |
| if (label_name == "_") |
| { |
| label = Label::create_dummy_label(); |
| if (ins.second) |
| ins.first->second = label; |
| } |
| else if (ins.second) |
| { |
| // This is a new label. |
| label = new Label(label_name); |
| ins.first->second = label; |
| } |
| else |
| { |
| // The label was already in the hash table. |
| label = ins.first->second; |
| if (label->is_defined()) |
| { |
| go_error_at(location, "label %qs already defined", |
| Gogo::message_name(label_name).c_str()); |
| go_inform(label->location(), "previous definition of %qs was here", |
| Gogo::message_name(label_name).c_str()); |
| return new Label(label_name); |
| } |
| } |
| |
| label->define(location, gogo->bindings_snapshot(location)); |
| |
| // Issue any errors appropriate for any previous goto's to this |
| // label. |
| const std::vector<Bindings_snapshot*>& refs(label->refs()); |
| for (std::vector<Bindings_snapshot*>::const_iterator p = refs.begin(); |
| p != refs.end(); |
| ++p) |
| (*p)->check_goto_to(gogo->current_block()); |
| label->clear_refs(); |
| |
| return label; |
| } |
| |
| // Add a reference to a label. |
| |
| Label* |
| Function::add_label_reference(Gogo* gogo, const std::string& label_name, |
| Location location, bool issue_goto_errors) |
| { |
| Label* lnull = NULL; |
| std::pair<Labels::iterator, bool> ins = |
| this->labels_.insert(std::make_pair(label_name, lnull)); |
| Label* label; |
| if (!ins.second) |
| { |
| // The label was already in the hash table. |
| label = ins.first->second; |
| } |
| else |
| { |
| go_assert(ins.first->second == NULL); |
| label = new Label(label_name); |
| ins.first->second = label; |
| } |
| |
| label->set_is_used(); |
| |
| if (issue_goto_errors) |
| { |
| Bindings_snapshot* snapshot = label->snapshot(); |
| if (snapshot != NULL) |
| snapshot->check_goto_from(gogo->current_block(), location); |
| else |
| label->add_snapshot_ref(gogo->bindings_snapshot(location)); |
| } |
| |
| return label; |
| } |
| |
| // Warn about labels that are defined but not used. |
| |
| void |
| Function::check_labels() const |
| { |
| for (Labels::const_iterator p = this->labels_.begin(); |
| p != this->labels_.end(); |
| p++) |
| { |
| Label* label = p->second; |
| if (!label->is_used()) |
| go_error_at(label->location(), "label %qs defined and not used", |
| Gogo::message_name(label->name()).c_str()); |
| } |
| } |
| |
| // 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) |
| { |
| go_assert(this->enclosing_ == x->enclosing_); |
| std::swap(this->results_, x->results_); |
| std::swap(this->closure_var_, x->closure_var_); |
| std::swap(this->block_, x->block_); |
| go_assert(this->location_ == x->location_); |
| go_assert(this->fndecl_ == NULL && x->fndecl_ == NULL); |
| go_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(); |
| } |
| |
| // Return the function descriptor, the value you get when you refer to |
| // the function in Go code without calling it. |
| |
| Expression* |
| Function::descriptor(Gogo*, Named_object* no) |
| { |
| go_assert(!this->is_method()); |
| go_assert(this->closure_var_ == NULL); |
| if (this->descriptor_ == NULL) |
| this->descriptor_ = Expression::make_func_descriptor(no); |
| return this->descriptor_; |
| } |
| |
| // Get a pointer to the variable representing the defer stack for this |
| // function, making it if necessary. The value of the variable is set |
| // by the runtime routines to true if the function is returning, |
| // rather than panicing through. A pointer to this variable is used |
| // as a marker for the functions on the defer stack associated with |
| // this function. A function-specific variable permits inlining a |
| // function which uses defer. |
| |
| Expression* |
| Function::defer_stack(Location location) |
| { |
| if (this->defer_stack_ == NULL) |
| { |
| Type* t = Type::lookup_bool_type(); |
| Expression* n = Expression::make_boolean(false, location); |
| this->defer_stack_ = Statement::make_temporary(t, n, location); |
| this->defer_stack_->set_is_address_taken(); |
| } |
| Expression* ref = Expression::make_temporary_reference(this->defer_stack_, |
| location); |
| return Expression::make_unary(OPERATOR_AND, ref, location); |
| } |
| |
| // Export the function. |
| |
| void |
| Function::export_func(Export* exp, const Named_object* no) const |
| { |
| Block* block = NULL; |
| if (this->export_for_inlining()) |
| block = this->block_; |
| Function::export_func_with_type(exp, no, this->type_, this->results_, |
| this->is_method() && this->nointerface(), |
| this->asm_name(), block, this->location_); |
| } |
| |
| // Export a function with a type. |
| |
| void |
| Function::export_func_with_type(Export* exp, const Named_object* no, |
| const Function_type* fntype, |
| Function::Results* result_vars, |
| bool nointerface, const std::string& asm_name, |
| Block* block, Location loc) |
| { |
| exp->write_c_string("func "); |
| |
| if (nointerface) |
| { |
| go_assert(fntype->is_method()); |
| exp->write_c_string("/*nointerface*/ "); |
| } |
| |
| if (!asm_name.empty()) |
| { |
| exp->write_c_string("/*asm "); |
| exp->write_string(asm_name); |
| exp->write_c_string(" */ "); |
| } |
| |
| if (fntype->is_method()) |
| { |
| exp->write_c_string("("); |
| const Typed_identifier* receiver = fntype->receiver(); |
| exp->write_name(receiver->name()); |
| exp->write_escape(receiver->note()); |
| exp->write_c_string(" "); |
| exp->write_type(receiver->type()); |
| exp->write_c_string(") "); |
| } |
| |
| if (no->package() != NULL && !fntype->is_method()) |
| { |
| char buf[50]; |
| snprintf(buf, sizeof buf, "<p%d>", exp->package_index(no->package())); |
| exp->write_c_string(buf); |
| } |
| |
| const std::string& name(no->name()); |
| if (!Gogo::is_hidden_name(name)) |
| exp->write_string(name); |
| else |
| { |
| exp->write_c_string("."); |
| exp->write_string(Gogo::unpack_hidden_name(name)); |
| } |
| |
| exp->write_c_string(" ("); |
| const Typed_identifier_list* parameters = fntype->parameters(); |
| if (parameters != NULL) |
| { |
| size_t i = 0; |
| bool is_varargs = fntype->is_varargs(); |
| bool first = true; |
| for (Typed_identifier_list::const_iterator p = parameters->begin(); |
| p != parameters->end(); |
| ++p, ++i) |
| { |
| if (first) |
| first = false; |
| else |
| exp->write_c_string(", "); |
| exp->write_name(p->name()); |
| exp->write_escape(p->note()); |
| 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* result_decls = fntype->results(); |
| if (result_decls != NULL) |
| { |
| if (result_decls->size() == 1 |
| && result_decls->begin()->name().empty() |
| && block == NULL) |
| { |
| exp->write_c_string(" "); |
| exp->write_type(result_decls->begin()->type()); |
| } |
| else |
| { |
| exp->write_c_string(" ("); |
| bool first = true; |
| Results::const_iterator pr; |
| if (result_vars != NULL) |
| pr = result_vars->begin(); |
| for (Typed_identifier_list::const_iterator pd = result_decls->begin(); |
| pd != result_decls->end(); |
| ++pd) |
| { |
| if (first) |
| first = false; |
| else |
| exp->write_c_string(", "); |
| // We only use pr->name, which may be artificial, if |
| // need it for inlining. |
| if (block == NULL || result_vars == NULL) |
| exp->write_name(pd->name()); |
| else |
| exp->write_name((*pr)->name()); |
| exp->write_escape(pd->note()); |
| exp->write_c_string(" "); |
| exp->write_type(pd->type()); |
| if (result_vars != NULL) |
| ++pr; |
| } |
| if (result_vars != NULL) |
| go_assert(pr == result_vars->end()); |
| exp->write_c_string(")"); |
| } |
| } |
| |
| if (block == NULL) |
| exp->write_c_string("\n"); |
| else |
| { |
| int indent = 1; |
| if (fntype->is_method()) |
| indent++; |
| |
| Export_function_body efb(exp, indent); |
| |
| efb.indent(); |
| efb.write_c_string("// "); |
| efb.write_string(Linemap::location_to_file(block->start_location())); |
| efb.write_char(':'); |
| char buf[100]; |
| snprintf(buf, sizeof buf, "%d", Linemap::location_to_line(loc)); |
| efb.write_c_string(buf); |
| efb.write_char('\n'); |
| block->export_block(&efb); |
| |
| const std::string& body(efb.body()); |
| |
| snprintf(buf, sizeof buf, " <inl:%lu>\n", |
| static_cast<unsigned long>(body.length())); |
| exp->write_c_string(buf); |
| |
| exp->write_string(body); |
| } |
| } |
| |
| // Import a function. |
| |
| bool |
| Function::import_func(Import* imp, std::string* pname, |
| Package** ppkg, bool* pis_exported, |
| Typed_identifier** preceiver, |
| Typed_identifier_list** pparameters, |
| Typed_identifier_list** presults, |
| bool* is_varargs, |
| bool* nointerface, |
| std::string* asm_name, |
| std::string* body) |
| { |
| imp->require_c_string("func "); |
| |
| *nointerface = false; |
| while (imp->match_c_string("/*")) |
| { |
| imp->advance(2); |
| if (imp->match_c_string("nointerface")) |
| { |
| imp->require_c_string("nointerface*/ "); |
| *nointerface = true; |
| } |
| else if (imp->match_c_string("asm")) |
| { |
| imp->require_c_string("asm "); |
| *asm_name = imp->read_identifier(); |
| imp->require_c_string(" */ "); |
| } |
| else |
| { |
| go_error_at(imp->location(), |
| "import error at %d: unrecognized function comment", |
| imp->pos()); |
| return false; |
| } |
| } |
| |
| if (*nointerface) |
| { |
| // Only a method can be nointerface. |
| go_assert(imp->peek_char() == '('); |
| } |
| |
| *preceiver = NULL; |
| if (imp->peek_char() == '(') |
| { |
| imp->require_c_string("("); |
| std::string name = imp->read_name(); |
| std::string escape_note = imp->read_escape(); |
| imp->require_c_string(" "); |
| Type* rtype = imp->read_type(); |
| *preceiver = new Typed_identifier(name, rtype, imp->location()); |
| (*preceiver)->set_note(escape_note); |
| imp->require_c_string(") "); |
| } |
| |
| if (!Import::read_qualified_identifier(imp, pname, ppkg, pis_exported)) |
| { |
| go_error_at(imp->location(), |
| "import error at %d: bad function name in export data", |
| imp->pos()); |
| return false; |
| } |
| |
| 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) |
| { |
| std::string name = imp->read_name(); |
| std::string escape_note = imp->read_escape(); |
| imp->require_c_string(" "); |
| |
| 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); |
| Typed_identifier t = Typed_identifier(name, ptype, imp->location()); |
| t.set_note(escape_note); |
| parameters->push_back(t); |
| if (imp->peek_char() != ',') |
| break; |
| go_assert(!*is_varargs); |
| imp->require_c_string(", "); |
| } |
| } |
| imp->require_c_string(")"); |
| *pparameters = parameters; |
| |
| Typed_identifier_list* results; |
| if (imp->peek_char() != ' ' || imp->match_c_string(" <inl")) |
| 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("", rtype, imp->location())); |
| } |
| else |
| { |
| imp->require_c_string("("); |
| while (true) |
| { |
| std::string name = imp->read_name(); |
| std::string note = imp->read_escape(); |
| imp->require_c_string(" "); |
| Type* rtype = imp->read_type(); |
| Typed_identifier t = Typed_identifier(name, rtype, |
| imp->location()); |
| t.set_note(note); |
| results->push_back(t); |
| if (imp->peek_char() != ',') |
| break; |
| imp->require_c_string(", "); |
| } |
| imp->require_c_string(")"); |
| } |
| } |
| *presults = results; |
| |
| if (!imp->match_c_string(" <inl:")) |
| { |
| imp->require_semicolon_if_old_version(); |
| imp->require_c_string("\n"); |
| body->clear(); |
| } |
| else |
| { |
| imp->require_c_string(" <inl:"); |
| std::string lenstr; |
| int c; |
| while (true) |
| { |
| c = imp->peek_char(); |
| if (c < '0' || c > '9') |
| break; |
| lenstr += c; |
| imp->get_char(); |
| } |
| imp->require_c_string(">\n"); |
| |
| errno = 0; |
| char* end; |
| long llen = strtol(lenstr.c_str(), &end, 10); |
| if (*end != '\0' |
| || llen < 0 |
| || (llen == LONG_MAX && errno == ERANGE)) |
| { |
| go_error_at(imp->location(), "invalid inline function length %s", |
| lenstr.c_str()); |
| return false; |
| } |
| |
| imp->read(static_cast<size_t>(llen), body); |
| } |
| |
| return true; |
| } |
| |
| // Get the backend name. |
| |
| void |
| Function::backend_name(Gogo* gogo, Named_object* no, Backend_name *bname) |
| { |
| if (!this->asm_name_.empty()) |
| bname->set_asm_name(this->asm_name_); |
| else if (no->package() == NULL && no->name() == gogo->get_init_fn_name()) |
| { |
| // These names appear in the export data and are used |
| // directly in the assembler code. If we change this here |
| // we need to change Gogo::init_imports. |
| bname->set_asm_name(no->name()); |
| } |
| else if (this->enclosing_ != NULL) |
| { |
| // Rewrite the nested name to use the enclosing function name. |
| // We don't do this earlier because we just store simple names |
| // in a Named_object, not Backend_names. |
| |
| // The name was set by nested_function_name, which always |
| // appends ..funcNNN. We want that to be our suffix. |
| size_t pos = no->name().find("..func"); |
| go_assert(pos != std::string::npos); |
| |
| Named_object* enclosing = this->enclosing_; |
| while (true) |
| { |
| Named_object* parent = enclosing->func_value()->enclosing(); |
| if (parent == NULL) |
| break; |
| enclosing = parent; |
| } |
| |
| Type* rtype = NULL; |
| if (enclosing->func_value()->type()->is_method()) |
| rtype = enclosing->func_value()->type()->receiver()->type(); |
| gogo->function_backend_name(enclosing->name(), enclosing->package(), |
| rtype, bname); |
| bname->append_suffix(no->name().substr(pos)); |
| } |
| else |
| { |
| Type* rtype = NULL; |
| if (this->type_->is_method()) |
| rtype = this->type_->receiver()->type(); |
| gogo->function_backend_name(no->name(), no->package(), rtype, bname); |
| } |
| } |
| |
| // Get the backend representation. |
| |
| Bfunction* |
| Function::get_or_make_decl(Gogo* gogo, Named_object* no) |
| { |
| if (this->fndecl_ == NULL) |
| { |
| unsigned int flags = 0; |
| if (no->package() != NULL) |
| { |
| // Functions defined in other packages must be visible. |
| flags |= Backend::function_is_visible; |
| } |
| else if (this->enclosing_ != NULL || Gogo::is_thunk(no)) |
| ; |
| else if (Gogo::unpack_hidden_name(no->name()) == "init" |
| && !this->type_->is_method()) |
| ; |
| else if (no->name() == gogo->get_init_fn_name()) |
| flags |= Backend::function_is_visible; |
| else if (Gogo::unpack_hidden_name(no->name()) == "main" |
| && gogo->is_main_package()) |
| flags |= Backend::function_is_visible; |
| // Methods have to be public even if they are hidden because |
| // they can be pulled into type descriptors when using |
| // anonymous fields. |
| else if (!Gogo::is_hidden_name(no->name()) |
| || this->type_->is_method()) |
| { |
| if (!this->is_unnamed_type_stub_method_) |
| flags |= Backend::function_is_visible; |
| } |
| |
| if (!this->asm_name_.empty()) |
| { |
| // If an assembler name is explicitly specified, there must |
| // be some reason to refer to the symbol from a different |
| // object file. |
| flags |= Backend::function_is_visible; |
| } |
| |
| // If an inline body refers to this function, then it |
| // needs to be visible in the symbol table. |
| if (this->is_referenced_by_inline_) |
| flags |= Backend::function_is_visible; |
| |
| // A go:linkname directive can be used to force a function to be |
| // visible. |
| if (this->is_exported_by_linkname_) |
| flags |= Backend::function_is_visible; |
| |
| // If a function calls the predeclared recover function, we |
| // can't inline it, because recover behaves differently in a |
| // function passed directly to defer. If this is a recover |
| // thunk that we built to test whether a function can be |
| // recovered, we can't inline it, because that will mess up |
| // our return address comparison. |
| bool is_inlinable = !(this->calls_recover_ || this->is_recover_thunk_); |
| |
| // If a function calls __go_set_defer_retaddr, then mark it as |
| // uninlinable. This prevents the GCC backend from splitting |
| // the function; splitting the function is a bad idea because we |
| // want the return address label to be in the same function as |
| // the call. |
| if (this->calls_defer_retaddr_) |
| is_inlinable = false; |
| |
| // Check the //go:noinline compiler directive. |
| if ((this->pragmas_ & GOPRAGMA_NOINLINE) != 0) |
| is_inlinable = false; |
| |
| if (is_inlinable) |
| flags |= Backend::function_is_inlinable; |
| |
| // If this is a thunk created to call a function which calls |
| // the predeclared recover function, we need to disable |
| // stack splitting for the thunk. |
| bool disable_split_stack = this->is_recover_thunk_; |
| |
| // Check the //go:nosplit compiler directive. |
| if ((this->pragmas_ & GOPRAGMA_NOSPLIT) != 0) |
| disable_split_stack = true; |
| |
| if (disable_split_stack) |
| flags |= Backend::function_no_split_stack; |
| |
| // This should go into a unique section if that has been |
| // requested elsewhere, or if this is a nointerface function. |
| // We want to put a nointerface function into a unique section |
| // because there is a good chance that the linker garbage |
| // collection can discard it. |
| if (this->in_unique_section_ |
| || (this->is_method() && this->nointerface())) |
| flags |= Backend::function_in_unique_section; |
| |
| if (this->is_inline_only_) |
| flags |= Backend::function_only_inline; |
| |
| Btype* functype = this->type_->get_backend_fntype(gogo); |
| |
| Backend_name bname; |
| this->backend_name(gogo, no, &bname); |
| |
| this->fndecl_ = gogo->backend()->function(functype, |
| bname.name(), |
| bname.optional_asm_name(), |
| flags, |
| this->location()); |
| } |
| return this->fndecl_; |
| } |
| |
| // Get the backend name. |
| |
| void |
| Function_declaration::backend_name(Gogo* gogo, Named_object* no, |
| Backend_name* bname) |
| { |
| if (!this->asm_name_.empty()) |
| bname->set_asm_name(this->asm_name_); |
| else |
| { |
| Type* rtype = NULL; |
| if (this->fntype_->is_method()) |
| rtype = this->fntype_->receiver()->type(); |
| gogo->function_backend_name(no->name(), no->package(), rtype, bname); |
| } |
| } |
| |
| // Get the backend representation. |
| |
| Bfunction* |
| Function_declaration::get_or_make_decl(Gogo* gogo, Named_object* no) |
| { |
| if (this->fndecl_ == NULL) |
| { |
| unsigned int flags = |
| (Backend::function_is_visible |
| | Backend::function_is_declaration |
| | Backend::function_is_inlinable); |
| |
| // Let Go code use an asm declaration to pick up a builtin |
| // function. |
| if (!this->asm_name_.empty()) |
| { |
| Bfunction* builtin_decl = |
| gogo->backend()->lookup_builtin(this->asm_name_); |
| if (builtin_decl != NULL) |
| { |
| this->fndecl_ = builtin_decl; |
| return this->fndecl_; |
| } |
| |
| if (this->asm_name_ == "runtime.gopanic" |
| || this->asm_name_.compare(0, 13, "runtime.panic") == 0 |
| || this->asm_name_.compare(0, 15, "runtime.goPanic") == 0 |
| || this->asm_name_ == "runtime.block") |
| flags |= Backend::function_does_not_return; |
| } |
| |
| Btype* functype = this->fntype_->get_backend_fntype(gogo); |
| |
| Backend_name bname; |
| this->backend_name(gogo, no, &bname); |
| |
| this->fndecl_ = gogo->backend()->function(functype, |
| bname.name(), |
| bname.optional_asm_name(), |
| flags, |
| this->location()); |
| } |
| |
| return this->fndecl_; |
| } |
| |
| // Build the descriptor for a function declaration. This won't |
| // necessarily happen if the package has just a declaration for the |
| // function and no other reference to it, but we may still need the |
| // descriptor for references from other packages. |
| void |
| Function_declaration::build_backend_descriptor(Gogo* gogo) |
| { |
| if (this->descriptor_ != NULL) |
| { |
| Translate_context context(gogo, NULL, NULL, NULL); |
| this->descriptor_->get_backend(&context); |
| } |
| } |
| |
| // Check that the types used in this declaration's signature are defined. |
| // Reports errors for any undefined type. |
| |
| void |
| Function_declaration::check_types() const |
| { |
| // Calling Type::base will give errors for any undefined types. |
| Function_type* fntype = this->type(); |
| if (fntype->receiver() != NULL) |
| fntype->receiver()->type()->base(); |
| if (fntype->parameters() != NULL) |
| { |
| const Typed_identifier_list* params = fntype->parameters(); |
| for (Typed_identifier_list::const_iterator p = params->begin(); |
| p != params->end(); |
| ++p) |
| p->type()->base(); |
| } |
| } |
| |
| // Return the function's decl after it has been built. |
| |
| Bfunction* |
| Function::get_decl() const |
| { |
| go_assert(this->fndecl_ != NULL); |
| return this->fndecl_; |
| } |
| |
| // Build the backend representation for the function code. |
| |
| void |
| Function::build(Gogo* gogo, Named_object* named_function) |
| { |
| Translate_context context(gogo, named_function, NULL, NULL); |
| |
| // A list of parameter variables for this function. |
| std::vector<Bvariable*> param_vars; |
| |
| // Variables that need to be declared for this function and their |
| // initial values. |
| std::vector<Bvariable*> vars; |
| std::vector<Expression*> var_inits; |
| std::vector<Statement*> var_decls_stmts; |
| for (Bindings::const_definitions_iterator p = |
| this->block_->bindings()->begin_definitions(); |
| p != this->block_->bindings()->end_definitions(); |
| ++p) |
| { |
| Location loc = (*p)->location(); |
| if ((*p)->is_variable() && (*p)->var_value()->is_parameter()) |
| { |
| Bvariable* bvar = (*p)->get_backend_variable(gogo, named_function); |
| Bvariable* parm_bvar = bvar; |
| |
| // We always pass the receiver to a method as a pointer. If |
| // the receiver is declared as a non-pointer type, then we |
| // copy the value into a local variable. For direct interface |
| // type we pack the pointer into the type. |
| if ((*p)->var_value()->is_receiver() |
| && (*p)->var_value()->type()->points_to() == NULL) |
| { |
| std::string name = (*p)->name() + ".pointer"; |
| Type* var_type = (*p)->var_value()->type(); |
| Variable* parm_var = |
| new Variable(Type::make_pointer_type(var_type), NULL, false, |
| true, false, loc); |
| Named_object* parm_no = |
| Named_object::make_variable(name, NULL, parm_var); |
| parm_bvar = parm_no->get_backend_variable(gogo, named_function); |
| |
| vars.push_back(bvar); |
| |
| Expression* parm_ref = |
| Expression::make_var_reference(parm_no, loc); |
| Type* recv_type = (*p)->var_value()->type(); |
| if (recv_type->is_direct_iface_type()) |
| parm_ref = Expression::pack_direct_iface(recv_type, parm_ref, loc); |
| else |
| parm_ref = |
| Expression::make_dereference(parm_ref, |
| Expression::NIL_CHECK_NEEDED, |
| loc); |
| if ((*p)->var_value()->is_in_heap()) |
| parm_ref = Expression::make_heap_expression(parm_ref, loc); |
| var_inits.push_back(parm_ref); |
| } |
| else if ((*p)->var_value()->is_in_heap()) |
| { |
| // If we take the address of a parameter, then we need |
| // to copy it into the heap. |
| std::string parm_name = (*p)->name() + ".param"; |
| Variable* parm_var = new Variable((*p)->var_value()->type(), NULL, |
| false, true, false, loc); |
| Named_object* parm_no = |
| Named_object::make_variable(parm_name, NULL, parm_var); |
| parm_bvar = parm_no->get_backend_variable(gogo, named_function); |
| |
| vars.push_back(bvar); |
| Expression* var_ref = |
| Expression::make_var_reference(parm_no, loc); |
| var_ref = Expression::make_heap_expression(var_ref, loc); |
| var_inits.push_back(var_ref); |
| } |
| param_vars.push_back(parm_bvar); |
| } |
| else if ((*p)->is_result_variable()) |
| { |
| Bvariable* bvar = (*p)->get_backend_variable(gogo, named_function); |
| |
| Type* type = (*p)->result_var_value()->type(); |
| Expression* init; |
| if (!(*p)->result_var_value()->is_in_heap()) |
| { |
| Btype* btype = type->get_backend(gogo); |
| Bexpression* binit = gogo->backend()->zero_expression(btype); |
| init = Expression::make_backend(binit, type, loc); |
| } |
| else |
| init = Expression::make_allocation(type, loc); |
| |
| vars.push_back(bvar); |
| var_inits.push_back(init); |
| } |
| else if (this->defer_stack_ != NULL |
| && (*p)->is_variable() |
| && (*p)->var_value()->is_non_escaping_address_taken() |
| && !(*p)->var_value()->is_in_heap()) |
| { |
| // Local variable captured by deferred closure needs to be live |
| // until the end of the function. We create a top-level |
| // declaration for it. |
| // TODO: we don't need to do this if the variable is not captured |
| // by the defer closure. There is no easy way to check it here, |
| // so we do this for all address-taken variables for now. |
| Variable* var = (*p)->var_value(); |
| Temporary_statement* ts = |
| Statement::make_temporary(var->type(), NULL, var->location()); |
| ts->set_is_address_taken(); |
| var->set_toplevel_decl(ts); |
| var_decls_stmts.push_back(ts); |
| } |
| } |
| if (!gogo->backend()->function_set_parameters(this->fndecl_, param_vars)) |
| { |
| go_assert(saw_errors()); |
| return; |
| } |
| |
| // If we need a closure variable, make sure to create it. |
| // It gets installed in the function as a side effect of creation. |
| if (this->closure_var_ != NULL) |
| { |
| go_assert(this->closure_var_->var_value()->is_closure()); |
| this->closure_var_->get_backend_variable(gogo, named_function); |
| } |
| |
| if (this->block_ != NULL) |
| { |
| // Declare variables if necessary. |
| Bblock* var_decls = NULL; |
| std::vector<Bstatement*> var_decls_bstmt_list; |
| Bstatement* defer_init = NULL; |
| if (!vars.empty() || this->defer_stack_ != NULL) |
| { |
| var_decls = |
| gogo->backend()->block(this->fndecl_, NULL, vars, |
| this->block_->start_location(), |
| this->block_->end_location()); |
| |
| if (this->defer_stack_ != NULL) |
| { |
| Translate_context dcontext(gogo, named_function, this->block_, |
| var_decls); |
| defer_init = this->defer_stack_->get_backend(&dcontext); |
| var_decls_bstmt_list.push_back(defer_init); |
| for (std::vector<Statement*>::iterator p = var_decls_stmts.begin(); |
| p != var_decls_stmts.end(); |
| ++p) |
| { |
| Bstatement* bstmt = (*p)->get_backend(&dcontext); |
| var_decls_bstmt_list.push_back(bstmt); |
| } |
| } |
| } |
| |
| // Build the backend representation for all the statements in the |
| // function. |
| Translate_context bcontext(gogo, named_function, NULL, NULL); |
| Bblock* code_block = this->block_->get_backend(&bcontext); |
| |
| // Initialize variables if necessary. |
| Translate_context icontext(gogo, named_function, this->block_, |
| var_decls); |
| std::vector<Bstatement*> init; |
| go_assert(vars.size() == var_inits.size()); |
| for (size_t i = 0; i < vars.size(); ++i) |
| { |
| Bexpression* binit = var_inits[i]->get_backend(&icontext); |
| Bstatement* init_stmt = |
| gogo->backend()->init_statement(this->fndecl_, vars[i], |
| binit); |
| init.push_back(init_stmt); |
| } |
| Bstatement* var_init = gogo->backend()->statement_list(init); |
| |
| // Initialize all variables before executing this code block. |
| Bstatement* code_stmt = gogo->backend()->block_statement(code_block); |
| code_stmt = gogo->backend()->compound_statement(var_init, code_stmt); |
| |
| // If we have a defer stack, initialize it at the start of a |
| // function. |
| Bstatement* except = NULL; |
| Bstatement* fini = NULL; |
| if (defer_init != NULL) |
| { |
| // Clean up the defer stack when we leave the function. |
| this->build_defer_wrapper(gogo, named_function, &except, &fini); |
| |
| // Wrap the code for this function in an exception handler to handle |
| // defer calls. |
| code_stmt = |
| gogo->backend()->exception_handler_statement(code_stmt, |
| except, fini, |
| this->location_); |
| } |
| |
| // Stick the code into the block we built for the receiver, if |
| // we built one. |
| if (var_decls != NULL) |
| { |
| var_decls_bstmt_list.push_back(code_stmt); |
| gogo->backend()->block_add_statements(var_decls, var_decls_bstmt_list); |
| code_stmt = gogo->backend()->block_statement(var_decls); |
| } |
| |
| if (!gogo->backend()->function_set_body(this->fndecl_, code_stmt)) |
| { |
| go_assert(saw_errors()); |
| return; |
| } |
| } |
| |
| // If we created a descriptor for the function, make sure we emit it. |
| if (this->descriptor_ != NULL) |
| { |
| Translate_context dcontext(gogo, NULL, NULL, NULL); |
| this->descriptor_->get_backend(&dcontext); |
| } |
| } |
| |
| // Build the wrappers around function code needed if the function has |
| // any defer statements. This sets *EXCEPT to an exception handler |
| // and *FINI to a finally handler. |
| |
| void |
| Function::build_defer_wrapper(Gogo* gogo, Named_object* named_function, |
| Bstatement** except, Bstatement** fini) |
| { |
| Location end_loc = this->block_->end_location(); |
| |
| // Add an exception handler. This is used if a panic occurs. Its |
| // purpose is to stop the stack unwinding if a deferred function |
| // calls recover. There are more details in |
| // libgo/runtime/go-unwind.c. |
| |
| std::vector<Bstatement*> stmts; |
| Expression* call = Runtime::make_call(Runtime::CHECKDEFER, end_loc, 1, |
| this->defer_stack(end_loc)); |
| Translate_context context(gogo, named_function, NULL, NULL); |
| Bexpression* defer = call->get_backend(&context); |
| stmts.push_back(gogo->backend()->expression_statement(this->fndecl_, defer)); |
| |
| Bstatement* ret_bstmt = this->return_value(gogo, named_function, end_loc); |
| if (ret_bstmt != NULL) |
| stmts.push_back(ret_bstmt); |
| |
| go_assert(*except == NULL); |
| *except = gogo->backend()->statement_list(stmts); |
| |
| call = Runtime::make_call(Runtime::CHECKDEFER, end_loc, 1, |
| this->defer_stack(end_loc)); |
| defer = call->get_backend(&context); |
| |
| call = Runtime::make_call(Runtime::DEFERRETURN, end_loc, 1, |
| this->defer_stack(end_loc)); |
| Bexpression* undefer = call->get_backend(&context); |
| Bstatement* function_defer = |
| gogo->backend()->function_defer_statement(this->fndecl_, undefer, defer, |
| end_loc); |
| stmts = std::vector<Bstatement*>(1, function_defer); |
| if (this->type_->results() != NULL |
| && !this->type_->results()->empty() |
| && !this->type_->results()->front().name().empty()) |
| { |
| // If the result variables are named, and we are returning from |
| // this function rather than panicing through it, we need to |
| // return them again, because they might have been changed by a |
| // defer function. The runtime routines set the defer_stack |
| // variable to true if we are returning from this function. |
| |
| ret_bstmt = this->return_value(gogo, named_function, end_loc); |
| Bexpression* nil = Expression::make_nil(end_loc)->get_backend(&context); |
| Bexpression* ret = |
| gogo->backend()->compound_expression(ret_bstmt, nil, end_loc); |
| Expression* ref = |
| Expression::make_temporary_reference(this->defer_stack_, end_loc); |
| Bexpression* bref = ref->get_backend(&context); |
| ret = gogo->backend()->conditional_expression(this->fndecl_, |
| NULL, bref, ret, NULL, |
| end_loc); |
| stmts.push_back(gogo->backend()->expression_statement(this->fndecl_, ret)); |
| } |
| |
| go_assert(*fini == NULL); |
| *fini = gogo->backend()->statement_list(stmts); |
| } |
| |
| // Return the statement that assigns values to this function's result struct. |
| |
| Bstatement* |
| Function::return_value(Gogo* gogo, Named_object* named_function, |
| Location location) const |
| { |
| const Typed_identifier_list* results = this->type_->results(); |
| if (results == NULL || results->empty()) |
| return NULL; |
| |
| go_assert(this->results_ != NULL); |
| if (this->results_->size() != results->size()) |
| { |
| go_assert(saw_errors()); |
| return gogo->backend()->error_statement(); |
| } |
| |
| std::vector<Bexpression*> vals(results->size()); |
| for (size_t i = 0; i < vals.size(); ++i) |
| { |
| Named_object* no = (*this->results_)[i]; |
| Bvariable* bvar = no->get_backend_variable(gogo, named_function); |
| Bexpression* val = gogo->backend()->var_expression(bvar, location); |
| if (no->result_var_value()->is_in_heap()) |
| { |
| Btype* bt = no->result_var_value()->type()->get_backend(gogo); |
| val = gogo->backend()->indirect_expression(bt, val, true, location); |
| } |
| vals[i] = val; |
| } |
| return gogo->backend()->return_statement(this->fndecl_, vals, location); |
| } |
| |
| // Class Block. |
| |
| Block::Block(Block* enclosing, Location location) |
| : enclosing_(enclosing), statements_(), |
| bindings_(new Bindings(enclosing == NULL |
| ? NULL |
| : enclosing->bindings())), |
| start_location_(location), |
| end_location_(Linemap::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) |
| { |
| go_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) |
| { |
| go_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) |
| { |
| go_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) |
| { |
| const unsigned int e_or_t = (Traverse::traverse_expressions |
| | Traverse::traverse_types); |
| const unsigned int e_or_t_or_s = (e_or_t |
| | Traverse::traverse_statements); |
| for (Bindings::const_definitions_iterator pb = |
| this->bindings_->begin_definitions(); |
| pb != this->bindings_->end_definitions(); |
| ++pb) |
| { |
| int t = TRAVERSE_CONTINUE; |
| switch ((*pb)->classification()) |
| { |
| case Named_object::NAMED_OBJECT_CONST: |
| if ((traverse_mask & Traverse::traverse_constants) != 0) |
| t = traverse->constant(*pb, false); |
| if (t == TRAVERSE_CONTINUE |
| && (traverse_mask & e_or_t) != 0) |
| { |
| Type* tc = (*pb)->const_value()->type(); |
| if (tc != NULL |
| && Type::traverse(tc, traverse) == TRAVERSE_EXIT) |
| return TRAVERSE_EXIT; |
| t = (*pb)->const_value()->traverse_expression(traverse); |
| } |
| break; |
| |
| case Named_object::NAMED_OBJECT_VAR: |
| case Named_object::NAMED_OBJECT_RESULT_VAR: |
| if ((traverse_mask & Traverse::traverse_variables) != 0) |
| t = traverse->variable(*pb); |
| if (t == TRAVERSE_CONTINUE |
| && (traverse_mask & e_or_t) != 0) |
| { |
| if ((*pb)->is_result_variable() |
| || (*pb)->var_value()->has_type()) |
| { |
| Type* tv = ((*pb)->is_variable() |
| ? (*pb)->var_value()->type() |
| : (*pb)->result_var_value()->type()); |
| if (tv != NULL |
| && Type::traverse(tv, traverse) == TRAVERSE_EXIT) |
| return TRAVERSE_EXIT; |
| } |
| } |
| if (t == TRAVERSE_CONTINUE |
| && (traverse_mask & e_or_t_or_s) != 0 |
| && (*pb)->is_variable()) |
| t = (*pb)->var_value()->traverse_expression(traverse, |
| traverse_mask); |
| break; |
| |
| case Named_object::NAMED_OBJECT_FUNC: |
| case Named_object::NAMED_OBJECT_FUNC_DECLARATION: |
| go_unreachable(); |
| |
| case Named_object::NAMED_OBJECT_TYPE: |
| if ((traverse_mask & e_or_t) != 0) |
| t = Type::traverse((*pb)->type_value(), traverse); |
| break; |
| |
| case Named_object::NAMED_OBJECT_TYPE_DECLARATION: |
| case Named_object::NAMED_OBJECT_UNKNOWN: |
| case Named_object::NAMED_OBJECT_ERRONEOUS: |
| break; |
| |
| case Named_object::NAMED_OBJECT_PACKAGE: |
| case Named_object::NAMED_OBJECT_SINK: |
| go_unreachable(); |
| |
| default: |
| go_unreachable(); |
| } |
| |
| if (t == TRAVERSE_EXIT) |
| return TRAVERSE_EXIT; |
| } |
| } |
| |
| // 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(); |
| } |
| |
| // Write export data for a block. |
| |
| void |
| Block::export_block(Export_function_body* efb) |
| { |
| for (Block::iterator p = this->begin(); |
| p != this->end(); |
| ++p) |
| { |
| efb->indent(); |
| |
| efb->increment_indent(); |
| (*p)->export_statement(efb); |
| efb->decrement_indent(); |
| |
| Location loc = (*p)->location(); |
| if ((*p)->is_block_statement()) |
| { |
| // For a block we put the start location on the first brace |
| // in Block_statement::do_export_statement. Here we put the |
| // end location on the final brace. |
| loc = (*p)->block_statement()->block()->end_location(); |
| } |
| char buf[50]; |
| snprintf(buf, sizeof buf, " //%d\n", Linemap::location_to_line(loc)); |
| efb->write_c_string(buf); |
| } |
| } |
| |
| // Add exported block data to SET, reading from BODY starting at OFF. |
| // Returns whether the import succeeded. |
| |
| bool |
| Block::import_block(Block* set, Import_function_body *ifb, Location loc) |
| { |
| Location eloc = ifb->location(); |
| Location sloc = loc; |
| const std::string& body(ifb->body()); |
| size_t off = ifb->off(); |
| while (off < body.length()) |
| { |
| int indent = ifb->indent(); |
| if (off + indent >= body.length()) |
| { |
| go_error_at(eloc, |
| "invalid export data for %qs: insufficient indentation", |
| ifb->name().c_str()); |
| return false; |
| } |
| for (int i = 0; i < indent - 1; i++) |
| { |
| if (body[off + i] != ' ') |
| { |
| go_error_at(eloc, |
| "invalid export data for %qs: bad indentation", |
| ifb->name().c_str()); |
| return false; |
| } |
| } |
| |
| bool at_end = false; |
| if (body[off + indent - 1] == '}') |
| at_end = true; |
| else if (body[off + indent - 1] != ' ') |
| { |
| go_error_at(eloc, |
| "invalid export data for %qs: bad indentation", |
| ifb->name().c_str()); |
| return false; |
| } |
| |
| off += indent; |
| |
| size_t nl = body.find('\n', off); |
| if (nl == std::string::npos) |
| { |
| go_error_at(eloc, "invalid export data for %qs: missing newline", |
| ifb->name().c_str()); |
| return false; |
| } |
| |
| size_t lineno_pos = body.find(" //", off); |
| if (lineno_pos == std::string::npos || lineno_pos >= nl) |
| { |
| go_error_at(eloc, "invalid export data for %qs: missing line number", |
| ifb->name().c_str()); |
| return false; |
| } |
| |
| unsigned int lineno = 0; |
| for (size_t i = lineno_pos + 3; i < nl; ++i) |
| { |
| char c = body[i]; |
| if (c < '0' || c > '9') |
| { |
| go_error_at(loc, |
| "invalid export data for %qs: invalid line number", |
| ifb->name().c_str()); |
| return false; |
| } |
| lineno = lineno * 10 + c - '0'; |
| } |
| |
| ifb->gogo()->linemap()->start_line(lineno, 1); |
| sloc = ifb->gogo()->linemap()->get_location(0); |
| |
| if (at_end) |
| { |
| // An if statement can have an "else" following the "}", in |
| // which case we want to leave the offset where it is, just |
| // after the "}". We don't get the block ending location |
| // quite right for if statements. |
| if (body.compare(off, 6, " else ") != 0) |
| off = nl + 1; |
| break; |
| } |
| |
| ifb->set_off(off); |
| Statement* s = Statement::import_statement(ifb, sloc); |
| if (s == NULL) |
| return false; |
| |
| set->add_statement(s); |
| |
| size_t at = ifb->off(); |
| if (at < nl + 1) |
| off = nl + 1; |
| else |
| off = at; |
| } |
| |
| ifb->set_off(off); |
| set->set_end_location(sloc); |
| return true; |
| } |
| |
| // Convert a block to the backend representation. |
| |
| Bblock* |
| Block::get_backend(Translate_context* context) |
| { |
| Gogo* gogo = context->gogo(); |
| Named_object* function = context->function(); |
| std::vector<Bvariable*> vars; |
| vars.reserve(this->bindings_->size_definitions()); |
| for (Bindings::const_definitions_iterator pv = |
| this->bindings_->begin_definitions(); |
| pv != this->bindings_->end_definitions(); |
| ++pv) |
| { |
| if ((*pv)->is_variable() && !(*pv)->var_value()->is_parameter()) |
| vars.push_back((*pv)->get_backend_variable(gogo, function)); |
| } |
| |
| go_assert(function != NULL); |
| Bfunction* bfunction = |
| function->func_value()->get_or_make_decl(gogo, function); |
| Bblock* ret = context->backend()->block(bfunction, context->bblock(), |
| vars, this->start_location_, |
| this->end_location_); |
| |
| Translate_context subcontext(gogo, function, this, ret); |
| std::vector<Bstatement*> bstatements; |
| bstatements.reserve(this->statements_.size()); |
| for (std::vector<Statement*>::const_iterator p = this->statements_.begin(); |
| p != this->statements_.end(); |
| ++p) |
| bstatements.push_back((*p)->get_backend(&subcontext)); |
| |
| context->backend()->block_add_statements(ret, bstatements); |
| |
| return ret; |
| } |
| |
| // Class Bindings_snapshot. |
| |
| Bindings_snapshot::Bindings_snapshot(const Block* b, Location location) |
| : block_(b), counts_(), location_(location) |
| { |
| while (b != NULL) |
| { |
| this->counts_.push_back(b->bindings()->size_definitions()); |
| b = b->enclosing(); |
| } |
| } |
| |
| // Report errors appropriate for a goto from B to this. |
| |
| void |
| Bindings_snapshot::check_goto_from(const Block* b, Location loc) |
| { |
| size_t dummy; |
| if (!this->check_goto_block(loc, b, this->block_, &dummy)) |
| return; |
| this->check_goto_defs(loc, this->block_, |
| this->block_->bindings()->size_definitions(), |
| this->counts_[0]); |
| } |
| |
| // Report errors appropriate for a goto from this to B. |
| |
| void |
| Bindings_snapshot::check_goto_to(const Block* b) |
| { |
| size_t index; |
| if (!this->check_goto_block(this->location_, this->block_, b, &index)) |
| return; |
| this->check_goto_defs(this->location_, b, this->counts_[index], |
| b->bindings()->size_definitions()); |
| } |
| |
| // Report errors appropriate for a goto at LOC from BFROM to BTO. |
| // Return true if all is well, false if we reported an error. If this |
| // returns true, it sets *PINDEX to the number of blocks BTO is above |
| // BFROM. |
| |
| bool |
| Bindings_snapshot::check_goto_block(Location loc, const Block* bfrom, |
| const Block* bto, size_t* pindex) |
| { |
| // It is an error if BTO is not either BFROM or above BFROM. |
| size_t index = 0; |
| for (const Block* pb = bfrom; pb != bto; pb = pb->enclosing(), ++index) |
| { |
| if (pb == NULL) |
| { |
| go_error_at(loc, "goto jumps into block"); |
| go_inform(bto->start_location(), "goto target block starts here"); |
| return false; |
| } |
| } |
| *pindex = index; |
| return true; |
| } |
| |
| // Report errors appropriate for a goto at LOC ending at BLOCK, where |
| // CFROM is the number of names defined at the point of the goto and |
| // CTO is the number of names defined at the point of the label. |
| |
| void |
| Bindings_snapshot::check_goto_defs(Location loc, const Block* block, |
| size_t cfrom, size_t cto) |
| { |
| if (cfrom < cto) |
| { |
| Bindings::const_definitions_iterator p = |
| block->bindings()->begin_definitions(); |
| for (size_t i = 0; i < cfrom; ++i) |
| { |
| go_assert(p != block->bindings()->end_definitions()); |
| ++p; |
| } |
| go_assert(p != block->bindings()->end_definitions()); |
| |
| for (; p != block->bindings()->end_definitions(); ++p) |
| { |
| if ((*p)->is_variable()) |
| { |
| std::string n = (*p)->message_name(); |
| go_error_at(loc, "goto jumps over declaration of %qs", n.c_str()); |
| go_inform((*p)->location(), "%qs defined here", n.c_str()); |
| } |
| } |
| } |
| } |
| |
| // Class Function_declaration. |
| |
| // Whether this declares a method. |
| |
| bool |
| Function_declaration::is_method() const |
| { |
| return this->fntype_->is_method(); |
| } |
| |
| // Whether this method should not be included in the type descriptor. |
| |
| bool |
| Function_declaration::nointerface() const |
| { |
| go_assert(this->is_method()); |
| return (this->pragmas_ & GOPRAGMA_NOINTERFACE) != 0; |
| } |
| |
| // Record that this method should not be included in the type |
| // descriptor. |
| |
| void |
| Function_declaration::set_nointerface() |
| { |
| this->pragmas_ |= GOPRAGMA_NOINTERFACE; |
| } |
| |
| // Import an inlinable function. This is used for an inlinable |
| // function whose body is recorded in the export data. Parse the |
| // export data into a Block and create a regular function using that |
| // Block as its body. Redeclare this function declaration as the |
| // function. |
| |
| void |
| Function_declaration::import_function_body(Gogo* gogo, Named_object* no) |
| { |
| go_assert(no->func_declaration_value() == this); |
| go_assert(no->package() != NULL); |
| const std::string& body(this->imported_body_); |
| go_assert(!body.empty()); |
| |
| // Read the "//FILE:LINE" comment starts the export data. |
| |
| size_t indent = 1; |
| if (this->is_method()) |
| indent = 2; |
| size_t i = 0; |
| for (; i < indent; i++) |
| { |
| if (body.at(i) != ' ') |
| { |
| go_error_at(this->location_, |
| "invalid export body for %qs: bad initial indentation", |
| no->message_name().c_str()); |
| return; |
| } |
| } |
| |
| if (body.substr(i, 2) != "//") |
| { |
| go_error_at(this->location_, |
| "invalid export body for %qs: missing file comment", |
| no->message_name().c_str()); |
| return; |
| } |
| |
| size_t colon = body.find(':', i + 2); |
| size_t nl = body.find('\n', i + 2); |
| if (nl == std::string::npos) |
| { |
| go_error_at(this->location_, |
| "invalid export body for %qs: missing file name", |
| no->message_name().c_str()); |
| return; |
| } |
| if (colon == std::string::npos || nl < colon) |
| { |
| go_error_at(this->location_, |
| "invalid export body for %qs: missing initial line number", |
| no->message_name().c_str()); |
| return; |
| } |
| |
| std::string file = body.substr(i + 2, colon - (i + 2)); |
| std::string linestr = body.substr(colon + 1, nl - (colon + 1)); |
| char* end; |
| long linenol = strtol(linestr.c_str(), &end, 10); |
| if (*end != '\0') |
| { |
| go_error_at(this->location_, |
| "invalid export body for %qs: invalid initial line number", |
| no->message_name().c_str()); |
| return; |
| } |
| unsigned int lineno = static_cast<unsigned int>(linenol); |
| |
| // Turn the file/line into a location. |
| |
| char* alc = new char[file.length() + 1]; |
| memcpy(alc, file.data(), file.length()); |
| alc[file.length()] = '\0'; |
| gogo->linemap()->start_file(alc, lineno); |
| gogo->linemap()->start_line(lineno, 1); |
| Location start_loc = gogo->linemap()->get_location(0); |
| |
| // Define the function with an outer block that declares the |
| // parameters. |
| |
| Function_type* fntype = this->fntype_; |
| |
| Block* outer = new Block(NULL, start_loc); |
| |
| Function* fn = new Function(fntype, NULL, outer, start_loc); |
| fn->set_is_inline_only(); |
| |
| if (fntype->is_method()) |
| { |
| if (this->nointerface()) |
| fn->set_nointerface(); |
| const Typed_identifier* receiver = fntype->receiver(); |
| Variable* recv_param = new Variable(receiver->type(), NULL, false, |
| true, true, start_loc); |
| |
| std::string rname = receiver->name(); |
| unsigned rcounter = 0; |
| |
| // We need to give a nameless receiver a name to avoid having it |
| // clash with some other nameless param. FIXME. |
| Gogo::rename_if_empty(&rname, "r", &rcounter); |
| |
| outer->bindings()->add_variable(rname, NULL, recv_param); |
| } |
| |
| const Typed_identifier_list* params = fntype->parameters(); |
| bool is_varargs = fntype->is_varargs(); |
| unsigned pcounter = 0; |
| if (params != NULL) |
| { |
| for (Typed_identifier_list::const_iterator p = params->begin(); |
| p != params->end(); |
| ++p) |
| { |
| Variable* param = new Variable(p->type(), NULL, false, true, false, |
| start_loc); |
| if (is_varargs && p + 1 == params->end()) |
| param->set_is_varargs_parameter(); |
| |
| std::string pname = p->name(); |
| |
| // We need to give each nameless parameter a non-empty name to avoid |
| // having it clash with some other nameless param. FIXME. |
| Gogo::rename_if_empty(&pname, "p", &pcounter); |
| |
| outer->bindings()->add_variable(pname, NULL, param); |
| } |
| } |
| |
| fn->create_result_variables(gogo); |
| |
| if (!fntype->is_method()) |
| { |
| const Package* package = no->package(); |
| no = package->bindings()->add_function(no->name(), package, fn); |
| } |
| else |
| { |
| Named_type* rtype = fntype->receiver()->type()->deref()->named_type(); |
| go_assert(rtype != NULL); |
| no = rtype->add_method(no->name(), fn); |
| const Package* package = rtype->named_object()->package(); |
| package->bindings()->add_method(no); |
| } |
| |
| Import_function_body ifb(gogo, this->imp_, no, body, nl + 1, outer, indent); |
| |
| if (!Block::import_block(outer, &ifb, start_loc)) |
| return; |
| |
| gogo->lower_block(no, outer); |
| outer->determine_types(); |
| |
| gogo->add_imported_inline_function(no); |
| } |
| |
| // Return the function descriptor. |
| |
| Expression* |
| Function_declaration::descriptor(Gogo*, Named_object* no) |
| { |
| go_assert(!this->fntype_->is_method()); |
| if (this->descriptor_ == NULL) |
| this->descriptor_ = Expression::make_func_descriptor(no); |
| return this->descriptor_; |
| } |
| |
| // Class Variable. |
| |
| Variable::Variable(Type* type, Expression* init, bool is_global, |
| bool is_parameter, bool is_receiver, |
| Location location) |
| : type_(type), init_(init), preinit_(NULL), location_(location), |
| backend_(NULL), is_global_(is_global), is_parameter_(is_parameter), |
| is_closure_(false), is_receiver_(is_receiver), |
| is_varargs_parameter_(false), is_used_(false), |
| is_address_taken_(false), is_non_escaping_address_taken_(false), |
| seen_(false), init_is_lowered_(false), init_is_flattened_(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), |
| in_unique_section_(false), is_referenced_by_inline_(false), |
| toplevel_decl_(NULL) |
| { |
| go_assert(type != NULL || init != NULL); |
| go_assert(!is_parameter || init == NULL); |
| } |
| |
| // Traverse the initializer expression. |
| |
| int |
| Variable::traverse_expression(Traverse* traverse, unsigned int traverse_mask) |
| { |
| if (this->preinit_ != NULL) |
| { |
| if (this->preinit_->traverse(traverse) == TRAVERSE_EXIT) |
| return TRAVERSE_EXIT; |
| } |
| if (this->init_ != NULL |
| && ((traverse_mask |
| & (Traverse::traverse_expressions | Traverse::traverse_types)) |
| != 0)) |
| { |
| 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, |
| Statement_inserter* inserter) |
| { |
| Named_object* dep = gogo->var_depends_on(this); |
| if (dep != NULL && dep->is_variable()) |
| dep->var_value()->lower_init_expression(gogo, function, inserter); |
| |
| 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; |
| |
| Statement_inserter global_inserter; |
| if (this->is_global_) |
| { |
| global_inserter = Statement_inserter(gogo, this); |
| inserter = &global_inserter; |
| } |
| |
| gogo->lower_expression(function, inserter, &this->init_); |
| |
| this->seen_ = false; |
| |
| this->init_is_lowered_ = true; |
| } |
| } |
| |
| // Flatten the initialization expression after ordering evaluations. |
| |
| void |
| Variable::flatten_init_expression(Gogo* gogo, Named_object* function, |
| Statement_inserter* inserter) |
| { |
| Named_object* dep = gogo->var_depends_on(this); |
| if (dep != NULL && dep->is_variable()) |
| dep->var_value()->flatten_init_expression(gogo, function, inserter); |
| |
| if (this->init_ != NULL && !this->init_is_flattened_) |
| { |
| if (this->seen_) |
| { |
| // We will give an error elsewhere, this is just to prevent |
| // an infinite loop. |
| return; |
| } |
| this->seen_ = true; |
| |
| Statement_inserter global_inserter; |
| if (this->is_global_) |
| { |
| global_inserter = Statement_inserter(gogo, this); |
| inserter = &global_inserter; |
| } |
| |
| gogo->flatten_expression(function, inserter, &this->init_); |
| |
| // If an interface conversion is needed, we need a temporary |
| // variable. |
| if (this->type_ != NULL |
| && !Type::are_identical(this->type_, this->init_->type(), |
| Type::COMPARE_ERRORS | Type::COMPARE_TAGS, |
| NULL) |
| && this->init_->type()->interface_type() != NULL |
| && !this->init_->is_variable()) |
| { |
| Temporary_statement* temp = |
| Statement::make_temporary(NULL, this->init_, this->location_); |
| inserter->insert(temp); |
| this->init_ = Expression::make_temporary_reference(temp, |
| this->location_); |
| } |
| |
| this->seen_ = false; |
| this->init_is_flattened_ = true; |
| } |
| } |
| |
| // Get the preinit block. |
| |
| Block* |
| Variable::preinit_block(Gogo* gogo) |
| { |
| go_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()); |
| } |
| |
| // Whether this variable has a type. |
| |
| bool |
| Variable::has_type() const |
| { |
| if (this->type_ == NULL) |
| return false; |
| |
| // A variable created in a type switch case nil does not actually |
| // have a type yet. It will be changed to use the initializer's |
| // type in determine_type. |
| if (this->is_type_switch_var_ |
| && this->type_->is_nil_constant_as_type()) |
| return false; |
| |
| return true; |
| } |
| |
| // 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) |
| go_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_slice_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()) |
| { |
| if (get_index_type) |
| return Type::lookup_integer_type("int"); |
| else |
| return Type::lookup_integer_type("int32"); |
| } |
| 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) |
| go_error_at(this->location(), |
| ("invalid definition of value variable " |
| "for channel range")); |
| return Type::make_error_type(); |
| } |
| } |
| else |
| { |
| if (report_error) |
| go_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) |
| go_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_ |
| && type != NULL |
| && this->type_->is_nil_constant_as_type()) |
| { |
| Type_guard_expression* tge = this->init_->type_guard_expression(); |
| go_assert(tge != NULL); |
| init = tge->expr(); |
| type = NULL; |
| } |
| |
| if (this->seen_) |
| { |
| if (this->type_ == NULL || !this->type_->is_error_type()) |
| { |
| go_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 |
| { |
| go_assert(init != NULL); |
| type = init->type(); |
| go_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 |
| { |
| go_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_ != NULL |
| && this->type_->is_nil_constant_as_type()) |
| { |
| Type_guard_expression* tge = this->init_->type_guard_expression(); |
| go_assert(tge != NULL); |
| this->type_ = NULL; |
| this->init_ = tge->expr(); |
| } |
| |
| if (this->init_ == NULL) |
| go_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 if (this->type_from_chan_element_) |
| { |
| Expression* init = this->init_; |
| init->determine_type_no_context(); |
| this->type_ = this->type_from_chan_element(init, true); |
| this->init_ = NULL; |
| } |
| else |
| { |
| Type_context context(this->type_, false); |
| this->init_->determine_type(&context); |
| if (this->type_ == NULL) |
| { |
| Type* type = this->init_->type(); |
| go_assert(type != NULL); |
| if (type->is_abstract()) |
| type = type->make_non_abstract_type(); |
| |
| if (type->is_void_type()) |
| { |
| go_error_at(this->location_, "variable has no type"); |
| type = Type::make_error_type(); |
| } |
| else if (type->is_nil_type()) |
| { |
| go_error_at(this->location_, "variable defined to nil type"); |
| type = Type::make_error_type(); |
| } |
| else if (type->is_call_multiple_result_type()) |
| { |
| go_error_at(this->location_, |
| "single variable set to multiple-value function call"); |
| type = Type::make_error_type(); |
| } |
| |
| this->type_ = type; |
| } |
| } |
| } |
| |
| // Get the initial value of a variable. This does not |
| // consider whether the variable is in the heap--it returns the |
| // initial value as though it were always stored in the stack. |
| |
| Bexpression* |
| Variable::get_init(Gogo* gogo, Named_object* function) |
| { |
| go_assert(this->preinit_ == NULL); |
| Location loc = this->location(); |
| if (this->init_ == NULL) |
| { |
| go_assert(!this->is_parameter_); |
| if (this->is_global_ || this->is_in_heap()) |
| return NULL; |
| Btype* btype = this->type()->get_backend(gogo); |
| return gogo->backend()->zero_expression(btype); |
| } |
| else |
| { |
| Translate_context context(gogo, function, NULL, NULL); |
| Expression* init = Expression::make_cast(this->type(), this->init_, loc); |
| return init->get_backend(&context); |
| } |
| } |
| |
| // Get the initial value of a variable when a block is required. |
| // VAR_DECL is the decl to set; it may be NULL for a sink variable. |
| |
| Bstatement* |
| Variable::get_init_block(Gogo* gogo, Named_object* function, |
| Bvariable* var_decl) |
| { |
| go_assert(this->preinit_ != NULL); |
| |
| // We want to add the variable assignment to the end of the preinit |
| // block. |
| |
| Translate_context context(gogo, function, NULL, NULL); |
| Bblock* bblock = this->preinit_->get_backend(&context); |
| Bfunction* bfunction = |
| function->func_value()->get_or_make_decl(gogo, function); |
| |
| // It's possible to have pre-init statements without an initializer |
| // if the pre-init statements set the variable. |
| Bstatement* decl_init = NULL; |
| if (this->init_ != NULL) |
| { |
| if (var_decl == NULL) |
| { |
| Bexpression* init_bexpr = this->init_->get_backend(&context); |
| decl_init = gogo->backend()->expression_statement(bfunction, |
| init_bexpr); |
| } |
| else |
| { |
| Location loc = this->location(); |
| Expression* val_expr = |
| Expression::make_cast(this->type(), this->init_, loc); |
| Bexpression* val = val_expr->get_backend(&context); |
| Bexpression* var_ref = |
| gogo->backend()->var_expression(var_decl, loc); |
| decl_init = gogo->backend()->assignment_statement(bfunction, var_ref, |
| val, loc); |
| } |
| } |
| Bstatement* block_stmt = gogo->backend()->block_statement(bblock); |
| if (decl_init != NULL) |
| block_stmt = gogo->backend()->compound_statement(block_stmt, decl_init); |
| return block_stmt; |
| } |
| |
| // Export the variable |
| |
| void |
| Variable::export_var(Export* exp, const Named_object* no) const |
| { |
| go_assert(this->is_global_); |
| exp->write_c_string("var "); |
| if (no->package() != NULL) |
| { |
| char buf[50]; |
| snprintf(buf, sizeof buf, "<p%d>", exp->package_index(no->package())); |
| exp->write_c_string(buf); |
| } |
| |
| if (!Gogo::is_hidden_name(no->name())) |
| exp->write_string(no->name()); |
| else |
| { |
| exp->write_c_string("."); |
| exp->write_string(Gogo::unpack_hidden_name(no->name())); |
| } |
| |
| exp->write_c_string(" "); |
| exp->write_type(this->type()); |
| exp->write_c_string("\n"); |
| } |
| |
| // Import a variable. |
| |
| bool |
| Variable::import_var(Import* imp, std::string* pname, Package** ppkg, |
| bool* pis_exported, Type** ptype) |
| { |
| imp->require_c_string("var "); |
| if (!Import::read_qualified_identifier(imp, pname, ppkg, pis_exported)) |
| { |
| go_error_at(imp->location(), |
| "import error at %d: bad variable name in export data", |
| imp->pos()); |
| return false; |
| } |
| imp->require_c_string(" "); |
| *ptype = imp->read_type(); |
| imp->require_semicolon_if_old_version(); |
| imp->require_c_string("\n"); |
| return true; |
| } |
| |
| // Convert a variable to the backend representation. |
| |
| Bvariable* |
| Variable::get_backend_variable(Gogo* gogo, Named_object* function, |
| const Package* package, const std::string& name) |
| { |
| if (this->backend_ == NULL) |
| { |
| Backend* backend = gogo->backend(); |
| Type* type = this->type_; |
| if (type->is_error_type() |
| || (type->is_undefined() |
| && (!this->is_global_ || package == NULL))) |
| this->backend_ = backend->error_variable(); |
| else |
| { |
| bool is_parameter = this->is_parameter_; |
| if (this->is_receiver_ && type->points_to() == NULL) |
| is_parameter = false; |
| if (this->is_in_heap()) |
| { |
| is_parameter = false; |
| type = Type::make_pointer_type(type); |
| } |
| |
| Btype* btype = type->get_backend(gogo); |
| |
| Bvariable* bvar; |
| if (Map_type::is_zero_value(this)) |
| bvar = Map_type::backend_zero_value(gogo); |
| else if (this->is_global_) |
| { |
| Backend_name bname; |
| gogo->global_var_backend_name(name, package, &bname); |
| |
| bool is_hidden = Gogo::is_hidden_name(name); |
| // Hack to export runtime.writeBarrier. FIXME. |
| // This is because go:linkname doesn't work on variables. |
| if (gogo->compiling_runtime() |
| && bname.name() == "runtime.writeBarrier") |
| is_hidden = false; |
| |
| // If an inline body refers to this variable, then it |
| // needs to be visible in the symbol table. |
| if (this->is_referenced_by_inline_) |
| is_hidden = false; |
| |
| // If this variable is in a different package, then it |
| // can't be treated as a hidden symbol. This case can |
| // arise when an inlined function refers to a |
| // package-scope unexported variable. |
| if (package != NULL) |
| is_hidden = false; |
| |
| // For some reason asm_name can't be the empty string |
| // for global_variable, so we call asm_name rather than |
| // optional_asm_name here. FIXME. |
| |
| bvar = backend->global_variable(bname.name(), |
| bname.asm_name(), |
| btype, |
| package != NULL, |
| is_hidden, |
| this->in_unique_section_, |
| this->location_); |
| } |
| else if (function == NULL) |
| { |
| go_assert(saw_errors()); |
| bvar = backend->error_variable(); |
| } |
| else |
| { |
| const std::string n = Gogo::unpack_hidden_name(name); |
| Bfunction* bfunction = function->func_value()->get_decl(); |
| bool is_address_taken = (this->is_non_escaping_address_taken_ |
| && !this->is_in_heap()); |
| if (this->is_closure()) |
| bvar = backend->static_chain_variable(bfunction, n, btype, |
| this->location_); |
| else if (is_parameter) |
| bvar = backend->parameter_variable(bfunction, n, btype, |
| is_address_taken, |
| this->location_); |
| else |
| { |
| Bvariable* bvar_decl = NULL; |
| if (this->toplevel_decl_ != NULL) |
| { |
| Translate_context context(gogo, NULL, NULL, NULL); |
| bvar_decl = this->toplevel_decl_->temporary_statement() |
| ->get_backend_variable(&context); |
| } |
| bvar = backend->local_variable(bfunction, n, btype, |
| bvar_decl, |
| is_address_taken, |
| this->location_); |
| } |
| } |
| this->backend_ = bvar; |
| } |
| } |
| return this->backend_; |
| } |
| |
| // Class Result_variable. |
| |
| // Convert a result variable to the backend representation. |
| |
| Bvariable* |
| Result_variable::get_backend_variable(Gogo* gogo, Named_object* function, |
| const std::string& name) |
| { |
| if (this->backend_ == NULL) |
| { |
| Backend* backend = gogo->backend(); |
| Type* type = this->type_; |
| if (type->is_error()) |
| this->backend_ = backend->error_variable(); |
| else |
| { |
| if (this->is_in_heap()) |
| type = Type::make_pointer_type(type); |
| Btype* btype = type->get_backend(gogo); |
| Bfunction* bfunction = function->func_value()->get_decl(); |
| std::string n = Gogo::unpack_hidden_name(name); |
| bool is_address_taken = (this->is_non_escaping_address_taken_ |
| && !this->is_in_heap()); |
| this->backend_ = backend->local_variable(bfunction, n, btype, |
| NULL, is_address_taken, |
| this->location_); |
| } |
| } |
| return this->backend_; |
| } |
| |
| // Class Named_constant. |
| |
| // Set the type of a named constant. This is only used to set the |
| // type to an error type. |
| |
| void |
| Named_constant::set_type(Type* t) |
| { |
| go_assert(this->type_ == NULL || t->is_error_type()); |
| this->type_ = t; |
| } |
| |
| // 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(); |
| go_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("= "); |
| |
| Export_function_body efb(exp, 0); |
| if (!this->type_->is_abstract()) |
| efb.set_type_context(this->type_); |
| this->expr()->export_expression(&efb); |
| exp->write_string(efb.body()); |
| |
| 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->location()); |
| imp->require_semicolon_if_old_version(); |
| imp->require_c_string("\n"); |
| } |
| |
| // Get the backend representation. |
| |
| Bexpression* |
| Named_constant::get_backend(Gogo* gogo, Named_object* const_no) |
| { |
| if (this->bconst_ == NULL) |
| { |
| Translate_context subcontext(gogo, NULL, NULL, NULL); |
| Type* type = this->type(); |
| Location loc = this->location(); |
| |
| Expression* const_ref = Expression::make_const_reference(const_no, loc); |
| Bexpression* const_decl = const_ref->get_backend(&subcontext); |
| if (type != NULL && type->is_numeric_type()) |
| { |
| Btype* btype = type->get_backend(gogo); |
| std::string name; |
| if (const_no->package() == NULL) |
| name = gogo->pkgpath(); |
| else |
| name = const_no->package()->pkgpath(); |
| name.push_back('.'); |
| name.append(Gogo::unpack_hidden_name(const_no->name())); |
| const_decl = |
| gogo->backend()->named_constant_expression(btype, name, |
| const_decl, loc); |
| } |
| this->bconst_ = const_decl; |
| } |
| return this->bconst_; |
| } |
| |
| // 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, |
| Package* package, |
| Function_type* type, |
| Location location) |
| { |
| Named_object* ret = Named_object::make_function_declaration(name, package, |
| type, location); |
| this->methods_.push_back(ret); |
| return ret; |
| } |
| |
| // Return whether any methods are 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) |
| { |
| if (this->methods_.empty()) |
| return; |
| |
| while (nt->is_alias()) |
| { |
| Type *t = nt->real_type()->forwarded(); |
| if (t->named_type() != NULL) |
| nt = t->named_type(); |
| else if (t->forward_declaration_type() != NULL) |
| { |
| Named_object* no = t->forward_declaration_type()->named_object(); |
| Type_declaration* td = no->type_declaration_value(); |
| td->methods_.insert(td->methods_.end(), this->methods_.begin(), |
| this->methods_.end()); |
| this->methods_.clear(); |
| return; |
| } |
| else |
| { |
| for (std::vector<Named_object*>::const_iterator p = |
| this->methods_.begin(); |
| p != this->methods_.end(); |
| ++p) |
| go_error_at((*p)->location(), |
| ("invalid receiver type " |
| "(receiver must be a named type)")); |
| return; |
| } |
| } |
| |
| for (std::vector<Named_object*>::const_iterator p = this->methods_.begin(); |
| p != this->methods_.end(); |
| ++p) |
| { |
| if ((*p)->is_function_declaration() |
| || !(*p)->func_value()->is_sink()) |
| 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) |
| { |
| go_assert(this->real_named_object_ == NULL); |
| go_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), |
| is_redefinition_(false) |
| { |
| if (Gogo::is_sink_name(name)) |
| go_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, |
| 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, 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, |
| 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, |
| 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; |
| if (this->package_->has_package_name()) |
| ret = this->package_->package_name(); |
| else |
| ret = this->package_->pkgpath(); |
| ret = Gogo::message_name(ret); |
| 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) |
| { |
| go_assert(this->classification_ == NAMED_OBJECT_TYPE_DECLARATION); |
| Type_declaration* td = this->u_.type_declaration; |
| td->define_methods(named_type); |
| unsigned int index; |
| Named_object* in_function = td->in_function(&index); |
| if (in_function != NULL) |
| named_type->set_in_function(in_function, index); |
| 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) |
| { |
| go_assert(this->classification_ == NAMED_OBJECT_FUNC_DECLARATION); |
| if (this->func_declaration_value()->has_descriptor()) |
| { |
| Expression* descriptor = |
| this->func_declaration_value()->descriptor(NULL, NULL); |
| function->set_descriptor(descriptor); |
| } |
| 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() |
| { |
| go_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. |
| |
| Location |
| Named_object::location() const |
| { |
| switch (this->classification_) |
| { |
| default: |
| case NAMED_OBJECT_UNINITIALIZED: |
| go_unreachable(); |
| |
| case NAMED_OBJECT_ERRONEOUS: |
| return Linemap::unknown_location(); |
| |
| 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()->location(); |
| |
| case NAMED_OBJECT_SINK: |
| go_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: |
| go_unreachable(); |
| |
| case NAMED_OBJECT_ERRONEOUS: |
| break; |
| |
| case NAMED_OBJECT_CONST: |
| this->const_value()->export_const(exp, this->name_); |
| break; |
| |
| case NAMED_OBJECT_TYPE: |
| // Types are handled by export::write_types. |
| go_unreachable(); |
| |
| case NAMED_OBJECT_TYPE_DECLARATION: |
| go_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); |
| break; |
| |
| case NAMED_OBJECT_VAR: |
| this->var_value()->export_var(exp, this); |
| break; |
| |
| case NAMED_OBJECT_RESULT_VAR: |
| case NAMED_OBJECT_SINK: |
| go_unreachable(); |
| |
| case NAMED_OBJECT_FUNC: |
| this->func_value()->export_func(exp, this); |
| break; |
| } |
| } |
| |
| // Convert a variable to the backend representation. |
| |
| Bvariable* |
| Named_object::get_backend_variable(Gogo* gogo, Named_object* function) |
| { |
| if (this->classification_ == NAMED_OBJECT_VAR) |
| return this->var_value()->get_backend_variable(gogo, function, |
| this->package_, this->name_); |
| else if (this->classification_ == NAMED_OBJECT_RESULT_VAR) |
| return this->result_var_value()->get_backend_variable(gogo, function, |
| this->name_); |
| else |
| go_unreachable(); |
| } |
| |
| void |
| debug_go_named_object(Named_object* no) |
| { |
| if (no == NULL) |
| { |
| std::cerr << "<null>"; |
| return; |
| } |
| std::cerr << "'" << no->name() << "': "; |
| const char *tag; |
| switch (no->classification()) |
| { |
| case Named_object::NAMED_OBJECT_UNINITIALIZED: |
| tag = "uninitialized"; |
| break; |
| case Named_object::NAMED_OBJECT_ERRONEOUS: |
| tag = "<error>"; |
| break; |
| case Named_object::NAMED_OBJECT_UNKNOWN: |
| tag = "<unknown>"; |
| break; |
| case Named_object::NAMED_OBJECT_CONST: |
| tag = "constant"; |
| break; |
| case Named_object::NAMED_OBJECT_TYPE: |
| tag = "type"; |
| break; |
| case Named_object::NAMED_OBJECT_TYPE_DECLARATION: |
| tag = "type_decl"; |
| break; |
| case Named_object::NAMED_OBJECT_VAR: |
| tag = "var"; |
| break; |
| case Named_object::NAMED_OBJECT_RESULT_VAR: |
| tag = "result_var"; |
| break; |
| case Named_object::NAMED_OBJECT_SINK: |
| tag = "<sink>"; |
| break; |
| case Named_object::NAMED_OBJECT_FUNC: |
| tag = "func"; |
| break; |
| case Named_object::NAMED_OBJECT_FUNC_DECLARATION: |
| tag = "func_decl"; |
| break; |
| case Named_object::NAMED_OBJECT_PACKAGE: |
| tag = "package"; |
| break; |
| default: |
| tag = "<unknown named object classification>"; |
| break; |
| }; |
| std::cerr << tag << "\n"; |
| } |
| |
| // Get the backend representation for this named object. |
| |
| void |
| Named_object::get_backend(Gogo* gogo, std::vector<Bexpression*>& const_decls, |
| std::vector<Btype*>& type_decls, |
| std::vector<Bfunction*>& func_decls) |
| { |
| // If this is a definition, avoid trying to get the backend |
| // representation, as that can crash. |
| if (this->is_redefinition_) |
| { |
| go_assert(saw_errors()); |
| return; |
| } |
| |
| switch (this->classification_) |
| { |
| case NAMED_OBJECT_CONST: |
| if (!Gogo::is_erroneous_name(this->name_)) |
| const_decls.push_back(this->u_.const_value->get_backend(gogo, this)); |
| break; |
| |
| case NAMED_OBJECT_TYPE: |
| { |
| Named_type* named_type = this->u_.type_value; |
| |
| // No need to do anything for aliases-- whatever has to be done |
| // can be done for the alias target. |
| if (named_type->is_alias()) |
| break; |
| |
| if (!Gogo::is_erroneous_name(this->name_)) |
| type_decls.push_back(named_type->get_backend(gogo)); |
| |
| // We need to produce a type descriptor for every named |
| // type, and for a pointer to every named type, since |
| // other files or packages might refer to them. We need |
| // to do this even for hidden types, because they might |
| // still be returned by some function. Simply calling the |
| // type_descriptor method is enough to create the type |
| // descriptor, even though we don't do anything with it. |
| if (this->package_ == NULL && !saw_errors()) |
| { |
| named_type-> |
| type_descriptor_pointer(gogo, Linemap::predeclared_location()); |
| named_type->gc_symbol_pointer(gogo); |
| Type* pn = Type::make_pointer_type(named_type); |
| pn->type_descriptor_pointer(gogo, Linemap::predeclared_location()); |
| pn->gc_symbol_pointer(gogo); |
| } |
| } |
| break; |
| |
| case NAMED_OBJECT_TYPE_DECLARATION: |
| go_error_at(Linemap::unknown_location(), |
| "reference to undefined type %qs", |
| this->message_name().c_str()); |
| return; |
| |
| case NAMED_OBJECT_VAR: |
| case NAMED_OBJECT_RESULT_VAR: |
| case NAMED_OBJECT_SINK: |
| go_unreachable(); |
| |
| case NAMED_OBJECT_FUNC: |
| { |
| Function* func = this->u_.func_value; |
| if (!Gogo::is_erroneous_name(this->name_)) |
| func_decls.push_back(func->get_or_make_decl(gogo, this)); |
| |
| if (func->block() != NULL) |
| func->build(gogo, this); |
| } |
| break; |
| |
| case NAMED_OBJECT_ERRONEOUS: |
| break; |
| |
| default: |
| go_unreachable(); |
| } |
| } |
| |
| // Class Bindings. |
| |
| Bindings::Bindings(Bindings* enclosing) |
| : enclosing_(enclosing), named_objects_(), bindings_() |
| { |
| } |
| |
| // Clear imports. |
| |
| void |
| Bindings::clear_file_scope(Gogo* gogo) |
| { |
| 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 |
| { |
| gogo->add_file_block_name(p->second->name(), p->second->location()); |
| 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()); |
| go_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; |
| } |
| } |
| go_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) |
| { |
| go_assert(named_object == named_object->resolve()); |
| const std::string& name(named_object->name()); |
| go_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) |
| { |
| if (new_object->is_erroneous() && !old_object->is_erroneous()) |
| return new_object; |
| |
| std::string reason; |
| switch (old_object->classification()) |
| { |
| default: |
| case Named_object::NAMED_OBJECT_UNINITIALIZED: |
| go_unreachable(); |
| |
| case Named_object::NAMED_OBJECT_ERRONEOUS: |
| return old_object; |
| |
| 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); |
| go_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: |
| // We have already given an error in the parser for cases where |
| // one parameter or result variable redeclares another one. |
| if ((new_object->is_variable() |
| && new_object->var_value()->is_parameter()) |
| || new_object->is_result_variable()) |
| return old_object; |
| break; |
| |
| case Named_object::NAMED_OBJECT_SINK: |
| go_unreachable(); |
| |
| case Named_object::NAMED_OBJECT_FUNC: |
| break; |
| |
| case Named_object::NAMED_OBJECT_FUNC_DECLARATION: |
| { |
| // We declare the hash and equality functions before defining |
| // them, because we sometimes see that we need the declaration |
| // while we are in the middle of a different function. |
| // |
| // We declare the main function before the user defines it, to |
| // give better error messages. |
| // |
| // We declare inline functions before we define them, as we |
| // only define them if we need them. |
| if (new_object->is_function() |
| && ((Linemap::is_predeclared_location(old_object->location()) |
| && Linemap::is_predeclared_location(new_object->location())) |
| || (Gogo::unpack_hidden_name(old_object->name()) == "main" |
| && Linemap::is_unknown_location(old_object->location())) |
| || (new_object->package() != NULL |
| && old_object->func_declaration_value()->has_imported_body() |
| && new_object->func_value()->is_inline_only()))) |
| { |
| Function_type* old_type = |
| old_object->func_declaration_value()->type(); |
| Function_type* new_type = new_object->func_value()->type(); |
| if (old_type->is_valid_redeclaration(new_type, &reason)) |
| { |
| Function_declaration* fd = |
| old_object->func_declaration_value(); |
| go_assert(fd->asm_name().empty()); |
| 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: |
| break; |
| } |
| |
| std::string n = old_object->message_name(); |
| if (reason.empty()) |
| go_error_at(new_object->location(), "redefinition of %qs", n.c_str()); |
| else |
| go_error_at(new_object->location(), "redefinition of %qs: %s", n.c_str(), |
| reason.c_str()); |
| old_object->set_is_redefinition(); |
| new_object->set_is_redefinition(); |
| |
| if (!Linemap::is_unknown_location(old_object->location()) |
| && !Linemap::is_predeclared_location(old_object->location())) |
| go_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, |
| 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); |
| } |
| |
| // Mark all local variables as used. This is used for some types of |
| // parse error. |
| |
| void |
| Bindings::mark_locals_used() |
| { |
| for (std::vector<Named_object*>::iterator p = this->named_objects_.begin(); |
| p != this->named_objects_.end(); |
| ++p) |
| if ((*p)->is_variable()) |
| (*p)->var_value()->set_is_used(); |
| } |
| |
| // 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. |
| const unsigned int e_or_t = (Traverse::traverse_expressions |
| | Traverse::traverse_types); |
| const unsigned int e_or_t_or_s = (e_or_t |
| | Traverse::traverse_statements); |
| for (size_t i = 0; i < this->named_objects_.size(); ++i) |
| { |
| Named_object* p = this->named_objects_[i]; |
| int t = TRAVERSE_CONTINUE; |
| switch (p->classification()) |
| { |
| case Named_object::NAMED_OBJECT_CONST: |
| if ((traverse_mask & Traverse::traverse_constants) != 0) |
| t = traverse->constant(p, is_global); |
| if (t == TRAVERSE_CONTINUE |
| && (traverse_mask & e_or_t) != 0) |
| { |
| Type* tc = p->const_value()->type(); |
| if (tc != NULL |
| && Type::traverse(tc, traverse) == TRAVERSE_EXIT) |
| return TRAVERSE_EXIT; |
| t = p->const_value()->traverse_expression(traverse); |
| } |
| break; |
| |
| case Named_object::NAMED_OBJECT_VAR: |
| case Named_object::NAMED_OBJECT_RESULT_VAR: |
| if ((traverse_mask & Traverse::traverse_variables) != 0) |
| t = traverse->variable(p); |
| if (t == TRAVERSE_CONTINUE |
| && (traverse_mask & e_or_t) != 0) |
| { |
| if (p->is_result_variable() |
| || p->var_value()->has_type()) |
| { |
| Type* tv = (p->is_variable() |
| ? p->var_value()->type() |
| : p->result_var_value()->type()); |
| if (tv != NULL |
| && Type::traverse(tv, traverse) == TRAVERSE_EXIT) |
| return TRAVERSE_EXIT; |
| } |
| } |
| if (t == TRAVERSE_CONTINUE |
| && (traverse_mask & e_or_t_or_s) != 0 |
| && p->is_variable()) |
| t = p->var_value()->traverse_expression(traverse, traverse_mask); |
| break; |
| |
| case Named_object::NAMED_OBJECT_FUNC: |
| if ((traverse_mask & Traverse::traverse_functions) != 0) |
| t = traverse->function(p); |
| |
| if (t == TRAVERSE_CONTINUE |
| && (traverse_mask |
| & (Traverse::traverse_variables |
| | Traverse::traverse_constants |
| | Traverse::traverse_functions |
| | Traverse::traverse_blocks |
| | Traverse::traverse_statements |
| | Traverse::traverse_expressions |
| | Traverse::traverse_types)) != 0) |
| t = p->func_value()->traverse(traverse); |
| break; |
| |
| case Named_object::NAMED_OBJECT_PACKAGE: |
| // These are traversed in Gogo::traverse. |
| go_assert(is_global); |
| break; |
| |
| case Named_object::NAMED_OBJECT_TYPE: |
| if ((traverse_mask & e_or_t) != 0) |
| t = Type::traverse(p->type_value(), traverse); |
| break; |
| |
| case Named_object::NAMED_OBJECT_TYPE_DECLARATION: |
| case Named_object::NAMED_OBJECT_FUNC_DECLARATION: |
| case Named_object::NAMED_OBJECT_UNKNOWN: |
| case Named_object::NAMED_OBJECT_ERRONEOUS: |
| break; |
| |
| case Named_object::NAMED_OBJECT_SINK: |
| default: |
| go_unreachable(); |
| } |
| |
| if (t == TRAVERSE_EXIT) |
| return TRAVERSE_EXIT; |
| } |
| |
| // If we need to traverse types, check the function declarations, |
| // which have types. Also check any methods of a type declaration. |
| if ((traverse_mask & e_or_t) != 0) |
| { |
| for (Bindings::const_declarations_iterator p = |
| this->begin_declarations(); |
| p != this->end_declarations(); |
| ++p) |
| { |
| if (p->second->is_function_declaration()) |
| { |
| if (Type::traverse(p->second->func_declaration_value()->type(), |
| traverse) |
| == TRAVERSE_EXIT) |
| return TRAVERSE_EXIT; |
| } |
| else if (p->second->is_type_declaration()) |
| { |
| const std::vector<Named_object*>* methods = |
| p->second->type_declaration_value()->methods(); |
| for (std::vector<Named_object*>::const_iterator pm = |
| methods->begin(); |
| pm != methods->end(); |
| pm++) |
| { |
| Named_object* no = *pm; |
| Type *t; |
| if (no->is_function()) |
| t = no->func_value()->type(); |
| else if (no->is_function_declaration()) |
| t = no->func_declaration_value()->type(); |
| else |
| continue; |
| if (Type::traverse(t, traverse) == TRAVERSE_EXIT) |
| return TRAVERSE_EXIT; |
| } |
| } |
| } |
| } |
| |
| // Traverse function declarations when needed. |
| if ((traverse_mask & Traverse::traverse_func_declarations) != 0) |
| { |
| for (Bindings::const_declarations_iterator p = this->begin_declarations(); |
| p != this->end_declarations(); |
| ++p) |
| { |
| if (p->second->is_function_declaration()) |
| { |
| if (traverse->function_declaration(p->second) == TRAVERSE_EXIT) |
| return TRAVERSE_EXIT; |
| } |
| } |
| } |
| |
| return TRAVERSE_CONTINUE; |
| } |
| |
| void |
| Bindings::debug_dump() |
| { |
| std::set<Named_object*> defs; |
| for (size_t i = 0; i < this->named_objects_.size(); ++i) |
| defs.insert(this->named_objects_[i]); |
| for (Contour::iterator p = this->bindings_.begin(); |
| p != this->bindings_.end(); |
| ++p) |
| { |
| const char* tag = " "; |
| if (defs.find(p->second) != defs.end()) |
| tag = "* "; |
| std::cerr << tag; |
| debug_go_named_object(p->second); |
| } |
| } |
| |
| void |
| debug_go_bindings(Bindings* bindings) |
| { |
| if (bindings != NULL) |
| bindings->debug_dump(); |
| } |
| |
| // Class Label. |
| |
| // Clear any references to this label. |
| |
| void |
| Label::clear_refs() |
| { |
| for (std::vector<Bindings_snapshot*>::iterator p = this->refs_.begin(); |
| p != this->refs_.end(); |
| ++p) |
| delete *p; |
| this->refs_.clear(); |
| } |
| |
| // Get the backend representation for a label. |
| |
| Blabel* |
| Label::get_backend_label(Translate_context* context) |
| { |
| if (this->blabel_ == NULL) |
| { |
| Function* function = context->function()->func_value(); |
| Bfunction* bfunction = function->get_decl(); |
| this->blabel_ = context->backend()->label(bfunction, this->name_, |
| this->location_); |
| } |
| return this->blabel_; |
| } |
| |
| // Return an expression for the address of this label. |
| |
| Bexpression* |
| Label::get_addr(Translate_context* context, Location location) |
| { |
| Blabel* label = this->get_backend_label(context); |
| return context->backend()->label_address(label, location); |
| } |
| |
| // Return the dummy label that represents any instance of the blank label. |
| |
| Label* |
| Label::create_dummy_label() |
| { |
| static Label* dummy_label; |
| if (dummy_label == NULL) |
| { |
| dummy_label = new Label("_"); |
| dummy_label->set_is_used(); |
| } |
| return dummy_label; |
| } |
| |
| // Class Unnamed_label. |
| |
| // Get the backend representation for an unnamed label. |
| |
| Blabel* |
| Unnamed_label::get_blabel(Translate_context* context) |
| { |
| if (this->blabel_ == NULL) |
| { |
| Function* function = context->function()->func_value(); |
| Bfunction* bfunction = function->get_decl(); |
| this->blabel_ = context->backend()->label(bfunction, "", |
| this->location_); |
| } |
| return this->blabel_; |
| } |
| |
| // Return a statement which defines this unnamed label. |
| |
| Bstatement* |
| Unnamed_label::get_definition(Translate_context* context) |
| { |
| Blabel* blabel = this->get_blabel(context); |
| return context->backend()->label_definition_statement(blabel); |
| } |
| |
| // Return a goto statement to this unnamed label. |
| |
| Bstatement* |
| Unnamed_label::get_goto(Translate_context* context, Location location) |
| { |
| Blabel* blabel = this->get_blabel(context); |
| return context->backend()->goto_statement(blabel, location); |
| } |
| |
| // Class Package. |
| |
| Package::Package(const std::string& pkgpath, |
| const std::string& pkgpath_symbol, Location location) |
| : pkgpath_(pkgpath), pkgpath_symbol_(pkgpath_symbol), |
| package_name_(), bindings_(new Bindings(NULL)), |
| location_(location) |
| { |
| go_assert(!pkgpath.empty()); |
| } |
| |
| // Set the package name. |
| |
| void |
| Package::set_package_name(const std::string& package_name, Location location) |
| { |
| go_assert(!package_name.empty()); |
| if (this->package_name_.empty()) |
| this->package_name_ = package_name; |
| else if (this->package_name_ != package_name) |
| go_error_at(location, |
| ("saw two different packages with " |
| "the same package path %s: %s, %s"), |
| this->pkgpath_.c_str(), this->package_name_.c_str(), |
| package_name.c_str()); |
| } |
| |
| // Return the pkgpath symbol, which is a prefix for symbols defined in |
| // this package. |
| |
| std::string |
| Package::pkgpath_symbol() const |
| { |
| if (this->pkgpath_symbol_.empty()) |
| return Gogo::pkgpath_for_symbol(this->pkgpath_); |
| return this->pkgpath_symbol_; |
| } |
| |
| // Set the package path symbol. |
| |
| void |
| Package::set_pkgpath_symbol(const std::string& pkgpath_symbol) |
| { |
| go_assert(!pkgpath_symbol.empty()); |
| if (this->pkgpath_symbol_.empty()) |
| this->pkgpath_symbol_ = pkgpath_symbol; |
| else |
| go_assert(this->pkgpath_symbol_ == pkgpath_symbol); |
| } |
| |
| // Note that symbol from this package was and qualified by ALIAS. |
| |
| void |
| Package::note_usage(const std::string& alias) const |
| { |
| Aliases::const_iterator p = this->aliases_.find(alias); |
| go_assert(p != this->aliases_.end()); |
| p->second->note_usage(); |
| } |
| |
| // Forget a given usage. If forgetting this usage means this package becomes |
| // unused, report that error. |
| |
| void |
| Package::forget_usage(Expression* usage) const |
| { |
| if (this->fake_uses_.empty()) |
| return; |
| |
| std::set<Expression*>::iterator p = this->fake_uses_.find(usage); |
| go_assert(p != this->fake_uses_.end()); |
| this->fake_uses_.erase(p); |
| |
| if (this->fake_uses_.empty()) |
| go_error_at(this->location(), "imported and not used: %s", |
| Gogo::message_name(this->package_name()).c_str()); |
| } |
| |
| // Clear the used field for the next file. If the only usages of this package |
| // are possibly fake, keep the fake usages for lowering. |
| |
| void |
| Package::clear_used() |
| { |
| std::string dot_alias = "." + this->package_name(); |
| Aliases::const_iterator p = this->aliases_.find(dot_alias); |
| if (p != this->aliases_.end() && p->second->used() > this->fake_uses_.size()) |
| this->fake_uses_.clear(); |
| |
| this->aliases_.clear(); |
| } |
| |
| Package_alias* |
| Package::add_alias(const std::string& alias, Location location) |
| { |
| Aliases::const_iterator p = this->aliases_.find(alias); |
| if (p == this->aliases_.end()) |
| { |
| std::pair<Aliases::iterator, bool> ret; |
| ret = this->aliases_.insert(std::make_pair(alias, |
| new Package_alias(location))); |
| p = ret.first; |
| } |
| return p->second; |
| } |
| |
| // 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; |
| go_assert((this->traverse_mask() & traverse_types) != 0 |
| || (this->traverse_mask() & traverse_expressions) != 0); |
| // We mostly only have to remember named types. But it turns out |
| // that an interface type can refer to itself without using a name |
| // by relying on interface inheritance, as in |
| // |
| // type I interface { F() interface{I} } |
| // |
| // Similarly it is possible for array types to refer to themselves |
| // without a name, e.g. |
| // |
| // var x [uintptr(unsafe.Sizeof(&x))]byte |
| // |
| if (type->classification() != Type::TYPE_NAMED |
| && type->classification() != Type::TYPE_ARRAY |
| && type->classification() != Type::TYPE_INTERFACE) |
| 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. NB: this routine used to assert if the traverse |
| // mask did not include expressions/types -- this is no longer the case, |
| // since it can be useful to remember specific expressions during |
| // walks that only cover statements. |
| |
| bool |
| Traverse::remember_expression(const Expression* expression) |
| { |
| 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*) |
| { |
| go_unreachable(); |
| } |
| |
| int |
| Traverse::constant(Named_object*, bool) |
| { |
| go_unreachable(); |
| } |
| |
| int |
| Traverse::function(Named_object*) |
| { |
| go_unreachable(); |
| } |
| |
| int |
| Traverse::block(Block*) |
| { |
| go_unreachable(); |
| } |
| |
| int |
| Traverse::statement(Block*, size_t*, Statement*) |
| { |
| go_unreachable(); |
| } |
| |
| int |
| Traverse::expression(Expression**) |
| { |
| go_unreachable(); |
| } |
| |
| int |
| Traverse::type(Type*) |
| { |
| go_unreachable(); |
| } |
| |
| int |
| Traverse::function_declaration(Named_object*) |
| { |
| go_unreachable(); |
| } |
| |
| // Class Statement_inserter. |
| |
| void |
| Statement_inserter::insert(Statement* s) |
| { |
| if (this->statements_added_ != NULL) |
| this->statements_added_->insert(s); |
| |
| if (this->block_ != NULL) |
| { |
| go_assert(this->pindex_ != NULL); |
| this->block_->insert_statement_before(*this->pindex_, s); |
| ++*this->pindex_; |
| } |
| else if (this->var_ != NULL) |
| this->var_->add_preinit_statement(this->gogo_, s); |
| else |
| go_assert(saw_errors()); |
| } |