// Class Gogo.
-Gogo::Gogo(Backend* backend, int int_type_size, int pointer_size)
+Gogo::Gogo(Backend* backend, Linemap* linemap, int int_type_size,
+ int pointer_size)
: backend_(backend),
+ linemap_(linemap),
package_(NULL),
functions_(),
globals_(new Bindings(NULL)),
unique_prefix_(),
unique_prefix_specified_(false),
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);
// to the same Named_object.
Named_object* byte_type = this->declare_type("byte", loc);
byte_type->set_type_value(uint8_type);
+ uint8_type->integer_type()->set_is_byte();
+
+ // "rune" is an alias for "int".
+ Named_object* rune_type = this->declare_type("rune", loc);
+ rune_type->set_type_value(int_type);
+ int_type->integer_type()->set_is_rune();
this->add_named_type(Type::make_integer_type("uintptr", true,
pointer_size,
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));
+ Type *error_iface = Type::make_interface_type(methods, loc);
+ 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),
imag_type->set_is_varargs();
imag_type->set_is_builtin();
this->globals_->add_function_declaration("imag", NULL, imag_type, loc);
+
+ Function_type* delete_type = Type::make_function_type(NULL, NULL, NULL, loc);
+ delete_type->set_is_varargs();
+ delete_type->set_is_builtin();
+ this->globals_->add_function_declaration("delete", NULL, delete_type, loc);
}
// Munge name for use in an error message.
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")
{
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)
{
go_assert(this->in_global_scope());
Package*
Gogo::register_package(const std::string& package_name,
const std::string& unique_prefix,
- source_location location)
+ Location location)
{
go_assert(!unique_prefix.empty() && !package_name.empty());
std::string name = unique_prefix + '.' + 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
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();
// Finish compiling a function.
void
-Gogo::finish_function(source_location location)
+Gogo::finish_function(Location location)
{
this->finish_block(location);
go_assert(this->functions_.back().blocks.empty());
// Start a new block.
void
-Gogo::start_block(source_location location)
+Gogo::start_block(Location location)
{
go_assert(!this->functions_.empty());
Block* block = new Block(this->current_block(), location);
// Finish a block.
Block*
-Gogo::finish_block(source_location location)
+Gogo::finish_block(Location location)
{
go_assert(!this->functions_.empty());
go_assert(!this->functions_.back().blocks.empty());
// 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,
Label*
Gogo::add_label_definition(const std::string& label_name,
- source_location location)
+ Location location)
{
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)
{
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.
// Add a block.
void
-Gogo::add_block(Block* block, source_location location)
+Gogo::add_block(Block* block, Location location)
{
go_assert(!this->functions_.empty()
&& !this->functions_.back().blocks.empty());
// 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);
// 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
{
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:
+ {
+ if (!t->compare_is_identity(this->gogo_) && t->is_comparable())
+ t->type_functions(this->gogo_, t->named_type(), 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 = t->named_type()->real_type();
+ if (rt->struct_type() == NULL)
+ {
+ if (Type::traverse(rt, this) == TRAVERSE_EXIT)
+ return TRAVERSE_EXIT;
+ }
+ else
+ {
+ if (rt->struct_type()->traverse_field_types(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
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);
if (is_thunk && p + 1 == find_eval_ordering.end())
break;
- source_location loc = (*pexpr)->location();
+ Location loc = (*pexpr)->location();
Statement* s;
if ((*pexpr)->call_expression() == NULL
|| (*pexpr)->call_expression()->result_count() < 2)
// 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);
+ s = Statement::make_statement(*pexpr, true);
}
block->insert_statement_before(*pindex, s);
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();
+ Location loc = (*pexpr)->location();
Statement* s;
if ((*pexpr)->call_expression() == NULL
|| (*pexpr)->call_expression()->result_count() < 2)
{
// A call expression which returns multiple results needs to
// be handled specially.
- s = Statement::make_statement(*pexpr);
+ 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];
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();
// __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));
// Class Function.
Function::Function(Function_type* type, Function* enclosing, Block* block,
- source_location location)
+ Location location)
: type_(type), enclosing_(enclosing), results_(NULL),
closure_var_(NULL), block_(block), location_(location), fndecl_(NULL),
defer_stack_(NULL), results_are_named_(false), calls_recover_(false),
{
// 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),
// 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->define(location);
- return label;
- }
- else
+ label = ins.first->second;
+ if (label->is_defined())
{
error_at(location, "label %qs already defined",
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.
- Label* label = ins.first->second;
- label->set_is_used();
- return label;
+ label = ins.first->second;
}
else
{
go_assert(ins.first->second == NULL);
- Label* label = new Label(label_name);
+ label = new Label(label_name);
ins.first->second = label;
- label->set_is_used();
- 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.
// function which uses defer.
Expression*
-Function::defer_stack(source_location location)
+Function::defer_stack(Location location)
{
if (this->defer_stack_ == NULL)
{
// Class Block.
-Block::Block(Block* enclosing, source_location location)
+Block::Block(Block* enclosing, Location location)
: enclosing_(enclosing), statements_(),
bindings_(new Bindings(enclosing == NULL
? NULL
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),
backend_(NULL), is_global_(is_global), is_parameter_(is_parameter),
is_receiver_(is_receiver), is_varargs_parameter_(false),
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");
Named_object*
Type_declaration::add_method_declaration(const std::string& name,
Function_type* type,
- source_location location)
+ Location location)
{
Named_object* ret = Named_object::make_function_declaration(name, NULL, type,
location);
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);
// Return the location of a named object.
-source_location
+Location
Named_object::location() const
{
switch (this->classification_)
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:
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);
// 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*
// Return an expression for the address of this label.
Bexpression*
-Label::get_addr(Translate_context* context, source_location location)
+Label::get_addr(Translate_context* context, Location location)
{
Blabel* label = this->get_backend_label(context);
return context->backend()->label_address(label, location);
// Return a goto statement to this unnamed label.
Bstatement*
-Unnamed_label::get_goto(Translate_context* context, source_location location)
+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)