Skip to content

Commit

Permalink
compiler: include transitive imports in the type descriptor list
Browse files Browse the repository at this point in the history
In CL 179598, we were using Gogo::packages_, when compiling the
main package, as the list of packages of which we need to
register the type descriptors. This is not complete. It only
includes main's direct import and one-level indirect imports. It
does not include all the packages transitively imported.

To fix that, we need to track all the transitive imports. We
have almost already done that, for init functions. However, there
may be packages that don't need init functions but do need to
register type descriptors. For them, we add a dummy init function
to its export data. So when we compile the main package we will
see all the transitive imports. The dummy init functions are not
real functions and are not called.

Fixes golang/go#32901.

Change-Id: I683045273fb1b2ac7cf41617e641d8b4340b85a9
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/184717
Reviewed-by: Ian Lance Taylor <iant@golang.org>
  • Loading branch information
cherrymui authored and ianlancetaylor committed Jul 3, 2019
1 parent aebd2d6 commit ae7d7e0
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 17 deletions.
6 changes: 5 additions & 1 deletion go/export.cc
Original file line number Diff line number Diff line change
Expand Up @@ -909,6 +909,8 @@ Export::populate_init_graph(Init_graph* init_graph,
++p)
{
const Import_init* ii = *p;
if (ii->is_dummy())
continue;
std::map<std::string, unsigned>::const_iterator srcit =
init_idx.find(ii->init_name());
go_assert(srcit != init_idx.end());
Expand Down Expand Up @@ -1007,7 +1009,7 @@ Export::write_imported_init_fns(const std::string& package_name,

// Now add edges from the local init function to each of the
// imported fcns.
if (!import_init_fn.empty())
if (!import_init_fn.empty() && import_init_fn[0] != '~')
{
unsigned src = 0;
go_assert(init_idx[import_init_fn] == 0);
Expand All @@ -1016,6 +1018,8 @@ Export::write_imported_init_fns(const std::string& package_name,
++p)
{
const Import_init* ii = *p;
if (ii->is_dummy())
continue;
unsigned sink = init_idx[ii->init_name()];
add_init_graph_edge(&init_graph, src, sink);
}
Expand Down
42 changes: 30 additions & 12 deletions go/gogo.cc
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,9 @@ Gogo::init_imports(std::vector<Bstatement*>& init_stmts, Bfunction *bfunction)
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",
Expand Down Expand Up @@ -941,7 +944,7 @@ Gogo::build_type_descriptor_list()
Btype* bat = list_type->field(1)->type()->get_backend(this);

// Create the variable
std::string name = this->type_descriptor_list_symbol(this->package_);
std::string name = this->type_descriptor_list_symbol(this->pkgpath_symbol());
Bvariable* bv = this->backend()->implicit_variable(name, name, bt,
false, true, false,
0);
Expand Down Expand Up @@ -986,20 +989,29 @@ Gogo::register_type_descriptors(std::vector<Bstatement*>& init_stmts,
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 =
this->pkgpath_from_init_fn_name((*it)->init_name());
list_names.push_back(this->type_descriptor_list_symbol(pkgpath));
}
// 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 (Packages::iterator it = this->packages_.begin();
it != this->packages_.end();
++it)
for (std::vector<std::string>::iterator p = list_names.begin();
p != list_names.end();
++p)
{
if (it->second->pkgpath() == "unsafe")
continue;

std::string name = this->type_descriptor_list_symbol(it->second);
Bvariable* bv =
this->backend()->implicit_variable_reference(name, name, bt);
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);

Expand Down Expand Up @@ -5158,16 +5170,22 @@ Gogo::do_exports()
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_,
(this->need_init_fn_ && !this->is_main_package()
? this->get_init_fn_name()
: ""),
init_fn_name,
this->imported_init_fns_,
this->package_->bindings());

Expand Down
17 changes: 16 additions & 1 deletion go/gogo.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ class Import_init
precursors() const
{ return this->precursor_functions_; }

// Whether this is a dummy init, which is used only to record transitive import.
bool
is_dummy() const
{ return this->init_name_[0] == '~'; }

private:
// The name of the package being imported.
std::string package_name_;
Expand Down Expand Up @@ -912,13 +917,23 @@ class Gogo
const std::string&
get_init_fn_name();

// Return the name for a dummy init function, which is not a real
// function but only for tracking transitive import.
std::string
dummy_init_fn_name();

// Return the package path symbol from an init function name, which
// can be a real init function or a dummy one.
std::string
pkgpath_from_init_fn_name(std::string);

// Return the name for a type descriptor symbol.
std::string
type_descriptor_name(const Type*, Named_type*);

// Return the name of the type descriptor list symbol of a package.
std::string
type_descriptor_list_symbol(Package*);
type_descriptor_list_symbol(std::string);

// Return the name of the list of all type descriptor lists.
std::string
Expand Down
31 changes: 28 additions & 3 deletions go/names.cc
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@
//
// The import function for the main package is referenced by C code,
// and is named __go_init_main. For other packages it is
// PKGPATH..import.
// PKGPATH..import. If a package doesn't need an init function, it
// will have a dummy one, named ~PKGPATH.
//
// In each pacakge there is a list of all the type descriptors defined
// in this package. The name of the list is PKGPATH..types.
Expand Down Expand Up @@ -531,6 +532,30 @@ Gogo::get_init_fn_name()
return this->init_fn_name_;
}

// Return the name for a dummy init function, which is not a real
// function but only for tracking transitive import.

std::string
Gogo::dummy_init_fn_name()
{
return "~" + this->pkgpath_symbol();
}

// Return the package path symbol from an init function name, which
// can be a real init function or a dummy one.

std::string
Gogo::pkgpath_from_init_fn_name(std::string name)
{
go_assert(!name.empty());
if (name[0] == '~')
return name.substr(1);
size_t pos = name.find("..import");
if (pos != std::string::npos)
return name.substr(0, pos);
go_unreachable();
}

// Return a mangled name for a type. These names appear in symbol
// names in the assembler file for things like type descriptors and
// methods.
Expand Down Expand Up @@ -994,9 +1019,9 @@ Gogo::type_descriptor_name(const Type* type, Named_type* nt)
// Return the name of the type descriptor list symbol of a package.

std::string
Gogo::type_descriptor_list_symbol(Package* pkg)
Gogo::type_descriptor_list_symbol(std::string pkgpath)
{
return pkg->pkgpath_symbol() + "..types";
return pkgpath + "..types";
}

// Return the name of the list of all type descriptor lists. This is
Expand Down

0 comments on commit ae7d7e0

Please sign in to comment.