// Class Gogo.
-Gogo::Gogo(int int_type_size, int float_type_size, int pointer_size)
+Gogo::Gogo(int int_type_size, int pointer_size)
: package_(NULL),
functions_(),
globals_(new Bindings(NULL)),
init_fn_name_(),
imported_init_fns_(),
unique_prefix_(),
- interface_types_()
+ unique_prefix_specified_(false),
+ interface_types_(),
+ named_types_are_converted_(false)
{
const source_location loc = BUILTINS_LOCATION;
pointer_size,
RUNTIME_TYPE_KIND_UINTPTR));
- this->add_named_type(Type::make_float_type("float", float_type_size,
- RUNTIME_TYPE_KIND_FLOAT));
-
- this->add_named_type(Type::make_complex_type("complex", float_type_size * 2,
- RUNTIME_TYPE_KIND_COMPLEX));
-
this->add_named_type(Type::make_named_bool_type());
this->add_named_type(Type::make_named_string_type());
append_type->set_is_builtin();
this->globals_->add_function_declaration("append", NULL, append_type, loc);
- Function_type* cmplx_type = Type::make_function_type(NULL, NULL, NULL, loc);
- cmplx_type->set_is_varargs();
- cmplx_type->set_is_builtin();
- this->globals_->add_function_declaration("cmplx", NULL, cmplx_type, loc);
+ Function_type* complex_type = Type::make_function_type(NULL, NULL, NULL, loc);
+ complex_type->set_is_varargs();
+ complex_type->set_is_builtin();
+ this->globals_->add_function_declaration("complex", NULL, complex_type, loc);
Function_type* real_type = Type::make_function_type(NULL, NULL, NULL, loc);
real_type->set_is_varargs();
Function_type* imag_type = Type::make_function_type(NULL, NULL, NULL, loc);
imag_type->set_is_varargs();
imag_type->set_is_builtin();
- this->globals_->add_function_declaration("imag", NULL, cmplx_type, loc);
+ this->globals_->add_function_declaration("imag", NULL, imag_type, loc);
this->define_builtin_function_trees();
// package name (e.g., P.x), but we no longer do.
// this->globals_->add_package(package_name, this->package_);
- if (package_name == "main")
+ if (this->is_main_package())
{
// Declare "main" as a function which takes no parameters and
// returns no value.
}
}
+// Return whether this is the "main" package. This is not true if
+// -fgo-prefix was used.
+
+bool
+Gogo::is_main_package() const
+{
+ return this->package_name() == "main" && !this->unique_prefix_specified_;
+}
+
// Import a package.
void
Import imp(stream, location);
imp.register_builtin_types(this);
Package* package = imp.import(this, local_name, is_local_name_exported);
- this->imports_.insert(std::make_pair(filename, package));
- package->set_is_imported();
+ if (package != NULL)
+ {
+ if (package->name() == this->package_name()
+ && package->unique_prefix() == this->unique_prefix())
+ error_at(location,
+ ("imported package uses same package name and prefix "
+ "as package being compiled (see -fgo-prefix option)"));
+
+ this->imports_.insert(std::make_pair(filename, package));
+ package->set_is_imported();
+ }
delete stream;
}
Named_object*
Gogo::lookup(const std::string& name, Named_object** pfunction) const
{
+ if (pfunction != NULL)
+ *pfunction = NULL;
+
if (Gogo::is_sink_name(name))
return Named_object::make_sink();
}
}
- if (pfunction != NULL)
- *pfunction = NULL;
-
if (this->package_ != NULL)
{
Named_object* ret = this->package_->bindings()->lookup(name);
Named_object* ret;
if (Gogo::is_sink_name(*pname))
- ret = Named_object::make_sink();
+ {
+ static int sink_count;
+ char buf[30];
+ snprintf(buf, sizeof buf, ".$sink%d", sink_count);
+ ++sink_count;
+ ret = Named_object::make_function(buf, NULL, function);
+ }
else if (!type->is_method())
{
ret = this->package_->bindings()->add_function(*pname, NULL, function);
- if (!ret->is_function())
+ if (!ret->is_function() || ret->func_value() != function)
{
- // Redefinition error.
- ret = Named_object::make_function(name, NULL, function);
+ // Redefinition error. Invent a name to avoid knockon
+ // errors.
+ static int redefinition_count;
+ char buf[30];
+ snprintf(buf, sizeof buf, ".$redefined%d", redefinition_count);
+ ++redefinition_count;
+ ret = this->package_->bindings()->add_function(buf, NULL, function);
}
}
else
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;
{
public:
Lower_parse_tree(Gogo* gogo, Named_object* function)
- : Traverse(traverse_constants
+ : Traverse(traverse_variables
+ | traverse_constants
| traverse_functions
| traverse_statements
| traverse_expressions),
gogo_(gogo), function_(function), iota_value_(-1)
{ }
+ int
+ variable(Named_object*);
+
int
constant(Named_object*, bool);
int iota_value_;
};
+// 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.
+
+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;
+}
+
// Lower constants. We handle constants specially so that we can set
// the right value for the predeclared constant iota. This works in
// conjunction with the way we lower Const_expression objects.
{
Named_constant* nc = no->const_value();
- // We can recursively a constant if the initializer expression
- // manages to refer to itself.
+ // Don't get into trouble if the constant's initializer expression
+ // refers to the constant itself.
if (nc->lowering())
return TRAVERSE_CONTINUE;
nc->set_lowering();
// If this is a global variable which requires runtime
// initialization, we need an initialization function.
- if (!variable->is_global() || variable->init() == NULL)
+ if (!variable->is_global())
+ ;
+ else if (variable->init() == NULL)
;
else if (variable->type()->interface_type() != NULL)
this->need_init_fn_ = true;
class Shortcuts : public Traverse
{
public:
- Shortcuts()
+ Shortcuts(Gogo* gogo)
: Traverse(traverse_variables
- | traverse_statements)
+ | traverse_statements),
+ gogo_(gogo)
{ }
protected:
// Convert a shortcut operator.
Statement*
convert_shortcut(Block* enclosing, Expression** pshortcut);
+
+ // The IR.
+ Gogo* gogo_;
};
// Remove shortcut operators in a single statement.
return TRAVERSE_CONTINUE;
Statement* snew = this->convert_shortcut(NULL, pshortcut);
- var->add_preinit_statement(snew);
+ var->add_preinit_statement(this->gogo_, snew);
if (pshortcut == &init)
var->set_init(init);
}
delete shortcut;
// Now convert any shortcut operators in LEFT and RIGHT.
- Shortcuts shortcuts;
+ Shortcuts shortcuts(this->gogo_);
retblock->traverse(&shortcuts);
return Statement::make_block_statement(retblock, loc);
void
Gogo::remove_shortcuts()
{
- Shortcuts shortcuts;
+ Shortcuts shortcuts(this);
this->traverse(&shortcuts);
}
class Order_eval : public Traverse
{
public:
- Order_eval()
+ Order_eval(Gogo* gogo)
: Traverse(traverse_variables
- | traverse_statements)
+ | traverse_statements),
+ gogo_(gogo)
{ }
int
int
statement(Block*, size_t*, Statement*);
+
+ private:
+ // The IR.
+ Gogo* gogo_;
};
// Implement the order of evaluation rules for a statement.
Expression** pexpr = *p;
source_location loc = (*pexpr)->location();
Temporary_statement* ts = Statement::make_temporary(NULL, *pexpr, loc);
- var->add_preinit_statement(ts);
+ var->add_preinit_statement(this->gogo_, ts);
*pexpr = Expression::make_temporary_reference(ts, loc);
}
void
Gogo::order_evaluations()
{
- Order_eval order_eval;
+ Order_eval order_eval(this);
this->traverse(&order_eval);
}
for (Typed_identifier_list::const_iterator p = orig_results->begin();
p != orig_results->end();
++p)
- new_results->push_back(*p);
+ new_results->push_back(Typed_identifier("", p->type(), p->location()));
}
Function_type *new_fntype = Type::make_function_type(NULL, new_params,
Expression* fn = Expression::make_func_reference(new_no, closure, location);
Expression_list* args = new Expression_list();
- if (orig_fntype->is_method())
- {
- Named_object* rec_no = gogo->lookup(receiver_name, NULL);
- gcc_assert(rec_no != NULL
- && rec_no->is_variable()
- && rec_no->var_value()->is_parameter());
- args->push_back(Expression::make_var_reference(rec_no, location));
- }
if (new_params != NULL)
{
// Note that we skip the last parameter, which is the boolean
}
args->push_back(this->can_recover_arg(location));
- Expression* call = Expression::make_call(fn, args, false, location);
+ Call_expression* call = Expression::make_call(fn, args, false, location);
Statement* s;
if (orig_fntype->results() == NULL || orig_fntype->results()->empty())
else
{
Expression_list* vals = new Expression_list();
- vals->push_back(call);
+ size_t rc = orig_fntype->results()->size();
+ if (rc == 1)
+ vals->push_back(call);
+ else
+ {
+ 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);
}
&& !orig_rec_no->var_value()->is_receiver());
orig_rec_no->var_value()->set_is_receiver();
- Named_object* new_rec_no = new_bindings->lookup_local(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();
+ const std::string& new_receiver_name(orig_fntype->receiver()->name());
+ Named_object* new_rec_no = new_bindings->lookup_local(new_receiver_name);
+ if (new_rec_no == NULL)
+ gcc_assert(saw_errors());
+ else
+ {
+ gcc_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
Convert_recover convert_recover(can_recover_no);
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();
+
return TRAVERSE_CONTINUE;
}
{
gcc_assert(this->unique_prefix_.empty());
this->unique_prefix_ = arg;
+ this->unique_prefix_specified_ = true;
}
// Work out the package priority. It is one more than the maximum
exp.export_globals(this->package_name(),
this->unique_prefix(),
this->package_priority(),
- (this->need_init_fn_ && this->package_name() != "main"
+ (this->need_init_fn_ && !this->is_main_package()
? this->get_init_fn_name()
: ""),
this->imported_init_fns_,
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();
+ Channel_type::make_chan_type_descriptor_type();
+ Interface_type::make_interface_type_descriptor_type();
+ Type::convert_builtin_named_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,
}
Result_variable* result = new Result_variable(p->type(), this, index);
Named_object* no = block->bindings()->add_result_variable(name, result);
- this->named_results_->push_back(no);
+ if (no->is_result_variable())
+ this->named_results_->push_back(no);
}
}
+// Update the named result variables when cloning a function which
+// calls recover.
+
+void
+Function::update_named_result_variables()
+{
+ if (this->named_results_ == NULL)
+ return;
+
+ for (Named_results::iterator p = this->named_results_->begin();
+ p != this->named_results_->end();
+ ++p)
+ (*p)->result_var_value()->set_function(this);
+}
+
// Return the closure variable, creating it if necessary.
Named_object*
Function::swap_for_recover(Function *x)
{
gcc_assert(this->enclosing_ == x->enclosing_);
- gcc_assert(this->named_results_ == x->named_results_);
+ std::swap(this->named_results_, x->named_results_);
std::swap(this->closure_var_, x->closure_var_);
std::swap(this->block_, x->block_);
gcc_assert(this->location_ == x->location_);
// Get the preinit block.
Block*
-Variable::preinit_block()
+Variable::preinit_block(Gogo* gogo)
{
gcc_assert(this->is_global_);
if (this->preinit_ == NULL)
this->preinit_ = new Block(NULL, this->location());
+
+ // If a global variable has a preinitialization statement, then we
+ // need to have an initialization function.
+ gogo->set_need_init_fn();
+
return this->preinit_;
}
// Add a statement to be run before the initialization expression.
void
-Variable::add_preinit_statement(Statement* s)
+Variable::add_preinit_statement(Gogo* gogo, Statement* s)
{
- Block* b = this->preinit_block();
+ Block* b = this->preinit_block(gogo);
b->add_statement(s);
b->set_end_location(s->location());
}
Variable::type_from_tuple(Expression* expr, bool report_error) const
{
if (expr->map_index_expression() != NULL)
- return expr->map_index_expression()->get_map_type()->val_type();
+ {
+ Map_type* mt = expr->map_index_expression()->get_map_type();
+ if (mt == NULL)
+ return Type::make_error_type();
+ return mt->val_type();
+ }
else if (expr->receive_expression() != NULL)
{
Expression* channel = expr->receive_expression()->channel();
Type* channel_type = channel->type();
- if (channel_type->is_error_type())
+ if (channel_type->channel_type() == NULL)
return Type::make_error_type();
return channel_type->channel_type()->element_type();
}
void
Variable::determine_type()
{
+ if (this->preinit_ != NULL)
+ this->preinit_->determine_types();
+
// A variable in a type switch with a nil case will have the wrong
// type here. It will have an initializer which is a type guard.
// We want to initialize it to the value without the type guard, and
if (t != NULL
&& Type::traverse(t, traverse) == TRAVERSE_EXIT)
return TRAVERSE_EXIT;
- }
- if ((traverse_mask & Traverse::traverse_expressions) != 0)
- {
if (p->const_value()->traverse_expression(traverse)
== TRAVERSE_EXIT)
return TRAVERSE_EXIT;
return TRAVERSE_EXIT;
}
if (p->is_variable()
- && (traverse_mask & Traverse::traverse_expressions) != 0)
+ && ((traverse_mask & Traverse::traverse_types) != 0
+ || (traverse_mask & Traverse::traverse_expressions) != 0))
{
if (p->var_value()->traverse_expression(traverse)
== TRAVERSE_EXIT)