#include "statements.h"
#include "expressions.h"
#include "dataflow.h"
+#include "runtime.h"
#include "import.h"
#include "export.h"
+#include "backend.h"
#include "gogo.h"
// Class Gogo.
-Gogo::Gogo(int int_type_size, int pointer_size)
- : package_(NULL),
+Gogo::Gogo(Backend* backend, Linemap* linemap, int int_type_size,
+ int pointer_size)
+ : backend_(backend),
+ linemap_(linemap),
+ package_(NULL),
functions_(),
globals_(new Bindings(NULL)),
imports_(),
imported_unsafe_(false),
packages_(),
- map_descriptors_(NULL),
- type_descriptor_decls_(NULL),
init_functions_(),
need_init_fn_(false),
init_fn_name_(),
imported_init_fns_(),
unique_prefix_(),
unique_prefix_specified_(false),
- interface_types_()
+ verify_types_(),
+ interface_types_(),
+ specific_type_functions_(),
+ specific_type_functions_are_written_(false),
+ named_types_are_converted_(false)
{
- const source_location loc = BUILTINS_LOCATION;
+ const Location loc = Linemap::predeclared_location();
Named_type* uint8_type = Type::make_integer_type("uint8", true, 8,
RUNTIME_TYPE_KIND_UINT8);
RUNTIME_TYPE_KIND_INT8));
this->add_named_type(Type::make_integer_type("int16", false, 16,
RUNTIME_TYPE_KIND_INT16));
- this->add_named_type(Type::make_integer_type("int32", false, 32,
- RUNTIME_TYPE_KIND_INT32));
+ 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));
RUNTIME_TYPE_KIND_INT);
this->add_named_type(int_type);
- // "byte" is an alias for "uint8". Construct a Named_object which
- // points to UINT8_TYPE. Note that this breaks the normal pairing
- // in which a Named_object points to a Named_type which points back
- // to the same Named_object.
- Named_object* byte_type = this->declare_type("byte", loc);
- byte_type->set_type_value(uint8_type);
-
this->add_named_type(Type::make_integer_type("uintptr", true,
pointer_size,
RUNTIME_TYPE_KIND_UINTPTR));
+ // "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);
+ 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);
+ 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),
print_type->set_is_builtin();
this->globals_->add_function_declaration("println", NULL, print_type, loc);
- Type *empty = Type::make_interface_type(NULL, 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,
close_type->set_is_builtin();
this->globals_->add_function_declaration("close", NULL, close_type, loc);
- Typed_identifier_list* closed_result = new Typed_identifier_list();
- closed_result->push_back(Typed_identifier("", Type::lookup_bool_type(),
- loc));
- Function_type* closed_type = Type::make_function_type(NULL, NULL,
- closed_result, loc);
- closed_type->set_is_varargs();
- closed_type->set_is_builtin();
- this->globals_->add_function_declaration("closed", NULL, closed_type, loc);
-
Typed_identifier_list* copy_result = new Typed_identifier_list();
copy_result->push_back(Typed_identifier("", int_type, loc));
Function_type* copy_type = Type::make_function_type(NULL, NULL,
imag_type->set_is_builtin();
this->globals_->add_function_declaration("imag", NULL, imag_type, loc);
- this->define_builtin_function_trees();
-
- // Declare "init", to ensure that it is not defined with parameters
- // or return values.
- this->declare_function("init",
- Type::make_function_type(NULL, NULL, NULL, loc),
- 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);
}
// Munge name for use in an error message.
const std::string&
Gogo::package_name() const
{
- gcc_assert(this->package_ != NULL);
+ go_assert(this->package_ != NULL);
return this->package_->name();
}
void
Gogo::set_package_name(const std::string& package_name,
- source_location location)
+ Location location)
{
if (this->package_ != NULL && this->package_->name() != package_name)
{
{
// Declare "main" as a function which takes no parameters and
// returns no value.
+ Location uloc = Linemap::unknown_location();
this->declare_function("main",
- Type::make_function_type(NULL, NULL, NULL,
- BUILTINS_LOCATION),
- BUILTINS_LOCATION);
+ Type::make_function_type (NULL, NULL, NULL, uloc),
+ uloc);
}
}
Gogo::import_package(const std::string& filename,
const std::string& local_name,
bool is_local_name_exported,
- source_location location)
+ Location location)
{
if (filename == "unsafe")
{
ln = package->name();
is_ln_exported = Lex::is_exported_name(ln);
}
- if (ln != ".")
- {
- ln = this->pack_hidden_name(ln, is_ln_exported);
- this->package_->bindings()->add_package(ln, package);
- }
- else
+ if (ln == ".")
{
Bindings* bindings = package->bindings();
for (Bindings::const_declarations_iterator p =
++p)
this->add_named_object(p->second);
}
+ else if (ln == "_")
+ package->set_uses_sink_alias();
+ else
+ {
+ ln = this->pack_hidden_name(ln, is_ln_exported);
+ this->package_->bindings()->add_package(ln, package);
+ }
return;
}
Named_object*
Gogo::lookup_in_block(const std::string& name) const
{
- gcc_assert(!this->functions_.empty());
- gcc_assert(!this->functions_.back().blocks.empty());
+ go_assert(!this->functions_.empty());
+ go_assert(!this->functions_.back().blocks.empty());
return this->functions_.back().blocks.back()->bindings()->lookup_local(name);
}
const std::string& alias_arg,
bool is_alias_exported,
const std::string& unique_prefix,
- source_location location,
+ Location location,
bool* padd_to_globals)
{
// FIXME: Now that we compile packages as a whole, should we permit
Named_object*
Gogo::add_package(const std::string& real_name, const std::string& alias,
- const std::string& unique_prefix, source_location location)
+ const std::string& unique_prefix, Location location)
{
- gcc_assert(this->in_global_scope());
+ go_assert(this->in_global_scope());
// Register the package. Note that we might have already seen it in
// an earlier import.
Package*
Gogo::register_package(const std::string& package_name,
const std::string& unique_prefix,
- source_location location)
+ Location location)
{
- gcc_assert(!unique_prefix.empty() && !package_name.empty());
+ go_assert(!unique_prefix.empty() && !package_name.empty());
std::string name = unique_prefix + '.' + package_name;
Package* package = NULL;
std::pair<Packages::iterator, bool> ins =
{
// We have seen this package name before.
package = ins.first->second;
- gcc_assert(package != NULL);
- gcc_assert(package->name() == package_name
+ go_assert(package != NULL);
+ go_assert(package->name() == package_name
&& package->unique_prefix() == unique_prefix);
- if (package->location() == UNKNOWN_LOCATION)
+ if (Linemap::is_unknown_location(package->location()))
package->set_location(location);
}
else
{
// First time we have seen this package name.
package = new Package(package_name, unique_prefix, location);
- gcc_assert(ins.first->second == NULL);
+ go_assert(ins.first->second == NULL);
ins.first->second = package;
}
Named_object*
Gogo::start_function(const std::string& name, Function_type* type,
- bool add_method_to_type, source_location location)
+ bool add_method_to_type, Location location)
{
bool at_top_level = this->functions_.empty();
const Typed_identifier* receiver = type->receiver();
Variable* this_param = new Variable(receiver->type(), NULL, false,
true, true, location);
- std::string name = receiver->name();
- if (name.empty())
+ std::string rname = receiver->name();
+ if (rname.empty() || Gogo::is_sink_name(rname))
{
// We need to give receivers a name since they wind up in
// DECL_ARGUMENTS. FIXME.
char buf[50];
snprintf(buf, sizeof buf, "r.%u", count);
++count;
- name = buf;
+ rname = buf;
}
- block->bindings()->add_variable(name, NULL, this_param);
+ block->bindings()->add_variable(rname, NULL, this_param);
}
const Typed_identifier_list* parameters = type->parameters();
if (is_varargs && p + 1 == parameters->end())
param->set_is_varargs_parameter();
- std::string name = p->name();
- if (name.empty() || Gogo::is_sink_name(name))
+ std::string pname = p->name();
+ if (pname.empty() || Gogo::is_sink_name(pname))
{
// We need to give parameters a name since they wind up
// in DECL_ARGUMENTS. FIXME.
char buf[50];
snprintf(buf, sizeof buf, "p.%u", count);
++count;
- name = buf;
+ pname = buf;
}
- block->bindings()->add_variable(name, NULL, param);
+ block->bindings()->add_variable(pname, NULL, param);
}
}
- function->create_named_result_variables(this);
+ function->create_result_variables(this);
const std::string* pname;
std::string nested_name;
- if (!name.empty())
+ bool is_init = false;
+ if (Gogo::unpack_hidden_name(name) == "init" && !type->is_method())
+ {
+ if ((type->parameters() != NULL && !type->parameters()->empty())
+ || (type->results() != NULL && !type->results()->empty()))
+ error_at(location,
+ "func init must have no arguments and no return values");
+ // There can be multiple "init" functions, so give them each a
+ // different name.
+ static int init_count;
+ char buf[30];
+ snprintf(buf, sizeof buf, ".$init%d", init_count);
+ ++init_count;
+ nested_name = buf;
+ pname = &nested_name;
+ is_init = true;
+ }
+ else if (!name.empty())
pname = &name;
else
{
ret = Named_object::make_function(name, NULL, function);
else
{
- gcc_assert(at_top_level);
+ go_assert(at_top_level);
Type* rtype = type->receiver()->type();
// We want to look through the pointer created by the
Named_object* declared =
this->declare_package_type(type_no->name(),
type_no->location());
- gcc_assert(declared
+ go_assert(declared
== type_no->unknown_value()->real_named_object());
}
ret = rtype->forward_declaration_type()->add_method(name,
function);
}
else
- gcc_unreachable();
+ go_unreachable();
}
this->package_->bindings()->add_method(ret);
}
of.function = ret;
of.blocks.push_back(block);
- if (!type->is_method() && Gogo::unpack_hidden_name(name) == "init")
+ if (is_init)
{
this->init_functions_.push_back(ret);
this->need_init_fn_ = true;
// Finish compiling a function.
void
-Gogo::finish_function(source_location location)
+Gogo::finish_function(Location location)
{
this->finish_block(location);
- gcc_assert(this->functions_.back().blocks.empty());
+ go_assert(this->functions_.back().blocks.empty());
this->functions_.pop_back();
}
Named_object*
Gogo::current_function() const
{
- gcc_assert(!this->functions_.empty());
+ go_assert(!this->functions_.empty());
return this->functions_.back().function;
}
// Start a new block.
void
-Gogo::start_block(source_location location)
+Gogo::start_block(Location location)
{
- gcc_assert(!this->functions_.empty());
+ 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(source_location location)
+Gogo::finish_block(Location location)
{
- gcc_assert(!this->functions_.empty());
- gcc_assert(!this->functions_.back().blocks.empty());
+ 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, source_location location)
+Gogo::add_unknown_name(const std::string& name, Location location)
{
return this->package_->bindings()->add_unknown_name(name, location);
}
Named_object*
Gogo::declare_function(const std::string& name, Function_type* type,
- source_location location)
+ Location location)
{
if (!type->is_method())
return this->current_bindings()->add_function_declaration(name, NULL, type,
else if (rtype->forward_declaration_type() != NULL)
{
Forward_declaration_type* ftype = rtype->forward_declaration_type();
- return ftype->add_method_declaration(name, type, location);
+ return ftype->add_method_declaration(name, NULL, type, location);
}
else
- gcc_unreachable();
+ go_unreachable();
}
}
Label*
Gogo::add_label_definition(const std::string& label_name,
- source_location location)
+ Location location)
{
- gcc_assert(!this->functions_.empty());
+ go_assert(!this->functions_.empty());
Function* func = this->functions_.back().function->func_value();
- Label* label = func->add_label_definition(label_name, location);
+ 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)
+Gogo::add_label_reference(const std::string& label_name,
+ Location location, bool issue_goto_errors)
{
- gcc_assert(!this->functions_.empty());
+ go_assert(!this->functions_.empty());
Function* func = this->functions_.back().function->func_value();
- return func->add_label_reference(label_name);
+ 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)
{
- gcc_assert(!this->functions_.empty()
+ 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, source_location location)
+Gogo::add_block(Block* block, Location location)
{
- gcc_assert(!this->functions_.empty()
+ 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 type.
void
-Gogo::add_type(const std::string& name, Type* type, source_location location)
+Gogo::add_type(const std::string& name, Type* type, Location location)
{
Named_object* no = this->current_bindings()->add_type(name, NULL, type,
location);
void
Gogo::add_named_type(Named_type* type)
{
- gcc_assert(this->in_global_scope());
+ 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, source_location location)
+Gogo::declare_type(const std::string& name, Location location)
{
Bindings* bindings = this->current_bindings();
Named_object* no = bindings->add_type_declaration(name, NULL, location);
// Declare a type at the package level.
Named_object*
-Gogo::declare_package_type(const std::string& name, source_location location)
+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);
+}
+
// Define a type which was already declared.
void
this->current_bindings()->add_named_object(no);
}
+// 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
{
error_at(no->location(), "expected type");
Type* errtype = Type::make_error_type();
- Named_object* err = Named_object::make_type("error", NULL,
- errtype,
- BUILTINS_LOCATION);
+ Named_object* err =
+ Named_object::make_type("erroneous_type", NULL, errtype,
+ Linemap::predeclared_location());
no->set_type_value(err->type_value());
}
}
}
}
+// Queue up a type specific function for later writing. These are
+// written out in write_specific_type_functions, called after the
+// parse tree is lowered.
+
+void
+Gogo::queue_specific_type_function(Type* type, Named_type* name,
+ const std::string& hash_name,
+ Function_type* hash_fntype,
+ const std::string& equal_name,
+ Function_type* equal_fntype)
+{
+ go_assert(!this->specific_type_functions_are_written_);
+ go_assert(!this->in_global_scope());
+ Specific_type_function* tsf = new Specific_type_function(type, name,
+ hash_name,
+ hash_fntype,
+ equal_name,
+ 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)
+{
+ Named_object* hash_fn;
+ Named_object* equal_fn;
+ switch (t->classification())
+ {
+ case Type::TYPE_NAMED:
+ {
+ Named_type* nt = t->named_type();
+ if (!t->compare_is_identity(this->gogo_) && t->is_comparable())
+ t->type_functions(this->gogo_, nt, NULL, NULL, &hash_fn, &equal_fn);
+
+ // 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->compare_is_identity(this->gogo_) && t->is_comparable())
+ t->type_functions(this->gogo_, NULL, NULL, NULL, &hash_fn, &equal_fn);
+ 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();
+ tsf->type->write_specific_type_functions(this, tsf->name,
+ tsf->hash_name,
+ tsf->hash_fntype,
+ tsf->equal_name,
+ tsf->equal_fntype);
+ delete tsf;
+ }
+ this->specific_type_functions_are_written_ = true;
+}
+
// Traverse the tree.
void
}
}
+// 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
int
Verify_types::type(Type* t)
{
- // Don't verify types defined in other packages.
- Named_type* nt = t->named_type();
- if (nt != NULL && nt->named_object()->package() != NULL)
- return TRAVERSE_SKIP_COMPONENTS;
-
if (!t->verify())
return TRAVERSE_SKIP_COMPONENTS;
return TRAVERSE_CONTINUE;
{
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.
| traverse_functions
| traverse_statements
| traverse_expressions),
- gogo_(gogo), function_(function), iota_value_(-1)
+ gogo_(gogo), function_(function), iota_value_(-1), inserter_()
{ }
+ void
+ set_inserter(const Statement_inserter* inserter)
+ { this->inserter_ = *inserter; }
+
int
variable(Named_object*);
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. We handle variables specially to break loops in
-// which a variable initialization expression refers to itself. The
-// loop breaking is in lower_init_expression.
+// Lower variables.
int
Lower_parse_tree::variable(Named_object* no)
{
- if (no->is_variable())
- no->var_value()->lower_init_expression(this->gogo_, this->function_);
- return TRAVERSE_CONTINUE;
+ 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
return TRAVERSE_CONTINUE;
nc->set_lowering();
- gcc_assert(this->iota_value_ == -1);
+ go_assert(this->iota_value_ == -1);
this->iota_value_ = nc->iota_value();
nc->traverse_expression(this);
this->iota_value_ = -1;
{
no->func_value()->set_closure_type();
- gcc_assert(this->function_ == NULL);
+ go_assert(this->function_ == NULL);
this->function_ = no;
int t = no->func_value()->traverse(this);
this->function_ = NULL;
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)
- return t;
+ {
+ this->inserter_ = hold_inserter;
+ return t;
+ }
// Keep lowering until nothing changes.
Statement* s = sorig;
while (true)
{
- Statement* snew = s->lower(this->gogo_, block);
+ 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)
- return t;
+ {
+ this->inserter_ = hold_inserter;
+ return t;
+ }
}
if (s != sorig)
block->replace_statement(*pindex, s);
+ this->inserter_ = hold_inserter;
return TRAVERSE_SKIP_COMPONENTS;
}
{
Expression* e = *pexpr;
Expression* enew = e->lower(this->gogo_, this->function_,
- this->iota_value_);
+ &this->inserter_, this->iota_value_);
if (enew == e)
break;
+ if (enew->traverse_subexpressions(this) == TRAVERSE_EXIT)
+ return TRAVERSE_EXIT;
*pexpr = enew;
}
return TRAVERSE_SKIP_COMPONENTS;
this->traverse(&lower_parse_tree);
}
-// Lower an expression.
+// 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, Expression** pexpr)
+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);
}
void
Gogo::lower_constant(Named_object* no)
{
- gcc_assert(no->is_const());
+ go_assert(no->is_const());
Lower_parse_tree lower(this, NULL);
lower.constant(no, false);
}
// finalize the methods of the field types, not of the struct
// type itself. We don't want to add methods to the struct,
// since it has a name.
- Type* rt = t->named_type()->real_type();
+ Named_type* nt = t->named_type();
+ Type* rt = nt->real_type();
if (rt->classification() != Type::TYPE_STRUCT)
{
if (Type::traverse(rt, this) == TRAVERSE_EXIT)
return TRAVERSE_EXIT;
}
- t->named_type()->finalize_methods(this->gogo_);
+ nt->finalize_methods(this->gogo_);
+
+ // 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;
+ }
+ }
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_);
- break;
+ return TRAVERSE_SKIP_COMPONENTS;
default:
break;
Check_types_traverse(Gogo* gogo)
: Traverse(traverse_variables
| traverse_constants
+ | traverse_functions
| traverse_statements
| traverse_expressions),
gogo_(gogo)
constant(Named_object*, bool);
int
+ function(Named_object*);
+
+ int
statement(Block*, size_t* pindex, Statement*);
int
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
reason.c_str());
var->clear_init();
}
+ else 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()))
+ error_at(var->location(), "%qs declared and not used",
+ named_object->message_name().c_str());
}
return TRAVERSE_CONTINUE;
}
&& !ctype->is_boolean_type()
&& !ctype->is_string_type())
{
- if (!ctype->is_error_type())
+ if (ctype->is_nil_type())
+ error_at(constant->location(), "const initializer cannot be nil");
+ else if (!ctype->is_error())
error_at(constant->location(), "invalid constant 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
Operator op = be->op();
if (op != OPERATOR_OROR && op != OPERATOR_ANDAND)
return TRAVERSE_CONTINUE;
- gcc_assert(this->found_ == NULL);
+ go_assert(this->found_ == NULL);
this->found_ = pexpr;
return TRAVERSE_EXIT;
}
Binary_expression* shortcut = (*pshortcut)->binary_expression();
Expression* left = shortcut->left();
Expression* right = shortcut->right();
- source_location loc = shortcut->location();
+ Location loc = shortcut->location();
Block* retblock = new Block(enclosing, loc);
retblock->set_end_location(loc);
- Temporary_statement* ts = Statement::make_temporary(Type::make_boolean_type(),
+ Temporary_statement* ts = Statement::make_temporary(Type::lookup_bool_type(),
left, loc);
retblock->add_statement(ts);
{
Expression** pexpr = *p;
- // If the last expression is a send or receive expression, we
- // may be ignoring the value; we don't want to evaluate it
- // early.
- if (p + 1 == find_eval_ordering.end()
- && ((*pexpr)->classification() == Expression::EXPRESSION_SEND
- || (*pexpr)->classification() == Expression::EXPRESSION_RECEIVE))
- break;
-
// The last expression in a thunk will be the call passed to go
// or defer, which we must not evaluate early.
if (is_thunk && p + 1 == find_eval_ordering.end())
break;
- source_location loc = (*pexpr)->location();
- Temporary_statement* ts = Statement::make_temporary(NULL, *pexpr, loc);
- block->insert_statement_before(*pindex, ts);
- ++*pindex;
+ 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.
+ s = Statement::make_statement(*pexpr, true);
+ }
- *pexpr = Expression::make_temporary_reference(ts, loc);
+ block->insert_statement_before(*pindex, s);
+ ++*pindex;
}
if (init != orig_init)
return TRAVERSE_CONTINUE;
Find_eval_ordering find_eval_ordering;
- init->traverse_subexpressions(&find_eval_ordering);
+ Expression::traverse(&init, &find_eval_ordering);
if (find_eval_ordering.size() <= 1)
{
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;
- source_location loc = (*pexpr)->location();
- Temporary_statement* ts = Statement::make_temporary(NULL, *pexpr, loc);
- var->add_preinit_statement(this->gogo_, ts);
- *pexpr = Expression::make_temporary_reference(ts, loc);
+ 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;
}
private:
Expression*
- can_recover_arg(source_location);
+ can_recover_arg(Location);
// General IR.
Gogo* gogo_;
return TRAVERSE_CONTINUE;
Gogo* gogo = this->gogo_;
- source_location location = orig_func->location();
+ Location location = orig_func->location();
static int count;
char buf[50];
++count;
std::string can_recover_name = buf;
new_params->push_back(Typed_identifier(can_recover_name,
- Type::make_boolean_type(),
+ Type::lookup_bool_type(),
orig_fntype->location()));
const Typed_identifier_list* orig_results = orig_fntype->results();
++p)
{
Named_object* p_no = gogo->lookup(p->name(), NULL);
- gcc_assert(p_no != 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;
if (orig_fntype->results() == NULL || orig_fntype->results()->empty())
- s = Statement::make_statement(call);
+ s = Statement::make_statement(call, true);
else
{
Expression_list* vals = new Expression_list();
for (size_t i = 0; i < rc; ++i)
vals->push_back(Expression::make_call_result(call, i));
}
- s = Statement::make_return_statement(new_func->type()->results(),
- vals, location);
+ s = Statement::make_return_statement(vals, 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.
// We changed the receiver to be a regular parameter. We have
// to update the binding accordingly in both functions.
Named_object* orig_rec_no = orig_bindings->lookup_local(receiver_name);
- gcc_assert(orig_rec_no != NULL
+ 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();
const std::string& new_receiver_name(orig_fntype->receiver()->name());
Named_object* new_rec_no = new_bindings->lookup_local(new_receiver_name);
- gcc_assert(new_rec_no != NULL
- && new_rec_no->is_variable()
- && new_rec_no->var_value()->is_receiver());
- new_rec_no->var_value()->set_is_not_receiver();
+ 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);
- gcc_assert(can_recover_no != NULL
+ 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::make_boolean_type(), NULL,
+ 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);
new_func->traverse(&convert_recover);
// Update the function pointers in any named results.
- new_func->update_named_result_variables();
- orig_func->update_named_result_variables();
+ new_func->update_result_variables();
+ orig_func->update_result_variables();
return TRAVERSE_CONTINUE;
}
// __go_can_recover(__builtin_return_address()).
Expression*
-Build_recover_thunks::can_recover_arg(source_location location)
+Build_recover_thunks::can_recover_arg(Location location)
{
static Named_object* builtin_return_address;
if (builtin_return_address == NULL)
{
- const source_location bloc = BUILTINS_LOCATION;
+ const Location bloc = Linemap::predeclared_location();
Typed_identifier_list* param_types = new Typed_identifier_list();
Type* uint_type = Type::lookup_integer_type("uint");
static Named_object* can_recover;
if (can_recover == NULL)
{
- const source_location bloc = BUILTINS_LOCATION;
+ const Location bloc = Linemap::predeclared_location();
Typed_identifier_list* param_types = new Typed_identifier_list();
Type* voidptr_type = Type::make_pointer_type(Type::make_void_type());
param_types->push_back(Typed_identifier("a", voidptr_type, bloc));
- Type* boolean_type = Type::make_boolean_type();
+ 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,
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 =
const std::string&
Gogo::unique_prefix() const
{
- gcc_assert(!this->unique_prefix_.empty());
+ go_assert(!this->unique_prefix_.empty());
return this->unique_prefix_;
}
void
Gogo::set_unique_prefix(const std::string& arg)
{
- gcc_assert(this->unique_prefix_.empty());
+ go_assert(this->unique_prefix_.empty());
this->unique_prefix_ = arg;
this->unique_prefix_specified_ = true;
}
exp.export_globals(this->package_name(),
this->unique_prefix(),
this->package_priority(),
+ this->imports_,
(this->need_init_fn_ && !this->is_main_package()
? this->get_init_fn_name()
: ""),
this->package_->bindings());
}
+// Find the blocks in order to convert named types defined in blocks.
+
+class Convert_named_types : public Traverse
+{
+ public:
+ Convert_named_types(Gogo* gogo)
+ : Traverse(traverse_blocks),
+ gogo_(gogo)
+ { }
+
+ protected:
+ int
+ block(Block* block);
+
+ private:
+ Gogo* gogo_;
+};
+
+int
+Convert_named_types::block(Block* block)
+{
+ this->gogo_->convert_named_types_in_bindings(block->bindings());
+ return TRAVERSE_CONTINUE;
+}
+
+// Convert all named types to the backend representation. Since named
+// types can refer to other types, this needs to be done in the right
+// sequence, which is handled by Named_type::convert. Here we arrange
+// to call that for each named type.
+
+void
+Gogo::convert_named_types()
+{
+ this->convert_named_types_in_bindings(this->globals_);
+ for (Packages::iterator p = this->packages_.begin();
+ p != this->packages_.end();
+ ++p)
+ {
+ Package* package = p->second;
+ this->convert_named_types_in_bindings(package->bindings());
+ }
+
+ Convert_named_types cnt(this);
+ this->traverse(&cnt);
+
+ // Make all the builtin named types used for type descriptors, and
+ // then convert them. They will only be written out if they are
+ // needed.
+ Type::make_type_descriptor_type();
+ Type::make_type_descriptor_ptr_type();
+ Function_type::make_function_type_descriptor_type();
+ Pointer_type::make_pointer_type_descriptor_type();
+ Struct_type::make_struct_type_descriptor_type();
+ Array_type::make_array_type_descriptor_type();
+ Array_type::make_slice_type_descriptor_type();
+ Map_type::make_map_type_descriptor_type();
+ Map_type::make_map_descriptor_type();
+ Channel_type::make_chan_type_descriptor_type();
+ Interface_type::make_interface_type_descriptor_type();
+ Type::convert_builtin_named_types(this);
+
+ Runtime::convert_types(this);
+
+ this->named_types_are_converted_ = true;
+}
+
+// Convert all names types in a set of bindings.
+
+void
+Gogo::convert_named_types_in_bindings(Bindings* bindings)
+{
+ for (Bindings::const_definitions_iterator p = bindings->begin_definitions();
+ p != bindings->end_definitions();
+ ++p)
+ {
+ if ((*p)->is_type())
+ (*p)->type_value()->convert(this);
+ }
+}
+
// Class Function.
Function::Function(Function_type* type, Function* enclosing, Block* block,
- source_location location)
- : type_(type), enclosing_(enclosing), named_results_(NULL),
+ Location location)
+ : type_(type), enclosing_(enclosing), results_(NULL),
closure_var_(NULL), block_(block), location_(location), fndecl_(NULL),
- defer_stack_(NULL), calls_recover_(false), is_recover_thunk_(false),
- has_recover_thunk_(false)
+ defer_stack_(NULL), results_are_named_(false), calls_recover_(false),
+ is_recover_thunk_(false), has_recover_thunk_(false)
{
}
// Create the named result variables.
void
-Function::create_named_result_variables(Gogo* gogo)
+Function::create_result_variables(Gogo* gogo)
{
const Typed_identifier_list* results = this->type_->results();
- if (results == NULL
- || results->empty()
- || results->front().name().empty())
+ if (results == NULL || results->empty())
return;
- this->named_results_ = new Named_results();
- this->named_results_->reserve(results->size());
+ 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;
++p, ++index)
{
std::string name = p->name();
- if (Gogo::is_sink_name(name))
+ if (name.empty() || Gogo::is_sink_name(name))
{
- static int unnamed_result_counter;
+ static int result_counter;
char buf[100];
- snprintf(buf, sizeof buf, "_$%d", unnamed_result_counter);
- ++unnamed_result_counter;
+ 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);
+ 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->named_results_->push_back(no);
+ 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);
+ }
}
}
// calls recover.
void
-Function::update_named_result_variables()
+Function::update_result_variables()
{
- if (this->named_results_ == NULL)
+ if (this->results_ == NULL)
return;
- for (Named_results::iterator p = this->named_results_->begin();
- p != this->named_results_->end();
+ for (Results::iterator p = this->results_->begin();
+ p != this->results_->end();
++p)
(*p)->result_var_value()->set_function(this);
}
{
// We don't know the type of the variable yet. We add fields as
// we find them.
- source_location loc = this->type_->location();
+ Location loc = this->type_->location();
Struct_field_list* sfl = new Struct_field_list;
Type* struct_type = Type::make_struct_type(sfl, loc);
Variable* var = new Variable(Type::make_pointer_type(struct_type),
NULL, false, true, false, loc);
+ var->set_is_used();
this->closure_var_ = Named_object::make_variable("closure", NULL, var);
// Note that the new variable is not in any binding contour.
}
// Add a label definition.
Label*
-Function::add_label_definition(const std::string& label_name,
- source_location location)
+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 (ins.second)
{
// This is a new label.
- Label* label = new Label(label_name);
- label->define(location);
+ label = new Label(label_name);
ins.first->second = label;
- return label;
}
else
{
// The label was already in the hash table.
- Label* label = ins.first->second;
- if (!label->is_defined())
+ label = ins.first->second;
+ if (label->is_defined())
{
- label->define(location);
- return label;
- }
- else
- {
- error_at(location, "redefinition of label %qs",
+ error_at(location, "label %qs already defined",
Gogo::message_name(label_name).c_str());
inform(label->location(), "previous definition of %qs was here",
Gogo::message_name(label_name).c_str());
return new Label(label_name);
}
}
+
+ 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(const std::string& label_name)
+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.
- return ins.first->second;
+ label = ins.first->second;
}
else
{
- gcc_assert(ins.first->second == NULL);
- Label* label = new Label(label_name);
+ go_assert(ins.first->second == NULL);
+ label = new Label(label_name);
ins.first->second = label;
- return 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())
+ error_at(label->location(), "label %qs defined and not used",
+ Gogo::message_name(label->name()).c_str());
}
}
void
Function::swap_for_recover(Function *x)
{
- gcc_assert(this->enclosing_ == x->enclosing_);
- std::swap(this->named_results_, x->named_results_);
+ 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_);
- gcc_assert(this->location_ == x->location_);
- gcc_assert(this->fndecl_ == NULL && x->fndecl_ == NULL);
- gcc_assert(this->defer_stack_ == NULL && x->defer_stack_ == NULL);
+ 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.
this->block_->determine_types();
}
+// 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
if (fntype->is_method())
{
exp->write_c_string("(");
- exp->write_type(fntype->receiver()->type());
+ const Typed_identifier* receiver = fntype->receiver();
+ exp->write_name(receiver->name());
+ exp->write_c_string(" ");
+ exp->write_type(receiver->type());
exp->write_c_string(") ");
}
first = false;
else
exp->write_c_string(", ");
+ exp->write_name(p->name());
+ exp->write_c_string(" ");
if (!is_varargs || p + 1 != parameters->end())
exp->write_type(p->type());
else
const Typed_identifier_list* results = fntype->results();
if (results != NULL)
{
- if (results->size() == 1)
+ if (results->size() == 1 && results->begin()->name().empty())
{
exp->write_c_string(" ");
exp->write_type(results->begin()->type());
first = false;
else
exp->write_c_string(", ");
+ exp->write_name(p->name());
+ exp->write_c_string(" ");
exp->write_type(p->type());
}
exp->write_c_string(")");
if (imp->peek_char() == '(')
{
imp->require_c_string("(");
+ std::string name = imp->read_name();
+ imp->require_c_string(" ");
Type* rtype = imp->read_type();
- *preceiver = new Typed_identifier(Import::import_marker, rtype,
- imp->location());
+ *preceiver = new Typed_identifier(name, rtype, imp->location());
imp->require_c_string(") ");
}
parameters = new Typed_identifier_list();
while (true)
{
+ std::string name = imp->read_name();
+ imp->require_c_string(" ");
+
if (imp->match_c_string("..."))
{
imp->advance(3);
Type* ptype = imp->read_type();
if (*is_varargs)
ptype = Type::make_array_type(ptype, NULL);
- parameters->push_back(Typed_identifier(Import::import_marker,
- ptype, imp->location()));
+ parameters->push_back(Typed_identifier(name, ptype,
+ imp->location()));
if (imp->peek_char() != ',')
break;
- gcc_assert(!*is_varargs);
+ go_assert(!*is_varargs);
imp->require_c_string(", ");
}
}
if (imp->peek_char() != '(')
{
Type* rtype = imp->read_type();
- results->push_back(Typed_identifier(Import::import_marker, rtype,
- imp->location()));
+ results->push_back(Typed_identifier("", rtype, imp->location()));
}
else
{
imp->require_c_string("(");
while (true)
{
+ std::string name = imp->read_name();
+ imp->require_c_string(" ");
Type* rtype = imp->read_type();
- results->push_back(Typed_identifier(Import::import_marker,
- rtype, imp->location()));
+ results->push_back(Typed_identifier(name, rtype,
+ imp->location()));
if (imp->peek_char() != ',')
break;
imp->require_c_string(", ");
// Class Block.
-Block::Block(Block* enclosing, source_location location)
+Block::Block(Block* enclosing, Location location)
: enclosing_(enclosing), statements_(),
bindings_(new Bindings(enclosing == NULL
? NULL
void
Block::replace_statement(size_t index, Statement* s)
{
- gcc_assert(index < this->statements_.size());
+ go_assert(index < this->statements_.size());
this->statements_[index] = s;
}
void
Block::insert_statement_before(size_t index, Statement* s)
{
- gcc_assert(index < this->statements_.size());
+ go_assert(index < this->statements_.size());
this->statements_.insert(this->statements_.begin() + index, s);
}
void
Block::insert_statement_after(size_t index, Statement* s)
{
- gcc_assert(index < this->statements_.size());
+ go_assert(index < this->statements_.size());
this->statements_.insert(this->statements_.begin() + index + 1, s);
}
| 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)
{
- if (traverse->constant(*pb, false) == TRAVERSE_EXIT)
- return TRAVERSE_EXIT;
- }
- if ((traverse_mask & Traverse::traverse_types) != 0
- || (traverse_mask & Traverse::traverse_expressions) != 0)
- {
- Type* t = (*pb)->const_value()->type();
- if (t != NULL
- && Type::traverse(t, traverse) == TRAVERSE_EXIT)
- return TRAVERSE_EXIT;
- }
- if ((traverse_mask & Traverse::traverse_expressions) != 0
- || (traverse_mask & Traverse::traverse_types) != 0)
- {
- if ((*pb)->const_value()->traverse_expression(traverse)
- == TRAVERSE_EXIT)
+ 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 (traverse->variable(*pb) == TRAVERSE_EXIT)
- return TRAVERSE_EXIT;
- }
- if (((traverse_mask & Traverse::traverse_types) != 0
- || (traverse_mask & Traverse::traverse_expressions) != 0)
- && ((*pb)->is_result_variable()
- || (*pb)->var_value()->has_type()))
- {
- Type* t = ((*pb)->is_variable()
- ? (*pb)->var_value()->type()
- : (*pb)->result_var_value()->type());
- if (t != NULL
- && Type::traverse(t, traverse) == TRAVERSE_EXIT)
- return TRAVERSE_EXIT;
- }
- if ((*pb)->is_variable()
- && ((traverse_mask & Traverse::traverse_expressions) != 0
- || (traverse_mask & Traverse::traverse_types) != 0))
- {
- if ((*pb)->var_value()->traverse_expression(traverse)
- == TRAVERSE_EXIT)
- return TRAVERSE_EXIT;
+ 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:
- // FIXME: Where will nested functions be found?
- gcc_unreachable();
+ go_unreachable();
case Named_object::NAMED_OBJECT_TYPE:
- if ((traverse_mask & Traverse::traverse_types) != 0
- || (traverse_mask & Traverse::traverse_expressions) != 0)
- {
- if (Type::traverse((*pb)->type_value(), traverse)
- == TRAVERSE_EXIT)
- return TRAVERSE_EXIT;
- }
+ 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:
- gcc_unreachable();
+ go_unreachable();
default:
- gcc_unreachable();
+ go_unreachable();
}
+
+ if (t == TRAVERSE_EXIT)
+ return TRAVERSE_EXIT;
}
}
return this->statements_.back()->may_fall_through();
}
+// 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));
+ }
+
+ // FIXME: Permitting FUNCTION to be NULL here is a temporary measure
+ // until we have a proper representation of the init function.
+ Bfunction* bfunction;
+ if (function == NULL)
+ bfunction = NULL;
+ else
+ bfunction = tree_to_function(function->func_value()->get_decl());
+ 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)
+ {
+ error_at(loc, "goto jumps into block");
+ 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());
+
+ std::string n = (*p)->message_name();
+ error_at(loc, "goto jumps over declaration of %qs", n.c_str());
+ inform((*p)->location(), "%qs defined here", n.c_str());
+ }
+}
+
// Class Variable.
Variable::Variable(Type* type, Expression* init, bool is_global,
bool is_parameter, bool is_receiver,
- source_location location)
+ Location location)
: type_(type), init_(init), preinit_(NULL), location_(location),
- is_global_(is_global), is_parameter_(is_parameter),
- is_receiver_(is_receiver), is_varargs_parameter_(false),
- is_address_taken_(false), seen_(false), init_is_lowered_(false),
- type_from_init_tuple_(false), type_from_range_index_(false),
- type_from_range_value_(false), type_from_chan_element_(false),
- is_type_switch_var_(false)
+ backend_(NULL), is_global_(is_global), is_parameter_(is_parameter),
+ 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), type_from_init_tuple_(false),
+ type_from_range_index_(false), type_from_range_value_(false),
+ type_from_chan_element_(false), is_type_switch_var_(false),
+ determined_type_(false)
{
- gcc_assert(type != NULL || init != NULL);
- gcc_assert(!is_parameter || init == NULL);
+ go_assert(type != NULL || init != NULL);
+ go_assert(!is_parameter || init == NULL);
}
// Traverse the initializer expression.
int
-Variable::traverse_expression(Traverse* traverse)
+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)
+ if (this->init_ != NULL
+ && ((traverse_mask
+ & (Traverse::traverse_expressions | Traverse::traverse_types))
+ != 0))
{
if (Expression::traverse(&this->init_, traverse) == TRAVERSE_EXIT)
return TRAVERSE_EXIT;
// Lower the initialization expression after parsing is complete.
void
-Variable::lower_init_expression(Gogo* gogo, Named_object* function)
+Variable::lower_init_expression(Gogo* gogo, Named_object* function,
+ Statement_inserter* inserter)
{
if (this->init_ != NULL && !this->init_is_lowered_)
{
}
this->seen_ = true;
- gogo->lower_expression(function, &this->init_);
+ 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;
Block*
Variable::preinit_block(Gogo* gogo)
{
- gcc_assert(this->is_global_);
+ go_assert(this->is_global_);
if (this->preinit_ == NULL)
this->preinit_ = new Block(NULL, this->location());
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.
if (t->array_type() != NULL
|| (t->points_to() != NULL
&& t->points_to()->array_type() != NULL
- && !t->points_to()->is_open_array_type()))
+ && !t->points_to()->is_slice_type()))
{
if (get_index_type)
return Type::lookup_integer_type("int");
return t->deref()->array_type()->element_type();
}
else if (t->is_string_type())
- return Type::lookup_integer_type("int");
+ {
+ 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)
&& this->type_->is_nil_constant_as_type())
{
Type_guard_expression* tge = this->init_->type_guard_expression();
- gcc_assert(tge != NULL);
+ go_assert(tge != NULL);
init = tge->expr();
type = NULL;
}
type = this->type_from_chan_element(init, false);
else
{
- gcc_assert(init != NULL);
+ go_assert(init != NULL);
type = init->type();
- gcc_assert(type != NULL);
+ go_assert(type != NULL);
// Variables should not have abstract types.
if (type->is_abstract())
Type*
Variable::type() const
{
- gcc_assert(this->type_ != NULL);
+ go_assert(this->type_ != NULL);
return this->type_;
}
void
Variable::determine_type()
{
+ if (this->determined_type_)
+ return;
+ this->determined_type_ = true;
+
if (this->preinit_ != NULL)
this->preinit_->determine_types();
if (this->is_type_switch_var_ && this->type_->is_nil_constant_as_type())
{
Type_guard_expression* tge = this->init_->type_guard_expression();
- gcc_assert(tge != NULL);
+ go_assert(tge != NULL);
this->type_ = NULL;
this->init_ = tge->expr();
}
if (this->init_ == NULL)
- gcc_assert(this->type_ != NULL && !this->type_->is_abstract());
+ go_assert(this->type_ != NULL && !this->type_->is_abstract());
else if (this->type_from_init_tuple_)
{
Expression *init = this->init_;
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_from_chan_element_ should have been cleared during
- // lowering.
- gcc_assert(!this->type_from_chan_element_);
-
Type_context context(this->type_, false);
this->init_->determine_type(&context);
if (this->type_ == NULL)
{
Type* type = this->init_->type();
- gcc_assert(type != NULL);
+ go_assert(type != NULL);
if (type->is_abstract())
type = type->make_non_abstract_type();
void
Variable::export_var(Export* exp, const std::string& name) const
{
- gcc_assert(this->is_global_);
+ go_assert(this->is_global_);
exp->write_c_string("var ");
exp->write_string(name);
exp->write_c_string(" ");
imp->require_c_string(";\n");
}
+// 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);
+ }
+
+ std::string n = Gogo::unpack_hidden_name(name);
+ Btype* btype = type->get_backend(gogo);
+
+ Bvariable* bvar;
+ if (this->is_global_)
+ bvar = backend->global_variable((package == NULL
+ ? gogo->package_name()
+ : package->name()),
+ (package == NULL
+ ? gogo->unique_prefix()
+ : package->unique_prefix()),
+ n,
+ btype,
+ package != NULL,
+ Gogo::is_hidden_name(name),
+ this->location_);
+ else if (function == NULL)
+ {
+ go_assert(saw_errors());
+ bvar = backend->error_variable();
+ }
+ else
+ {
+ tree fndecl = function->func_value()->get_decl();
+ Bfunction* bfunction = tree_to_function(fndecl);
+ bool is_address_taken = (this->is_non_escaping_address_taken_
+ && !this->is_in_heap());
+ if (is_parameter)
+ bvar = backend->parameter_variable(bfunction, n, btype,
+ is_address_taken,
+ this->location_);
+ else
+ bvar = backend->local_variable(bfunction, n, btype,
+ 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);
+ tree fndecl = function->func_value()->get_decl();
+ Bfunction* bfunction = tree_to_function(fndecl);
+ 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,
+ is_address_taken,
+ this->location_);
+ }
+ }
+ return this->backend_;
+}
+
// Class Named_constant.
// Traverse the initializer expression.
Type_context context(NULL, true);
this->expr_->determine_type(&context);
this->type_ = this->expr_->type();
- gcc_assert(this->type_ != NULL);
+ go_assert(this->type_ != NULL);
}
}
Named_object*
Type_declaration::add_method_declaration(const std::string& name,
+ Package* package,
Function_type* type,
- source_location location)
+ Location location)
{
- Named_object* ret = Named_object::make_function_declaration(name, NULL, type,
- location);
+ Named_object* ret = Named_object::make_function_declaration(name, package,
+ type, location);
this->methods_.push_back(ret);
return ret;
}
void
Unknown_name::set_real_named_object(Named_object* no)
{
- gcc_assert(this->real_named_object_ == NULL);
- gcc_assert(!no->is_unknown());
+ go_assert(this->real_named_object_ == NULL);
+ go_assert(!no->is_unknown());
this->real_named_object_ = no;
}
tree_(NULL)
{
if (Gogo::is_sink_name(name))
- gcc_assert(classification == NAMED_OBJECT_SINK);
+ go_assert(classification == NAMED_OBJECT_SINK);
}
// Make an unknown name. This is used by the parser. The name must
Named_object*
Named_object::make_unknown_name(const std::string& name,
- source_location location)
+ Location location)
{
Named_object* named_object = new Named_object(name, NULL,
NAMED_OBJECT_UNKNOWN);
Named_object*
Named_object::make_type(const std::string& name, const Package* package,
- Type* type, source_location location)
+ Type* type, Location location)
{
Named_object* named_object = new Named_object(name, package,
NAMED_OBJECT_TYPE);
Named_object*
Named_object::make_type_declaration(const std::string& name,
const Package* package,
- source_location location)
+ Location location)
{
Named_object* named_object = new Named_object(name, package,
NAMED_OBJECT_TYPE_DECLARATION);
Named_object::make_function_declaration(const std::string& name,
const Package* package,
Function_type* fntype,
- source_location location)
+ Location location)
{
Named_object* named_object = new Named_object(name, package,
NAMED_OBJECT_FUNC_DECLARATION);
void
Named_object::set_type_value(Named_type* named_type)
{
- gcc_assert(this->classification_ == NAMED_OBJECT_TYPE_DECLARATION);
+ go_assert(this->classification_ == NAMED_OBJECT_TYPE_DECLARATION);
Type_declaration* td = this->u_.type_declaration;
td->define_methods(named_type);
Named_object* in_function = td->in_function();
void
Named_object::set_function_value(Function* function)
{
- gcc_assert(this->classification_ == NAMED_OBJECT_FUNC_DECLARATION);
+ go_assert(this->classification_ == NAMED_OBJECT_FUNC_DECLARATION);
this->classification_ = NAMED_OBJECT_FUNC;
// FIXME: We should free the old value.
this->u_.func_value = function;
void
Named_object::declare_as_type()
{
- gcc_assert(this->classification_ == NAMED_OBJECT_UNKNOWN);
+ 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());
// Return the location of a named object.
-source_location
+Location
Named_object::location() const
{
switch (this->classification_)
{
default:
case NAMED_OBJECT_UNINITIALIZED:
- gcc_unreachable();
+ go_unreachable();
+
+ case NAMED_OBJECT_ERRONEOUS:
+ return Linemap::unknown_location();
case NAMED_OBJECT_UNKNOWN:
return this->unknown_value()->location();
return this->var_value()->location();
case NAMED_OBJECT_RESULT_VAR:
- return this->result_var_value()->function()->location();
+ return this->result_var_value()->location();
case NAMED_OBJECT_SINK:
- gcc_unreachable();
+ go_unreachable();
case NAMED_OBJECT_FUNC:
return this->func_value()->location();
default:
case NAMED_OBJECT_UNINITIALIZED:
case NAMED_OBJECT_UNKNOWN:
- gcc_unreachable();
+ go_unreachable();
+
+ case NAMED_OBJECT_ERRONEOUS:
+ break;
case NAMED_OBJECT_CONST:
this->const_value()->export_const(exp, this->name_);
case NAMED_OBJECT_RESULT_VAR:
case NAMED_OBJECT_SINK:
- gcc_unreachable();
+ go_unreachable();
case NAMED_OBJECT_FUNC:
this->func_value()->export_func(exp, this->name_);
}
}
+// 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();
+}
+
// Class Bindings.
Bindings::Bindings(Bindings* enclosing)
Bindings::remove_binding(Named_object* no)
{
Contour::iterator pb = this->bindings_.find(no->name());
- gcc_assert(pb != this->bindings_.end());
+ 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();
return;
}
}
- gcc_unreachable();
+ go_unreachable();
}
// Add a method to the list of objects. This is not added to the
Bindings::add_named_object_to_contour(Contour* contour,
Named_object* named_object)
{
- gcc_assert(named_object == named_object->resolve());
+ go_assert(named_object == named_object->resolve());
const std::string& name(named_object->name());
- gcc_assert(!Gogo::is_sink_name(name));
+ go_assert(!Gogo::is_sink_name(name));
std::pair<Contour::iterator, bool> ins =
contour->insert(std::make_pair(name, named_object));
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:
- gcc_unreachable();
+ 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);
- gcc_assert(!new_object->is_unknown());
+ 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())
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:
- gcc_unreachable();
+ go_unreachable();
case Named_object::NAMED_OBJECT_FUNC:
if (new_object->is_function_declaration())
Bindings::add_function_declaration(const std::string& name,
const Package* package,
Function_type* type,
- source_location location)
+ Location location)
{
Named_object* no = Named_object::make_function_declaration(name, package,
type, location);
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
// 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)
{
- if (traverse->constant(p, is_global) == TRAVERSE_EXIT)
- return TRAVERSE_EXIT;
- }
- if ((traverse_mask & Traverse::traverse_types) != 0
- || (traverse_mask & Traverse::traverse_expressions) != 0)
- {
- Type* t = p->const_value()->type();
- if (t != NULL
- && Type::traverse(t, traverse) == TRAVERSE_EXIT)
- return TRAVERSE_EXIT;
- if (p->const_value()->traverse_expression(traverse)
- == TRAVERSE_EXIT)
+ 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 (traverse->variable(p) == TRAVERSE_EXIT)
- return TRAVERSE_EXIT;
- }
- if (((traverse_mask & Traverse::traverse_types) != 0
- || (traverse_mask & Traverse::traverse_expressions) != 0)
- && (p->is_result_variable()
- || p->var_value()->has_type()))
- {
- Type* t = (p->is_variable()
- ? p->var_value()->type()
- : p->result_var_value()->type());
- if (t != NULL
- && Type::traverse(t, traverse) == TRAVERSE_EXIT)
- return TRAVERSE_EXIT;
- }
- if (p->is_variable()
- && ((traverse_mask & Traverse::traverse_types) != 0
- || (traverse_mask & Traverse::traverse_expressions) != 0))
- {
- if (p->var_value()->traverse_expression(traverse)
- == TRAVERSE_EXIT)
- return TRAVERSE_EXIT;
+ 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)
- {
- int t = traverse->function(p);
- if (t == TRAVERSE_EXIT)
- return TRAVERSE_EXIT;
- else if (t == TRAVERSE_SKIP_COMPONENTS)
- break;
- }
-
- if ((traverse_mask
- & (Traverse::traverse_variables
- | Traverse::traverse_constants
- | Traverse::traverse_functions
- | Traverse::traverse_blocks
- | Traverse::traverse_statements
- | Traverse::traverse_expressions
- | Traverse::traverse_types)) != 0)
- {
- if (p->func_value()->traverse(traverse) == TRAVERSE_EXIT)
- return TRAVERSE_EXIT;
- }
+ 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.
- gcc_assert(is_global);
+ go_assert(is_global);
break;
case Named_object::NAMED_OBJECT_TYPE:
- if ((traverse_mask & Traverse::traverse_types) != 0
- || (traverse_mask & Traverse::traverse_expressions) != 0)
- {
- if (Type::traverse(p->type_value(), traverse) == TRAVERSE_EXIT)
- return TRAVERSE_EXIT;
- }
+ 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:
- gcc_unreachable();
+ go_unreachable();
+ }
+
+ if (t == TRAVERSE_EXIT)
+ return TRAVERSE_EXIT;
+ }
+
+ // If we need to traverse types, check the function declarations,
+ // which have types. We don't need to check the type declarations,
+ // as those are just names.
+ 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;
+ }
}
}
return TRAVERSE_CONTINUE;
}
+// 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();
+ tree fndecl = function->get_decl();
+ Bfunction* bfunction = tree_to_function(fndecl);
+ 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);
+}
+
+// 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();
+ tree fndecl = function->get_decl();
+ Bfunction* bfunction = tree_to_function(fndecl);
+ 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& name, const std::string& unique_prefix,
- source_location location)
+ Location location)
: name_(name), unique_prefix_(unique_prefix), bindings_(new Bindings(NULL)),
priority_(0), location_(location), used_(false), is_imported_(false),
uses_sink_alias_(false)
{
- gcc_assert(!name.empty() && !unique_prefix.empty());
+ go_assert(!name.empty() && !unique_prefix.empty());
}
// Set the priority. We may see multiple priorities for an imported
{
if (type->is_error_type())
return true;
- gcc_assert((this->traverse_mask() & traverse_types) != 0
+ go_assert((this->traverse_mask() & traverse_types) != 0
|| (this->traverse_mask() & traverse_expressions) != 0);
- // We only have to remember named types, as they are the only ones
- // we can see multiple times in a traversal.
- if (type->classification() != Type::TYPE_NAMED)
+ // 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} }
+ if (type->classification() != Type::TYPE_NAMED
+ && type->classification() != Type::TYPE_INTERFACE)
return false;
if (this->types_seen_ == NULL)
this->types_seen_ = new Types_seen();
bool
Traverse::remember_expression(const Expression* expression)
{
- gcc_assert((this->traverse_mask() & traverse_types) != 0
+ go_assert((this->traverse_mask() & traverse_types) != 0
|| (this->traverse_mask() & traverse_expressions) != 0);
if (this->expressions_seen_ == NULL)
this->expressions_seen_ = new Expressions_seen();
int
Traverse::variable(Named_object*)
{
- gcc_unreachable();
+ go_unreachable();
}
int
Traverse::constant(Named_object*, bool)
{
- gcc_unreachable();
+ go_unreachable();
}
int
Traverse::function(Named_object*)
{
- gcc_unreachable();
+ go_unreachable();
}
int
Traverse::block(Block*)
{
- gcc_unreachable();
+ go_unreachable();
}
int
Traverse::statement(Block*, size_t*, Statement*)
{
- gcc_unreachable();
+ go_unreachable();
}
int
Traverse::expression(Expression**)
{
- gcc_unreachable();
+ go_unreachable();
}
int
Traverse::type(Type*)
{
- gcc_unreachable();
+ go_unreachable();
+}
+
+// Class Statement_inserter.
+
+void
+Statement_inserter::insert(Statement* 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());
}