From 3e6f8fe1bc4c60859113bca7970b0b8db56eb442 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Mon, 3 Jun 2019 23:04:23 +0000 Subject: [PATCH] compiler: permit inlining references to global variables This requires tracking all references to unexported variables, so that we can make them global symbols in the object file, and can export them so that other compilations can see the right definition for their own inline bodies. This introduces a syntax for referencing names defined in other packages: a prefix, where NN is the package index. This will need to be added to gccgoimporter, but I didn't do it yet since it isn't yet possible to create an object for which gccgoimporter will see a prefix. This increases the number of inlinable functions in the standard library from 181 to 215, adding functions like context.Background. Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/177920 From-SVN: r271891 --- gcc/go/gofrontend/MERGE | 2 +- gcc/go/gofrontend/export.cc | 163 ++++++++++++++++++++++++++++--- gcc/go/gofrontend/export.h | 11 ++- gcc/go/gofrontend/expressions.cc | 91 ++++++++++++----- gcc/go/gofrontend/expressions.h | 10 +- gcc/go/gofrontend/gogo.cc | 102 +++++++++++++++---- gcc/go/gofrontend/gogo.h | 43 +++++--- gcc/go/gofrontend/import.cc | 97 +++++++++++++++--- gcc/go/gofrontend/import.h | 50 +++++++++- 9 files changed, 482 insertions(+), 87 deletions(-) diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index b1b36b2ac94..43df2f7a5de 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -95784e8eec75cfeb2363fb22b51085380e564af9 +37a47e4691b4602dd167f82c64a6569019584a80 The first line of this file holds the git revision number of the last merge done from the gofrontend repository. diff --git a/gcc/go/gofrontend/export.cc b/gcc/go/gofrontend/export.cc index 066455a4ee9..108fdac22e5 100644 --- a/gcc/go/gofrontend/export.cc +++ b/gcc/go/gofrontend/export.cc @@ -11,6 +11,7 @@ #include "gogo.h" #include "types.h" +#include "expressions.h" #include "statements.h" #include "export.h" @@ -89,13 +90,88 @@ typedef Unordered_map_hash(const Type*, int, Type_hash_alias_identical, static Type_refs type_refs; +// A traversal class to collect functions and global variables +// referenced by inlined functions. + +class Collect_references_from_inline : public Traverse +{ + public: + Collect_references_from_inline(Unordered_set(Named_object*)* exports, + std::vector* check_inline_refs) + : Traverse(traverse_expressions), + exports_(exports), check_inline_refs_(check_inline_refs) + { } + + int + expression(Expression**); + + private: + // The set of named objects to export. + Unordered_set(Named_object*)* exports_; + // Functions we are exporting with inline bodies that need to be checked. + std::vector* check_inline_refs_; +}; + +int +Collect_references_from_inline::expression(Expression** pexpr) +{ + const Expression* expr = *pexpr; + + const Var_expression* ve = expr->var_expression(); + if (ve != NULL) + { + Named_object* no = ve->named_object(); + if (no->is_variable() && no->var_value()->is_global()) + { + this->exports_->insert(no); + no->var_value()->set_is_referenced_by_inline(); + } + return TRAVERSE_CONTINUE; + } + + const Func_expression* fe = expr->func_expression(); + if (fe != NULL) + { + Named_object* no = fe->named_object(); + std::pair ins = + this->exports_->insert(no); + + if (no->is_function()) + no->func_value()->set_is_referenced_by_inline(); + + // If ins.second is false then this object was already in + // exports_, in which case it was already added to + // check_inline_refs_ the first time we added it to exports_, so + // we don't need to add it again. + if (ins.second + && no->is_function() + && no->func_value()->export_for_inlining()) + this->check_inline_refs_->push_back(no); + + return TRAVERSE_CONTINUE; + } + + return TRAVERSE_CONTINUE; +} + // A functor to sort Named_object pointers by name. struct Sort_bindings { bool operator()(const Named_object* n1, const Named_object* n2) const - { return n1->name() < n2->name(); } + { + if (n1->package() != n2->package()) + { + if (n1->package() == NULL) + return true; + if (n2->package() == NULL) + return false; + return n1->package()->pkgpath() < n2->package()->pkgpath(); + } + + return n1->name() < n2->name(); + } }; // Return true if we should export NO. @@ -153,17 +229,26 @@ Export::export_globals(const std::string& package_name, if (saw_errors()) return; - // Export the symbols in sorted order. That will reduce cases where - // irrelevant changes to the source code affect the exported - // interface. - std::vector exports; - exports.reserve(bindings->size_definitions()); + // EXPORTS is the set of objects to export. CHECK_INLINE_REFS is a + // list of exported function with inline bodies that need to be + // checked for references to other objects. Every function on + // CHECK_INLINE_REFS is also on EXPORTS. + Unordered_set(Named_object*) exports; + std::vector check_inline_refs; for (Bindings::const_definitions_iterator p = bindings->begin_definitions(); p != bindings->end_definitions(); ++p) - if (should_export(*p)) - exports.push_back(*p); + { + if (should_export(*p)) + { + exports.insert(*p); + + if ((*p)->is_function() + && (*p)->func_value()->export_for_inlining()) + check_inline_refs.push_back(*p); + } + } for (Bindings::const_declarations_iterator p = bindings->begin_declarations(); @@ -174,15 +259,47 @@ Export::export_globals(const std::string& package_name, // supporting C code. We do not export type declarations. if (p->second->is_function_declaration() && should_export(p->second)) - exports.push_back(p->second); + exports.insert(p->second); + } + + // Look through the bodies of the functions in CHECK_INLINE_REFS to + // find other names we may need to export, to satisfy those + // references. Use CHECKED to skip checking function bodies more + // than once. + Unordered_set(Named_object*) checked; + Collect_references_from_inline refs(&exports, &check_inline_refs); + while (!check_inline_refs.empty()) + { + Named_object* no = check_inline_refs.back(); + check_inline_refs.pop_back(); + std::pair ins = + checked.insert(no); + if (ins.second) + { + // This traversal may add new objects to EXPORTS and new + // functions to CHECK_INLINE_REFS. + no->func_value()->block()->traverse(&refs); + } } - std::sort(exports.begin(), exports.end(), Sort_bindings()); + // Export the symbols in sorted order. That will reduce cases where + // irrelevant changes to the source code affect the exported + // interface. + std::vector sorted_exports; + sorted_exports.reserve(exports.size()); + + for (Unordered_set(Named_object*)::const_iterator p = exports.begin(); + p != exports.end(); + ++p) + sorted_exports.push_back(*p); + + std::sort(sorted_exports.begin(), sorted_exports.end(), Sort_bindings()); // Assign indexes to all exported types and types referenced by // exported types, and collect all packages mentioned. Unordered_set(const Package*) type_imports; - int unexported_type_index = this->prepare_types(&exports, &type_imports); + int unexported_type_index = this->prepare_types(&sorted_exports, + &type_imports); // Although the export data is readable, at least this version is, // it is conceptually a binary format. Start with a four byte @@ -223,8 +340,8 @@ Export::export_globals(const std::string& package_name, this->write_types(unexported_type_index); // Write out the non-type export data. - for (std::vector::const_iterator p = exports.begin(); - p != exports.end(); + for (std::vector::const_iterator p = sorted_exports.begin(); + p != sorted_exports.end(); ++p) { if (!(*p)->is_type()) @@ -591,6 +708,7 @@ Export::write_imports(const std::map& imports, std::sort(sorted_imports.begin(), sorted_imports.end(), import_compare); + int package_index = 1; for (std::vector >::const_iterator p = sorted_imports.begin(); p != sorted_imports.end(); @@ -604,7 +722,8 @@ Export::write_imports(const std::map& imports, this->write_string(p->first); this->write_c_string("\"\n"); - this->packages_.insert(p->second); + this->packages_[p->second] = package_index; + package_index++; } // Write out a separate list of indirectly imported packages. @@ -631,6 +750,9 @@ Export::write_imports(const std::map& imports, this->write_c_string(" "); this->write_string((*p)->pkgpath()); this->write_c_string("\n"); + + this->packages_[*p] = package_index; + package_index++; } } @@ -983,6 +1105,19 @@ Export::write_unsigned(unsigned value) this->write_c_string(buf); } +// Return the index of a package. + +int +Export::package_index(const Package* pkg) const +{ + Unordered_map(const Package *, int)::const_iterator p = + this->packages_.find(pkg); + go_assert(p != this->packages_.end()); + int index = p->second; + go_assert(index != 0); + return index; +} + // Return the index of a type. int diff --git a/gcc/go/gofrontend/export.h b/gcc/go/gofrontend/export.h index 69fbd6e3bdb..e3932d48130 100644 --- a/gcc/go/gofrontend/export.h +++ b/gcc/go/gofrontend/export.h @@ -201,6 +201,10 @@ class Export : public String_dump void write_unsigned(unsigned); + // Return the index of a package. + int + package_index(const Package* p) const; + private: Export(const Export&); Export& operator=(const Export&); @@ -255,7 +259,7 @@ class Export : public String_dump // Index number of next type. int type_index_; // Packages we have written out. - Unordered_set(const Package*) packages_; + Unordered_map(const Package*, int) packages_; }; // An export streamer that puts the export stream in a named section. @@ -354,6 +358,11 @@ class Export_function_body : public String_dump decrement_indent() { --this->indent_; } + // Return the index of a package. + int + package_index(const Package* p) const + { return this->exp_->package_index(p); } + // Return a reference to the completed body. const std::string& body() const diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index 9d8c085aead..6aca5f86ec7 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -87,6 +87,27 @@ Expression::do_export(Export_function_body*) const go_unreachable(); } +// Write a name to the export data. + +void +Expression::export_name(Export_function_body* efb, const Named_object* no) +{ + if (no->package() != NULL) + { + char buf[50]; + snprintf(buf, sizeof buf, "", efb->package_index(no->package())); + efb->write_c_string(buf); + } + + if (!Gogo::is_hidden_name(no->name())) + efb->write_string(no->name()); + else + { + efb->write_c_string("."); + efb->write_string(Gogo::unpack_hidden_name(no->name())); + } +} + // Give an error saying that the value of the expression is not used. void @@ -842,29 +863,16 @@ Var_expression::do_address_taken(bool escapes) } } -// The cost to inline a variable reference. We currently only support -// references to parameters and local variables. - -int -Var_expression::do_inlining_cost() const -{ - if (this->variable_->is_variable()) - { - if (!this->variable_->var_value()->is_global()) - return 1; - } - else if (this->variable_->is_result_variable()) - return 1; - - return 0x100000; -} - // Export a reference to a variable. void Var_expression::do_export(Export_function_body* efb) const { - efb->write_string(Gogo::unpack_hidden_name(this->variable_->name())); + Named_object* no = this->variable_; + if (no->is_result_variable() || !no->var_value()->is_global()) + efb->write_string(Gogo::unpack_hidden_name(no->name())); + else + Expression::export_name(efb, no); } // Get the backend representation for a reference to a variable. @@ -17535,26 +17543,55 @@ Expression::import_expression(Import_expression* imp, Location loc) } if (ifb->saw_error()) return Expression::make_error(loc); - std::string id = ifb->read_identifier(); - if (id.empty()) + return Expression::import_identifier(ifb, loc); +} + +// Import an identifier in an expression. This is a reference to a +// variable or function. + +Expression* +Expression::import_identifier(Import_function_body* ifb, Location loc) +{ + std::string id; + Package* pkg; + bool is_exported; + if (!Import::read_qualified_identifier(ifb, &id, &pkg, &is_exported)) { if (!ifb->saw_error()) - go_error_at(imp->location(), - "import error: expected identifier at %lu", + go_error_at(ifb->location(), + "import error for %qs: bad qualified identifier at %lu", + ifb->name().c_str(), static_cast(ifb->off())); ifb->set_saw_error(); return Expression::make_error(loc); } - Named_object* var = ifb->block()->bindings()->lookup(id); - if (var == NULL) + + Named_object* no = NULL; + if (pkg == NULL && is_exported) + no = ifb->block()->bindings()->lookup(id); + if (no == NULL) + { + const Package* ipkg = pkg; + if (ipkg == NULL) + ipkg = ifb->function()->package(); + if (!is_exported) + id = '.' + ipkg->pkgpath() + '.' + id; + no = ipkg->bindings()->lookup(id); + } + if (no == NULL) + no = ifb->gogo()->lookup_global(id.c_str()); + + if (no == NULL) { if (!ifb->saw_error()) - go_error_at(imp->location(), "import error: lookup of %qs failed", - id.c_str()); + go_error_at(ifb->location(), + "import error for %qs: lookup of %qs failed", + ifb->name().c_str(), id.c_str()); ifb->set_saw_error(); return Expression::make_error(loc); } - return Expression::make_var_reference(var, loc); + + return Expression::make_var_reference(no, loc); } // Class Expression_list. diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h index ea98facf5dc..e17e8dc0c7f 100644 --- a/gcc/go/gofrontend/expressions.h +++ b/gcc/go/gofrontend/expressions.h @@ -1201,6 +1201,10 @@ class Expression void report_error(const char*); + // Write a name to export data. + static void + export_name(Export_function_body* efb, const Named_object*); + // Child class implements dumping to a dump context. virtual void do_dump_expression(Ast_dump_context*) const = 0; @@ -1246,6 +1250,9 @@ class Expression static Expression* convert_interface_to_type(Type*, Expression*, Location); + static Expression* + import_identifier(Import_function_body*, Location); + // The expression classification. Expression_classification classification_; // The location in the input file. @@ -1410,7 +1417,8 @@ class Var_expression : public Expression { return this; } int - do_inlining_cost() const; + do_inlining_cost() const + { return 1; } void do_export(Export_function_body*) const; diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc index e94c5679d6d..b97367ca066 100644 --- a/gcc/go/gofrontend/gogo.cc +++ b/gcc/go/gofrontend/gogo.cc @@ -5182,7 +5182,7 @@ Function::Function(Function_type* type, Named_object* enclosing, Block* block, 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_inline_only_(false), is_referenced_by_inline_(false) { } @@ -5534,12 +5534,12 @@ Function::defer_stack(Location location) // Export the function. void -Function::export_func(Export* exp, const std::string& name) const +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, name, this->type_, this->results_, + Function::export_func_with_type(exp, no, this->type_, this->results_, this->is_method() && this->nointerface(), block, this->location_); } @@ -5547,7 +5547,7 @@ Function::export_func(Export* exp, const std::string& name) const // Export a function with a type. void -Function::export_func_with_type(Export* exp, const std::string& name, +Function::export_func_with_type(Export* exp, const Named_object* no, const Function_type* fntype, Function::Results* result_vars, bool nointerface, Block* block, Location loc) @@ -5571,7 +5571,21 @@ Function::export_func_with_type(Export* exp, const std::string& name, exp->write_c_string(") "); } - exp->write_string(name); + if (no->package() != NULL && !fntype->is_method()) + { + char buf[50]; + snprintf(buf, sizeof buf, "", 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(); @@ -5677,8 +5691,9 @@ Function::export_func_with_type(Export* exp, const std::string& name, // Import a function. -void +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, @@ -5711,7 +5726,13 @@ Function::import_func(Import* imp, std::string* pname, imp->require_c_string(") "); } - *pname = imp->read_identifier(); + 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; @@ -5812,11 +5833,13 @@ Function::import_func(Import* imp, std::string* pname, { go_error_at(imp->location(), "invalid inline function length %s", lenstr.c_str()); - return; + return false; } *body = imp->read(static_cast(llen)); } + + return true; } // Get the backend representation. @@ -5829,7 +5852,10 @@ Function::get_or_make_decl(Gogo* gogo, Named_object* no) unsigned int flags = 0; bool is_init_fn = false; 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" @@ -5877,6 +5903,11 @@ Function::get_or_make_decl(Gogo* gogo, Named_object* no) else asm_name = gogo->function_asm_name(no->name(), no->package(), rtype); + // 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; + // 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 @@ -7015,7 +7046,8 @@ Variable::Variable(Type* type, Expression* init, bool is_global, 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), toplevel_decl_(NULL) + in_unique_section_(false), is_referenced_by_inline_(false), + toplevel_decl_(NULL) { go_assert(type != NULL || init != NULL); go_assert(!is_parameter || init == NULL); @@ -7497,11 +7529,25 @@ Variable::get_init_block(Gogo* gogo, Named_object* function, // Export the variable void -Variable::export_var(Export* exp, const std::string& name) const +Variable::export_var(Export* exp, const Named_object* no) const { go_assert(this->is_global_); exp->write_c_string("var "); - exp->write_string(name); + if (no->package() != NULL) + { + char buf[50]; + snprintf(buf, sizeof buf, "", 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"); @@ -7509,15 +7555,23 @@ Variable::export_var(Export* exp, const std::string& name) const // Import a variable. -void -Variable::import_var(Import* imp, std::string* pname, Type** ptype) +bool +Variable::import_var(Import* imp, std::string* pname, Package** ppkg, + bool* pis_exported, Type** ptype) { imp->require_c_string("var "); - *pname = imp->read_identifier(); + 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. @@ -7568,6 +7622,18 @@ Variable::get_backend_variable(Gogo* gogo, Named_object* function, && var_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; + bvar = backend->global_variable(var_name, asm_name, btype, @@ -8145,11 +8211,11 @@ Named_object::export_named_object(Export* exp) const break; case NAMED_OBJECT_FUNC_DECLARATION: - this->func_declaration_value()->export_func(exp, this->name_); + this->func_declaration_value()->export_func(exp, this); break; case NAMED_OBJECT_VAR: - this->var_value()->export_var(exp, this->name_); + this->var_value()->export_var(exp, this); break; case NAMED_OBJECT_RESULT_VAR: @@ -8157,7 +8223,7 @@ Named_object::export_named_object(Export* exp) const go_unreachable(); case NAMED_OBJECT_FUNC: - this->func_value()->export_func(exp, this->name_); + this->func_value()->export_func(exp, this); break; } } diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h index fd28ed1c3df..5b77d6d9327 100644 --- a/gcc/go/gofrontend/gogo.h +++ b/gcc/go/gofrontend/gogo.h @@ -1486,6 +1486,11 @@ class Function set_is_inline_only() { this->is_inline_only_ = true; } + // Mark the function as referenced by an inline body. + void + set_is_referenced_by_inline() + { this->is_referenced_by_inline_ = true; } + // Swap with another function. Used only for the thunk which calls // recover. void @@ -1538,17 +1543,18 @@ class Function // Export the function. void - export_func(Export*, const std::string& name) const; + export_func(Export*, const Named_object*) const; // Export a function with a type. static void - export_func_with_type(Export*, const std::string& name, + export_func_with_type(Export*, const Named_object*, const Function_type*, Results*, bool nointerface, Block* block, Location); - // Import a function. - static void - import_func(Import*, std::string* pname, Typed_identifier** receiver, + // Import a function. Reports whether the import succeeded. + static bool + import_func(Import*, std::string* pname, Package** pkg, + bool* is_exported, Typed_identifier** receiver, Typed_identifier_list** pparameters, Typed_identifier_list** presults, bool* is_varargs, bool* nointerface, std::string* body); @@ -1628,6 +1634,9 @@ class Function // True if this function is inline only: if it should not be emitted // if it is not inlined. bool is_inline_only_ : 1; + // True if this function is referenced from an inlined body that + // will be put into the export data. + bool is_referenced_by_inline_ : 1; }; // A snapshot of the current binding state. @@ -1768,9 +1777,9 @@ class Function_declaration // Export a function declaration. void - export_func(Export* exp, const std::string& name) const + export_func(Export* exp, const Named_object* no) const { - Function::export_func_with_type(exp, name, this->fntype_, NULL, + Function::export_func_with_type(exp, no, this->fntype_, NULL, this->is_method() && this->nointerface(), NULL, this->location_); } @@ -2022,6 +2031,14 @@ class Variable this->in_unique_section_ = true; } + // Mark the variable as referenced by an inline body. + void + set_is_referenced_by_inline() + { + go_assert(this->is_global_); + this->is_referenced_by_inline_ = true; + } + // Return the top-level declaration for this variable. Statement* toplevel_decl() @@ -2062,11 +2079,12 @@ class Variable // Export the variable. void - export_var(Export*, const std::string& name) const; + export_var(Export*, const Named_object*) const; - // Import a variable. - static void - import_var(Import*, std::string* pname, Type** ptype); + // Import a variable. Reports whether the import succeeded. + static bool + import_var(Import*, std::string* pname, Package** pkg, bool* is_exported, + Type** ptype); private: // The type of a tuple. @@ -2133,6 +2151,9 @@ class Variable // True if this variable should be put in a unique section. This is // used for field tracking. bool in_unique_section_ : 1; + // True if this variable is referenced from an inlined body that + // will be put into the export data. + bool is_referenced_by_inline_ : 1; // The top-level declaration for this variable. Only used for local // variables. Must be a Temporary_statement if not NULL. Statement* toplevel_decl_; diff --git a/gcc/go/gofrontend/import.cc b/gcc/go/gofrontend/import.cc index c1982eb4300..ff92b8248d6 100644 --- a/gcc/go/gofrontend/import.cc +++ b/gcc/go/gofrontend/import.cc @@ -288,8 +288,8 @@ Import::find_object_export_data(const std::string& filename, Import::Import(Stream* stream, Location location) : gogo_(NULL), stream_(stream), location_(location), package_(NULL), - add_to_globals_(false), type_data_(), type_pos_(0), type_offsets_(), - builtin_types_((- SMALLEST_BUILTIN_CODE) + 1), + add_to_globals_(false), packages_(), type_data_(), type_pos_(0), + type_offsets_(), builtin_types_((- SMALLEST_BUILTIN_CODE) + 1), types_(), version_(EXPORT_FORMAT_UNKNOWN) { } @@ -487,6 +487,8 @@ Import::read_one_import() Package* p = this->gogo_->register_package(pkgpath, "", Linemap::unknown_location()); p->set_package_name(package_name, this->location()); + + this->packages_.push_back(p); } // Read an indirectimport line. @@ -503,6 +505,8 @@ Import::read_one_indirect_import() Package* p = this->gogo_->register_package(pkgpath, "", Linemap::unknown_location()); p->set_package_name(package_name, this->location()); + + this->packages_.push_back(p); } // Read the list of import control functions and/or init graph. @@ -721,12 +725,19 @@ void Import::import_var() { std::string name; + Package* vpkg; + bool is_exported; Type* type; - Variable::import_var(this, &name, &type); + if (!Variable::import_var(this, &name, &vpkg, &is_exported, &type)) + return; + if (vpkg == NULL) + vpkg = this->package_; + if (!is_exported) + name = '.' + vpkg->pkgpath() + '.' + name; Variable* var = new Variable(type, NULL, true, false, false, this->location_); Named_object* no; - no = this->package_->add_variable(name, var); + no = vpkg->add_variable(name, var); if (this->add_to_globals_) this->gogo_->add_dot_import_object(no); } @@ -735,18 +746,26 @@ Import::import_var() // THIS->PACKAGE_, but it will be different for a method associated // with a type defined in a different package. -Named_object* +void Import::import_func(Package* package) { std::string name; + Package* fpkg; + bool is_exported; Typed_identifier* receiver; Typed_identifier_list* parameters; Typed_identifier_list* results; bool is_varargs; bool nointerface; std::string body; - Function::import_func(this, &name, &receiver, ¶meters, &results, - &is_varargs, &nointerface, &body); + if (!Function::import_func(this, &name, &fpkg, &is_exported, &receiver, + ¶meters, &results, &is_varargs, &nointerface, + &body)) + return; + if (fpkg == NULL) + fpkg = package; + if (!is_exported) + name = '.' + fpkg->pkgpath() + '.' + name; Function_type *fntype = Type::make_function_type(receiver, parameters, results, this->location_); if (is_varargs) @@ -768,13 +787,13 @@ Import::import_func(Package* package) rtype = rtype->points_to(); if (rtype->is_error_type()) - return NULL; + return; else if (rtype->named_type() != NULL) - no = rtype->named_type()->add_method_declaration(name, package, fntype, + no = rtype->named_type()->add_method_declaration(name, fpkg, fntype, loc); else if (rtype->forward_declaration_type() != NULL) no = rtype->forward_declaration_type()->add_method_declaration(name, - package, + fpkg, fntype, loc); else @@ -782,7 +801,7 @@ Import::import_func(Package* package) } else { - no = package->add_function_declaration(name, fntype, loc); + no = fpkg->add_function_declaration(name, fntype, loc); if (this->add_to_globals_) this->gogo_->add_dot_import_object(no); } @@ -791,8 +810,6 @@ Import::import_func(Package* package) no->func_declaration_value()->set_nointerface(); if (!body.empty() && !no->func_declaration_value()->has_imported_body()) no->func_declaration_value()->set_imported_body(this, body); - - return no; } // Read a type definition and initialize the entry in this->types_. @@ -1233,6 +1250,60 @@ Import::read_identifier() return ret; } +// Read a possibly qualified identifier from IMP. The qualification +// is , where ID is a package number. If the name has a leading +// '.', it is not exported; otherwise, it is. Set *NAME, *PKG and +// *IS_EXPORTED. Reports whether the read succeeded. + +bool +Import::read_qualified_identifier(Import_expression* imp, std::string* name, + Package** pkg, bool* is_exported) +{ + *pkg = NULL; + if (imp->match_c_string("advance(2); + char buf[50]; + char *pbuf = &buf[0]; + while (true) + { + int next = imp->peek_char(); + if (next == -1 || static_cast(pbuf - buf) >= sizeof buf - 1) + return false; + if (next == '>') + { + imp->advance(1); + break; + } + *pbuf = static_cast(next); + ++pbuf; + imp->advance(1); + } + + *pbuf = '\0'; + char *end; + long index = strtol(buf, &end, 10); + if (*end != '\0' + || index <= 0 + || static_cast(index) > imp->max_package_index()) + return false; + + *pkg = imp->package_at_index(index); + go_assert(*pkg != NULL); + } + + *is_exported = true; + if (imp->match_c_string(".")) + { + imp->advance(1); + *is_exported = false; + } + + *name = imp->read_identifier(); + + return !name->empty(); +} + // Read a name from the stream. std::string diff --git a/gcc/go/gofrontend/import.h b/gcc/go/gofrontend/import.h index c46a37e0ce9..ab30aed0ae5 100644 --- a/gcc/go/gofrontend/import.h +++ b/gcc/go/gofrontend/import.h @@ -72,6 +72,14 @@ class Import_expression virtual Type* read_type() = 0; + // Return the maximum valid package index. + virtual size_t + max_package_index() const = 0; + + // Return the package for a package index. + virtual Package* + package_at_index(int index) = 0; + // Return the version number of the export data we're reading. virtual Export_data_version version() const = 0; @@ -257,6 +265,11 @@ class Import : public Import_expression advance(size_t skip) { this->stream_->advance(skip); } + // Stream position, for error reporting. + int + pos() + { return this->stream_->pos(); } + // Return the version number of the export data we're reading. Export_data_version version() const { return this->version_; } @@ -279,6 +292,18 @@ class Import : public Import_expression std::string read_name(); + // Return the maximum valid package index. This is the size of + // packages_ because we will subtract 1 in package_at_index. + size_t + max_package_index() const + { return this->packages_.size(); } + + // Return the package at an index. (We subtract 1 because package + // index 0 is not used.) + Package* + package_at_index(int index) + { return this->packages_.at(index - 1); } + // Read a type. Type* read_type(); @@ -304,6 +329,12 @@ class Import : public Import_expression ifb() { return NULL; } + // Read a qualified identifier from an Import_expression. Sets + // *NAME, *PKG, and *IS_EXPORTED, and reports whether it succeeded. + static bool + read_qualified_identifier(Import_expression*, std::string* name, + Package** pkg, bool* is_exported); + private: static Stream* try_package_in_directory(const std::string&, Location); @@ -360,7 +391,7 @@ class Import : public Import_expression import_var(); // Import a function. - Named_object* + void import_func(Package*); // Parse a type definition. @@ -401,6 +432,8 @@ class Import : public Import_expression // Whether to add new objects to the global scope, rather than to a // package scope. bool add_to_globals_; + // Mapping from package index to package. + std::vector packages_; // All type data. std::string type_data_; // Position of type data in the stream. @@ -567,6 +600,11 @@ class Import_function_body : public Import_expression location() const { return this->imp_->location(); } + // The function we are importing. + Named_object* + function() const + { return this->named_object_; } + // A reference to the body we are reading. const std::string& body() const @@ -662,6 +700,16 @@ class Import_function_body : public Import_expression ifb() { return this; } + // Return the maximum valid package index. + size_t + max_package_index() const + { return this->imp_->max_package_index(); } + + // Return the package at an index. + Package* + package_at_index(int index) + { return this->imp_->package_at_index(index); } + // Return whether we have seen an error. bool saw_error() const -- 2.30.2