Commit 67f8f449 by Jerry DeLisle

2011-06-05 Jerry DeLisle <jvdelisle@gcc.gnu.org>

	Merge trunk into branch, part one.

[[Split portion of a mixed commit.]]

From-SVN: r174658.2
parent 419b55d0
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
// go.cc -- Go frontend main file for gcc.
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "go-system.h"
#include "go-c.h"
#include "lex.h"
#include "parse.h"
#include "gogo.h"
// The unique prefix to use for exported symbols. This is set during
// option processing.
static std::string unique_prefix;
// The data structures we build to represent the file.
static Gogo* gogo;
// Create the main IR data structure.
GO_EXTERN_C
void
go_create_gogo(int int_type_size, int float_type_size, int pointer_size)
{
gcc_assert(::gogo == NULL);
::gogo = new Gogo(int_type_size, float_type_size, pointer_size);
if (!unique_prefix.empty())
::gogo->set_unique_prefix(unique_prefix);
}
// Set the unique prefix we use for exported symbols.
GO_EXTERN_C
void
go_set_prefix(const char* arg)
{
unique_prefix = arg;
for (size_t i = 0; i < unique_prefix.length(); ++i)
{
char c = unique_prefix[i];
if ((c >= 'a' && c <= 'z')
|| (c >= 'A' && c <= 'Z')
|| (c >= '0' && c <= '9')
|| c == '_')
;
else
unique_prefix[i] = '_';
}
}
// Parse the input files.
GO_EXTERN_C
void
go_parse_input_files(const char** filenames, unsigned int filename_count,
bool only_check_syntax, bool require_return_statement)
{
gcc_assert(filename_count > 0);
for (unsigned int i = 0; i < filename_count; ++i)
{
if (i > 0)
::gogo->clear_file_scope();
const char* filename = filenames[i];
FILE* file;
if (strcmp(filename, "-") == 0)
file = stdin;
else
{
file = fopen(filename, "r");
if (file == NULL)
fatal_error("cannot open %s: %m", filename);
}
Lex lexer(filename, file);
Parse parse(&lexer, ::gogo);
parse.program();
if (strcmp(filename, "-") != 0)
fclose(file);
}
::gogo->clear_file_scope();
// If the global predeclared names are referenced but not defined,
// define them now.
::gogo->define_global_names();
// Finalize method lists and build stub methods for named types.
::gogo->finalize_methods();
// Now that we have seen all the names, lower the parse tree into a
// form which is easier to use.
::gogo->lower_parse_tree();
// Now that we have seen all the names, verify that types are
// correct.
::gogo->verify_types();
// Work out types of unspecified constants and variables.
::gogo->determine_types();
// Check types and issue errors as appropriate.
::gogo->check_types();
if (only_check_syntax)
return;
// Check that functions have return statements.
if (require_return_statement)
::gogo->check_return_statements();
// Export global identifiers as appropriate.
::gogo->do_exports();
// Build required interface method tables.
::gogo->build_interface_method_tables();
// Turn short-cut operators (&&, ||) into explicit if statements.
::gogo->remove_shortcuts();
// Use temporary variables to force order of evaluation.
::gogo->order_evaluations();
// Build thunks for functions which call recover.
::gogo->build_recover_thunks();
// Convert complicated go and defer statements into simpler ones.
::gogo->simplify_thunk_statements();
}
// Write out globals.
GO_EXTERN_C
void
go_write_globals()
{
return ::gogo->write_globals();
}
// Return the global IR structure. This is used by some of the
// langhooks to pass to other code.
Gogo*
go_get_gogo()
{
return ::gogo;
}
// go.cc -- Go frontend main file for gcc.
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "go-system.h"
#include "go-c.h"
#include "lex.h"
#include "parse.h"
#include "backend.h"
#include "gogo.h"
// The unique prefix to use for exported symbols. This is set during
// option processing.
static std::string unique_prefix;
// The data structures we build to represent the file.
static Gogo* gogo;
// Create the main IR data structure.
GO_EXTERN_C
void
go_create_gogo(int int_type_size, int pointer_size)
{
go_assert(::gogo == NULL);
::gogo = new Gogo(go_get_backend(), int_type_size, pointer_size);
if (!unique_prefix.empty())
::gogo->set_unique_prefix(unique_prefix);
}
// Set the unique prefix we use for exported symbols.
GO_EXTERN_C
void
go_set_prefix(const char* arg)
{
unique_prefix = arg;
for (size_t i = 0; i < unique_prefix.length(); ++i)
{
char c = unique_prefix[i];
if ((c >= 'a' && c <= 'z')
|| (c >= 'A' && c <= 'Z')
|| (c >= '0' && c <= '9')
|| c == '_')
;
else
unique_prefix[i] = '_';
}
}
// Parse the input files.
GO_EXTERN_C
void
go_parse_input_files(const char** filenames, unsigned int filename_count,
bool only_check_syntax, bool require_return_statement)
{
go_assert(filename_count > 0);
for (unsigned int i = 0; i < filename_count; ++i)
{
if (i > 0)
::gogo->clear_file_scope();
const char* filename = filenames[i];
FILE* file;
if (strcmp(filename, "-") == 0)
file = stdin;
else
{
file = fopen(filename, "r");
if (file == NULL)
fatal_error("cannot open %s: %m", filename);
}
Lex lexer(filename, file);
Parse parse(&lexer, ::gogo);
parse.program();
if (strcmp(filename, "-") != 0)
fclose(file);
}
::gogo->clear_file_scope();
// If the global predeclared names are referenced but not defined,
// define them now.
::gogo->define_global_names();
// Finalize method lists and build stub methods for named types.
::gogo->finalize_methods();
// Now that we have seen all the names, lower the parse tree into a
// form which is easier to use.
::gogo->lower_parse_tree();
// Now that we have seen all the names, verify that types are
// correct.
::gogo->verify_types();
// Work out types of unspecified constants and variables.
::gogo->determine_types();
// Check types and issue errors as appropriate.
::gogo->check_types();
if (only_check_syntax)
return;
// Check that functions have return statements.
if (require_return_statement)
::gogo->check_return_statements();
// Export global identifiers as appropriate.
::gogo->do_exports();
// Turn short-cut operators (&&, ||) into explicit if statements.
::gogo->remove_shortcuts();
// Use temporary variables to force order of evaluation.
::gogo->order_evaluations();
// Build thunks for functions which call recover.
::gogo->build_recover_thunks();
// Convert complicated go and defer statements into simpler ones.
::gogo->simplify_thunk_statements();
}
// Write out globals.
GO_EXTERN_C
void
go_write_globals()
{
return ::gogo->write_globals();
}
// Return the global IR structure. This is used by some of the
// langhooks to pass to other code.
Gogo*
go_get_gogo()
{
return ::gogo;
}
// go.cc -- Go frontend main file for gcc.
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "go-system.h"
#include "go-c.h"
#include "lex.h"
#include "parse.h"
#include "gogo.h"
// The unique prefix to use for exported symbols. This is set during
// option processing.
static std::string unique_prefix;
// The data structures we build to represent the file.
static Gogo* gogo;
// Create the main IR data structure.
GO_EXTERN_C
void
go_create_gogo(int int_type_size, int pointer_size)
{
gcc_assert(::gogo == NULL);
::gogo = new Gogo(int_type_size, pointer_size);
if (!unique_prefix.empty())
::gogo->set_unique_prefix(unique_prefix);
}
// Set the unique prefix we use for exported symbols.
GO_EXTERN_C
void
go_set_prefix(const char* arg)
{
unique_prefix = arg;
for (size_t i = 0; i < unique_prefix.length(); ++i)
{
char c = unique_prefix[i];
if ((c >= 'a' && c <= 'z')
|| (c >= 'A' && c <= 'Z')
|| (c >= '0' && c <= '9')
|| c == '_')
;
else
unique_prefix[i] = '_';
}
}
// Parse the input files.
GO_EXTERN_C
void
go_parse_input_files(const char** filenames, unsigned int filename_count,
bool only_check_syntax, bool require_return_statement)
{
gcc_assert(filename_count > 0);
for (unsigned int i = 0; i < filename_count; ++i)
{
if (i > 0)
::gogo->clear_file_scope();
const char* filename = filenames[i];
FILE* file;
if (strcmp(filename, "-") == 0)
file = stdin;
else
{
file = fopen(filename, "r");
if (file == NULL)
fatal_error("cannot open %s: %m", filename);
}
Lex lexer(filename, file);
Parse parse(&lexer, ::gogo);
parse.program();
if (strcmp(filename, "-") != 0)
fclose(file);
}
::gogo->clear_file_scope();
// If the global predeclared names are referenced but not defined,
// define them now.
::gogo->define_global_names();
// Finalize method lists and build stub methods for named types.
::gogo->finalize_methods();
// Now that we have seen all the names, lower the parse tree into a
// form which is easier to use.
::gogo->lower_parse_tree();
// Now that we have seen all the names, verify that types are
// correct.
::gogo->verify_types();
// Work out types of unspecified constants and variables.
::gogo->determine_types();
// Check types and issue errors as appropriate.
::gogo->check_types();
if (only_check_syntax)
return;
// Check that functions have return statements.
if (require_return_statement)
::gogo->check_return_statements();
// Export global identifiers as appropriate.
::gogo->do_exports();
// Turn short-cut operators (&&, ||) into explicit if statements.
::gogo->remove_shortcuts();
// Use temporary variables to force order of evaluation.
::gogo->order_evaluations();
// Build thunks for functions which call recover.
::gogo->build_recover_thunks();
// Convert complicated go and defer statements into simpler ones.
::gogo->simplify_thunk_statements();
}
// Write out globals.
GO_EXTERN_C
void
go_write_globals()
{
return ::gogo->write_globals();
}
// Return the global IR structure. This is used by some of the
// langhooks to pass to other code.
Gogo*
go_get_gogo()
{
return ::gogo;
}
// gogo-tree.cc -- convert Go frontend Gogo IR to gcc trees.
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "go-system.h"
#include <gmp.h>
#ifndef ENABLE_BUILD_WITH_CXX
extern "C"
{
#endif
#include "tm.h"
#include "toplev.h"
#include "tree.h"
#include "gimple.h"
#include "tree-iterator.h"
#include "cgraph.h"
#include "langhooks.h"
#include "convert.h"
#include "output.h"
#include "diagnostic.h"
#include "rtl.h"
#ifndef ENABLE_BUILD_WITH_CXX
}
#endif
#include "go-c.h"
#include "types.h"
#include "expressions.h"
#include "statements.h"
#include "gogo.h"
// Whether we have seen any errors.
bool
saw_errors()
{
return errorcount != 0 || sorrycount != 0;
}
// A helper function.
static inline tree
get_identifier_from_string(const std::string& str)
{
return get_identifier_with_length(str.data(), str.length());
}
// Builtin functions.
static std::map<std::string, tree> builtin_functions;
// Define a builtin function. BCODE is the builtin function code
// defined by builtins.def. NAME is the name of the builtin function.
// LIBNAME is the name of the corresponding library function, and is
// NULL if there isn't one. FNTYPE is the type of the function.
// CONST_P is true if the function has the const attribute.
static void
define_builtin(built_in_function bcode, const char* name, const char* libname,
tree fntype, bool const_p)
{
tree decl = add_builtin_function(name, fntype, bcode, BUILT_IN_NORMAL,
libname, NULL_TREE);
if (const_p)
TREE_READONLY(decl) = 1;
built_in_decls[bcode] = decl;
implicit_built_in_decls[bcode] = decl;
builtin_functions[name] = decl;
if (libname != NULL)
{
decl = add_builtin_function(libname, fntype, bcode, BUILT_IN_NORMAL,
NULL, NULL_TREE);
if (const_p)
TREE_READONLY(decl) = 1;
builtin_functions[libname] = decl;
}
}
// Create trees for implicit builtin functions.
void
Gogo::define_builtin_function_trees()
{
/* We need to define the fetch_and_add functions, since we use them
for ++ and --. */
tree t = go_type_for_size(BITS_PER_UNIT, 1);
tree p = build_pointer_type(build_qualified_type(t, TYPE_QUAL_VOLATILE));
define_builtin(BUILT_IN_ADD_AND_FETCH_1, "__sync_fetch_and_add_1", NULL,
build_function_type_list(t, p, t, NULL_TREE), false);
t = go_type_for_size(BITS_PER_UNIT * 2, 1);
p = build_pointer_type(build_qualified_type(t, TYPE_QUAL_VOLATILE));
define_builtin (BUILT_IN_ADD_AND_FETCH_2, "__sync_fetch_and_add_2", NULL,
build_function_type_list(t, p, t, NULL_TREE), false);
t = go_type_for_size(BITS_PER_UNIT * 4, 1);
p = build_pointer_type(build_qualified_type(t, TYPE_QUAL_VOLATILE));
define_builtin(BUILT_IN_ADD_AND_FETCH_4, "__sync_fetch_and_add_4", NULL,
build_function_type_list(t, p, t, NULL_TREE), false);
t = go_type_for_size(BITS_PER_UNIT * 8, 1);
p = build_pointer_type(build_qualified_type(t, TYPE_QUAL_VOLATILE));
define_builtin(BUILT_IN_ADD_AND_FETCH_8, "__sync_fetch_and_add_8", NULL,
build_function_type_list(t, p, t, NULL_TREE), false);
// We use __builtin_expect for magic import functions.
define_builtin(BUILT_IN_EXPECT, "__builtin_expect", NULL,
build_function_type_list(long_integer_type_node,
long_integer_type_node,
long_integer_type_node,
NULL_TREE),
true);
// We use __builtin_memmove for the predeclared copy function.
define_builtin(BUILT_IN_MEMMOVE, "__builtin_memmove", "memmove",
build_function_type_list(ptr_type_node,
ptr_type_node,
const_ptr_type_node,
size_type_node,
NULL_TREE),
false);
// We provide sqrt for the math library.
define_builtin(BUILT_IN_SQRT, "__builtin_sqrt", "sqrt",
build_function_type_list(double_type_node,
double_type_node,
NULL_TREE),
true);
define_builtin(BUILT_IN_SQRTL, "__builtin_sqrtl", "sqrtl",
build_function_type_list(long_double_type_node,
long_double_type_node,
NULL_TREE),
true);
// We use __builtin_return_address in the thunk we build for
// functions which call recover.
define_builtin(BUILT_IN_RETURN_ADDRESS, "__builtin_return_address", NULL,
build_function_type_list(ptr_type_node,
unsigned_type_node,
NULL_TREE),
false);
// The compiler uses __builtin_trap for some exception handling
// cases.
define_builtin(BUILT_IN_TRAP, "__builtin_trap", NULL,
build_function_type(void_type_node, void_list_node),
false);
}
// Get the name to use for the import control function. If there is a
// global function or variable, then we know that that name must be
// unique in the link, and we use it as the basis for our name.
const std::string&
Gogo::get_init_fn_name()
{
if (this->init_fn_name_.empty())
{
gcc_assert(this->package_ != NULL);
if (this->package_name() == "main")
{
// Use a name which the runtime knows.
this->init_fn_name_ = "__go_init_main";
}
else
{
std::string s = this->unique_prefix();
s.append(1, '.');
s.append(this->package_name());
s.append("..import");
this->init_fn_name_ = s;
}
}
return this->init_fn_name_;
}
// Add statements to INIT_STMT_LIST which run the initialization
// functions for imported packages. This is only used for the "main"
// package.
void
Gogo::init_imports(tree* init_stmt_list)
{
gcc_assert(this->package_name() == "main");
if (this->imported_init_fns_.empty())
return;
tree fntype = build_function_type(void_type_node, void_list_node);
// We must call them in increasing priority order.
std::vector<Import_init> v;
for (std::set<Import_init>::const_iterator p =
this->imported_init_fns_.begin();
p != this->imported_init_fns_.end();
++p)
v.push_back(*p);
std::sort(v.begin(), v.end());
for (std::vector<Import_init>::const_iterator p = v.begin();
p != v.end();
++p)
{
std::string user_name = p->package_name() + ".init";
tree decl = build_decl(UNKNOWN_LOCATION, FUNCTION_DECL,
get_identifier_from_string(user_name),
fntype);
const std::string& init_name(p->init_name());
SET_DECL_ASSEMBLER_NAME(decl, get_identifier_from_string(init_name));
TREE_PUBLIC(decl) = 1;
DECL_EXTERNAL(decl) = 1;
append_to_statement_list(build_call_expr(decl, 0), init_stmt_list);
}
}
// Register global variables with the garbage collector. We need to
// register all variables which can hold a pointer value. They become
// roots during the mark phase. We build a struct that is easy to
// hook into a list of roots.
// struct __go_gc_root_list
// {
// struct __go_gc_root_list* __next;
// struct __go_gc_root
// {
// void* __decl;
// size_t __size;
// } __roots[];
// };
// The last entry in the roots array has a NULL decl field.
void
Gogo::register_gc_vars(const std::vector<Named_object*>& var_gc,
tree* init_stmt_list)
{
if (var_gc.empty())
return;
size_t count = var_gc.size();
tree root_type = Gogo::builtin_struct(NULL, "__go_gc_root", NULL_TREE, 2,
"__next",
ptr_type_node,
"__size",
sizetype);
tree index_type = build_index_type(size_int(count));
tree array_type = build_array_type(root_type, index_type);
tree root_list_type = make_node(RECORD_TYPE);
root_list_type = Gogo::builtin_struct(NULL, "__go_gc_root_list",
root_list_type, 2,
"__next",
build_pointer_type(root_list_type),
"__roots",
array_type);
// Build an initialier for the __roots array.
VEC(constructor_elt,gc)* roots_init = VEC_alloc(constructor_elt, gc,
count + 1);
size_t i = 0;
for (std::vector<Named_object*>::const_iterator p = var_gc.begin();
p != var_gc.end();
++p, ++i)
{
VEC(constructor_elt,gc)* init = VEC_alloc(constructor_elt, gc, 2);
constructor_elt* elt = VEC_quick_push(constructor_elt, init, NULL);
tree field = TYPE_FIELDS(root_type);
elt->index = field;
tree decl = (*p)->get_tree(this, NULL);
gcc_assert(TREE_CODE(decl) == VAR_DECL);
elt->value = build_fold_addr_expr(decl);
elt = VEC_quick_push(constructor_elt, init, NULL);
field = DECL_CHAIN(field);
elt->index = field;
elt->value = DECL_SIZE_UNIT(decl);
elt = VEC_quick_push(constructor_elt, roots_init, NULL);
elt->index = size_int(i);
elt->value = build_constructor(root_type, init);
}
// The list ends with a NULL entry.
VEC(constructor_elt,gc)* init = VEC_alloc(constructor_elt, gc, 2);
constructor_elt* elt = VEC_quick_push(constructor_elt, init, NULL);
tree field = TYPE_FIELDS(root_type);
elt->index = field;
elt->value = fold_convert(TREE_TYPE(field), null_pointer_node);
elt = VEC_quick_push(constructor_elt, init, NULL);
field = DECL_CHAIN(field);
elt->index = field;
elt->value = size_zero_node;
elt = VEC_quick_push(constructor_elt, roots_init, NULL);
elt->index = size_int(i);
elt->value = build_constructor(root_type, init);
// Build a constructor for the struct.
VEC(constructor_elt,gc*) root_list_init = VEC_alloc(constructor_elt, gc, 2);
elt = VEC_quick_push(constructor_elt, root_list_init, NULL);
field = TYPE_FIELDS(root_list_type);
elt->index = field;
elt->value = fold_convert(TREE_TYPE(field), null_pointer_node);
elt = VEC_quick_push(constructor_elt, root_list_init, NULL);
field = DECL_CHAIN(field);
elt->index = field;
elt->value = build_constructor(array_type, roots_init);
// Build a decl to register.
tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL,
create_tmp_var_name("gc"), root_list_type);
DECL_EXTERNAL(decl) = 0;
TREE_PUBLIC(decl) = 0;
TREE_STATIC(decl) = 1;
DECL_ARTIFICIAL(decl) = 1;
DECL_INITIAL(decl) = build_constructor(root_list_type, root_list_init);
rest_of_decl_compilation(decl, 1, 0);
static tree register_gc_fndecl;
tree call = Gogo::call_builtin(&register_gc_fndecl, BUILTINS_LOCATION,
"__go_register_gc_roots",
1,
void_type_node,
build_pointer_type(root_list_type),
build_fold_addr_expr(decl));
append_to_statement_list(call, init_stmt_list);
}
// Build the decl for the initialization function.
tree
Gogo::initialization_function_decl()
{
// The tedious details of building your own function. There doesn't
// seem to be a helper function for this.
std::string name = this->package_name() + ".init";
tree fndecl = build_decl(BUILTINS_LOCATION, FUNCTION_DECL,
get_identifier_from_string(name),
build_function_type(void_type_node,
void_list_node));
const std::string& asm_name(this->get_init_fn_name());
SET_DECL_ASSEMBLER_NAME(fndecl, get_identifier_from_string(asm_name));
tree resdecl = build_decl(BUILTINS_LOCATION, RESULT_DECL, NULL_TREE,
void_type_node);
DECL_ARTIFICIAL(resdecl) = 1;
DECL_CONTEXT(resdecl) = fndecl;
DECL_RESULT(fndecl) = resdecl;
TREE_STATIC(fndecl) = 1;
TREE_USED(fndecl) = 1;
DECL_ARTIFICIAL(fndecl) = 1;
TREE_PUBLIC(fndecl) = 1;
DECL_INITIAL(fndecl) = make_node(BLOCK);
TREE_USED(DECL_INITIAL(fndecl)) = 1;
return fndecl;
}
// Create the magic initialization function. INIT_STMT_LIST is the
// code that it needs to run.
void
Gogo::write_initialization_function(tree fndecl, tree init_stmt_list)
{
// Make sure that we thought we needed an initialization function,
// as otherwise we will not have reported it in the export data.
gcc_assert(this->package_name() == "main" || this->need_init_fn_);
if (fndecl == NULL_TREE)
fndecl = this->initialization_function_decl();
DECL_SAVED_TREE(fndecl) = init_stmt_list;
current_function_decl = fndecl;
if (DECL_STRUCT_FUNCTION(fndecl) == NULL)
push_struct_function(fndecl);
else
push_cfun(DECL_STRUCT_FUNCTION(fndecl));
cfun->function_end_locus = BUILTINS_LOCATION;
gimplify_function_tree(fndecl);
cgraph_add_new_function(fndecl, false);
cgraph_mark_needed_node(cgraph_node(fndecl));
current_function_decl = NULL_TREE;
pop_cfun();
}
// Search for references to VAR in any statements or called functions.
class Find_var : public Traverse
{
public:
// A hash table we use to avoid looping. The index is the name of a
// named object. We only look through objects defined in this
// package.
typedef Unordered_set(std::string) Seen_objects;
Find_var(Named_object* var, Seen_objects* seen_objects)
: Traverse(traverse_expressions),
var_(var), seen_objects_(seen_objects), found_(false)
{ }
// Whether the variable was found.
bool
found() const
{ return this->found_; }
int
expression(Expression**);
private:
// The variable we are looking for.
Named_object* var_;
// Names of objects we have already seen.
Seen_objects* seen_objects_;
// True if the variable was found.
bool found_;
};
// See if EXPR refers to VAR, looking through function calls and
// variable initializations.
int
Find_var::expression(Expression** pexpr)
{
Expression* e = *pexpr;
Var_expression* ve = e->var_expression();
if (ve != NULL)
{
Named_object* v = ve->named_object();
if (v == this->var_)
{
this->found_ = true;
return TRAVERSE_EXIT;
}
if (v->is_variable() && v->package() == NULL)
{
Expression* init = v->var_value()->init();
if (init != NULL)
{
std::pair<Seen_objects::iterator, bool> ins =
this->seen_objects_->insert(v->name());
if (ins.second)
{
// This is the first time we have seen this name.
if (Expression::traverse(&init, this) == TRAVERSE_EXIT)
return TRAVERSE_EXIT;
}
}
}
}
// We traverse the code of any function we see. Note that this
// means that we will traverse the code of a function whose address
// is taken even if it is not called.
Func_expression* fe = e->func_expression();
if (fe != NULL)
{
const Named_object* f = fe->named_object();
if (f->is_function() && f->package() == NULL)
{
std::pair<Seen_objects::iterator, bool> ins =
this->seen_objects_->insert(f->name());
if (ins.second)
{
// This is the first time we have seen this name.
if (f->func_value()->block()->traverse(this) == TRAVERSE_EXIT)
return TRAVERSE_EXIT;
}
}
}
return TRAVERSE_CONTINUE;
}
// Return true if EXPR refers to VAR.
static bool
expression_requires(Expression* expr, Block* preinit, Named_object* var)
{
Find_var::Seen_objects seen_objects;
Find_var find_var(var, &seen_objects);
if (expr != NULL)
Expression::traverse(&expr, &find_var);
if (preinit != NULL)
preinit->traverse(&find_var);
return find_var.found();
}
// Sort variable initializations. If the initialization expression
// for variable A refers directly or indirectly to the initialization
// expression for variable B, then we must initialize B before A.
class Var_init
{
public:
Var_init()
: var_(NULL), init_(NULL_TREE), waiting_(0)
{ }
Var_init(Named_object* var, tree init)
: var_(var), init_(init), waiting_(0)
{ }
// Return the variable.
Named_object*
var() const
{ return this->var_; }
// Return the initialization expression.
tree
init() const
{ return this->init_; }
// Return the number of variables waiting for this one to be
// initialized.
size_t
waiting() const
{ return this->waiting_; }
// Increment the number waiting.
void
increment_waiting()
{ ++this->waiting_; }
private:
// The variable being initialized.
Named_object* var_;
// The initialization expression to run.
tree init_;
// The number of variables which are waiting for this one.
size_t waiting_;
};
typedef std::list<Var_init> Var_inits;
// Sort the variable initializations. The rule we follow is that we
// emit them in the order they appear in the array, except that if the
// initialization expression for a variable V1 depends upon another
// variable V2 then we initialize V1 after V2.
static void
sort_var_inits(Var_inits* var_inits)
{
Var_inits ready;
while (!var_inits->empty())
{
Var_inits::iterator p1 = var_inits->begin();
Named_object* var = p1->var();
Expression* init = var->var_value()->init();
Block* preinit = var->var_value()->preinit();
// Start walking through the list to see which variables VAR
// needs to wait for. We can skip P1->WAITING variables--that
// is the number we've already checked.
Var_inits::iterator p2 = p1;
++p2;
for (size_t i = p1->waiting(); i > 0; --i)
++p2;
for (; p2 != var_inits->end(); ++p2)
{
if (expression_requires(init, preinit, p2->var()))
{
// Check for cycles.
if (expression_requires(p2->var()->var_value()->init(),
p2->var()->var_value()->preinit(),
var))
{
error_at(var->location(),
("initialization expressions for %qs and "
"%qs depend upon each other"),
var->message_name().c_str(),
p2->var()->message_name().c_str());
inform(p2->var()->location(), "%qs defined here",
p2->var()->message_name().c_str());
p2 = var_inits->end();
}
else
{
// We can't emit P1 until P2 is emitted. Move P1.
// Note that the WAITING loop always executes at
// least once, which is what we want.
p2->increment_waiting();
Var_inits::iterator p3 = p2;
for (size_t i = p2->waiting(); i > 0; --i)
++p3;
var_inits->splice(p3, *var_inits, p1);
}
break;
}
}
if (p2 == var_inits->end())
{
// VAR does not depends upon any other initialization expressions.
// Check for a loop of VAR on itself. We only do this if
// INIT is not NULL; when INIT is NULL, it means that
// PREINIT sets VAR, which we will interpret as a loop.
if (init != NULL && expression_requires(init, preinit, var))
error_at(var->location(),
"initialization expression for %qs depends upon itself",
var->message_name().c_str());
ready.splice(ready.end(), *var_inits, p1);
}
}
// Now READY is the list in the desired initialization order.
var_inits->swap(ready);
}
// Write out the global definitions.
void
Gogo::write_globals()
{
Bindings* bindings = this->current_bindings();
size_t count = bindings->size_definitions();
tree* vec = new tree[count];
tree init_fndecl = NULL_TREE;
tree init_stmt_list = NULL_TREE;
if (this->package_name() == "main")
this->init_imports(&init_stmt_list);
// A list of variable initializations.
Var_inits var_inits;
// A list of variables which need to be registered with the garbage
// collector.
std::vector<Named_object*> var_gc;
var_gc.reserve(count);
tree var_init_stmt_list = NULL_TREE;
size_t i = 0;
for (Bindings::const_definitions_iterator p = bindings->begin_definitions();
p != bindings->end_definitions();
++p, ++i)
{
Named_object* no = *p;
gcc_assert(!no->is_type_declaration() && !no->is_function_declaration());
// There is nothing to do for a package.
if (no->is_package())
{
--i;
--count;
continue;
}
// There is nothing to do for an object which was imported from
// a different package into the global scope.
if (no->package() != NULL)
{
--i;
--count;
continue;
}
// There is nothing useful we can output for constants which
// have ideal or non-integeral type.
if (no->is_const())
{
Type* type = no->const_value()->type();
if (type == NULL)
type = no->const_value()->expr()->type();
if (type->is_abstract() || type->integer_type() == NULL)
{
--i;
--count;
continue;
}
}
vec[i] = no->get_tree(this, NULL);
if (vec[i] == error_mark_node)
{
gcc_assert(saw_errors());
--i;
--count;
continue;
}
// If a variable is initialized to a non-constant value, do the
// initialization in an initialization function.
if (TREE_CODE(vec[i]) == VAR_DECL)
{
gcc_assert(no->is_variable());
// Check for a sink variable, which may be used to run
// an initializer purely for its side effects.
bool is_sink = no->name()[0] == '_' && no->name()[1] == '.';
tree var_init_tree = NULL_TREE;
if (!no->var_value()->has_pre_init())
{
tree init = no->var_value()->get_init_tree(this, NULL);
if (init == error_mark_node)
gcc_assert(saw_errors());
else if (init == NULL_TREE)
;
else if (TREE_CONSTANT(init))
DECL_INITIAL(vec[i]) = init;
else if (is_sink)
var_init_tree = init;
else
var_init_tree = fold_build2_loc(no->location(), MODIFY_EXPR,
void_type_node, vec[i], init);
}
else
{
// We are going to create temporary variables which
// means that we need an fndecl.
if (init_fndecl == NULL_TREE)
init_fndecl = this->initialization_function_decl();
current_function_decl = init_fndecl;
if (DECL_STRUCT_FUNCTION(init_fndecl) == NULL)
push_struct_function(init_fndecl);
else
push_cfun(DECL_STRUCT_FUNCTION(init_fndecl));
tree var_decl = is_sink ? NULL_TREE : vec[i];
var_init_tree = no->var_value()->get_init_block(this, NULL,
var_decl);
current_function_decl = NULL_TREE;
pop_cfun();
}
if (var_init_tree != NULL_TREE)
{
if (no->var_value()->init() == NULL
&& !no->var_value()->has_pre_init())
append_to_statement_list(var_init_tree, &var_init_stmt_list);
else
var_inits.push_back(Var_init(no, var_init_tree));
}
if (!is_sink && no->var_value()->type()->has_pointer())
var_gc.push_back(no);
}
}
// Register global variables with the garbage collector.
this->register_gc_vars(var_gc, &init_stmt_list);
// Simple variable initializations, after all variables are
// registered.
append_to_statement_list(var_init_stmt_list, &init_stmt_list);
// Complex variable initializations, first sorting them into a
// workable order.
if (!var_inits.empty())
{
sort_var_inits(&var_inits);
for (Var_inits::const_iterator p = var_inits.begin();
p != var_inits.end();
++p)
append_to_statement_list(p->init(), &init_stmt_list);
}
// After all the variables are initialized, call the "init"
// functions if there are any.
for (std::vector<Named_object*>::const_iterator p =
this->init_functions_.begin();
p != this->init_functions_.end();
++p)
{
tree decl = (*p)->get_tree(this, NULL);
tree call = build_call_expr(decl, 0);
append_to_statement_list(call, &init_stmt_list);
}
// Set up a magic function to do all the initialization actions.
// This will be called if this package is imported.
if (init_stmt_list != NULL_TREE
|| this->need_init_fn_
|| this->package_name() == "main")
this->write_initialization_function(init_fndecl, init_stmt_list);
// Pass everything back to the middle-end.
if (this->imported_unsafe_)
{
// Importing the "unsafe" package automatically disables TBAA.
flag_strict_aliasing = false;
// This is a real hack. init_varasm_once has already grabbed an
// alias set, which we don't want when we aren't going strict
// aliasing. We reinitialize to make it do it again. FIXME.
init_varasm_once();
}
wrapup_global_declarations(vec, count);
cgraph_finalize_compilation_unit();
check_global_declarations(vec, count);
emit_debug_global_declarations(vec, count);
delete[] vec;
}
// Get a tree for the identifier for a named object.
tree
Named_object::get_id(Gogo* gogo)
{
std::string decl_name;
if (this->is_function_declaration()
&& !this->func_declaration_value()->asm_name().empty())
decl_name = this->func_declaration_value()->asm_name();
else if ((this->is_variable() && !this->var_value()->is_global())
|| (this->is_type()
&& this->type_value()->location() == BUILTINS_LOCATION))
{
// We don't need the package name for local variables or builtin
// types.
decl_name = Gogo::unpack_hidden_name(this->name_);
}
else if (this->is_function()
&& !this->func_value()->is_method()
&& this->package_ == NULL
&& Gogo::unpack_hidden_name(this->name_) == "init")
{
// A single package can have multiple "init" functions, which
// means that we need to give them different names.
static int init_index;
char buf[20];
snprintf(buf, sizeof buf, "%d", init_index);
++init_index;
decl_name = gogo->package_name() + ".init." + buf;
}
else
{
std::string package_name;
if (this->package_ == NULL)
package_name = gogo->package_name();
else
package_name = this->package_->name();
decl_name = package_name + '.' + Gogo::unpack_hidden_name(this->name_);
Function_type* fntype;
if (this->is_function())
fntype = this->func_value()->type();
else if (this->is_function_declaration())
fntype = this->func_declaration_value()->type();
else
fntype = NULL;
if (fntype != NULL && fntype->is_method())
{
decl_name.push_back('.');
decl_name.append(fntype->receiver()->type()->mangled_name(gogo));
}
}
if (this->is_type())
{
const Named_object* in_function = this->type_value()->in_function();
if (in_function != NULL)
decl_name += '$' + in_function->name();
}
return get_identifier_from_string(decl_name);
}
// Get a tree for a named object.
tree
Named_object::get_tree(Gogo* gogo, Named_object* function)
{
if (this->tree_ != NULL_TREE)
{
// If this is a variable whose address is taken, we must rebuild
// the INDIRECT_REF each time to avoid invalid sharing.
tree ret = this->tree_;
if (((this->classification_ == NAMED_OBJECT_VAR
&& this->var_value()->is_in_heap())
|| (this->classification_ == NAMED_OBJECT_RESULT_VAR
&& this->result_var_value()->is_in_heap()))
&& ret != error_mark_node)
{
gcc_assert(TREE_CODE(ret) == INDIRECT_REF);
ret = build_fold_indirect_ref(TREE_OPERAND(ret, 0));
TREE_THIS_NOTRAP(ret) = 1;
}
return ret;
}
tree name;
if (this->classification_ == NAMED_OBJECT_TYPE)
name = NULL_TREE;
else
name = this->get_id(gogo);
tree decl;
switch (this->classification_)
{
case NAMED_OBJECT_CONST:
{
Named_constant* named_constant = this->u_.const_value;
Translate_context subcontext(gogo, function, NULL, NULL_TREE);
tree expr_tree = named_constant->expr()->get_tree(&subcontext);
if (expr_tree == error_mark_node)
decl = error_mark_node;
else
{
Type* type = named_constant->type();
if (type != NULL && !type->is_abstract())
expr_tree = fold_convert(type->get_tree(gogo), expr_tree);
if (expr_tree == error_mark_node)
decl = error_mark_node;
else if (INTEGRAL_TYPE_P(TREE_TYPE(expr_tree)))
{
decl = build_decl(named_constant->location(), CONST_DECL,
name, TREE_TYPE(expr_tree));
DECL_INITIAL(decl) = expr_tree;
TREE_CONSTANT(decl) = 1;
TREE_READONLY(decl) = 1;
}
else
{
// A CONST_DECL is only for an enum constant, so we
// shouldn't use for non-integral types. Instead we
// just return the constant itself, rather than a
// decl.
decl = expr_tree;
}
}
}
break;
case NAMED_OBJECT_TYPE:
{
Named_type* named_type = this->u_.type_value;
tree type_tree = named_type->get_tree(gogo);
if (type_tree == error_mark_node)
decl = error_mark_node;
else
{
decl = TYPE_NAME(type_tree);
gcc_assert(decl != NULL_TREE);
// We need to produce a type descriptor for every named
// type, and for a pointer to every named type, since
// other files or packages might refer to them. We need
// to do this even for hidden types, because they might
// still be returned by some function. Simply calling the
// type_descriptor method is enough to create the type
// descriptor, even though we don't do anything with it.
if (this->package_ == NULL)
{
named_type->type_descriptor_pointer(gogo);
Type* pn = Type::make_pointer_type(named_type);
pn->type_descriptor_pointer(gogo);
}
}
}
break;
case NAMED_OBJECT_TYPE_DECLARATION:
error("reference to undefined type %qs",
this->message_name().c_str());
return error_mark_node;
case NAMED_OBJECT_VAR:
{
Variable* var = this->u_.var_value;
Type* type = var->type();
if (type->is_error_type()
|| (type->is_undefined()
&& (!var->is_global() || this->package() == NULL)))
{
// Force the error for an undefined type, just in case.
type->base();
decl = error_mark_node;
}
else
{
tree var_type = type->get_tree(gogo);
bool is_parameter = var->is_parameter();
if (var->is_receiver() && type->points_to() == NULL)
is_parameter = false;
if (var->is_in_heap())
{
is_parameter = false;
var_type = build_pointer_type(var_type);
}
decl = build_decl(var->location(),
is_parameter ? PARM_DECL : VAR_DECL,
name, var_type);
if (!var->is_global())
{
tree fnid = function->get_id(gogo);
tree fndecl = function->func_value()->get_or_make_decl(gogo,
function,
fnid);
DECL_CONTEXT(decl) = fndecl;
}
if (is_parameter)
DECL_ARG_TYPE(decl) = TREE_TYPE(decl);
if (var->is_global())
{
const Package* package = this->package();
if (package == NULL)
TREE_STATIC(decl) = 1;
else
DECL_EXTERNAL(decl) = 1;
if (!Gogo::is_hidden_name(this->name_))
{
TREE_PUBLIC(decl) = 1;
std::string asm_name = (package == NULL
? gogo->unique_prefix()
: package->unique_prefix());
asm_name.append(1, '.');
asm_name.append(IDENTIFIER_POINTER(name),
IDENTIFIER_LENGTH(name));
tree asm_id = get_identifier_from_string(asm_name);
SET_DECL_ASSEMBLER_NAME(decl, asm_id);
}
}
// FIXME: We should only set this for variables which are
// actually used somewhere.
TREE_USED(decl) = 1;
}
}
break;
case NAMED_OBJECT_RESULT_VAR:
{
Result_variable* result = this->u_.result_var_value;
Type* type = result->type();
if (type->is_error_type() || type->is_undefined())
{
// Force the error.
type->base();
decl = error_mark_node;
}
else
{
gcc_assert(result->function() == function->func_value());
source_location loc = function->location();
tree result_type = type->get_tree(gogo);
tree init;
if (!result->is_in_heap())
init = type->get_init_tree(gogo, false);
else
{
tree space = gogo->allocate_memory(type,
TYPE_SIZE_UNIT(result_type),
loc);
result_type = build_pointer_type(result_type);
tree subinit = type->get_init_tree(gogo, true);
if (subinit == NULL_TREE)
init = fold_convert_loc(loc, result_type, space);
else
{
space = save_expr(space);
space = fold_convert_loc(loc, result_type, space);
tree spaceref = build_fold_indirect_ref_loc(loc, space);
TREE_THIS_NOTRAP(spaceref) = 1;
tree set = fold_build2_loc(loc, MODIFY_EXPR, void_type_node,
spaceref, subinit);
init = fold_build2_loc(loc, COMPOUND_EXPR, TREE_TYPE(space),
set, space);
}
}
decl = build_decl(loc, VAR_DECL, name, result_type);
tree fnid = function->get_id(gogo);
tree fndecl = function->func_value()->get_or_make_decl(gogo,
function,
fnid);
DECL_CONTEXT(decl) = fndecl;
DECL_INITIAL(decl) = init;
TREE_USED(decl) = 1;
}
}
break;
case NAMED_OBJECT_SINK:
gcc_unreachable();
case NAMED_OBJECT_FUNC:
{
Function* func = this->u_.func_value;
decl = func->get_or_make_decl(gogo, this, name);
if (decl != error_mark_node)
{
if (func->block() != NULL)
{
if (DECL_STRUCT_FUNCTION(decl) == NULL)
push_struct_function(decl);
else
push_cfun(DECL_STRUCT_FUNCTION(decl));
cfun->function_end_locus = func->block()->end_location();
current_function_decl = decl;
func->build_tree(gogo, this);
gimplify_function_tree(decl);
cgraph_finalize_function(decl, true);
current_function_decl = NULL_TREE;
pop_cfun();
}
}
}
break;
default:
gcc_unreachable();
}
if (TREE_TYPE(decl) == error_mark_node)
decl = error_mark_node;
tree ret = decl;
// If this is a local variable whose address is taken, then we
// actually store it in the heap. For uses of the variable we need
// to return a reference to that heap location.
if (((this->classification_ == NAMED_OBJECT_VAR
&& this->var_value()->is_in_heap())
|| (this->classification_ == NAMED_OBJECT_RESULT_VAR
&& this->result_var_value()->is_in_heap()))
&& ret != error_mark_node)
{
gcc_assert(POINTER_TYPE_P(TREE_TYPE(ret)));
ret = build_fold_indirect_ref(ret);
TREE_THIS_NOTRAP(ret) = 1;
}
this->tree_ = ret;
if (ret != error_mark_node)
go_preserve_from_gc(ret);
return ret;
}
// Get the initial value of a variable as a tree. This does not
// consider whether the variable is in the heap--it returns the
// initial value as though it were always stored in the stack.
tree
Variable::get_init_tree(Gogo* gogo, Named_object* function)
{
gcc_assert(this->preinit_ == NULL);
if (this->init_ == NULL)
{
gcc_assert(!this->is_parameter_);
return this->type_->get_init_tree(gogo, this->is_global_);
}
else
{
Translate_context context(gogo, function, NULL, NULL_TREE);
tree rhs_tree = this->init_->get_tree(&context);
return Expression::convert_for_assignment(&context, this->type(),
this->init_->type(),
rhs_tree, this->location());
}
}
// Get the initial value of a variable when a block is required.
// VAR_DECL is the decl to set; it may be NULL for a sink variable.
tree
Variable::get_init_block(Gogo* gogo, Named_object* function, tree var_decl)
{
gcc_assert(this->preinit_ != NULL);
// We want to add the variable assignment to the end of the preinit
// block. The preinit block may have a TRY_FINALLY_EXPR and a
// TRY_CATCH_EXPR; if it does, we want to add to the end of the
// regular statements.
Translate_context context(gogo, function, NULL, NULL_TREE);
tree block_tree = this->preinit_->get_tree(&context);
gcc_assert(TREE_CODE(block_tree) == BIND_EXPR);
tree statements = BIND_EXPR_BODY(block_tree);
while (TREE_CODE(statements) == TRY_FINALLY_EXPR
|| TREE_CODE(statements) == TRY_CATCH_EXPR)
statements = TREE_OPERAND(statements, 0);
// It's possible to have pre-init statements without an initializer
// if the pre-init statements set the variable.
if (this->init_ != NULL)
{
tree rhs_tree = this->init_->get_tree(&context);
if (var_decl == NULL_TREE)
append_to_statement_list(rhs_tree, &statements);
else
{
tree val = Expression::convert_for_assignment(&context, this->type(),
this->init_->type(),
rhs_tree,
this->location());
tree set = fold_build2_loc(this->location(), MODIFY_EXPR,
void_type_node, var_decl, val);
append_to_statement_list(set, &statements);
}
}
return block_tree;
}
// Get a tree for a function decl.
tree
Function::get_or_make_decl(Gogo* gogo, Named_object* no, tree id)
{
if (this->fndecl_ == NULL_TREE)
{
tree functype = this->type_->get_tree(gogo);
if (functype == error_mark_node)
this->fndecl_ = error_mark_node;
else
{
// The type of a function comes back as a pointer, but we
// want the real function type for a function declaration.
gcc_assert(POINTER_TYPE_P(functype));
functype = TREE_TYPE(functype);
tree decl = build_decl(this->location(), FUNCTION_DECL, id, functype);
this->fndecl_ = decl;
gcc_assert(no->package() == NULL);
if (this->enclosing_ != NULL || Gogo::is_thunk(no))
;
else if (Gogo::unpack_hidden_name(no->name()) == "init"
&& !this->type_->is_method())
;
else if (Gogo::unpack_hidden_name(no->name()) == "main"
&& gogo->package_name() == "main")
TREE_PUBLIC(decl) = 1;
// Methods have to be public even if they are hidden because
// they can be pulled into type descriptors when using
// anonymous fields.
else if (!Gogo::is_hidden_name(no->name())
|| this->type_->is_method())
{
TREE_PUBLIC(decl) = 1;
std::string asm_name = gogo->unique_prefix();
asm_name.append(1, '.');
asm_name.append(IDENTIFIER_POINTER(id), IDENTIFIER_LENGTH(id));
SET_DECL_ASSEMBLER_NAME(decl,
get_identifier_from_string(asm_name));
}
// Why do we have to do this in the frontend?
tree restype = TREE_TYPE(functype);
tree resdecl = build_decl(this->location(), RESULT_DECL, NULL_TREE,
restype);
DECL_ARTIFICIAL(resdecl) = 1;
DECL_IGNORED_P(resdecl) = 1;
DECL_CONTEXT(resdecl) = decl;
DECL_RESULT(decl) = resdecl;
if (this->enclosing_ != NULL)
DECL_STATIC_CHAIN(decl) = 1;
// If a function calls the predeclared recover function, we
// can't inline it, because recover behaves differently in a
// function passed directly to defer.
if (this->calls_recover_ && !this->is_recover_thunk_)
DECL_UNINLINABLE(decl) = 1;
// If this is a thunk created to call a function which calls
// the predeclared recover function, we need to disable
// stack splitting for the thunk.
if (this->is_recover_thunk_)
{
tree attr = get_identifier("__no_split_stack__");
DECL_ATTRIBUTES(decl) = tree_cons(attr, NULL_TREE, NULL_TREE);
}
go_preserve_from_gc(decl);
if (this->closure_var_ != NULL)
{
push_struct_function(decl);
tree closure_decl = this->closure_var_->get_tree(gogo, no);
DECL_ARTIFICIAL(closure_decl) = 1;
DECL_IGNORED_P(closure_decl) = 1;
TREE_USED(closure_decl) = 1;
DECL_ARG_TYPE(closure_decl) = TREE_TYPE(closure_decl);
TREE_READONLY(closure_decl) = 1;
DECL_STRUCT_FUNCTION(decl)->static_chain_decl = closure_decl;
pop_cfun();
}
}
}
return this->fndecl_;
}
// Get a tree for a function declaration.
tree
Function_declaration::get_or_make_decl(Gogo* gogo, Named_object* no, tree id)
{
if (this->fndecl_ == NULL_TREE)
{
// Let Go code use an asm declaration to pick up a builtin
// function.
if (!this->asm_name_.empty())
{
std::map<std::string, tree>::const_iterator p =
builtin_functions.find(this->asm_name_);
if (p != builtin_functions.end())
{
this->fndecl_ = p->second;
return this->fndecl_;
}
}
tree functype = this->fntype_->get_tree(gogo);
tree decl;
if (functype == error_mark_node)
decl = error_mark_node;
else
{
// The type of a function comes back as a pointer, but we
// want the real function type for a function declaration.
gcc_assert(POINTER_TYPE_P(functype));
functype = TREE_TYPE(functype);
decl = build_decl(this->location(), FUNCTION_DECL, id, functype);
TREE_PUBLIC(decl) = 1;
DECL_EXTERNAL(decl) = 1;
if (this->asm_name_.empty())
{
std::string asm_name = (no->package() == NULL
? gogo->unique_prefix()
: no->package()->unique_prefix());
asm_name.append(1, '.');
asm_name.append(IDENTIFIER_POINTER(id), IDENTIFIER_LENGTH(id));
SET_DECL_ASSEMBLER_NAME(decl,
get_identifier_from_string(asm_name));
}
}
this->fndecl_ = decl;
go_preserve_from_gc(decl);
}
return this->fndecl_;
}
// We always pass the receiver to a method as a pointer. If the
// receiver is actually declared as a non-pointer type, then we copy
// the value into a local variable, so that it has the right type. In
// this function we create the real PARM_DECL to use, and set
// DEC_INITIAL of the var_decl to be the value passed in.
tree
Function::make_receiver_parm_decl(Gogo* gogo, Named_object* no, tree var_decl)
{
// If the function takes the address of a receiver which is passed
// by value, then we will have an INDIRECT_REF here. We need to get
// the real variable.
bool is_in_heap = no->var_value()->is_in_heap();
tree val_type;
if (TREE_CODE(var_decl) != INDIRECT_REF)
{
gcc_assert(!is_in_heap);
val_type = TREE_TYPE(var_decl);
}
else
{
gcc_assert(is_in_heap);
var_decl = TREE_OPERAND(var_decl, 0);
gcc_assert(POINTER_TYPE_P(TREE_TYPE(var_decl)));
val_type = TREE_TYPE(TREE_TYPE(var_decl));
}
gcc_assert(TREE_CODE(var_decl) == VAR_DECL);
source_location loc = DECL_SOURCE_LOCATION(var_decl);
std::string name = IDENTIFIER_POINTER(DECL_NAME(var_decl));
name += ".pointer";
tree id = get_identifier_from_string(name);
tree parm_decl = build_decl(loc, PARM_DECL, id, build_pointer_type(val_type));
DECL_CONTEXT(parm_decl) = current_function_decl;
DECL_ARG_TYPE(parm_decl) = TREE_TYPE(parm_decl);
gcc_assert(DECL_INITIAL(var_decl) == NULL_TREE);
// The receiver might be passed as a null pointer.
tree check = fold_build2_loc(loc, NE_EXPR, boolean_type_node, parm_decl,
fold_convert_loc(loc, TREE_TYPE(parm_decl),
null_pointer_node));
tree ind = build_fold_indirect_ref_loc(loc, parm_decl);
TREE_THIS_NOTRAP(ind) = 1;
tree zero_init = no->var_value()->type()->get_init_tree(gogo, false);
tree init = fold_build3_loc(loc, COND_EXPR, TREE_TYPE(ind),
check, ind, zero_init);
if (is_in_heap)
{
tree size = TYPE_SIZE_UNIT(val_type);
tree space = gogo->allocate_memory(no->var_value()->type(), size,
no->location());
space = save_expr(space);
space = fold_convert(build_pointer_type(val_type), space);
tree spaceref = build_fold_indirect_ref_loc(no->location(), space);
TREE_THIS_NOTRAP(spaceref) = 1;
tree check = fold_build2_loc(loc, NE_EXPR, boolean_type_node,
parm_decl,
fold_convert_loc(loc, TREE_TYPE(parm_decl),
null_pointer_node));
tree parmref = build_fold_indirect_ref_loc(no->location(), parm_decl);
TREE_THIS_NOTRAP(parmref) = 1;
tree set = fold_build2_loc(loc, MODIFY_EXPR, void_type_node,
spaceref, parmref);
init = fold_build2_loc(loc, COMPOUND_EXPR, TREE_TYPE(space),
build3(COND_EXPR, void_type_node,
check, set, NULL_TREE),
space);
}
DECL_INITIAL(var_decl) = init;
return parm_decl;
}
// If we take the address of a parameter, then we need to copy it into
// the heap. We will access it as a local variable via an
// indirection.
tree
Function::copy_parm_to_heap(Gogo* gogo, Named_object* no, tree ref)
{
gcc_assert(TREE_CODE(ref) == INDIRECT_REF);
tree var_decl = TREE_OPERAND(ref, 0);
gcc_assert(TREE_CODE(var_decl) == VAR_DECL);
source_location loc = DECL_SOURCE_LOCATION(var_decl);
std::string name = IDENTIFIER_POINTER(DECL_NAME(var_decl));
name += ".param";
tree id = get_identifier_from_string(name);
tree type = TREE_TYPE(var_decl);
gcc_assert(POINTER_TYPE_P(type));
type = TREE_TYPE(type);
tree parm_decl = build_decl(loc, PARM_DECL, id, type);
DECL_CONTEXT(parm_decl) = current_function_decl;
DECL_ARG_TYPE(parm_decl) = type;
tree size = TYPE_SIZE_UNIT(type);
tree space = gogo->allocate_memory(no->var_value()->type(), size, loc);
space = save_expr(space);
space = fold_convert(TREE_TYPE(var_decl), space);
tree spaceref = build_fold_indirect_ref_loc(loc, space);
TREE_THIS_NOTRAP(spaceref) = 1;
tree init = build2(COMPOUND_EXPR, TREE_TYPE(space),
build2(MODIFY_EXPR, void_type_node, spaceref, parm_decl),
space);
DECL_INITIAL(var_decl) = init;
return parm_decl;
}
// Get a tree for function code.
void
Function::build_tree(Gogo* gogo, Named_object* named_function)
{
tree fndecl = this->fndecl_;
gcc_assert(fndecl != NULL_TREE);
tree params = NULL_TREE;
tree* pp = &params;
tree declare_vars = NULL_TREE;
for (Bindings::const_definitions_iterator p =
this->block_->bindings()->begin_definitions();
p != this->block_->bindings()->end_definitions();
++p)
{
if ((*p)->is_variable() && (*p)->var_value()->is_parameter())
{
*pp = (*p)->get_tree(gogo, named_function);
// We always pass the receiver to a method as a pointer. If
// the receiver is declared as a non-pointer type, then we
// copy the value into a local variable.
if ((*p)->var_value()->is_receiver()
&& (*p)->var_value()->type()->points_to() == NULL)
{
tree parm_decl = this->make_receiver_parm_decl(gogo, *p, *pp);
tree var = *pp;
if (TREE_CODE(var) == INDIRECT_REF)
var = TREE_OPERAND(var, 0);
gcc_assert(TREE_CODE(var) == VAR_DECL);
DECL_CHAIN(var) = declare_vars;
declare_vars = var;
*pp = parm_decl;
}
else if ((*p)->var_value()->is_in_heap())
{
// If we take the address of a parameter, then we need
// to copy it into the heap.
tree parm_decl = this->copy_parm_to_heap(gogo, *p, *pp);
gcc_assert(TREE_CODE(*pp) == INDIRECT_REF);
tree var_decl = TREE_OPERAND(*pp, 0);
gcc_assert(TREE_CODE(var_decl) == VAR_DECL);
DECL_CHAIN(var_decl) = declare_vars;
declare_vars = var_decl;
*pp = parm_decl;
}
if (*pp != error_mark_node)
{
gcc_assert(TREE_CODE(*pp) == PARM_DECL);
pp = &DECL_CHAIN(*pp);
}
}
else if ((*p)->is_result_variable())
{
tree var_decl = (*p)->get_tree(gogo, named_function);
if ((*p)->result_var_value()->is_in_heap())
{
gcc_assert(TREE_CODE(var_decl) == INDIRECT_REF);
var_decl = TREE_OPERAND(var_decl, 0);
}
gcc_assert(TREE_CODE(var_decl) == VAR_DECL);
DECL_CHAIN(var_decl) = declare_vars;
declare_vars = var_decl;
}
}
*pp = NULL_TREE;
DECL_ARGUMENTS(fndecl) = params;
if (this->block_ != NULL)
{
gcc_assert(DECL_INITIAL(fndecl) == NULL_TREE);
// Declare variables if necessary.
tree bind = NULL_TREE;
if (declare_vars != NULL_TREE)
{
tree block = make_node(BLOCK);
BLOCK_SUPERCONTEXT(block) = fndecl;
DECL_INITIAL(fndecl) = block;
BLOCK_VARS(block) = declare_vars;
TREE_USED(block) = 1;
bind = build3(BIND_EXPR, void_type_node, BLOCK_VARS(block),
NULL_TREE, block);
TREE_SIDE_EFFECTS(bind) = 1;
}
// Build the trees for all the statements in the function.
Translate_context context(gogo, named_function, NULL, NULL_TREE);
tree code = this->block_->get_tree(&context);
tree init = NULL_TREE;
tree except = NULL_TREE;
tree fini = NULL_TREE;
// Initialize variables if necessary.
for (tree v = declare_vars; v != NULL_TREE; v = DECL_CHAIN(v))
{
tree dv = build1(DECL_EXPR, void_type_node, v);
SET_EXPR_LOCATION(dv, DECL_SOURCE_LOCATION(v));
append_to_statement_list(dv, &init);
}
// If we have a defer stack, initialize it at the start of a
// function.
if (this->defer_stack_ != NULL_TREE)
{
tree defer_init = build1(DECL_EXPR, void_type_node,
this->defer_stack_);
SET_EXPR_LOCATION(defer_init, this->block_->start_location());
append_to_statement_list(defer_init, &init);
// Clean up the defer stack when we leave the function.
this->build_defer_wrapper(gogo, named_function, &except, &fini);
}
if (code != NULL_TREE && code != error_mark_node)
{
if (init != NULL_TREE)
code = build2(COMPOUND_EXPR, void_type_node, init, code);
if (except != NULL_TREE)
code = build2(TRY_CATCH_EXPR, void_type_node, code,
build2(CATCH_EXPR, void_type_node, NULL, except));
if (fini != NULL_TREE)
code = build2(TRY_FINALLY_EXPR, void_type_node, code, fini);
}
// Stick the code into the block we built for the receiver, if
// we built on.
if (bind != NULL_TREE && code != NULL_TREE && code != error_mark_node)
{
BIND_EXPR_BODY(bind) = code;
code = bind;
}
DECL_SAVED_TREE(fndecl) = code;
}
}
// Build the wrappers around function code needed if the function has
// any defer statements. This sets *EXCEPT to an exception handler
// and *FINI to a finally handler.
void
Function::build_defer_wrapper(Gogo* gogo, Named_object* named_function,
tree *except, tree *fini)
{
source_location end_loc = this->block_->end_location();
// Add an exception handler. This is used if a panic occurs. Its
// purpose is to stop the stack unwinding if a deferred function
// calls recover. There are more details in
// libgo/runtime/go-unwind.c.
tree stmt_list = NULL_TREE;
static tree check_fndecl;
tree call = Gogo::call_builtin(&check_fndecl,
end_loc,
"__go_check_defer",
1,
void_type_node,
ptr_type_node,
this->defer_stack(end_loc));
append_to_statement_list(call, &stmt_list);
tree retval = this->return_value(gogo, named_function, end_loc, &stmt_list);
tree set;
if (retval == NULL_TREE)
set = NULL_TREE;
else
set = fold_build2_loc(end_loc, MODIFY_EXPR, void_type_node,
DECL_RESULT(this->fndecl_), retval);
tree ret_stmt = fold_build1_loc(end_loc, RETURN_EXPR, void_type_node, set);
append_to_statement_list(ret_stmt, &stmt_list);
gcc_assert(*except == NULL_TREE);
*except = stmt_list;
// Add some finally code to run the defer functions. This is used
// both in the normal case, when no panic occurs, and also if a
// panic occurs to run any further defer functions. Of course, it
// is possible for a defer function to call panic which should be
// caught by another defer function. To handle that we use a loop.
// finish:
// try { __go_undefer(); } catch { __go_check_defer(); goto finish; }
// if (return values are named) return named_vals;
stmt_list = NULL;
tree label = create_artificial_label(end_loc);
tree define_label = fold_build1_loc(end_loc, LABEL_EXPR, void_type_node,
label);
append_to_statement_list(define_label, &stmt_list);
static tree undefer_fndecl;
tree undefer = Gogo::call_builtin(&undefer_fndecl,
end_loc,
"__go_undefer",
1,
void_type_node,
ptr_type_node,
this->defer_stack(end_loc));
TREE_NOTHROW(undefer_fndecl) = 0;
tree defer = Gogo::call_builtin(&check_fndecl,
end_loc,
"__go_check_defer",
1,
void_type_node,
ptr_type_node,
this->defer_stack(end_loc));
tree jump = fold_build1_loc(end_loc, GOTO_EXPR, void_type_node, label);
tree catch_body = build2(COMPOUND_EXPR, void_type_node, defer, jump);
catch_body = build2(CATCH_EXPR, void_type_node, NULL, catch_body);
tree try_catch = build2(TRY_CATCH_EXPR, void_type_node, undefer, catch_body);
append_to_statement_list(try_catch, &stmt_list);
if (this->type_->results() != NULL
&& !this->type_->results()->empty()
&& !this->type_->results()->front().name().empty())
{
// If the result variables are named, we need to return them
// again, because they might have been changed by a defer
// function.
retval = this->return_value(gogo, named_function, end_loc,
&stmt_list);
set = fold_build2_loc(end_loc, MODIFY_EXPR, void_type_node,
DECL_RESULT(this->fndecl_), retval);
ret_stmt = fold_build1_loc(end_loc, RETURN_EXPR, void_type_node, set);
append_to_statement_list(ret_stmt, &stmt_list);
}
gcc_assert(*fini == NULL_TREE);
*fini = stmt_list;
}
// Return the value to assign to DECL_RESULT(this->fndecl_). This may
// also add statements to STMT_LIST, which need to be executed before
// the assignment. This is used for a return statement with no
// explicit values.
tree
Function::return_value(Gogo* gogo, Named_object* named_function,
source_location location, tree* stmt_list) const
{
const Typed_identifier_list* results = this->type_->results();
if (results == NULL || results->empty())
return NULL_TREE;
// In the case of an exception handler created for functions with
// defer statements, the result variables may be unnamed.
bool is_named = !results->front().name().empty();
if (is_named)
gcc_assert(this->named_results_ != NULL
&& this->named_results_->size() == results->size());
tree retval;
if (results->size() == 1)
{
if (is_named)
return this->named_results_->front()->get_tree(gogo, named_function);
else
return results->front().type()->get_init_tree(gogo, false);
}
else
{
tree rettype = TREE_TYPE(DECL_RESULT(this->fndecl_));
retval = create_tmp_var(rettype, "RESULT");
tree field = TYPE_FIELDS(rettype);
int index = 0;
for (Typed_identifier_list::const_iterator pr = results->begin();
pr != results->end();
++pr, ++index, field = DECL_CHAIN(field))
{
gcc_assert(field != NULL);
tree val;
if (is_named)
val = (*this->named_results_)[index]->get_tree(gogo,
named_function);
else
val = pr->type()->get_init_tree(gogo, false);
tree set = fold_build2_loc(location, MODIFY_EXPR, void_type_node,
build3(COMPONENT_REF, TREE_TYPE(field),
retval, field, NULL_TREE),
val);
append_to_statement_list(set, stmt_list);
}
return retval;
}
}
// Get the tree for the variable holding the defer stack for this
// function. At least at present, the value of this variable is not
// used. However, a pointer to this variable is used as a marker for
// the functions on the defer stack associated with this function.
// Doing things this way permits inlining a function which uses defer.
tree
Function::defer_stack(source_location location)
{
if (this->defer_stack_ == NULL_TREE)
{
tree var = create_tmp_var(ptr_type_node, "DEFER");
DECL_INITIAL(var) = null_pointer_node;
DECL_SOURCE_LOCATION(var) = location;
TREE_ADDRESSABLE(var) = 1;
this->defer_stack_ = var;
}
return fold_convert_loc(location, ptr_type_node,
build_fold_addr_expr_loc(location,
this->defer_stack_));
}
// Get a tree for the statements in a block.
tree
Block::get_tree(Translate_context* context)
{
Gogo* gogo = context->gogo();
tree block = make_node(BLOCK);
// Put the new block into the block tree.
if (context->block() == NULL)
{
tree fndecl;
if (context->function() != NULL)
fndecl = context->function()->func_value()->get_decl();
else
fndecl = current_function_decl;
gcc_assert(fndecl != NULL_TREE);
// We may have already created a block for the receiver.
if (DECL_INITIAL(fndecl) == NULL_TREE)
{
BLOCK_SUPERCONTEXT(block) = fndecl;
DECL_INITIAL(fndecl) = block;
}
else
{
tree superblock_tree = DECL_INITIAL(fndecl);
BLOCK_SUPERCONTEXT(block) = superblock_tree;
gcc_assert(BLOCK_CHAIN(block) == NULL_TREE);
BLOCK_CHAIN(block) = block;
}
}
else
{
tree superblock_tree = context->block_tree();
BLOCK_SUPERCONTEXT(block) = superblock_tree;
tree* pp;
for (pp = &BLOCK_SUBBLOCKS(superblock_tree);
*pp != NULL_TREE;
pp = &BLOCK_CHAIN(*pp))
;
*pp = block;
}
// Expand local variables in the block.
tree* pp = &BLOCK_VARS(block);
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())
&& !(*pv)->is_result_variable()
&& !(*pv)->is_const())
{
tree var = (*pv)->get_tree(gogo, context->function());
if (var != error_mark_node && TREE_TYPE(var) != error_mark_node)
{
if ((*pv)->is_variable() && (*pv)->var_value()->is_in_heap())
{
gcc_assert(TREE_CODE(var) == INDIRECT_REF);
var = TREE_OPERAND(var, 0);
gcc_assert(TREE_CODE(var) == VAR_DECL);
}
*pp = var;
pp = &DECL_CHAIN(*pp);
}
}
}
*pp = NULL_TREE;
Translate_context subcontext(context->gogo(), context->function(),
this, block);
tree statements = NULL_TREE;
// Expand the statements.
for (std::vector<Statement*>::const_iterator p = this->statements_.begin();
p != this->statements_.end();
++p)
{
tree statement = (*p)->get_tree(&subcontext);
if (statement != error_mark_node)
append_to_statement_list(statement, &statements);
}
TREE_USED(block) = 1;
tree bind = build3(BIND_EXPR, void_type_node, BLOCK_VARS(block), statements,
block);
TREE_SIDE_EFFECTS(bind) = 1;
return bind;
}
// Get the LABEL_DECL for a label.
tree
Label::get_decl()
{
if (this->decl_ == NULL)
{
tree id = get_identifier_from_string(this->name_);
this->decl_ = build_decl(this->location_, LABEL_DECL, id, void_type_node);
DECL_CONTEXT(this->decl_) = current_function_decl;
}
return this->decl_;
}
// Return an expression for the address of this label.
tree
Label::get_addr(source_location location)
{
tree decl = this->get_decl();
TREE_USED(decl) = 1;
TREE_ADDRESSABLE(decl) = 1;
return fold_convert_loc(location, ptr_type_node,
build_fold_addr_expr_loc(location, decl));
}
// Get the LABEL_DECL for an unnamed label.
tree
Unnamed_label::get_decl()
{
if (this->decl_ == NULL)
this->decl_ = create_artificial_label(this->location_);
return this->decl_;
}
// Get the LABEL_EXPR for an unnamed label.
tree
Unnamed_label::get_definition()
{
tree t = build1(LABEL_EXPR, void_type_node, this->get_decl());
SET_EXPR_LOCATION(t, this->location_);
return t;
}
// Return a goto to this label.
tree
Unnamed_label::get_goto(source_location location)
{
tree t = build1(GOTO_EXPR, void_type_node, this->get_decl());
SET_EXPR_LOCATION(t, location);
return t;
}
// Return the integer type to use for a size.
GO_EXTERN_C
tree
go_type_for_size(unsigned int bits, int unsignedp)
{
const char* name;
switch (bits)
{
case 8:
name = unsignedp ? "uint8" : "int8";
break;
case 16:
name = unsignedp ? "uint16" : "int16";
break;
case 32:
name = unsignedp ? "uint32" : "int32";
break;
case 64:
name = unsignedp ? "uint64" : "int64";
break;
default:
if (bits == POINTER_SIZE && unsignedp)
name = "uintptr";
else
return NULL_TREE;
}
Type* type = Type::lookup_integer_type(name);
return type->get_tree(go_get_gogo());
}
// Return the type to use for a mode.
GO_EXTERN_C
tree
go_type_for_mode(enum machine_mode mode, int unsignedp)
{
// FIXME: This static_cast should be in machmode.h.
enum mode_class mc = static_cast<enum mode_class>(GET_MODE_CLASS(mode));
if (mc == MODE_INT)
return go_type_for_size(GET_MODE_BITSIZE(mode), unsignedp);
else if (mc == MODE_FLOAT)
{
Type* type;
switch (GET_MODE_BITSIZE (mode))
{
case 32:
type = Type::lookup_float_type("float32");
break;
case 64:
type = Type::lookup_float_type("float64");
break;
default:
// We have to check for long double in order to support
// i386 excess precision.
if (mode == TYPE_MODE(long_double_type_node))
return long_double_type_node;
return NULL_TREE;
}
return type->float_type()->type_tree();
}
else if (mc == MODE_COMPLEX_FLOAT)
{
Type *type;
switch (GET_MODE_BITSIZE (mode))
{
case 64:
type = Type::lookup_complex_type("complex64");
break;
case 128:
type = Type::lookup_complex_type("complex128");
break;
default:
// We have to check for long double in order to support
// i386 excess precision.
if (mode == TYPE_MODE(complex_long_double_type_node))
return complex_long_double_type_node;
return NULL_TREE;
}
return type->complex_type()->type_tree();
}
else
return NULL_TREE;
}
// Return a tree which allocates SIZE bytes which will holds value of
// type TYPE.
tree
Gogo::allocate_memory(Type* type, tree size, source_location location)
{
// If the package imports unsafe, then it may play games with
// pointers that look like integers.
if (this->imported_unsafe_ || type->has_pointer())
{
static tree new_fndecl;
return Gogo::call_builtin(&new_fndecl,
location,
"__go_new",
1,
ptr_type_node,
sizetype,
size);
}
else
{
static tree new_nopointers_fndecl;
return Gogo::call_builtin(&new_nopointers_fndecl,
location,
"__go_new_nopointers",
1,
ptr_type_node,
sizetype,
size);
}
}
// Build a builtin struct with a list of fields. The name is
// STRUCT_NAME. STRUCT_TYPE is NULL_TREE or an empty RECORD_TYPE
// node; this exists so that the struct can have fields which point to
// itself. If PTYPE is not NULL, store the result in *PTYPE. There
// are NFIELDS fields. Each field is a name (a const char*) followed
// by a type (a tree).
tree
Gogo::builtin_struct(tree* ptype, const char* struct_name, tree struct_type,
int nfields, ...)
{
if (ptype != NULL && *ptype != NULL_TREE)
return *ptype;
va_list ap;
va_start(ap, nfields);
tree fields = NULL_TREE;
for (int i = 0; i < nfields; ++i)
{
const char* field_name = va_arg(ap, const char*);
tree type = va_arg(ap, tree);
if (type == error_mark_node)
{
if (ptype != NULL)
*ptype = error_mark_node;
return error_mark_node;
}
tree field = build_decl(BUILTINS_LOCATION, FIELD_DECL,
get_identifier(field_name), type);
DECL_CHAIN(field) = fields;
fields = field;
}
va_end(ap);
if (struct_type == NULL_TREE)
struct_type = make_node(RECORD_TYPE);
finish_builtin_struct(struct_type, struct_name, fields, NULL_TREE);
if (ptype != NULL)
{
go_preserve_from_gc(struct_type);
*ptype = struct_type;
}
return struct_type;
}
// Return a type to use for pointer to const char for a string.
tree
Gogo::const_char_pointer_type_tree()
{
static tree type;
if (type == NULL_TREE)
{
tree const_char_type = build_qualified_type(unsigned_char_type_node,
TYPE_QUAL_CONST);
type = build_pointer_type(const_char_type);
go_preserve_from_gc(type);
}
return type;
}
// Return a tree for a string constant.
tree
Gogo::string_constant_tree(const std::string& val)
{
tree index_type = build_index_type(size_int(val.length()));
tree const_char_type = build_qualified_type(unsigned_char_type_node,
TYPE_QUAL_CONST);
tree string_type = build_array_type(const_char_type, index_type);
string_type = build_variant_type_copy(string_type);
TYPE_STRING_FLAG(string_type) = 1;
tree string_val = build_string(val.length(), val.data());
TREE_TYPE(string_val) = string_type;
return string_val;
}
// Return a tree for a Go string constant.
tree
Gogo::go_string_constant_tree(const std::string& val)
{
tree string_type = Type::make_string_type()->get_tree(this);
VEC(constructor_elt, gc)* init = VEC_alloc(constructor_elt, gc, 2);
constructor_elt* elt = VEC_quick_push(constructor_elt, init, NULL);
tree field = TYPE_FIELDS(string_type);
gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__data") == 0);
elt->index = field;
tree str = Gogo::string_constant_tree(val);
elt->value = fold_convert(TREE_TYPE(field),
build_fold_addr_expr(str));
elt = VEC_quick_push(constructor_elt, init, NULL);
field = DECL_CHAIN(field);
gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__length") == 0);
elt->index = field;
elt->value = build_int_cst_type(TREE_TYPE(field), val.length());
tree constructor = build_constructor(string_type, init);
TREE_READONLY(constructor) = 1;
TREE_CONSTANT(constructor) = 1;
return constructor;
}
// Return a tree for a pointer to a Go string constant. This is only
// used for type descriptors, so we return a pointer to a constant
// decl.
tree
Gogo::ptr_go_string_constant_tree(const std::string& val)
{
tree pval = this->go_string_constant_tree(val);
tree decl = build_decl(UNKNOWN_LOCATION, VAR_DECL,
create_tmp_var_name("SP"), TREE_TYPE(pval));
DECL_EXTERNAL(decl) = 0;
TREE_PUBLIC(decl) = 0;
TREE_USED(decl) = 1;
TREE_READONLY(decl) = 1;
TREE_CONSTANT(decl) = 1;
TREE_STATIC(decl) = 1;
DECL_ARTIFICIAL(decl) = 1;
DECL_INITIAL(decl) = pval;
rest_of_decl_compilation(decl, 1, 0);
return build_fold_addr_expr(decl);
}
// Build the type of the struct that holds a slice for the given
// element type.
tree
Gogo::slice_type_tree(tree element_type_tree)
{
// We use int for the count and capacity fields in a slice header.
// This matches 6g. The language definition guarantees that we
// can't allocate space of a size which does not fit in int
// anyhow. FIXME: integer_type_node is the the C type "int" but is
// not necessarily the Go type "int". They will differ when the C
// type "int" has fewer than 32 bits.
return Gogo::builtin_struct(NULL, "__go_slice", NULL_TREE, 3,
"__values",
build_pointer_type(element_type_tree),
"__count",
integer_type_node,
"__capacity",
integer_type_node);
}
// Given the tree for a slice type, return the tree for the type of
// the elements of the slice.
tree
Gogo::slice_element_type_tree(tree slice_type_tree)
{
gcc_assert(TREE_CODE(slice_type_tree) == RECORD_TYPE
&& POINTER_TYPE_P(TREE_TYPE(TYPE_FIELDS(slice_type_tree))));
return TREE_TYPE(TREE_TYPE(TYPE_FIELDS(slice_type_tree)));
}
// Build a constructor for a slice. SLICE_TYPE_TREE is the type of
// the slice. VALUES is the value pointer and COUNT is the number of
// entries. If CAPACITY is not NULL, it is the capacity; otherwise
// the capacity and the count are the same.
tree
Gogo::slice_constructor(tree slice_type_tree, tree values, tree count,
tree capacity)
{
gcc_assert(TREE_CODE(slice_type_tree) == RECORD_TYPE);
VEC(constructor_elt,gc)* init = VEC_alloc(constructor_elt, gc, 3);
tree field = TYPE_FIELDS(slice_type_tree);
gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__values") == 0);
constructor_elt* elt = VEC_quick_push(constructor_elt, init, NULL);
elt->index = field;
gcc_assert(TYPE_MAIN_VARIANT(TREE_TYPE(field))
== TYPE_MAIN_VARIANT(TREE_TYPE(values)));
elt->value = values;
count = fold_convert(sizetype, count);
if (capacity == NULL_TREE)
{
count = save_expr(count);
capacity = count;
}
field = DECL_CHAIN(field);
gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__count") == 0);
elt = VEC_quick_push(constructor_elt, init, NULL);
elt->index = field;
elt->value = fold_convert(TREE_TYPE(field), count);
field = DECL_CHAIN(field);
gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__capacity") == 0);
elt = VEC_quick_push(constructor_elt, init, NULL);
elt->index = field;
elt->value = fold_convert(TREE_TYPE(field), capacity);
return build_constructor(slice_type_tree, init);
}
// Build a constructor for an empty slice.
tree
Gogo::empty_slice_constructor(tree slice_type_tree)
{
tree element_field = TYPE_FIELDS(slice_type_tree);
tree ret = Gogo::slice_constructor(slice_type_tree,
fold_convert(TREE_TYPE(element_field),
null_pointer_node),
size_zero_node,
size_zero_node);
TREE_CONSTANT(ret) = 1;
return ret;
}
// Build a map descriptor for a map of type MAPTYPE.
tree
Gogo::map_descriptor(Map_type* maptype)
{
if (this->map_descriptors_ == NULL)
this->map_descriptors_ = new Map_descriptors(10);
std::pair<const Map_type*, tree> val(maptype, NULL);
std::pair<Map_descriptors::iterator, bool> ins =
this->map_descriptors_->insert(val);
Map_descriptors::iterator p = ins.first;
if (!ins.second)
{
gcc_assert(p->second != NULL_TREE && DECL_P(p->second));
return build_fold_addr_expr(p->second);
}
Type* keytype = maptype->key_type();
Type* valtype = maptype->val_type();
std::string mangled_name = ("__go_map_" + maptype->mangled_name(this));
tree id = get_identifier_from_string(mangled_name);
// Get the type of the map descriptor. This is __go_map_descriptor
// in libgo/map.h.
tree struct_type = this->map_descriptor_type();
// The map entry type is a struct with three fields. This struct is
// specific to MAPTYPE. Build it.
tree map_entry_type = make_node(RECORD_TYPE);
map_entry_type = Gogo::builtin_struct(NULL, "__map", map_entry_type, 3,
"__next",
build_pointer_type(map_entry_type),
"__key",
keytype->get_tree(this),
"__val",
valtype->get_tree(this));
if (map_entry_type == error_mark_node)
return error_mark_node;
tree map_entry_key_field = DECL_CHAIN(TYPE_FIELDS(map_entry_type));
gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(map_entry_key_field)),
"__key") == 0);
tree map_entry_val_field = DECL_CHAIN(map_entry_key_field);
gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(map_entry_val_field)),
"__val") == 0);
// Initialize the entries.
tree map_descriptor_field = TYPE_FIELDS(struct_type);
gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(map_descriptor_field)),
"__map_descriptor") == 0);
tree entry_size_field = DECL_CHAIN(map_descriptor_field);
gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(entry_size_field)),
"__entry_size") == 0);
tree key_offset_field = DECL_CHAIN(entry_size_field);
gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(key_offset_field)),
"__key_offset") == 0);
tree val_offset_field = DECL_CHAIN(key_offset_field);
gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(val_offset_field)),
"__val_offset") == 0);
VEC(constructor_elt, gc)* descriptor = VEC_alloc(constructor_elt, gc, 6);
constructor_elt* elt = VEC_quick_push(constructor_elt, descriptor, NULL);
elt->index = map_descriptor_field;
elt->value = maptype->type_descriptor_pointer(this);
elt = VEC_quick_push(constructor_elt, descriptor, NULL);
elt->index = entry_size_field;
elt->value = TYPE_SIZE_UNIT(map_entry_type);
elt = VEC_quick_push(constructor_elt, descriptor, NULL);
elt->index = key_offset_field;
elt->value = byte_position(map_entry_key_field);
elt = VEC_quick_push(constructor_elt, descriptor, NULL);
elt->index = val_offset_field;
elt->value = byte_position(map_entry_val_field);
tree constructor = build_constructor(struct_type, descriptor);
tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL, id, struct_type);
TREE_STATIC(decl) = 1;
TREE_USED(decl) = 1;
TREE_READONLY(decl) = 1;
TREE_CONSTANT(decl) = 1;
DECL_INITIAL(decl) = constructor;
make_decl_one_only(decl, DECL_ASSEMBLER_NAME(decl));
resolve_unique_section(decl, 1, 0);
rest_of_decl_compilation(decl, 1, 0);
go_preserve_from_gc(decl);
p->second = decl;
return build_fold_addr_expr(decl);
}
// Return a tree for the type of a map descriptor. This is struct
// __go_map_descriptor in libgo/runtime/map.h. This is the same for
// all map types.
tree
Gogo::map_descriptor_type()
{
static tree struct_type;
tree dtype = Type::make_type_descriptor_type()->get_tree(this);
dtype = build_qualified_type(dtype, TYPE_QUAL_CONST);
return Gogo::builtin_struct(&struct_type, "__go_map_descriptor", NULL_TREE,
4,
"__map_descriptor",
build_pointer_type(dtype),
"__entry_size",
sizetype,
"__key_offset",
sizetype,
"__val_offset",
sizetype);
}
// Return the name to use for a type descriptor decl for TYPE. This
// is used when TYPE does not have a name.
std::string
Gogo::unnamed_type_descriptor_decl_name(const Type* type)
{
return "__go_td_" + type->mangled_name(this);
}
// Return the name to use for a type descriptor decl for a type named
// NAME, defined in the function IN_FUNCTION. IN_FUNCTION will
// normally be NULL.
std::string
Gogo::type_descriptor_decl_name(const Named_object* no,
const Named_object* in_function)
{
std::string ret = "__go_tdn_";
if (no->type_value()->is_builtin())
gcc_assert(in_function == NULL);
else
{
const std::string& unique_prefix(no->package() == NULL
? this->unique_prefix()
: no->package()->unique_prefix());
const std::string& package_name(no->package() == NULL
? this->package_name()
: no->package()->name());
ret.append(unique_prefix);
ret.append(1, '.');
ret.append(package_name);
ret.append(1, '.');
if (in_function != NULL)
{
ret.append(Gogo::unpack_hidden_name(in_function->name()));
ret.append(1, '.');
}
}
ret.append(no->name());
return ret;
}
// Where a type descriptor decl should be defined.
Gogo::Type_descriptor_location
Gogo::type_descriptor_location(const Type* type)
{
const Named_type* name = type->named_type();
if (name != NULL)
{
if (name->named_object()->package() != NULL)
{
// This is a named type defined in a different package. The
// descriptor should be defined in that package.
return TYPE_DESCRIPTOR_UNDEFINED;
}
else if (name->is_builtin())
{
// We create the descriptor for a builtin type whenever we
// need it.
return TYPE_DESCRIPTOR_COMMON;
}
else
{
// This is a named type defined in this package. The
// descriptor should be defined here.
return TYPE_DESCRIPTOR_DEFINED;
}
}
else
{
if (type->points_to() != NULL
&& type->points_to()->named_type() != NULL
&& type->points_to()->named_type()->named_object()->package() != NULL)
{
// This is an unnamed pointer to a named type defined in a
// different package. The descriptor should be defined in
// that package.
return TYPE_DESCRIPTOR_UNDEFINED;
}
else
{
// This is an unnamed type. The descriptor could be defined
// in any package where it is needed, and the linker will
// pick one descriptor to keep.
return TYPE_DESCRIPTOR_COMMON;
}
}
}
// Build a type descriptor decl for TYPE. INITIALIZER is a struct
// composite literal which initializers the type descriptor.
void
Gogo::build_type_descriptor_decl(const Type* type, Expression* initializer,
tree* pdecl)
{
const Named_type* name = type->named_type();
// We can have multiple instances of unnamed types, but we only want
// to emit the type descriptor once. We use a hash table to handle
// this. This is not necessary for named types, as they are unique,
// and we store the type descriptor decl in the type itself.
tree* phash = NULL;
if (name == NULL)
{
if (this->type_descriptor_decls_ == NULL)
this->type_descriptor_decls_ = new Type_descriptor_decls(10);
std::pair<Type_descriptor_decls::iterator, bool> ins =
this->type_descriptor_decls_->insert(std::make_pair(type, NULL_TREE));
if (!ins.second)
{
// We've already built a type descriptor for this type.
*pdecl = ins.first->second;
return;
}
phash = &ins.first->second;
}
std::string decl_name;
if (name == NULL)
decl_name = this->unnamed_type_descriptor_decl_name(type);
else
decl_name = this->type_descriptor_decl_name(name->named_object(),
name->in_function());
tree id = get_identifier_from_string(decl_name);
tree descriptor_type_tree = initializer->type()->get_tree(this);
if (descriptor_type_tree == error_mark_node)
{
*pdecl = error_mark_node;
return;
}
tree decl = build_decl(name == NULL ? BUILTINS_LOCATION : name->location(),
VAR_DECL, id,
build_qualified_type(descriptor_type_tree,
TYPE_QUAL_CONST));
TREE_READONLY(decl) = 1;
TREE_CONSTANT(decl) = 1;
DECL_ARTIFICIAL(decl) = 1;
go_preserve_from_gc(decl);
if (phash != NULL)
*phash = decl;
// We store the new DECL now because we may need to refer to it when
// expanding INITIALIZER.
*pdecl = decl;
// If appropriate, just refer to the exported type identifier.
Gogo::Type_descriptor_location type_descriptor_location =
this->type_descriptor_location(type);
if (type_descriptor_location == TYPE_DESCRIPTOR_UNDEFINED)
{
TREE_PUBLIC(decl) = 1;
DECL_EXTERNAL(decl) = 1;
return;
}
TREE_STATIC(decl) = 1;
TREE_USED(decl) = 1;
Translate_context context(this, NULL, NULL, NULL);
context.set_is_const();
tree constructor = initializer->get_tree(&context);
if (constructor == error_mark_node)
gcc_assert(saw_errors());
DECL_INITIAL(decl) = constructor;
if (type_descriptor_location == TYPE_DESCRIPTOR_COMMON)
{
make_decl_one_only(decl, DECL_ASSEMBLER_NAME(decl));
resolve_unique_section(decl, 1, 0);
}
else
{
#ifdef OBJECT_FORMAT_ELF
// Give the decl protected visibility. This avoids out-of-range
// references with shared libraries with the x86_64 small model
// when the type descriptor gets a COPY reloc into the main
// executable. There is no need to have unique pointers to type
// descriptors, as the runtime code compares reflection strings
// if necessary.
DECL_VISIBILITY(decl) = VISIBILITY_PROTECTED;
DECL_VISIBILITY_SPECIFIED(decl) = 1;
#endif
TREE_PUBLIC(decl) = 1;
}
rest_of_decl_compilation(decl, 1, 0);
}
// Build an interface method table for a type: a list of function
// pointers, one for each interface method. This is used for
// interfaces.
tree
Gogo::interface_method_table_for_type(const Interface_type* interface,
Named_type* type,
bool is_pointer)
{
const Typed_identifier_list* interface_methods = interface->methods();
gcc_assert(!interface_methods->empty());
std::string mangled_name = ((is_pointer ? "__go_pimt__" : "__go_imt_")
+ interface->mangled_name(this)
+ "__"
+ type->mangled_name(this));
tree id = get_identifier_from_string(mangled_name);
// See whether this interface has any hidden methods.
bool has_hidden_methods = false;
for (Typed_identifier_list::const_iterator p = interface_methods->begin();
p != interface_methods->end();
++p)
{
if (Gogo::is_hidden_name(p->name()))
{
has_hidden_methods = true;
break;
}
}
// We already know that the named type is convertible to the
// interface. If the interface has hidden methods, and the named
// type is defined in a different package, then the interface
// conversion table will be defined by that other package.
if (has_hidden_methods && type->named_object()->package() != NULL)
{
tree array_type = build_array_type(const_ptr_type_node, NULL);
tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL, id, array_type);
TREE_READONLY(decl) = 1;
TREE_CONSTANT(decl) = 1;
TREE_PUBLIC(decl) = 1;
DECL_EXTERNAL(decl) = 1;
go_preserve_from_gc(decl);
return decl;
}
size_t count = interface_methods->size();
VEC(constructor_elt, gc)* pointers = VEC_alloc(constructor_elt, gc,
count + 1);
// The first element is the type descriptor.
constructor_elt* elt = VEC_quick_push(constructor_elt, pointers, NULL);
elt->index = size_zero_node;
Type* td_type;
if (!is_pointer)
td_type = type;
else
td_type = Type::make_pointer_type(type);
elt->value = fold_convert(const_ptr_type_node,
td_type->type_descriptor_pointer(this));
size_t i = 1;
for (Typed_identifier_list::const_iterator p = interface_methods->begin();
p != interface_methods->end();
++p, ++i)
{
bool is_ambiguous;
Method* m = type->method_function(p->name(), &is_ambiguous);
gcc_assert(m != NULL);
Named_object* no = m->named_object();
tree fnid = no->get_id(this);
tree fndecl;
if (no->is_function())
fndecl = no->func_value()->get_or_make_decl(this, no, fnid);
else if (no->is_function_declaration())
fndecl = no->func_declaration_value()->get_or_make_decl(this, no,
fnid);
else
gcc_unreachable();
fndecl = build_fold_addr_expr(fndecl);
elt = VEC_quick_push(constructor_elt, pointers, NULL);
elt->index = size_int(i);
elt->value = fold_convert(const_ptr_type_node, fndecl);
}
gcc_assert(i == count + 1);
tree array_type = build_array_type(const_ptr_type_node,
build_index_type(size_int(count)));
tree constructor = build_constructor(array_type, pointers);
tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL, id, array_type);
TREE_STATIC(decl) = 1;
TREE_USED(decl) = 1;
TREE_READONLY(decl) = 1;
TREE_CONSTANT(decl) = 1;
DECL_INITIAL(decl) = constructor;
// If the interface type has hidden methods, then this is the only
// definition of the table. Otherwise it is a comdat table which
// may be defined in multiple packages.
if (has_hidden_methods)
{
#ifdef OBJECT_FORMAT_ELF
// Give the decl protected visibility. This avoids out-of-range
// references with shared libraries with the x86_64 small model
// when the table gets a COPY reloc into the main executable.
DECL_VISIBILITY(decl) = VISIBILITY_PROTECTED;
DECL_VISIBILITY_SPECIFIED(decl) = 1;
#endif
TREE_PUBLIC(decl) = 1;
}
else
{
make_decl_one_only(decl, DECL_ASSEMBLER_NAME(decl));
resolve_unique_section(decl, 1, 0);
}
rest_of_decl_compilation(decl, 1, 0);
go_preserve_from_gc(decl);
return decl;
}
// Mark a function as a builtin library function.
void
Gogo::mark_fndecl_as_builtin_library(tree fndecl)
{
DECL_EXTERNAL(fndecl) = 1;
TREE_PUBLIC(fndecl) = 1;
DECL_ARTIFICIAL(fndecl) = 1;
TREE_NOTHROW(fndecl) = 1;
DECL_VISIBILITY(fndecl) = VISIBILITY_DEFAULT;
DECL_VISIBILITY_SPECIFIED(fndecl) = 1;
}
// Build a call to a builtin function.
tree
Gogo::call_builtin(tree* pdecl, source_location location, const char* name,
int nargs, tree rettype, ...)
{
if (rettype == error_mark_node)
return error_mark_node;
tree* types = new tree[nargs];
tree* args = new tree[nargs];
va_list ap;
va_start(ap, rettype);
for (int i = 0; i < nargs; ++i)
{
types[i] = va_arg(ap, tree);
args[i] = va_arg(ap, tree);
if (types[i] == error_mark_node || args[i] == error_mark_node)
return error_mark_node;
}
va_end(ap);
if (*pdecl == NULL_TREE)
{
tree fnid = get_identifier(name);
tree argtypes = NULL_TREE;
tree* pp = &argtypes;
for (int i = 0; i < nargs; ++i)
{
*pp = tree_cons(NULL_TREE, types[i], NULL_TREE);
pp = &TREE_CHAIN(*pp);
}
*pp = void_list_node;
tree fntype = build_function_type(rettype, argtypes);
*pdecl = build_decl(BUILTINS_LOCATION, FUNCTION_DECL, fnid, fntype);
Gogo::mark_fndecl_as_builtin_library(*pdecl);
go_preserve_from_gc(*pdecl);
}
tree fnptr = build_fold_addr_expr(*pdecl);
if (CAN_HAVE_LOCATION_P(fnptr))
SET_EXPR_LOCATION(fnptr, location);
tree ret = build_call_array(rettype, fnptr, nargs, args);
SET_EXPR_LOCATION(ret, location);
delete[] types;
delete[] args;
return ret;
}
// Build a call to the runtime error function.
tree
Gogo::runtime_error(int code, source_location location)
{
static tree runtime_error_fndecl;
tree ret = Gogo::call_builtin(&runtime_error_fndecl,
location,
"__go_runtime_error",
1,
void_type_node,
integer_type_node,
build_int_cst(integer_type_node, code));
// The runtime error function panics and does not return.
TREE_NOTHROW(runtime_error_fndecl) = 0;
TREE_THIS_VOLATILE(runtime_error_fndecl) = 1;
return ret;
}
// Send VAL on CHANNEL. If BLOCKING is true, the resulting tree has a
// void type. If BLOCKING is false, the resulting tree has a boolean
// type, and it will evaluate as true if the value was sent. If
// FOR_SELECT is true, this is being done because it was chosen in a
// select statement.
tree
Gogo::send_on_channel(tree channel, tree val, bool blocking, bool for_select,
source_location location)
{
if (int_size_in_bytes(TREE_TYPE(val)) <= 8
&& !AGGREGATE_TYPE_P(TREE_TYPE(val))
&& !FLOAT_TYPE_P(TREE_TYPE(val)))
{
val = convert_to_integer(uint64_type_node, val);
if (blocking)
{
static tree send_small_fndecl;
tree ret = Gogo::call_builtin(&send_small_fndecl,
location,
"__go_send_small",
3,
void_type_node,
ptr_type_node,
channel,
uint64_type_node,
val,
boolean_type_node,
(for_select
? boolean_true_node
: boolean_false_node));
// This can panic if there are too many operations on a
// closed channel.
TREE_NOTHROW(send_small_fndecl) = 0;
return ret;
}
else
{
gcc_assert(!for_select);
static tree send_nonblocking_small_fndecl;
tree ret = Gogo::call_builtin(&send_nonblocking_small_fndecl,
location,
"__go_send_nonblocking_small",
2,
boolean_type_node,
ptr_type_node,
channel,
uint64_type_node,
val);
// This can panic if there are too many operations on a
// closed channel.
TREE_NOTHROW(send_nonblocking_small_fndecl) = 0;
return ret;
}
}
else
{
tree make_tmp;
if (TREE_ADDRESSABLE(TREE_TYPE(val)) || TREE_CODE(val) == VAR_DECL)
{
make_tmp = NULL_TREE;
val = build_fold_addr_expr(val);
if (DECL_P(val))
TREE_ADDRESSABLE(val) = 1;
}
else
{
tree tmp = create_tmp_var(TREE_TYPE(val), get_name(val));
DECL_IGNORED_P(tmp) = 0;
DECL_INITIAL(tmp) = val;
TREE_ADDRESSABLE(tmp) = 1;
make_tmp = build1(DECL_EXPR, void_type_node, tmp);
SET_EXPR_LOCATION(make_tmp, location);
val = build_fold_addr_expr(tmp);
}
val = fold_convert(ptr_type_node, val);
tree call;
if (blocking)
{
static tree send_big_fndecl;
call = Gogo::call_builtin(&send_big_fndecl,
location,
"__go_send_big",
3,
void_type_node,
ptr_type_node,
channel,
ptr_type_node,
val,
boolean_type_node,
(for_select
? boolean_true_node
: boolean_false_node));
// This can panic if there are too many operations on a
// closed channel.
TREE_NOTHROW(send_big_fndecl) = 0;
}
else
{
gcc_assert(!for_select);
static tree send_nonblocking_big_fndecl;
call = Gogo::call_builtin(&send_nonblocking_big_fndecl,
location,
"__go_send_nonblocking_big",
2,
boolean_type_node,
ptr_type_node,
channel,
ptr_type_node,
val);
// This can panic if there are too many operations on a
// closed channel.
TREE_NOTHROW(send_nonblocking_big_fndecl) = 0;
}
if (make_tmp == NULL_TREE)
return call;
else
{
tree ret = build2(COMPOUND_EXPR, TREE_TYPE(call), make_tmp, call);
SET_EXPR_LOCATION(ret, location);
return ret;
}
}
}
// Return a tree for receiving a value of type TYPE_TREE on CHANNEL.
// This does a blocking receive and returns the value read from the
// channel. If FOR_SELECT is true, this is being done because it was
// chosen in a select statement.
tree
Gogo::receive_from_channel(tree type_tree, tree channel, bool for_select,
source_location location)
{
if (int_size_in_bytes(type_tree) <= 8
&& !AGGREGATE_TYPE_P(type_tree)
&& !FLOAT_TYPE_P(type_tree))
{
static tree receive_small_fndecl;
tree call = Gogo::call_builtin(&receive_small_fndecl,
location,
"__go_receive_small",
2,
uint64_type_node,
ptr_type_node,
channel,
boolean_type_node,
(for_select
? boolean_true_node
: boolean_false_node));
// This can panic if there are too many operations on a closed
// channel.
TREE_NOTHROW(receive_small_fndecl) = 0;
int bitsize = GET_MODE_BITSIZE(TYPE_MODE(type_tree));
tree int_type_tree = go_type_for_size(bitsize, 1);
return fold_convert_loc(location, type_tree,
fold_convert_loc(location, int_type_tree,
call));
}
else
{
tree tmp = create_tmp_var(type_tree, get_name(type_tree));
DECL_IGNORED_P(tmp) = 0;
TREE_ADDRESSABLE(tmp) = 1;
tree make_tmp = build1(DECL_EXPR, void_type_node, tmp);
SET_EXPR_LOCATION(make_tmp, location);
tree tmpaddr = build_fold_addr_expr(tmp);
tmpaddr = fold_convert(ptr_type_node, tmpaddr);
static tree receive_big_fndecl;
tree call = Gogo::call_builtin(&receive_big_fndecl,
location,
"__go_receive_big",
3,
void_type_node,
ptr_type_node,
channel,
ptr_type_node,
tmpaddr,
boolean_type_node,
(for_select
? boolean_true_node
: boolean_false_node));
// This can panic if there are too many operations on a closed
// channel.
TREE_NOTHROW(receive_big_fndecl) = 0;
return build2(COMPOUND_EXPR, type_tree, make_tmp,
build2(COMPOUND_EXPR, type_tree, call, tmp));
}
}
// Return the type of a function trampoline. This is like
// get_trampoline_type in tree-nested.c.
tree
Gogo::trampoline_type_tree()
{
static tree type_tree;
if (type_tree == NULL_TREE)
{
unsigned int align = TRAMPOLINE_ALIGNMENT;
unsigned int size = TRAMPOLINE_SIZE;
tree t = build_index_type(build_int_cst(integer_type_node, size - 1));
t = build_array_type(char_type_node, t);
type_tree = Gogo::builtin_struct(NULL, "__go_trampoline", NULL_TREE, 1,
"__data", t);
t = TYPE_FIELDS(type_tree);
DECL_ALIGN(t) = align;
DECL_USER_ALIGN(t) = 1;
go_preserve_from_gc(type_tree);
}
return type_tree;
}
// Make a trampoline which calls FNADDR passing CLOSURE.
tree
Gogo::make_trampoline(tree fnaddr, tree closure, source_location location)
{
tree trampoline_type = Gogo::trampoline_type_tree();
tree trampoline_size = TYPE_SIZE_UNIT(trampoline_type);
closure = save_expr(closure);
// We allocate the trampoline using a special function which will
// mark it as executable.
static tree trampoline_fndecl;
tree x = Gogo::call_builtin(&trampoline_fndecl,
location,
"__go_allocate_trampoline",
2,
ptr_type_node,
size_type_node,
trampoline_size,
ptr_type_node,
fold_convert_loc(location, ptr_type_node,
closure));
x = save_expr(x);
// Initialize the trampoline.
tree ini = build_call_expr(implicit_built_in_decls[BUILT_IN_INIT_TRAMPOLINE],
3, x, fnaddr, closure);
// On some targets the trampoline address needs to be adjusted. For
// example, when compiling in Thumb mode on the ARM, the address
// needs to have the low bit set.
x = build_call_expr(implicit_built_in_decls[BUILT_IN_ADJUST_TRAMPOLINE],
1, x);
x = fold_convert(TREE_TYPE(fnaddr), x);
return build2(COMPOUND_EXPR, TREE_TYPE(x), ini, x);
}
// gogo-tree.cc -- convert Go frontend Gogo IR to gcc trees.
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "go-system.h"
#include <gmp.h>
#ifndef ENABLE_BUILD_WITH_CXX
extern "C"
{
#endif
#include "toplev.h"
#include "tree.h"
#include "gimple.h"
#include "tree-iterator.h"
#include "cgraph.h"
#include "langhooks.h"
#include "convert.h"
#include "output.h"
#include "diagnostic.h"
#ifndef ENABLE_BUILD_WITH_CXX
}
#endif
#include "go-c.h"
#include "types.h"
#include "expressions.h"
#include "statements.h"
#include "runtime.h"
#include "backend.h"
#include "gogo.h"
// Whether we have seen any errors.
bool
saw_errors()
{
return errorcount != 0 || sorrycount != 0;
}
// A helper function.
static inline tree
get_identifier_from_string(const std::string& str)
{
return get_identifier_with_length(str.data(), str.length());
}
// Builtin functions.
static std::map<std::string, tree> builtin_functions;
// Define a builtin function. BCODE is the builtin function code
// defined by builtins.def. NAME is the name of the builtin function.
// LIBNAME is the name of the corresponding library function, and is
// NULL if there isn't one. FNTYPE is the type of the function.
// CONST_P is true if the function has the const attribute.
static void
define_builtin(built_in_function bcode, const char* name, const char* libname,
tree fntype, bool const_p)
{
tree decl = add_builtin_function(name, fntype, bcode, BUILT_IN_NORMAL,
libname, NULL_TREE);
if (const_p)
TREE_READONLY(decl) = 1;
built_in_decls[bcode] = decl;
implicit_built_in_decls[bcode] = decl;
builtin_functions[name] = decl;
if (libname != NULL)
{
decl = add_builtin_function(libname, fntype, bcode, BUILT_IN_NORMAL,
NULL, NULL_TREE);
if (const_p)
TREE_READONLY(decl) = 1;
builtin_functions[libname] = decl;
}
}
// Create trees for implicit builtin functions.
void
Gogo::define_builtin_function_trees()
{
/* We need to define the fetch_and_add functions, since we use them
for ++ and --. */
tree t = go_type_for_size(BITS_PER_UNIT, 1);
tree p = build_pointer_type(build_qualified_type(t, TYPE_QUAL_VOLATILE));
define_builtin(BUILT_IN_ADD_AND_FETCH_1, "__sync_fetch_and_add_1", NULL,
build_function_type_list(t, p, t, NULL_TREE), false);
t = go_type_for_size(BITS_PER_UNIT * 2, 1);
p = build_pointer_type(build_qualified_type(t, TYPE_QUAL_VOLATILE));
define_builtin (BUILT_IN_ADD_AND_FETCH_2, "__sync_fetch_and_add_2", NULL,
build_function_type_list(t, p, t, NULL_TREE), false);
t = go_type_for_size(BITS_PER_UNIT * 4, 1);
p = build_pointer_type(build_qualified_type(t, TYPE_QUAL_VOLATILE));
define_builtin(BUILT_IN_ADD_AND_FETCH_4, "__sync_fetch_and_add_4", NULL,
build_function_type_list(t, p, t, NULL_TREE), false);
t = go_type_for_size(BITS_PER_UNIT * 8, 1);
p = build_pointer_type(build_qualified_type(t, TYPE_QUAL_VOLATILE));
define_builtin(BUILT_IN_ADD_AND_FETCH_8, "__sync_fetch_and_add_8", NULL,
build_function_type_list(t, p, t, NULL_TREE), false);
// We use __builtin_expect for magic import functions.
define_builtin(BUILT_IN_EXPECT, "__builtin_expect", NULL,
build_function_type_list(long_integer_type_node,
long_integer_type_node,
long_integer_type_node,
NULL_TREE),
true);
// We use __builtin_memmove for the predeclared copy function.
define_builtin(BUILT_IN_MEMMOVE, "__builtin_memmove", "memmove",
build_function_type_list(ptr_type_node,
ptr_type_node,
const_ptr_type_node,
size_type_node,
NULL_TREE),
false);
// We provide sqrt for the math library.
define_builtin(BUILT_IN_SQRT, "__builtin_sqrt", "sqrt",
build_function_type_list(double_type_node,
double_type_node,
NULL_TREE),
true);
define_builtin(BUILT_IN_SQRTL, "__builtin_sqrtl", "sqrtl",
build_function_type_list(long_double_type_node,
long_double_type_node,
NULL_TREE),
true);
// We use __builtin_return_address in the thunk we build for
// functions which call recover.
define_builtin(BUILT_IN_RETURN_ADDRESS, "__builtin_return_address", NULL,
build_function_type_list(ptr_type_node,
unsigned_type_node,
NULL_TREE),
false);
// The compiler uses __builtin_trap for some exception handling
// cases.
define_builtin(BUILT_IN_TRAP, "__builtin_trap", NULL,
build_function_type(void_type_node, void_list_node),
false);
}
// Get the name to use for the import control function. If there is a
// global function or variable, then we know that that name must be
// unique in the link, and we use it as the basis for our name.
const std::string&
Gogo::get_init_fn_name()
{
if (this->init_fn_name_.empty())
{
go_assert(this->package_ != NULL);
if (this->is_main_package())
{
// Use a name which the runtime knows.
this->init_fn_name_ = "__go_init_main";
}
else
{
std::string s = this->unique_prefix();
s.append(1, '.');
s.append(this->package_name());
s.append("..import");
this->init_fn_name_ = s;
}
}
return this->init_fn_name_;
}
// Add statements to INIT_STMT_LIST which run the initialization
// functions for imported packages. This is only used for the "main"
// package.
void
Gogo::init_imports(tree* init_stmt_list)
{
go_assert(this->is_main_package());
if (this->imported_init_fns_.empty())
return;
tree fntype = build_function_type(void_type_node, void_list_node);
// We must call them in increasing priority order.
std::vector<Import_init> v;
for (std::set<Import_init>::const_iterator p =
this->imported_init_fns_.begin();
p != this->imported_init_fns_.end();
++p)
v.push_back(*p);
std::sort(v.begin(), v.end());
for (std::vector<Import_init>::const_iterator p = v.begin();
p != v.end();
++p)
{
std::string user_name = p->package_name() + ".init";
tree decl = build_decl(UNKNOWN_LOCATION, FUNCTION_DECL,
get_identifier_from_string(user_name),
fntype);
const std::string& init_name(p->init_name());
SET_DECL_ASSEMBLER_NAME(decl, get_identifier_from_string(init_name));
TREE_PUBLIC(decl) = 1;
DECL_EXTERNAL(decl) = 1;
append_to_statement_list(build_call_expr(decl, 0), init_stmt_list);
}
}
// Register global variables with the garbage collector. We need to
// register all variables which can hold a pointer value. They become
// roots during the mark phase. We build a struct that is easy to
// hook into a list of roots.
// struct __go_gc_root_list
// {
// struct __go_gc_root_list* __next;
// struct __go_gc_root
// {
// void* __decl;
// size_t __size;
// } __roots[];
// };
// The last entry in the roots array has a NULL decl field.
void
Gogo::register_gc_vars(const std::vector<Named_object*>& var_gc,
tree* init_stmt_list)
{
if (var_gc.empty())
return;
size_t count = var_gc.size();
tree root_type = Gogo::builtin_struct(NULL, "__go_gc_root", NULL_TREE, 2,
"__next",
ptr_type_node,
"__size",
sizetype);
tree index_type = build_index_type(size_int(count));
tree array_type = build_array_type(root_type, index_type);
tree root_list_type = make_node(RECORD_TYPE);
root_list_type = Gogo::builtin_struct(NULL, "__go_gc_root_list",
root_list_type, 2,
"__next",
build_pointer_type(root_list_type),
"__roots",
array_type);
// Build an initialier for the __roots array.
VEC(constructor_elt,gc)* roots_init = VEC_alloc(constructor_elt, gc,
count + 1);
size_t i = 0;
for (std::vector<Named_object*>::const_iterator p = var_gc.begin();
p != var_gc.end();
++p, ++i)
{
VEC(constructor_elt,gc)* init = VEC_alloc(constructor_elt, gc, 2);
constructor_elt* elt = VEC_quick_push(constructor_elt, init, NULL);
tree field = TYPE_FIELDS(root_type);
elt->index = field;
Bvariable* bvar = (*p)->get_backend_variable(this, NULL);
tree decl = var_to_tree(bvar);
go_assert(TREE_CODE(decl) == VAR_DECL);
elt->value = build_fold_addr_expr(decl);
elt = VEC_quick_push(constructor_elt, init, NULL);
field = DECL_CHAIN(field);
elt->index = field;
elt->value = DECL_SIZE_UNIT(decl);
elt = VEC_quick_push(constructor_elt, roots_init, NULL);
elt->index = size_int(i);
elt->value = build_constructor(root_type, init);
}
// The list ends with a NULL entry.
VEC(constructor_elt,gc)* init = VEC_alloc(constructor_elt, gc, 2);
constructor_elt* elt = VEC_quick_push(constructor_elt, init, NULL);
tree field = TYPE_FIELDS(root_type);
elt->index = field;
elt->value = fold_convert(TREE_TYPE(field), null_pointer_node);
elt = VEC_quick_push(constructor_elt, init, NULL);
field = DECL_CHAIN(field);
elt->index = field;
elt->value = size_zero_node;
elt = VEC_quick_push(constructor_elt, roots_init, NULL);
elt->index = size_int(i);
elt->value = build_constructor(root_type, init);
// Build a constructor for the struct.
VEC(constructor_elt,gc*) root_list_init = VEC_alloc(constructor_elt, gc, 2);
elt = VEC_quick_push(constructor_elt, root_list_init, NULL);
field = TYPE_FIELDS(root_list_type);
elt->index = field;
elt->value = fold_convert(TREE_TYPE(field), null_pointer_node);
elt = VEC_quick_push(constructor_elt, root_list_init, NULL);
field = DECL_CHAIN(field);
elt->index = field;
elt->value = build_constructor(array_type, roots_init);
// Build a decl to register.
tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL,
create_tmp_var_name("gc"), root_list_type);
DECL_EXTERNAL(decl) = 0;
TREE_PUBLIC(decl) = 0;
TREE_STATIC(decl) = 1;
DECL_ARTIFICIAL(decl) = 1;
DECL_INITIAL(decl) = build_constructor(root_list_type, root_list_init);
rest_of_decl_compilation(decl, 1, 0);
static tree register_gc_fndecl;
tree call = Gogo::call_builtin(&register_gc_fndecl, BUILTINS_LOCATION,
"__go_register_gc_roots",
1,
void_type_node,
build_pointer_type(root_list_type),
build_fold_addr_expr(decl));
if (call != error_mark_node)
append_to_statement_list(call, init_stmt_list);
}
// Build the decl for the initialization function.
tree
Gogo::initialization_function_decl()
{
// The tedious details of building your own function. There doesn't
// seem to be a helper function for this.
std::string name = this->package_name() + ".init";
tree fndecl = build_decl(BUILTINS_LOCATION, FUNCTION_DECL,
get_identifier_from_string(name),
build_function_type(void_type_node,
void_list_node));
const std::string& asm_name(this->get_init_fn_name());
SET_DECL_ASSEMBLER_NAME(fndecl, get_identifier_from_string(asm_name));
tree resdecl = build_decl(BUILTINS_LOCATION, RESULT_DECL, NULL_TREE,
void_type_node);
DECL_ARTIFICIAL(resdecl) = 1;
DECL_CONTEXT(resdecl) = fndecl;
DECL_RESULT(fndecl) = resdecl;
TREE_STATIC(fndecl) = 1;
TREE_USED(fndecl) = 1;
DECL_ARTIFICIAL(fndecl) = 1;
TREE_PUBLIC(fndecl) = 1;
DECL_INITIAL(fndecl) = make_node(BLOCK);
TREE_USED(DECL_INITIAL(fndecl)) = 1;
return fndecl;
}
// Create the magic initialization function. INIT_STMT_LIST is the
// code that it needs to run.
void
Gogo::write_initialization_function(tree fndecl, tree init_stmt_list)
{
// Make sure that we thought we needed an initialization function,
// as otherwise we will not have reported it in the export data.
go_assert(this->is_main_package() || this->need_init_fn_);
if (fndecl == NULL_TREE)
fndecl = this->initialization_function_decl();
DECL_SAVED_TREE(fndecl) = init_stmt_list;
current_function_decl = fndecl;
if (DECL_STRUCT_FUNCTION(fndecl) == NULL)
push_struct_function(fndecl);
else
push_cfun(DECL_STRUCT_FUNCTION(fndecl));
cfun->function_end_locus = BUILTINS_LOCATION;
gimplify_function_tree(fndecl);
cgraph_add_new_function(fndecl, false);
cgraph_mark_needed_node(cgraph_get_node(fndecl));
current_function_decl = NULL_TREE;
pop_cfun();
}
// Search for references to VAR in any statements or called functions.
class Find_var : public Traverse
{
public:
// A hash table we use to avoid looping. The index is the name of a
// named object. We only look through objects defined in this
// package.
typedef Unordered_set(std::string) Seen_objects;
Find_var(Named_object* var, Seen_objects* seen_objects)
: Traverse(traverse_expressions),
var_(var), seen_objects_(seen_objects), found_(false)
{ }
// Whether the variable was found.
bool
found() const
{ return this->found_; }
int
expression(Expression**);
private:
// The variable we are looking for.
Named_object* var_;
// Names of objects we have already seen.
Seen_objects* seen_objects_;
// True if the variable was found.
bool found_;
};
// See if EXPR refers to VAR, looking through function calls and
// variable initializations.
int
Find_var::expression(Expression** pexpr)
{
Expression* e = *pexpr;
Var_expression* ve = e->var_expression();
if (ve != NULL)
{
Named_object* v = ve->named_object();
if (v == this->var_)
{
this->found_ = true;
return TRAVERSE_EXIT;
}
if (v->is_variable() && v->package() == NULL)
{
Expression* init = v->var_value()->init();
if (init != NULL)
{
std::pair<Seen_objects::iterator, bool> ins =
this->seen_objects_->insert(v->name());
if (ins.second)
{
// This is the first time we have seen this name.
if (Expression::traverse(&init, this) == TRAVERSE_EXIT)
return TRAVERSE_EXIT;
}
}
}
}
// We traverse the code of any function we see. Note that this
// means that we will traverse the code of a function whose address
// is taken even if it is not called.
Func_expression* fe = e->func_expression();
if (fe != NULL)
{
const Named_object* f = fe->named_object();
if (f->is_function() && f->package() == NULL)
{
std::pair<Seen_objects::iterator, bool> ins =
this->seen_objects_->insert(f->name());
if (ins.second)
{
// This is the first time we have seen this name.
if (f->func_value()->block()->traverse(this) == TRAVERSE_EXIT)
return TRAVERSE_EXIT;
}
}
}
return TRAVERSE_CONTINUE;
}
// Return true if EXPR refers to VAR.
static bool
expression_requires(Expression* expr, Block* preinit, Named_object* var)
{
Find_var::Seen_objects seen_objects;
Find_var find_var(var, &seen_objects);
if (expr != NULL)
Expression::traverse(&expr, &find_var);
if (preinit != NULL)
preinit->traverse(&find_var);
return find_var.found();
}
// Sort variable initializations. If the initialization expression
// for variable A refers directly or indirectly to the initialization
// expression for variable B, then we must initialize B before A.
class Var_init
{
public:
Var_init()
: var_(NULL), init_(NULL_TREE), waiting_(0)
{ }
Var_init(Named_object* var, tree init)
: var_(var), init_(init), waiting_(0)
{ }
// Return the variable.
Named_object*
var() const
{ return this->var_; }
// Return the initialization expression.
tree
init() const
{ return this->init_; }
// Return the number of variables waiting for this one to be
// initialized.
size_t
waiting() const
{ return this->waiting_; }
// Increment the number waiting.
void
increment_waiting()
{ ++this->waiting_; }
private:
// The variable being initialized.
Named_object* var_;
// The initialization expression to run.
tree init_;
// The number of variables which are waiting for this one.
size_t waiting_;
};
typedef std::list<Var_init> Var_inits;
// Sort the variable initializations. The rule we follow is that we
// emit them in the order they appear in the array, except that if the
// initialization expression for a variable V1 depends upon another
// variable V2 then we initialize V1 after V2.
static void
sort_var_inits(Var_inits* var_inits)
{
Var_inits ready;
while (!var_inits->empty())
{
Var_inits::iterator p1 = var_inits->begin();
Named_object* var = p1->var();
Expression* init = var->var_value()->init();
Block* preinit = var->var_value()->preinit();
// Start walking through the list to see which variables VAR
// needs to wait for. We can skip P1->WAITING variables--that
// is the number we've already checked.
Var_inits::iterator p2 = p1;
++p2;
for (size_t i = p1->waiting(); i > 0; --i)
++p2;
for (; p2 != var_inits->end(); ++p2)
{
if (expression_requires(init, preinit, p2->var()))
{
// Check for cycles.
if (expression_requires(p2->var()->var_value()->init(),
p2->var()->var_value()->preinit(),
var))
{
error_at(var->location(),
("initialization expressions for %qs and "
"%qs depend upon each other"),
var->message_name().c_str(),
p2->var()->message_name().c_str());
inform(p2->var()->location(), "%qs defined here",
p2->var()->message_name().c_str());
p2 = var_inits->end();
}
else
{
// We can't emit P1 until P2 is emitted. Move P1.
// Note that the WAITING loop always executes at
// least once, which is what we want.
p2->increment_waiting();
Var_inits::iterator p3 = p2;
for (size_t i = p2->waiting(); i > 0; --i)
++p3;
var_inits->splice(p3, *var_inits, p1);
}
break;
}
}
if (p2 == var_inits->end())
{
// VAR does not depends upon any other initialization expressions.
// Check for a loop of VAR on itself. We only do this if
// INIT is not NULL; when INIT is NULL, it means that
// PREINIT sets VAR, which we will interpret as a loop.
if (init != NULL && expression_requires(init, preinit, var))
error_at(var->location(),
"initialization expression for %qs depends upon itself",
var->message_name().c_str());
ready.splice(ready.end(), *var_inits, p1);
}
}
// Now READY is the list in the desired initialization order.
var_inits->swap(ready);
}
// Write out the global definitions.
void
Gogo::write_globals()
{
this->convert_named_types();
this->build_interface_method_tables();
Bindings* bindings = this->current_bindings();
size_t count = bindings->size_definitions();
tree* vec = new tree[count];
tree init_fndecl = NULL_TREE;
tree init_stmt_list = NULL_TREE;
if (this->is_main_package())
this->init_imports(&init_stmt_list);
// A list of variable initializations.
Var_inits var_inits;
// A list of variables which need to be registered with the garbage
// collector.
std::vector<Named_object*> var_gc;
var_gc.reserve(count);
tree var_init_stmt_list = NULL_TREE;
size_t i = 0;
for (Bindings::const_definitions_iterator p = bindings->begin_definitions();
p != bindings->end_definitions();
++p, ++i)
{
Named_object* no = *p;
go_assert(!no->is_type_declaration() && !no->is_function_declaration());
// There is nothing to do for a package.
if (no->is_package())
{
--i;
--count;
continue;
}
// There is nothing to do for an object which was imported from
// a different package into the global scope.
if (no->package() != NULL)
{
--i;
--count;
continue;
}
// There is nothing useful we can output for constants which
// have ideal or non-integeral type.
if (no->is_const())
{
Type* type = no->const_value()->type();
if (type == NULL)
type = no->const_value()->expr()->type();
if (type->is_abstract() || type->integer_type() == NULL)
{
--i;
--count;
continue;
}
}
if (!no->is_variable())
{
vec[i] = no->get_tree(this, NULL);
if (vec[i] == error_mark_node)
{
go_assert(saw_errors());
--i;
--count;
continue;
}
}
else
{
Bvariable* var = no->get_backend_variable(this, NULL);
vec[i] = var_to_tree(var);
if (vec[i] == error_mark_node)
{
go_assert(saw_errors());
--i;
--count;
continue;
}
// Check for a sink variable, which may be used to run an
// initializer purely for its side effects.
bool is_sink = no->name()[0] == '_' && no->name()[1] == '.';
tree var_init_tree = NULL_TREE;
if (!no->var_value()->has_pre_init())
{
tree init = no->var_value()->get_init_tree(this, NULL);
if (init == error_mark_node)
go_assert(saw_errors());
else if (init == NULL_TREE)
;
else if (TREE_CONSTANT(init))
this->backend()->global_variable_set_init(var,
tree_to_expr(init));
else if (is_sink)
var_init_tree = init;
else
var_init_tree = fold_build2_loc(no->location(), MODIFY_EXPR,
void_type_node, vec[i], init);
}
else
{
// We are going to create temporary variables which
// means that we need an fndecl.
if (init_fndecl == NULL_TREE)
init_fndecl = this->initialization_function_decl();
current_function_decl = init_fndecl;
if (DECL_STRUCT_FUNCTION(init_fndecl) == NULL)
push_struct_function(init_fndecl);
else
push_cfun(DECL_STRUCT_FUNCTION(init_fndecl));
tree var_decl = is_sink ? NULL_TREE : vec[i];
var_init_tree = no->var_value()->get_init_block(this, NULL,
var_decl);
current_function_decl = NULL_TREE;
pop_cfun();
}
if (var_init_tree != NULL_TREE && var_init_tree != error_mark_node)
{
if (no->var_value()->init() == NULL
&& !no->var_value()->has_pre_init())
append_to_statement_list(var_init_tree, &var_init_stmt_list);
else
var_inits.push_back(Var_init(no, var_init_tree));
}
if (!is_sink && no->var_value()->type()->has_pointer())
var_gc.push_back(no);
}
}
// Register global variables with the garbage collector.
this->register_gc_vars(var_gc, &init_stmt_list);
// Simple variable initializations, after all variables are
// registered.
append_to_statement_list(var_init_stmt_list, &init_stmt_list);
// Complex variable initializations, first sorting them into a
// workable order.
if (!var_inits.empty())
{
sort_var_inits(&var_inits);
for (Var_inits::const_iterator p = var_inits.begin();
p != var_inits.end();
++p)
append_to_statement_list(p->init(), &init_stmt_list);
}
// After all the variables are initialized, call the "init"
// functions if there are any.
for (std::vector<Named_object*>::const_iterator p =
this->init_functions_.begin();
p != this->init_functions_.end();
++p)
{
tree decl = (*p)->get_tree(this, NULL);
tree call = build_call_expr(decl, 0);
append_to_statement_list(call, &init_stmt_list);
}
// Set up a magic function to do all the initialization actions.
// This will be called if this package is imported.
if (init_stmt_list != NULL_TREE
|| this->need_init_fn_
|| this->is_main_package())
this->write_initialization_function(init_fndecl, init_stmt_list);
// Pass everything back to the middle-end.
wrapup_global_declarations(vec, count);
cgraph_finalize_compilation_unit();
check_global_declarations(vec, count);
emit_debug_global_declarations(vec, count);
delete[] vec;
}
// Get a tree for the identifier for a named object.
tree
Named_object::get_id(Gogo* gogo)
{
go_assert(!this->is_variable() && !this->is_result_variable());
std::string decl_name;
if (this->is_function_declaration()
&& !this->func_declaration_value()->asm_name().empty())
decl_name = this->func_declaration_value()->asm_name();
else if (this->is_type()
&& this->type_value()->location() == BUILTINS_LOCATION)
{
// We don't need the package name for builtin types.
decl_name = Gogo::unpack_hidden_name(this->name_);
}
else
{
std::string package_name;
if (this->package_ == NULL)
package_name = gogo->package_name();
else
package_name = this->package_->name();
decl_name = package_name + '.' + Gogo::unpack_hidden_name(this->name_);
Function_type* fntype;
if (this->is_function())
fntype = this->func_value()->type();
else if (this->is_function_declaration())
fntype = this->func_declaration_value()->type();
else
fntype = NULL;
if (fntype != NULL && fntype->is_method())
{
decl_name.push_back('.');
decl_name.append(fntype->receiver()->type()->mangled_name(gogo));
}
}
if (this->is_type())
{
const Named_object* in_function = this->type_value()->in_function();
if (in_function != NULL)
decl_name += '$' + in_function->name();
}
return get_identifier_from_string(decl_name);
}
// Get a tree for a named object.
tree
Named_object::get_tree(Gogo* gogo, Named_object* function)
{
if (this->tree_ != NULL_TREE)
return this->tree_;
tree name;
if (this->classification_ == NAMED_OBJECT_TYPE)
name = NULL_TREE;
else
name = this->get_id(gogo);
tree decl;
switch (this->classification_)
{
case NAMED_OBJECT_CONST:
{
Named_constant* named_constant = this->u_.const_value;
Translate_context subcontext(gogo, function, NULL, NULL);
tree expr_tree = named_constant->expr()->get_tree(&subcontext);
if (expr_tree == error_mark_node)
decl = error_mark_node;
else
{
Type* type = named_constant->type();
if (type != NULL && !type->is_abstract())
{
if (!type->is_error())
expr_tree = fold_convert(type->get_tree(gogo), expr_tree);
else
expr_tree = error_mark_node;
}
if (expr_tree == error_mark_node)
decl = error_mark_node;
else if (INTEGRAL_TYPE_P(TREE_TYPE(expr_tree)))
{
decl = build_decl(named_constant->location(), CONST_DECL,
name, TREE_TYPE(expr_tree));
DECL_INITIAL(decl) = expr_tree;
TREE_CONSTANT(decl) = 1;
TREE_READONLY(decl) = 1;
}
else
{
// A CONST_DECL is only for an enum constant, so we
// shouldn't use for non-integral types. Instead we
// just return the constant itself, rather than a
// decl.
decl = expr_tree;
}
}
}
break;
case NAMED_OBJECT_TYPE:
{
Named_type* named_type = this->u_.type_value;
tree type_tree = named_type->get_tree(gogo);
if (type_tree == error_mark_node)
decl = error_mark_node;
else
{
decl = TYPE_NAME(type_tree);
go_assert(decl != NULL_TREE);
// We need to produce a type descriptor for every named
// type, and for a pointer to every named type, since
// other files or packages might refer to them. We need
// to do this even for hidden types, because they might
// still be returned by some function. Simply calling the
// type_descriptor method is enough to create the type
// descriptor, even though we don't do anything with it.
if (this->package_ == NULL)
{
named_type->type_descriptor_pointer(gogo);
Type* pn = Type::make_pointer_type(named_type);
pn->type_descriptor_pointer(gogo);
}
}
}
break;
case NAMED_OBJECT_TYPE_DECLARATION:
error("reference to undefined type %qs",
this->message_name().c_str());
return error_mark_node;
case NAMED_OBJECT_VAR:
case NAMED_OBJECT_RESULT_VAR:
case NAMED_OBJECT_SINK:
go_unreachable();
case NAMED_OBJECT_FUNC:
{
Function* func = this->u_.func_value;
decl = func->get_or_make_decl(gogo, this, name);
if (decl != error_mark_node)
{
if (func->block() != NULL)
{
if (DECL_STRUCT_FUNCTION(decl) == NULL)
push_struct_function(decl);
else
push_cfun(DECL_STRUCT_FUNCTION(decl));
cfun->function_end_locus = func->block()->end_location();
current_function_decl = decl;
func->build_tree(gogo, this);
gimplify_function_tree(decl);
cgraph_finalize_function(decl, true);
current_function_decl = NULL_TREE;
pop_cfun();
}
}
}
break;
default:
go_unreachable();
}
if (TREE_TYPE(decl) == error_mark_node)
decl = error_mark_node;
tree ret = decl;
this->tree_ = ret;
if (ret != error_mark_node)
go_preserve_from_gc(ret);
return ret;
}
// Get the initial value of a variable as a tree. This does not
// consider whether the variable is in the heap--it returns the
// initial value as though it were always stored in the stack.
tree
Variable::get_init_tree(Gogo* gogo, Named_object* function)
{
go_assert(this->preinit_ == NULL);
if (this->init_ == NULL)
{
go_assert(!this->is_parameter_);
return this->type_->get_init_tree(gogo,
(this->is_global_
|| this->is_in_heap()));
}
else
{
Translate_context context(gogo, function, NULL, NULL);
tree rhs_tree = this->init_->get_tree(&context);
return Expression::convert_for_assignment(&context, this->type(),
this->init_->type(),
rhs_tree, this->location());
}
}
// Get the initial value of a variable when a block is required.
// VAR_DECL is the decl to set; it may be NULL for a sink variable.
tree
Variable::get_init_block(Gogo* gogo, Named_object* function, tree var_decl)
{
go_assert(this->preinit_ != NULL);
// We want to add the variable assignment to the end of the preinit
// block. The preinit block may have a TRY_FINALLY_EXPR and a
// TRY_CATCH_EXPR; if it does, we want to add to the end of the
// regular statements.
Translate_context context(gogo, function, NULL, NULL);
Bblock* bblock = this->preinit_->get_backend(&context);
tree block_tree = block_to_tree(bblock);
if (block_tree == error_mark_node)
return error_mark_node;
go_assert(TREE_CODE(block_tree) == BIND_EXPR);
tree statements = BIND_EXPR_BODY(block_tree);
while (statements != NULL_TREE
&& (TREE_CODE(statements) == TRY_FINALLY_EXPR
|| TREE_CODE(statements) == TRY_CATCH_EXPR))
statements = TREE_OPERAND(statements, 0);
// It's possible to have pre-init statements without an initializer
// if the pre-init statements set the variable.
if (this->init_ != NULL)
{
tree rhs_tree = this->init_->get_tree(&context);
if (rhs_tree == error_mark_node)
return error_mark_node;
if (var_decl == NULL_TREE)
append_to_statement_list(rhs_tree, &statements);
else
{
tree val = Expression::convert_for_assignment(&context, this->type(),
this->init_->type(),
rhs_tree,
this->location());
if (val == error_mark_node)
return error_mark_node;
tree set = fold_build2_loc(this->location(), MODIFY_EXPR,
void_type_node, var_decl, val);
append_to_statement_list(set, &statements);
}
}
return block_tree;
}
// Get a tree for a function decl.
tree
Function::get_or_make_decl(Gogo* gogo, Named_object* no, tree id)
{
if (this->fndecl_ == NULL_TREE)
{
tree functype = this->type_->get_tree(gogo);
if (functype == error_mark_node)
this->fndecl_ = error_mark_node;
else
{
// The type of a function comes back as a pointer, but we
// want the real function type for a function declaration.
go_assert(POINTER_TYPE_P(functype));
functype = TREE_TYPE(functype);
tree decl = build_decl(this->location(), FUNCTION_DECL, id, functype);
this->fndecl_ = decl;
if (no->package() != NULL)
;
else if (this->enclosing_ != NULL || Gogo::is_thunk(no))
;
else if (Gogo::unpack_hidden_name(no->name()) == "init"
&& !this->type_->is_method())
;
else if (Gogo::unpack_hidden_name(no->name()) == "main"
&& gogo->is_main_package())
TREE_PUBLIC(decl) = 1;
// Methods have to be public even if they are hidden because
// they can be pulled into type descriptors when using
// anonymous fields.
else if (!Gogo::is_hidden_name(no->name())
|| this->type_->is_method())
{
TREE_PUBLIC(decl) = 1;
std::string asm_name = gogo->unique_prefix();
asm_name.append(1, '.');
asm_name.append(IDENTIFIER_POINTER(id), IDENTIFIER_LENGTH(id));
SET_DECL_ASSEMBLER_NAME(decl,
get_identifier_from_string(asm_name));
}
// Why do we have to do this in the frontend?
tree restype = TREE_TYPE(functype);
tree resdecl = build_decl(this->location(), RESULT_DECL, NULL_TREE,
restype);
DECL_ARTIFICIAL(resdecl) = 1;
DECL_IGNORED_P(resdecl) = 1;
DECL_CONTEXT(resdecl) = decl;
DECL_RESULT(decl) = resdecl;
if (this->enclosing_ != NULL)
DECL_STATIC_CHAIN(decl) = 1;
// If a function calls the predeclared recover function, we
// can't inline it, because recover behaves differently in a
// function passed directly to defer.
if (this->calls_recover_ && !this->is_recover_thunk_)
DECL_UNINLINABLE(decl) = 1;
// If this is a thunk created to call a function which calls
// the predeclared recover function, we need to disable
// stack splitting for the thunk.
if (this->is_recover_thunk_)
{
tree attr = get_identifier("__no_split_stack__");
DECL_ATTRIBUTES(decl) = tree_cons(attr, NULL_TREE, NULL_TREE);
}
go_preserve_from_gc(decl);
if (this->closure_var_ != NULL)
{
push_struct_function(decl);
Bvariable* bvar = this->closure_var_->get_backend_variable(gogo,
no);
tree closure_decl = var_to_tree(bvar);
if (closure_decl == error_mark_node)
this->fndecl_ = error_mark_node;
else
{
DECL_ARTIFICIAL(closure_decl) = 1;
DECL_IGNORED_P(closure_decl) = 1;
TREE_USED(closure_decl) = 1;
DECL_ARG_TYPE(closure_decl) = TREE_TYPE(closure_decl);
TREE_READONLY(closure_decl) = 1;
DECL_STRUCT_FUNCTION(decl)->static_chain_decl = closure_decl;
}
pop_cfun();
}
}
}
return this->fndecl_;
}
// Get a tree for a function declaration.
tree
Function_declaration::get_or_make_decl(Gogo* gogo, Named_object* no, tree id)
{
if (this->fndecl_ == NULL_TREE)
{
// Let Go code use an asm declaration to pick up a builtin
// function.
if (!this->asm_name_.empty())
{
std::map<std::string, tree>::const_iterator p =
builtin_functions.find(this->asm_name_);
if (p != builtin_functions.end())
{
this->fndecl_ = p->second;
return this->fndecl_;
}
}
tree functype = this->fntype_->get_tree(gogo);
tree decl;
if (functype == error_mark_node)
decl = error_mark_node;
else
{
// The type of a function comes back as a pointer, but we
// want the real function type for a function declaration.
go_assert(POINTER_TYPE_P(functype));
functype = TREE_TYPE(functype);
decl = build_decl(this->location(), FUNCTION_DECL, id, functype);
TREE_PUBLIC(decl) = 1;
DECL_EXTERNAL(decl) = 1;
if (this->asm_name_.empty())
{
std::string asm_name = (no->package() == NULL
? gogo->unique_prefix()
: no->package()->unique_prefix());
asm_name.append(1, '.');
asm_name.append(IDENTIFIER_POINTER(id), IDENTIFIER_LENGTH(id));
SET_DECL_ASSEMBLER_NAME(decl,
get_identifier_from_string(asm_name));
}
}
this->fndecl_ = decl;
go_preserve_from_gc(decl);
}
return this->fndecl_;
}
// We always pass the receiver to a method as a pointer. If the
// receiver is actually declared as a non-pointer type, then we copy
// the value into a local variable, so that it has the right type. In
// this function we create the real PARM_DECL to use, and set
// DEC_INITIAL of the var_decl to be the value passed in.
tree
Function::make_receiver_parm_decl(Gogo* gogo, Named_object* no, tree var_decl)
{
if (var_decl == error_mark_node)
return error_mark_node;
go_assert(TREE_CODE(var_decl) == VAR_DECL);
tree val_type = TREE_TYPE(var_decl);
bool is_in_heap = no->var_value()->is_in_heap();
if (is_in_heap)
{
go_assert(POINTER_TYPE_P(val_type));
val_type = TREE_TYPE(val_type);
}
source_location loc = DECL_SOURCE_LOCATION(var_decl);
std::string name = IDENTIFIER_POINTER(DECL_NAME(var_decl));
name += ".pointer";
tree id = get_identifier_from_string(name);
tree parm_decl = build_decl(loc, PARM_DECL, id, build_pointer_type(val_type));
DECL_CONTEXT(parm_decl) = current_function_decl;
DECL_ARG_TYPE(parm_decl) = TREE_TYPE(parm_decl);
go_assert(DECL_INITIAL(var_decl) == NULL_TREE);
// The receiver might be passed as a null pointer.
tree check = fold_build2_loc(loc, NE_EXPR, boolean_type_node, parm_decl,
fold_convert_loc(loc, TREE_TYPE(parm_decl),
null_pointer_node));
tree ind = build_fold_indirect_ref_loc(loc, parm_decl);
TREE_THIS_NOTRAP(ind) = 1;
tree zero_init = no->var_value()->type()->get_init_tree(gogo, false);
tree init = fold_build3_loc(loc, COND_EXPR, TREE_TYPE(ind),
check, ind, zero_init);
if (is_in_heap)
{
tree size = TYPE_SIZE_UNIT(val_type);
tree space = gogo->allocate_memory(no->var_value()->type(), size,
no->location());
space = save_expr(space);
space = fold_convert(build_pointer_type(val_type), space);
tree spaceref = build_fold_indirect_ref_loc(no->location(), space);
TREE_THIS_NOTRAP(spaceref) = 1;
tree check = fold_build2_loc(loc, NE_EXPR, boolean_type_node,
parm_decl,
fold_convert_loc(loc, TREE_TYPE(parm_decl),
null_pointer_node));
tree parmref = build_fold_indirect_ref_loc(no->location(), parm_decl);
TREE_THIS_NOTRAP(parmref) = 1;
tree set = fold_build2_loc(loc, MODIFY_EXPR, void_type_node,
spaceref, parmref);
init = fold_build2_loc(loc, COMPOUND_EXPR, TREE_TYPE(space),
build3(COND_EXPR, void_type_node,
check, set, NULL_TREE),
space);
}
DECL_INITIAL(var_decl) = init;
return parm_decl;
}
// If we take the address of a parameter, then we need to copy it into
// the heap. We will access it as a local variable via an
// indirection.
tree
Function::copy_parm_to_heap(Gogo* gogo, Named_object* no, tree var_decl)
{
if (var_decl == error_mark_node)
return error_mark_node;
go_assert(TREE_CODE(var_decl) == VAR_DECL);
source_location loc = DECL_SOURCE_LOCATION(var_decl);
std::string name = IDENTIFIER_POINTER(DECL_NAME(var_decl));
name += ".param";
tree id = get_identifier_from_string(name);
tree type = TREE_TYPE(var_decl);
go_assert(POINTER_TYPE_P(type));
type = TREE_TYPE(type);
tree parm_decl = build_decl(loc, PARM_DECL, id, type);
DECL_CONTEXT(parm_decl) = current_function_decl;
DECL_ARG_TYPE(parm_decl) = type;
tree size = TYPE_SIZE_UNIT(type);
tree space = gogo->allocate_memory(no->var_value()->type(), size, loc);
space = save_expr(space);
space = fold_convert(TREE_TYPE(var_decl), space);
tree spaceref = build_fold_indirect_ref_loc(loc, space);
TREE_THIS_NOTRAP(spaceref) = 1;
tree init = build2(COMPOUND_EXPR, TREE_TYPE(space),
build2(MODIFY_EXPR, void_type_node, spaceref, parm_decl),
space);
DECL_INITIAL(var_decl) = init;
return parm_decl;
}
// Get a tree for function code.
void
Function::build_tree(Gogo* gogo, Named_object* named_function)
{
tree fndecl = this->fndecl_;
go_assert(fndecl != NULL_TREE);
tree params = NULL_TREE;
tree* pp = &params;
tree declare_vars = NULL_TREE;
for (Bindings::const_definitions_iterator p =
this->block_->bindings()->begin_definitions();
p != this->block_->bindings()->end_definitions();
++p)
{
if ((*p)->is_variable() && (*p)->var_value()->is_parameter())
{
Bvariable* bvar = (*p)->get_backend_variable(gogo, named_function);
*pp = var_to_tree(bvar);
// We always pass the receiver to a method as a pointer. If
// the receiver is declared as a non-pointer type, then we
// copy the value into a local variable.
if ((*p)->var_value()->is_receiver()
&& (*p)->var_value()->type()->points_to() == NULL)
{
tree parm_decl = this->make_receiver_parm_decl(gogo, *p, *pp);
tree var = *pp;
if (var != error_mark_node)
{
go_assert(TREE_CODE(var) == VAR_DECL);
DECL_CHAIN(var) = declare_vars;
declare_vars = var;
}
*pp = parm_decl;
}
else if ((*p)->var_value()->is_in_heap())
{
// If we take the address of a parameter, then we need
// to copy it into the heap.
tree parm_decl = this->copy_parm_to_heap(gogo, *p, *pp);
tree var = *pp;
if (var != error_mark_node)
{
go_assert(TREE_CODE(var) == VAR_DECL);
DECL_CHAIN(var) = declare_vars;
declare_vars = var;
}
*pp = parm_decl;
}
if (*pp != error_mark_node)
{
go_assert(TREE_CODE(*pp) == PARM_DECL);
pp = &DECL_CHAIN(*pp);
}
}
else if ((*p)->is_result_variable())
{
Bvariable* bvar = (*p)->get_backend_variable(gogo, named_function);
tree var_decl = var_to_tree(bvar);
Type* type = (*p)->result_var_value()->type();
tree init;
if (!(*p)->result_var_value()->is_in_heap())
init = type->get_init_tree(gogo, false);
else
{
source_location loc = (*p)->location();
tree type_tree = type->get_tree(gogo);
tree space = gogo->allocate_memory(type,
TYPE_SIZE_UNIT(type_tree),
loc);
tree ptr_type_tree = build_pointer_type(type_tree);
tree subinit = type->get_init_tree(gogo, true);
if (subinit == NULL_TREE)
init = fold_convert_loc(loc, ptr_type_tree, space);
else
{
space = save_expr(space);
space = fold_convert_loc(loc, ptr_type_tree, space);
tree spaceref = build_fold_indirect_ref_loc(loc, space);
TREE_THIS_NOTRAP(spaceref) = 1;
tree set = fold_build2_loc(loc, MODIFY_EXPR, void_type_node,
spaceref, subinit);
init = fold_build2_loc(loc, COMPOUND_EXPR, TREE_TYPE(space),
set, space);
}
}
if (var_decl != error_mark_node)
{
go_assert(TREE_CODE(var_decl) == VAR_DECL);
DECL_INITIAL(var_decl) = init;
DECL_CHAIN(var_decl) = declare_vars;
declare_vars = var_decl;
}
}
}
*pp = NULL_TREE;
DECL_ARGUMENTS(fndecl) = params;
if (this->block_ != NULL)
{
go_assert(DECL_INITIAL(fndecl) == NULL_TREE);
// Declare variables if necessary.
tree bind = NULL_TREE;
tree defer_init = NULL_TREE;
if (declare_vars != NULL_TREE || this->defer_stack_ != NULL)
{
tree block = make_node(BLOCK);
BLOCK_SUPERCONTEXT(block) = fndecl;
DECL_INITIAL(fndecl) = block;
BLOCK_VARS(block) = declare_vars;
TREE_USED(block) = 1;
bind = build3(BIND_EXPR, void_type_node, BLOCK_VARS(block),
NULL_TREE, block);
TREE_SIDE_EFFECTS(bind) = 1;
if (this->defer_stack_ != NULL)
{
Translate_context dcontext(gogo, named_function, this->block_,
tree_to_block(bind));
Bstatement* bdi = this->defer_stack_->get_backend(&dcontext);
defer_init = stat_to_tree(bdi);
}
}
// Build the trees for all the statements in the function.
Translate_context context(gogo, named_function, NULL, NULL);
Bblock* bblock = this->block_->get_backend(&context);
tree code = block_to_tree(bblock);
tree init = NULL_TREE;
tree except = NULL_TREE;
tree fini = NULL_TREE;
// Initialize variables if necessary.
for (tree v = declare_vars; v != NULL_TREE; v = DECL_CHAIN(v))
{
tree dv = build1(DECL_EXPR, void_type_node, v);
SET_EXPR_LOCATION(dv, DECL_SOURCE_LOCATION(v));
append_to_statement_list(dv, &init);
}
// If we have a defer stack, initialize it at the start of a
// function.
if (defer_init != NULL_TREE && defer_init != error_mark_node)
{
SET_EXPR_LOCATION(defer_init, this->block_->start_location());
append_to_statement_list(defer_init, &init);
// Clean up the defer stack when we leave the function.
this->build_defer_wrapper(gogo, named_function, &except, &fini);
}
if (code != NULL_TREE && code != error_mark_node)
{
if (init != NULL_TREE)
code = build2(COMPOUND_EXPR, void_type_node, init, code);
if (except != NULL_TREE)
code = build2(TRY_CATCH_EXPR, void_type_node, code,
build2(CATCH_EXPR, void_type_node, NULL, except));
if (fini != NULL_TREE)
code = build2(TRY_FINALLY_EXPR, void_type_node, code, fini);
}
// Stick the code into the block we built for the receiver, if
// we built on.
if (bind != NULL_TREE && code != NULL_TREE && code != error_mark_node)
{
BIND_EXPR_BODY(bind) = code;
code = bind;
}
DECL_SAVED_TREE(fndecl) = code;
}
}
// Build the wrappers around function code needed if the function has
// any defer statements. This sets *EXCEPT to an exception handler
// and *FINI to a finally handler.
void
Function::build_defer_wrapper(Gogo* gogo, Named_object* named_function,
tree *except, tree *fini)
{
source_location end_loc = this->block_->end_location();
// Add an exception handler. This is used if a panic occurs. Its
// purpose is to stop the stack unwinding if a deferred function
// calls recover. There are more details in
// libgo/runtime/go-unwind.c.
tree stmt_list = NULL_TREE;
Expression* call = Runtime::make_call(Runtime::CHECK_DEFER, end_loc, 1,
this->defer_stack(end_loc));
Translate_context context(gogo, named_function, NULL, NULL);
tree call_tree = call->get_tree(&context);
if (call_tree != error_mark_node)
append_to_statement_list(call_tree, &stmt_list);
tree retval = this->return_value(gogo, named_function, end_loc, &stmt_list);
tree set;
if (retval == NULL_TREE)
set = NULL_TREE;
else
set = fold_build2_loc(end_loc, MODIFY_EXPR, void_type_node,
DECL_RESULT(this->fndecl_), retval);
tree ret_stmt = fold_build1_loc(end_loc, RETURN_EXPR, void_type_node, set);
append_to_statement_list(ret_stmt, &stmt_list);
go_assert(*except == NULL_TREE);
*except = stmt_list;
// Add some finally code to run the defer functions. This is used
// both in the normal case, when no panic occurs, and also if a
// panic occurs to run any further defer functions. Of course, it
// is possible for a defer function to call panic which should be
// caught by another defer function. To handle that we use a loop.
// finish:
// try { __go_undefer(); } catch { __go_check_defer(); goto finish; }
// if (return values are named) return named_vals;
stmt_list = NULL;
tree label = create_artificial_label(end_loc);
tree define_label = fold_build1_loc(end_loc, LABEL_EXPR, void_type_node,
label);
append_to_statement_list(define_label, &stmt_list);
call = Runtime::make_call(Runtime::UNDEFER, end_loc, 1,
this->defer_stack(end_loc));
tree undefer = call->get_tree(&context);
call = Runtime::make_call(Runtime::CHECK_DEFER, end_loc, 1,
this->defer_stack(end_loc));
tree defer = call->get_tree(&context);
if (undefer == error_mark_node || defer == error_mark_node)
return;
tree jump = fold_build1_loc(end_loc, GOTO_EXPR, void_type_node, label);
tree catch_body = build2(COMPOUND_EXPR, void_type_node, defer, jump);
catch_body = build2(CATCH_EXPR, void_type_node, NULL, catch_body);
tree try_catch = build2(TRY_CATCH_EXPR, void_type_node, undefer, catch_body);
append_to_statement_list(try_catch, &stmt_list);
if (this->type_->results() != NULL
&& !this->type_->results()->empty()
&& !this->type_->results()->front().name().empty())
{
// If the result variables are named, we need to return them
// again, because they might have been changed by a defer
// function.
retval = this->return_value(gogo, named_function, end_loc,
&stmt_list);
set = fold_build2_loc(end_loc, MODIFY_EXPR, void_type_node,
DECL_RESULT(this->fndecl_), retval);
ret_stmt = fold_build1_loc(end_loc, RETURN_EXPR, void_type_node, set);
append_to_statement_list(ret_stmt, &stmt_list);
}
go_assert(*fini == NULL_TREE);
*fini = stmt_list;
}
// Return the value to assign to DECL_RESULT(this->fndecl_). This may
// also add statements to STMT_LIST, which need to be executed before
// the assignment. This is used for a return statement with no
// explicit values.
tree
Function::return_value(Gogo* gogo, Named_object* named_function,
source_location location, tree* stmt_list) const
{
const Typed_identifier_list* results = this->type_->results();
if (results == NULL || results->empty())
return NULL_TREE;
go_assert(this->results_ != NULL);
if (this->results_->size() != results->size())
{
go_assert(saw_errors());
return error_mark_node;
}
tree retval;
if (results->size() == 1)
{
Bvariable* bvar =
this->results_->front()->get_backend_variable(gogo,
named_function);
tree ret = var_to_tree(bvar);
if (this->results_->front()->result_var_value()->is_in_heap())
ret = build_fold_indirect_ref_loc(location, ret);
return ret;
}
else
{
tree rettype = TREE_TYPE(DECL_RESULT(this->fndecl_));
retval = create_tmp_var(rettype, "RESULT");
tree field = TYPE_FIELDS(rettype);
int index = 0;
for (Typed_identifier_list::const_iterator pr = results->begin();
pr != results->end();
++pr, ++index, field = DECL_CHAIN(field))
{
go_assert(field != NULL);
Named_object* no = (*this->results_)[index];
Bvariable* bvar = no->get_backend_variable(gogo, named_function);
tree val = var_to_tree(bvar);
if (no->result_var_value()->is_in_heap())
val = build_fold_indirect_ref_loc(location, val);
tree set = fold_build2_loc(location, MODIFY_EXPR, void_type_node,
build3(COMPONENT_REF, TREE_TYPE(field),
retval, field, NULL_TREE),
val);
append_to_statement_list(set, stmt_list);
}
return retval;
}
}
// Return the integer type to use for a size.
GO_EXTERN_C
tree
go_type_for_size(unsigned int bits, int unsignedp)
{
const char* name;
switch (bits)
{
case 8:
name = unsignedp ? "uint8" : "int8";
break;
case 16:
name = unsignedp ? "uint16" : "int16";
break;
case 32:
name = unsignedp ? "uint32" : "int32";
break;
case 64:
name = unsignedp ? "uint64" : "int64";
break;
default:
if (bits == POINTER_SIZE && unsignedp)
name = "uintptr";
else
return NULL_TREE;
}
Type* type = Type::lookup_integer_type(name);
return type->get_tree(go_get_gogo());
}
// Return the type to use for a mode.
GO_EXTERN_C
tree
go_type_for_mode(enum machine_mode mode, int unsignedp)
{
// FIXME: This static_cast should be in machmode.h.
enum mode_class mc = static_cast<enum mode_class>(GET_MODE_CLASS(mode));
if (mc == MODE_INT)
return go_type_for_size(GET_MODE_BITSIZE(mode), unsignedp);
else if (mc == MODE_FLOAT)
{
Type* type;
switch (GET_MODE_BITSIZE (mode))
{
case 32:
type = Type::lookup_float_type("float32");
break;
case 64:
type = Type::lookup_float_type("float64");
break;
default:
// We have to check for long double in order to support
// i386 excess precision.
if (mode == TYPE_MODE(long_double_type_node))
return long_double_type_node;
return NULL_TREE;
}
return type->float_type()->type_tree();
}
else if (mc == MODE_COMPLEX_FLOAT)
{
Type *type;
switch (GET_MODE_BITSIZE (mode))
{
case 64:
type = Type::lookup_complex_type("complex64");
break;
case 128:
type = Type::lookup_complex_type("complex128");
break;
default:
// We have to check for long double in order to support
// i386 excess precision.
if (mode == TYPE_MODE(complex_long_double_type_node))
return complex_long_double_type_node;
return NULL_TREE;
}
return type->complex_type()->type_tree();
}
else
return NULL_TREE;
}
// Return a tree which allocates SIZE bytes which will holds value of
// type TYPE.
tree
Gogo::allocate_memory(Type* type, tree size, source_location location)
{
// If the package imports unsafe, then it may play games with
// pointers that look like integers.
if (this->imported_unsafe_ || type->has_pointer())
{
static tree new_fndecl;
return Gogo::call_builtin(&new_fndecl,
location,
"__go_new",
1,
ptr_type_node,
sizetype,
size);
}
else
{
static tree new_nopointers_fndecl;
return Gogo::call_builtin(&new_nopointers_fndecl,
location,
"__go_new_nopointers",
1,
ptr_type_node,
sizetype,
size);
}
}
// Build a builtin struct with a list of fields. The name is
// STRUCT_NAME. STRUCT_TYPE is NULL_TREE or an empty RECORD_TYPE
// node; this exists so that the struct can have fields which point to
// itself. If PTYPE is not NULL, store the result in *PTYPE. There
// are NFIELDS fields. Each field is a name (a const char*) followed
// by a type (a tree).
tree
Gogo::builtin_struct(tree* ptype, const char* struct_name, tree struct_type,
int nfields, ...)
{
if (ptype != NULL && *ptype != NULL_TREE)
return *ptype;
va_list ap;
va_start(ap, nfields);
tree fields = NULL_TREE;
for (int i = 0; i < nfields; ++i)
{
const char* field_name = va_arg(ap, const char*);
tree type = va_arg(ap, tree);
if (type == error_mark_node)
{
if (ptype != NULL)
*ptype = error_mark_node;
return error_mark_node;
}
tree field = build_decl(BUILTINS_LOCATION, FIELD_DECL,
get_identifier(field_name), type);
DECL_CHAIN(field) = fields;
fields = field;
}
va_end(ap);
if (struct_type == NULL_TREE)
struct_type = make_node(RECORD_TYPE);
finish_builtin_struct(struct_type, struct_name, fields, NULL_TREE);
if (ptype != NULL)
{
go_preserve_from_gc(struct_type);
*ptype = struct_type;
}
return struct_type;
}
// Return a type to use for pointer to const char for a string.
tree
Gogo::const_char_pointer_type_tree()
{
static tree type;
if (type == NULL_TREE)
{
tree const_char_type = build_qualified_type(unsigned_char_type_node,
TYPE_QUAL_CONST);
type = build_pointer_type(const_char_type);
go_preserve_from_gc(type);
}
return type;
}
// Return a tree for a string constant.
tree
Gogo::string_constant_tree(const std::string& val)
{
tree index_type = build_index_type(size_int(val.length()));
tree const_char_type = build_qualified_type(unsigned_char_type_node,
TYPE_QUAL_CONST);
tree string_type = build_array_type(const_char_type, index_type);
string_type = build_variant_type_copy(string_type);
TYPE_STRING_FLAG(string_type) = 1;
tree string_val = build_string(val.length(), val.data());
TREE_TYPE(string_val) = string_type;
return string_val;
}
// Return a tree for a Go string constant.
tree
Gogo::go_string_constant_tree(const std::string& val)
{
tree string_type = Type::make_string_type()->get_tree(this);
VEC(constructor_elt, gc)* init = VEC_alloc(constructor_elt, gc, 2);
constructor_elt* elt = VEC_quick_push(constructor_elt, init, NULL);
tree field = TYPE_FIELDS(string_type);
go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__data") == 0);
elt->index = field;
tree str = Gogo::string_constant_tree(val);
elt->value = fold_convert(TREE_TYPE(field),
build_fold_addr_expr(str));
elt = VEC_quick_push(constructor_elt, init, NULL);
field = DECL_CHAIN(field);
go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__length") == 0);
elt->index = field;
elt->value = build_int_cst_type(TREE_TYPE(field), val.length());
tree constructor = build_constructor(string_type, init);
TREE_READONLY(constructor) = 1;
TREE_CONSTANT(constructor) = 1;
return constructor;
}
// Return a tree for a pointer to a Go string constant. This is only
// used for type descriptors, so we return a pointer to a constant
// decl.
tree
Gogo::ptr_go_string_constant_tree(const std::string& val)
{
tree pval = this->go_string_constant_tree(val);
tree decl = build_decl(UNKNOWN_LOCATION, VAR_DECL,
create_tmp_var_name("SP"), TREE_TYPE(pval));
DECL_EXTERNAL(decl) = 0;
TREE_PUBLIC(decl) = 0;
TREE_USED(decl) = 1;
TREE_READONLY(decl) = 1;
TREE_CONSTANT(decl) = 1;
TREE_STATIC(decl) = 1;
DECL_ARTIFICIAL(decl) = 1;
DECL_INITIAL(decl) = pval;
rest_of_decl_compilation(decl, 1, 0);
return build_fold_addr_expr(decl);
}
// Build the type of the struct that holds a slice for the given
// element type.
tree
Gogo::slice_type_tree(tree element_type_tree)
{
// We use int for the count and capacity fields in a slice header.
// This matches 6g. The language definition guarantees that we
// can't allocate space of a size which does not fit in int
// anyhow. FIXME: integer_type_node is the the C type "int" but is
// not necessarily the Go type "int". They will differ when the C
// type "int" has fewer than 32 bits.
return Gogo::builtin_struct(NULL, "__go_slice", NULL_TREE, 3,
"__values",
build_pointer_type(element_type_tree),
"__count",
integer_type_node,
"__capacity",
integer_type_node);
}
// Given the tree for a slice type, return the tree for the type of
// the elements of the slice.
tree
Gogo::slice_element_type_tree(tree slice_type_tree)
{
go_assert(TREE_CODE(slice_type_tree) == RECORD_TYPE
&& POINTER_TYPE_P(TREE_TYPE(TYPE_FIELDS(slice_type_tree))));
return TREE_TYPE(TREE_TYPE(TYPE_FIELDS(slice_type_tree)));
}
// Build a constructor for a slice. SLICE_TYPE_TREE is the type of
// the slice. VALUES is the value pointer and COUNT is the number of
// entries. If CAPACITY is not NULL, it is the capacity; otherwise
// the capacity and the count are the same.
tree
Gogo::slice_constructor(tree slice_type_tree, tree values, tree count,
tree capacity)
{
go_assert(TREE_CODE(slice_type_tree) == RECORD_TYPE);
VEC(constructor_elt,gc)* init = VEC_alloc(constructor_elt, gc, 3);
tree field = TYPE_FIELDS(slice_type_tree);
go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__values") == 0);
constructor_elt* elt = VEC_quick_push(constructor_elt, init, NULL);
elt->index = field;
go_assert(TYPE_MAIN_VARIANT(TREE_TYPE(field))
== TYPE_MAIN_VARIANT(TREE_TYPE(values)));
elt->value = values;
count = fold_convert(sizetype, count);
if (capacity == NULL_TREE)
{
count = save_expr(count);
capacity = count;
}
field = DECL_CHAIN(field);
go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__count") == 0);
elt = VEC_quick_push(constructor_elt, init, NULL);
elt->index = field;
elt->value = fold_convert(TREE_TYPE(field), count);
field = DECL_CHAIN(field);
go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__capacity") == 0);
elt = VEC_quick_push(constructor_elt, init, NULL);
elt->index = field;
elt->value = fold_convert(TREE_TYPE(field), capacity);
return build_constructor(slice_type_tree, init);
}
// Build a constructor for an empty slice.
tree
Gogo::empty_slice_constructor(tree slice_type_tree)
{
tree element_field = TYPE_FIELDS(slice_type_tree);
tree ret = Gogo::slice_constructor(slice_type_tree,
fold_convert(TREE_TYPE(element_field),
null_pointer_node),
size_zero_node,
size_zero_node);
TREE_CONSTANT(ret) = 1;
return ret;
}
// Build a map descriptor for a map of type MAPTYPE.
tree
Gogo::map_descriptor(Map_type* maptype)
{
if (this->map_descriptors_ == NULL)
this->map_descriptors_ = new Map_descriptors(10);
std::pair<const Map_type*, tree> val(maptype, NULL);
std::pair<Map_descriptors::iterator, bool> ins =
this->map_descriptors_->insert(val);
Map_descriptors::iterator p = ins.first;
if (!ins.second)
{
if (p->second == error_mark_node)
return error_mark_node;
go_assert(p->second != NULL_TREE && DECL_P(p->second));
return build_fold_addr_expr(p->second);
}
Type* keytype = maptype->key_type();
Type* valtype = maptype->val_type();
std::string mangled_name = ("__go_map_" + maptype->mangled_name(this));
tree id = get_identifier_from_string(mangled_name);
// Get the type of the map descriptor. This is __go_map_descriptor
// in libgo/map.h.
tree struct_type = this->map_descriptor_type();
// The map entry type is a struct with three fields. This struct is
// specific to MAPTYPE. Build it.
tree map_entry_type = make_node(RECORD_TYPE);
map_entry_type = Gogo::builtin_struct(NULL, "__map", map_entry_type, 3,
"__next",
build_pointer_type(map_entry_type),
"__key",
keytype->get_tree(this),
"__val",
valtype->get_tree(this));
if (map_entry_type == error_mark_node)
{
p->second = error_mark_node;
return error_mark_node;
}
tree map_entry_key_field = DECL_CHAIN(TYPE_FIELDS(map_entry_type));
go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(map_entry_key_field)),
"__key") == 0);
tree map_entry_val_field = DECL_CHAIN(map_entry_key_field);
go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(map_entry_val_field)),
"__val") == 0);
// Initialize the entries.
tree map_descriptor_field = TYPE_FIELDS(struct_type);
go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(map_descriptor_field)),
"__map_descriptor") == 0);
tree entry_size_field = DECL_CHAIN(map_descriptor_field);
go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(entry_size_field)),
"__entry_size") == 0);
tree key_offset_field = DECL_CHAIN(entry_size_field);
go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(key_offset_field)),
"__key_offset") == 0);
tree val_offset_field = DECL_CHAIN(key_offset_field);
go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(val_offset_field)),
"__val_offset") == 0);
VEC(constructor_elt, gc)* descriptor = VEC_alloc(constructor_elt, gc, 6);
constructor_elt* elt = VEC_quick_push(constructor_elt, descriptor, NULL);
elt->index = map_descriptor_field;
elt->value = maptype->type_descriptor_pointer(this);
elt = VEC_quick_push(constructor_elt, descriptor, NULL);
elt->index = entry_size_field;
elt->value = TYPE_SIZE_UNIT(map_entry_type);
elt = VEC_quick_push(constructor_elt, descriptor, NULL);
elt->index = key_offset_field;
elt->value = byte_position(map_entry_key_field);
elt = VEC_quick_push(constructor_elt, descriptor, NULL);
elt->index = val_offset_field;
elt->value = byte_position(map_entry_val_field);
tree constructor = build_constructor(struct_type, descriptor);
tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL, id, struct_type);
TREE_STATIC(decl) = 1;
TREE_USED(decl) = 1;
TREE_READONLY(decl) = 1;
TREE_CONSTANT(decl) = 1;
DECL_INITIAL(decl) = constructor;
make_decl_one_only(decl, DECL_ASSEMBLER_NAME(decl));
resolve_unique_section(decl, 1, 0);
rest_of_decl_compilation(decl, 1, 0);
go_preserve_from_gc(decl);
p->second = decl;
return build_fold_addr_expr(decl);
}
// Return a tree for the type of a map descriptor. This is struct
// __go_map_descriptor in libgo/runtime/map.h. This is the same for
// all map types.
tree
Gogo::map_descriptor_type()
{
static tree struct_type;
tree dtype = Type::make_type_descriptor_type()->get_tree(this);
dtype = build_qualified_type(dtype, TYPE_QUAL_CONST);
return Gogo::builtin_struct(&struct_type, "__go_map_descriptor", NULL_TREE,
4,
"__map_descriptor",
build_pointer_type(dtype),
"__entry_size",
sizetype,
"__key_offset",
sizetype,
"__val_offset",
sizetype);
}
// Return the name to use for a type descriptor decl for TYPE. This
// is used when TYPE does not have a name.
std::string
Gogo::unnamed_type_descriptor_decl_name(const Type* type)
{
return "__go_td_" + type->mangled_name(this);
}
// Return the name to use for a type descriptor decl for a type named
// NAME, defined in the function IN_FUNCTION. IN_FUNCTION will
// normally be NULL.
std::string
Gogo::type_descriptor_decl_name(const Named_object* no,
const Named_object* in_function)
{
std::string ret = "__go_tdn_";
if (no->type_value()->is_builtin())
go_assert(in_function == NULL);
else
{
const std::string& unique_prefix(no->package() == NULL
? this->unique_prefix()
: no->package()->unique_prefix());
const std::string& package_name(no->package() == NULL
? this->package_name()
: no->package()->name());
ret.append(unique_prefix);
ret.append(1, '.');
ret.append(package_name);
ret.append(1, '.');
if (in_function != NULL)
{
ret.append(Gogo::unpack_hidden_name(in_function->name()));
ret.append(1, '.');
}
}
ret.append(no->name());
return ret;
}
// Where a type descriptor decl should be defined.
Gogo::Type_descriptor_location
Gogo::type_descriptor_location(const Type* type)
{
const Named_type* name = type->named_type();
if (name != NULL)
{
if (name->named_object()->package() != NULL)
{
// This is a named type defined in a different package. The
// descriptor should be defined in that package.
return TYPE_DESCRIPTOR_UNDEFINED;
}
else if (name->is_builtin())
{
// We create the descriptor for a builtin type whenever we
// need it.
return TYPE_DESCRIPTOR_COMMON;
}
else
{
// This is a named type defined in this package. The
// descriptor should be defined here.
return TYPE_DESCRIPTOR_DEFINED;
}
}
else
{
if (type->points_to() != NULL
&& type->points_to()->named_type() != NULL
&& type->points_to()->named_type()->named_object()->package() != NULL)
{
// This is an unnamed pointer to a named type defined in a
// different package. The descriptor should be defined in
// that package.
return TYPE_DESCRIPTOR_UNDEFINED;
}
else
{
// This is an unnamed type. The descriptor could be defined
// in any package where it is needed, and the linker will
// pick one descriptor to keep.
return TYPE_DESCRIPTOR_COMMON;
}
}
}
// Build a type descriptor decl for TYPE. INITIALIZER is a struct
// composite literal which initializers the type descriptor.
void
Gogo::build_type_descriptor_decl(const Type* type, Expression* initializer,
tree* pdecl)
{
const Named_type* name = type->named_type();
// We can have multiple instances of unnamed types, but we only want
// to emit the type descriptor once. We use a hash table to handle
// this. This is not necessary for named types, as they are unique,
// and we store the type descriptor decl in the type itself.
tree* phash = NULL;
if (name == NULL)
{
if (this->type_descriptor_decls_ == NULL)
this->type_descriptor_decls_ = new Type_descriptor_decls(10);
std::pair<Type_descriptor_decls::iterator, bool> ins =
this->type_descriptor_decls_->insert(std::make_pair(type, NULL_TREE));
if (!ins.second)
{
// We've already built a type descriptor for this type.
*pdecl = ins.first->second;
return;
}
phash = &ins.first->second;
}
std::string decl_name;
if (name == NULL)
decl_name = this->unnamed_type_descriptor_decl_name(type);
else
decl_name = this->type_descriptor_decl_name(name->named_object(),
name->in_function());
tree id = get_identifier_from_string(decl_name);
tree descriptor_type_tree = initializer->type()->get_tree(this);
if (descriptor_type_tree == error_mark_node)
{
*pdecl = error_mark_node;
return;
}
tree decl = build_decl(name == NULL ? BUILTINS_LOCATION : name->location(),
VAR_DECL, id,
build_qualified_type(descriptor_type_tree,
TYPE_QUAL_CONST));
TREE_READONLY(decl) = 1;
TREE_CONSTANT(decl) = 1;
DECL_ARTIFICIAL(decl) = 1;
go_preserve_from_gc(decl);
if (phash != NULL)
*phash = decl;
// We store the new DECL now because we may need to refer to it when
// expanding INITIALIZER.
*pdecl = decl;
// If appropriate, just refer to the exported type identifier.
Gogo::Type_descriptor_location type_descriptor_location =
this->type_descriptor_location(type);
if (type_descriptor_location == TYPE_DESCRIPTOR_UNDEFINED)
{
TREE_PUBLIC(decl) = 1;
DECL_EXTERNAL(decl) = 1;
return;
}
TREE_STATIC(decl) = 1;
TREE_USED(decl) = 1;
Translate_context context(this, NULL, NULL, NULL);
context.set_is_const();
tree constructor = initializer->get_tree(&context);
if (constructor == error_mark_node)
go_assert(saw_errors());
DECL_INITIAL(decl) = constructor;
if (type_descriptor_location == TYPE_DESCRIPTOR_DEFINED)
TREE_PUBLIC(decl) = 1;
else
{
go_assert(type_descriptor_location == TYPE_DESCRIPTOR_COMMON);
make_decl_one_only(decl, DECL_ASSEMBLER_NAME(decl));
resolve_unique_section(decl, 1, 0);
}
rest_of_decl_compilation(decl, 1, 0);
}
// Build an interface method table for a type: a list of function
// pointers, one for each interface method. This is used for
// interfaces.
tree
Gogo::interface_method_table_for_type(const Interface_type* interface,
Named_type* type,
bool is_pointer)
{
const Typed_identifier_list* interface_methods = interface->methods();
go_assert(!interface_methods->empty());
std::string mangled_name = ((is_pointer ? "__go_pimt__" : "__go_imt_")
+ interface->mangled_name(this)
+ "__"
+ type->mangled_name(this));
tree id = get_identifier_from_string(mangled_name);
// See whether this interface has any hidden methods.
bool has_hidden_methods = false;
for (Typed_identifier_list::const_iterator p = interface_methods->begin();
p != interface_methods->end();
++p)
{
if (Gogo::is_hidden_name(p->name()))
{
has_hidden_methods = true;
break;
}
}
// We already know that the named type is convertible to the
// interface. If the interface has hidden methods, and the named
// type is defined in a different package, then the interface
// conversion table will be defined by that other package.
if (has_hidden_methods && type->named_object()->package() != NULL)
{
tree array_type = build_array_type(const_ptr_type_node, NULL);
tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL, id, array_type);
TREE_READONLY(decl) = 1;
TREE_CONSTANT(decl) = 1;
TREE_PUBLIC(decl) = 1;
DECL_EXTERNAL(decl) = 1;
go_preserve_from_gc(decl);
return decl;
}
size_t count = interface_methods->size();
VEC(constructor_elt, gc)* pointers = VEC_alloc(constructor_elt, gc,
count + 1);
// The first element is the type descriptor.
constructor_elt* elt = VEC_quick_push(constructor_elt, pointers, NULL);
elt->index = size_zero_node;
Type* td_type;
if (!is_pointer)
td_type = type;
else
td_type = Type::make_pointer_type(type);
elt->value = fold_convert(const_ptr_type_node,
td_type->type_descriptor_pointer(this));
size_t i = 1;
for (Typed_identifier_list::const_iterator p = interface_methods->begin();
p != interface_methods->end();
++p, ++i)
{
bool is_ambiguous;
Method* m = type->method_function(p->name(), &is_ambiguous);
go_assert(m != NULL);
Named_object* no = m->named_object();
tree fnid = no->get_id(this);
tree fndecl;
if (no->is_function())
fndecl = no->func_value()->get_or_make_decl(this, no, fnid);
else if (no->is_function_declaration())
fndecl = no->func_declaration_value()->get_or_make_decl(this, no,
fnid);
else
go_unreachable();
fndecl = build_fold_addr_expr(fndecl);
elt = VEC_quick_push(constructor_elt, pointers, NULL);
elt->index = size_int(i);
elt->value = fold_convert(const_ptr_type_node, fndecl);
}
go_assert(i == count + 1);
tree array_type = build_array_type(const_ptr_type_node,
build_index_type(size_int(count)));
tree constructor = build_constructor(array_type, pointers);
tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL, id, array_type);
TREE_STATIC(decl) = 1;
TREE_USED(decl) = 1;
TREE_READONLY(decl) = 1;
TREE_CONSTANT(decl) = 1;
DECL_INITIAL(decl) = constructor;
// If the interface type has hidden methods, then this is the only
// definition of the table. Otherwise it is a comdat table which
// may be defined in multiple packages.
if (has_hidden_methods)
TREE_PUBLIC(decl) = 1;
else
{
make_decl_one_only(decl, DECL_ASSEMBLER_NAME(decl));
resolve_unique_section(decl, 1, 0);
}
rest_of_decl_compilation(decl, 1, 0);
go_preserve_from_gc(decl);
return decl;
}
// Mark a function as a builtin library function.
void
Gogo::mark_fndecl_as_builtin_library(tree fndecl)
{
DECL_EXTERNAL(fndecl) = 1;
TREE_PUBLIC(fndecl) = 1;
DECL_ARTIFICIAL(fndecl) = 1;
TREE_NOTHROW(fndecl) = 1;
DECL_VISIBILITY(fndecl) = VISIBILITY_DEFAULT;
DECL_VISIBILITY_SPECIFIED(fndecl) = 1;
}
// Build a call to a builtin function.
tree
Gogo::call_builtin(tree* pdecl, source_location location, const char* name,
int nargs, tree rettype, ...)
{
if (rettype == error_mark_node)
return error_mark_node;
tree* types = new tree[nargs];
tree* args = new tree[nargs];
va_list ap;
va_start(ap, rettype);
for (int i = 0; i < nargs; ++i)
{
types[i] = va_arg(ap, tree);
args[i] = va_arg(ap, tree);
if (types[i] == error_mark_node || args[i] == error_mark_node)
{
delete[] types;
delete[] args;
return error_mark_node;
}
}
va_end(ap);
if (*pdecl == NULL_TREE)
{
tree fnid = get_identifier(name);
tree argtypes = NULL_TREE;
tree* pp = &argtypes;
for (int i = 0; i < nargs; ++i)
{
*pp = tree_cons(NULL_TREE, types[i], NULL_TREE);
pp = &TREE_CHAIN(*pp);
}
*pp = void_list_node;
tree fntype = build_function_type(rettype, argtypes);
*pdecl = build_decl(BUILTINS_LOCATION, FUNCTION_DECL, fnid, fntype);
Gogo::mark_fndecl_as_builtin_library(*pdecl);
go_preserve_from_gc(*pdecl);
}
tree fnptr = build_fold_addr_expr(*pdecl);
if (CAN_HAVE_LOCATION_P(fnptr))
SET_EXPR_LOCATION(fnptr, location);
tree ret = build_call_array(rettype, fnptr, nargs, args);
SET_EXPR_LOCATION(ret, location);
delete[] types;
delete[] args;
return ret;
}
// Build a call to the runtime error function.
tree
Gogo::runtime_error(int code, source_location location)
{
static tree runtime_error_fndecl;
tree ret = Gogo::call_builtin(&runtime_error_fndecl,
location,
"__go_runtime_error",
1,
void_type_node,
integer_type_node,
build_int_cst(integer_type_node, code));
if (ret == error_mark_node)
return error_mark_node;
// The runtime error function panics and does not return.
TREE_NOTHROW(runtime_error_fndecl) = 0;
TREE_THIS_VOLATILE(runtime_error_fndecl) = 1;
return ret;
}
// Return a tree for receiving a value of type TYPE_TREE on CHANNEL.
// This does a blocking receive and returns the value read from the
// channel. If FOR_SELECT is true, this is being done because it was
// chosen in a select statement.
tree
Gogo::receive_from_channel(tree type_tree, tree channel, bool for_select,
source_location location)
{
if (type_tree == error_mark_node || channel == error_mark_node)
return error_mark_node;
if (int_size_in_bytes(type_tree) <= 8
&& !AGGREGATE_TYPE_P(type_tree)
&& !FLOAT_TYPE_P(type_tree))
{
static tree receive_small_fndecl;
tree call = Gogo::call_builtin(&receive_small_fndecl,
location,
"__go_receive_small",
2,
uint64_type_node,
ptr_type_node,
channel,
boolean_type_node,
(for_select
? boolean_true_node
: boolean_false_node));
if (call == error_mark_node)
return error_mark_node;
// This can panic if there are too many operations on a closed
// channel.
TREE_NOTHROW(receive_small_fndecl) = 0;
int bitsize = GET_MODE_BITSIZE(TYPE_MODE(type_tree));
tree int_type_tree = go_type_for_size(bitsize, 1);
return fold_convert_loc(location, type_tree,
fold_convert_loc(location, int_type_tree,
call));
}
else
{
tree tmp = create_tmp_var(type_tree, get_name(type_tree));
DECL_IGNORED_P(tmp) = 0;
TREE_ADDRESSABLE(tmp) = 1;
tree make_tmp = build1(DECL_EXPR, void_type_node, tmp);
SET_EXPR_LOCATION(make_tmp, location);
tree tmpaddr = build_fold_addr_expr(tmp);
tmpaddr = fold_convert(ptr_type_node, tmpaddr);
static tree receive_big_fndecl;
tree call = Gogo::call_builtin(&receive_big_fndecl,
location,
"__go_receive_big",
3,
boolean_type_node,
ptr_type_node,
channel,
ptr_type_node,
tmpaddr,
boolean_type_node,
(for_select
? boolean_true_node
: boolean_false_node));
if (call == error_mark_node)
return error_mark_node;
// This can panic if there are too many operations on a closed
// channel.
TREE_NOTHROW(receive_big_fndecl) = 0;
return build2(COMPOUND_EXPR, type_tree, make_tmp,
build2(COMPOUND_EXPR, type_tree, call, tmp));
}
}
// Return the type of a function trampoline. This is like
// get_trampoline_type in tree-nested.c.
tree
Gogo::trampoline_type_tree()
{
static tree type_tree;
if (type_tree == NULL_TREE)
{
unsigned int size;
unsigned int align;
go_trampoline_info(&size, &align);
tree t = build_index_type(build_int_cst(integer_type_node, size - 1));
t = build_array_type(char_type_node, t);
type_tree = Gogo::builtin_struct(NULL, "__go_trampoline", NULL_TREE, 1,
"__data", t);
t = TYPE_FIELDS(type_tree);
DECL_ALIGN(t) = align;
DECL_USER_ALIGN(t) = 1;
go_preserve_from_gc(type_tree);
}
return type_tree;
}
// Make a trampoline which calls FNADDR passing CLOSURE.
tree
Gogo::make_trampoline(tree fnaddr, tree closure, source_location location)
{
tree trampoline_type = Gogo::trampoline_type_tree();
tree trampoline_size = TYPE_SIZE_UNIT(trampoline_type);
closure = save_expr(closure);
// We allocate the trampoline using a special function which will
// mark it as executable.
static tree trampoline_fndecl;
tree x = Gogo::call_builtin(&trampoline_fndecl,
location,
"__go_allocate_trampoline",
2,
ptr_type_node,
size_type_node,
trampoline_size,
ptr_type_node,
fold_convert_loc(location, ptr_type_node,
closure));
if (x == error_mark_node)
return error_mark_node;
x = save_expr(x);
// Initialize the trampoline.
tree ini = build_call_expr(implicit_built_in_decls[BUILT_IN_INIT_TRAMPOLINE],
3, x, fnaddr, closure);
// On some targets the trampoline address needs to be adjusted. For
// example, when compiling in Thumb mode on the ARM, the address
// needs to have the low bit set.
x = build_call_expr(implicit_built_in_decls[BUILT_IN_ADJUST_TRAMPOLINE],
1, x);
x = fold_convert(TREE_TYPE(fnaddr), x);
return build2(COMPOUND_EXPR, TREE_TYPE(x), ini, x);
}
// gogo-tree.cc -- convert Go frontend Gogo IR to gcc trees.
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "go-system.h"
#include <gmp.h>
#ifndef ENABLE_BUILD_WITH_CXX
extern "C"
{
#endif
#include "toplev.h"
#include "tree.h"
#include "gimple.h"
#include "tree-iterator.h"
#include "cgraph.h"
#include "langhooks.h"
#include "convert.h"
#include "output.h"
#include "diagnostic.h"
#ifndef ENABLE_BUILD_WITH_CXX
}
#endif
#include "go-c.h"
#include "types.h"
#include "expressions.h"
#include "statements.h"
#include "gogo.h"
// Whether we have seen any errors.
bool
saw_errors()
{
return errorcount != 0 || sorrycount != 0;
}
// A helper function.
static inline tree
get_identifier_from_string(const std::string& str)
{
return get_identifier_with_length(str.data(), str.length());
}
// Builtin functions.
static std::map<std::string, tree> builtin_functions;
// Define a builtin function. BCODE is the builtin function code
// defined by builtins.def. NAME is the name of the builtin function.
// LIBNAME is the name of the corresponding library function, and is
// NULL if there isn't one. FNTYPE is the type of the function.
// CONST_P is true if the function has the const attribute.
static void
define_builtin(built_in_function bcode, const char* name, const char* libname,
tree fntype, bool const_p)
{
tree decl = add_builtin_function(name, fntype, bcode, BUILT_IN_NORMAL,
libname, NULL_TREE);
if (const_p)
TREE_READONLY(decl) = 1;
built_in_decls[bcode] = decl;
implicit_built_in_decls[bcode] = decl;
builtin_functions[name] = decl;
if (libname != NULL)
{
decl = add_builtin_function(libname, fntype, bcode, BUILT_IN_NORMAL,
NULL, NULL_TREE);
if (const_p)
TREE_READONLY(decl) = 1;
builtin_functions[libname] = decl;
}
}
// Create trees for implicit builtin functions.
void
Gogo::define_builtin_function_trees()
{
/* We need to define the fetch_and_add functions, since we use them
for ++ and --. */
tree t = go_type_for_size(BITS_PER_UNIT, 1);
tree p = build_pointer_type(build_qualified_type(t, TYPE_QUAL_VOLATILE));
define_builtin(BUILT_IN_ADD_AND_FETCH_1, "__sync_fetch_and_add_1", NULL,
build_function_type_list(t, p, t, NULL_TREE), false);
t = go_type_for_size(BITS_PER_UNIT * 2, 1);
p = build_pointer_type(build_qualified_type(t, TYPE_QUAL_VOLATILE));
define_builtin (BUILT_IN_ADD_AND_FETCH_2, "__sync_fetch_and_add_2", NULL,
build_function_type_list(t, p, t, NULL_TREE), false);
t = go_type_for_size(BITS_PER_UNIT * 4, 1);
p = build_pointer_type(build_qualified_type(t, TYPE_QUAL_VOLATILE));
define_builtin(BUILT_IN_ADD_AND_FETCH_4, "__sync_fetch_and_add_4", NULL,
build_function_type_list(t, p, t, NULL_TREE), false);
t = go_type_for_size(BITS_PER_UNIT * 8, 1);
p = build_pointer_type(build_qualified_type(t, TYPE_QUAL_VOLATILE));
define_builtin(BUILT_IN_ADD_AND_FETCH_8, "__sync_fetch_and_add_8", NULL,
build_function_type_list(t, p, t, NULL_TREE), false);
// We use __builtin_expect for magic import functions.
define_builtin(BUILT_IN_EXPECT, "__builtin_expect", NULL,
build_function_type_list(long_integer_type_node,
long_integer_type_node,
long_integer_type_node,
NULL_TREE),
true);
// We use __builtin_memmove for the predeclared copy function.
define_builtin(BUILT_IN_MEMMOVE, "__builtin_memmove", "memmove",
build_function_type_list(ptr_type_node,
ptr_type_node,
const_ptr_type_node,
size_type_node,
NULL_TREE),
false);
// We provide sqrt for the math library.
define_builtin(BUILT_IN_SQRT, "__builtin_sqrt", "sqrt",
build_function_type_list(double_type_node,
double_type_node,
NULL_TREE),
true);
define_builtin(BUILT_IN_SQRTL, "__builtin_sqrtl", "sqrtl",
build_function_type_list(long_double_type_node,
long_double_type_node,
NULL_TREE),
true);
// We use __builtin_return_address in the thunk we build for
// functions which call recover.
define_builtin(BUILT_IN_RETURN_ADDRESS, "__builtin_return_address", NULL,
build_function_type_list(ptr_type_node,
unsigned_type_node,
NULL_TREE),
false);
// The compiler uses __builtin_trap for some exception handling
// cases.
define_builtin(BUILT_IN_TRAP, "__builtin_trap", NULL,
build_function_type(void_type_node, void_list_node),
false);
}
// Get the name to use for the import control function. If there is a
// global function or variable, then we know that that name must be
// unique in the link, and we use it as the basis for our name.
const std::string&
Gogo::get_init_fn_name()
{
if (this->init_fn_name_.empty())
{
gcc_assert(this->package_ != NULL);
if (this->is_main_package())
{
// Use a name which the runtime knows.
this->init_fn_name_ = "__go_init_main";
}
else
{
std::string s = this->unique_prefix();
s.append(1, '.');
s.append(this->package_name());
s.append("..import");
this->init_fn_name_ = s;
}
}
return this->init_fn_name_;
}
// Add statements to INIT_STMT_LIST which run the initialization
// functions for imported packages. This is only used for the "main"
// package.
void
Gogo::init_imports(tree* init_stmt_list)
{
gcc_assert(this->is_main_package());
if (this->imported_init_fns_.empty())
return;
tree fntype = build_function_type(void_type_node, void_list_node);
// We must call them in increasing priority order.
std::vector<Import_init> v;
for (std::set<Import_init>::const_iterator p =
this->imported_init_fns_.begin();
p != this->imported_init_fns_.end();
++p)
v.push_back(*p);
std::sort(v.begin(), v.end());
for (std::vector<Import_init>::const_iterator p = v.begin();
p != v.end();
++p)
{
std::string user_name = p->package_name() + ".init";
tree decl = build_decl(UNKNOWN_LOCATION, FUNCTION_DECL,
get_identifier_from_string(user_name),
fntype);
const std::string& init_name(p->init_name());
SET_DECL_ASSEMBLER_NAME(decl, get_identifier_from_string(init_name));
TREE_PUBLIC(decl) = 1;
DECL_EXTERNAL(decl) = 1;
append_to_statement_list(build_call_expr(decl, 0), init_stmt_list);
}
}
// Register global variables with the garbage collector. We need to
// register all variables which can hold a pointer value. They become
// roots during the mark phase. We build a struct that is easy to
// hook into a list of roots.
// struct __go_gc_root_list
// {
// struct __go_gc_root_list* __next;
// struct __go_gc_root
// {
// void* __decl;
// size_t __size;
// } __roots[];
// };
// The last entry in the roots array has a NULL decl field.
void
Gogo::register_gc_vars(const std::vector<Named_object*>& var_gc,
tree* init_stmt_list)
{
if (var_gc.empty())
return;
size_t count = var_gc.size();
tree root_type = Gogo::builtin_struct(NULL, "__go_gc_root", NULL_TREE, 2,
"__next",
ptr_type_node,
"__size",
sizetype);
tree index_type = build_index_type(size_int(count));
tree array_type = build_array_type(root_type, index_type);
tree root_list_type = make_node(RECORD_TYPE);
root_list_type = Gogo::builtin_struct(NULL, "__go_gc_root_list",
root_list_type, 2,
"__next",
build_pointer_type(root_list_type),
"__roots",
array_type);
// Build an initialier for the __roots array.
VEC(constructor_elt,gc)* roots_init = VEC_alloc(constructor_elt, gc,
count + 1);
size_t i = 0;
for (std::vector<Named_object*>::const_iterator p = var_gc.begin();
p != var_gc.end();
++p, ++i)
{
VEC(constructor_elt,gc)* init = VEC_alloc(constructor_elt, gc, 2);
constructor_elt* elt = VEC_quick_push(constructor_elt, init, NULL);
tree field = TYPE_FIELDS(root_type);
elt->index = field;
tree decl = (*p)->get_tree(this, NULL);
gcc_assert(TREE_CODE(decl) == VAR_DECL);
elt->value = build_fold_addr_expr(decl);
elt = VEC_quick_push(constructor_elt, init, NULL);
field = DECL_CHAIN(field);
elt->index = field;
elt->value = DECL_SIZE_UNIT(decl);
elt = VEC_quick_push(constructor_elt, roots_init, NULL);
elt->index = size_int(i);
elt->value = build_constructor(root_type, init);
}
// The list ends with a NULL entry.
VEC(constructor_elt,gc)* init = VEC_alloc(constructor_elt, gc, 2);
constructor_elt* elt = VEC_quick_push(constructor_elt, init, NULL);
tree field = TYPE_FIELDS(root_type);
elt->index = field;
elt->value = fold_convert(TREE_TYPE(field), null_pointer_node);
elt = VEC_quick_push(constructor_elt, init, NULL);
field = DECL_CHAIN(field);
elt->index = field;
elt->value = size_zero_node;
elt = VEC_quick_push(constructor_elt, roots_init, NULL);
elt->index = size_int(i);
elt->value = build_constructor(root_type, init);
// Build a constructor for the struct.
VEC(constructor_elt,gc*) root_list_init = VEC_alloc(constructor_elt, gc, 2);
elt = VEC_quick_push(constructor_elt, root_list_init, NULL);
field = TYPE_FIELDS(root_list_type);
elt->index = field;
elt->value = fold_convert(TREE_TYPE(field), null_pointer_node);
elt = VEC_quick_push(constructor_elt, root_list_init, NULL);
field = DECL_CHAIN(field);
elt->index = field;
elt->value = build_constructor(array_type, roots_init);
// Build a decl to register.
tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL,
create_tmp_var_name("gc"), root_list_type);
DECL_EXTERNAL(decl) = 0;
TREE_PUBLIC(decl) = 0;
TREE_STATIC(decl) = 1;
DECL_ARTIFICIAL(decl) = 1;
DECL_INITIAL(decl) = build_constructor(root_list_type, root_list_init);
rest_of_decl_compilation(decl, 1, 0);
static tree register_gc_fndecl;
tree call = Gogo::call_builtin(&register_gc_fndecl, BUILTINS_LOCATION,
"__go_register_gc_roots",
1,
void_type_node,
build_pointer_type(root_list_type),
build_fold_addr_expr(decl));
if (call != error_mark_node)
append_to_statement_list(call, init_stmt_list);
}
// Build the decl for the initialization function.
tree
Gogo::initialization_function_decl()
{
// The tedious details of building your own function. There doesn't
// seem to be a helper function for this.
std::string name = this->package_name() + ".init";
tree fndecl = build_decl(BUILTINS_LOCATION, FUNCTION_DECL,
get_identifier_from_string(name),
build_function_type(void_type_node,
void_list_node));
const std::string& asm_name(this->get_init_fn_name());
SET_DECL_ASSEMBLER_NAME(fndecl, get_identifier_from_string(asm_name));
tree resdecl = build_decl(BUILTINS_LOCATION, RESULT_DECL, NULL_TREE,
void_type_node);
DECL_ARTIFICIAL(resdecl) = 1;
DECL_CONTEXT(resdecl) = fndecl;
DECL_RESULT(fndecl) = resdecl;
TREE_STATIC(fndecl) = 1;
TREE_USED(fndecl) = 1;
DECL_ARTIFICIAL(fndecl) = 1;
TREE_PUBLIC(fndecl) = 1;
DECL_INITIAL(fndecl) = make_node(BLOCK);
TREE_USED(DECL_INITIAL(fndecl)) = 1;
return fndecl;
}
// Create the magic initialization function. INIT_STMT_LIST is the
// code that it needs to run.
void
Gogo::write_initialization_function(tree fndecl, tree init_stmt_list)
{
// Make sure that we thought we needed an initialization function,
// as otherwise we will not have reported it in the export data.
gcc_assert(this->is_main_package() || this->need_init_fn_);
if (fndecl == NULL_TREE)
fndecl = this->initialization_function_decl();
DECL_SAVED_TREE(fndecl) = init_stmt_list;
current_function_decl = fndecl;
if (DECL_STRUCT_FUNCTION(fndecl) == NULL)
push_struct_function(fndecl);
else
push_cfun(DECL_STRUCT_FUNCTION(fndecl));
cfun->function_end_locus = BUILTINS_LOCATION;
gimplify_function_tree(fndecl);
cgraph_add_new_function(fndecl, false);
cgraph_mark_needed_node(cgraph_node(fndecl));
current_function_decl = NULL_TREE;
pop_cfun();
}
// Search for references to VAR in any statements or called functions.
class Find_var : public Traverse
{
public:
// A hash table we use to avoid looping. The index is the name of a
// named object. We only look through objects defined in this
// package.
typedef Unordered_set(std::string) Seen_objects;
Find_var(Named_object* var, Seen_objects* seen_objects)
: Traverse(traverse_expressions),
var_(var), seen_objects_(seen_objects), found_(false)
{ }
// Whether the variable was found.
bool
found() const
{ return this->found_; }
int
expression(Expression**);
private:
// The variable we are looking for.
Named_object* var_;
// Names of objects we have already seen.
Seen_objects* seen_objects_;
// True if the variable was found.
bool found_;
};
// See if EXPR refers to VAR, looking through function calls and
// variable initializations.
int
Find_var::expression(Expression** pexpr)
{
Expression* e = *pexpr;
Var_expression* ve = e->var_expression();
if (ve != NULL)
{
Named_object* v = ve->named_object();
if (v == this->var_)
{
this->found_ = true;
return TRAVERSE_EXIT;
}
if (v->is_variable() && v->package() == NULL)
{
Expression* init = v->var_value()->init();
if (init != NULL)
{
std::pair<Seen_objects::iterator, bool> ins =
this->seen_objects_->insert(v->name());
if (ins.second)
{
// This is the first time we have seen this name.
if (Expression::traverse(&init, this) == TRAVERSE_EXIT)
return TRAVERSE_EXIT;
}
}
}
}
// We traverse the code of any function we see. Note that this
// means that we will traverse the code of a function whose address
// is taken even if it is not called.
Func_expression* fe = e->func_expression();
if (fe != NULL)
{
const Named_object* f = fe->named_object();
if (f->is_function() && f->package() == NULL)
{
std::pair<Seen_objects::iterator, bool> ins =
this->seen_objects_->insert(f->name());
if (ins.second)
{
// This is the first time we have seen this name.
if (f->func_value()->block()->traverse(this) == TRAVERSE_EXIT)
return TRAVERSE_EXIT;
}
}
}
return TRAVERSE_CONTINUE;
}
// Return true if EXPR refers to VAR.
static bool
expression_requires(Expression* expr, Block* preinit, Named_object* var)
{
Find_var::Seen_objects seen_objects;
Find_var find_var(var, &seen_objects);
if (expr != NULL)
Expression::traverse(&expr, &find_var);
if (preinit != NULL)
preinit->traverse(&find_var);
return find_var.found();
}
// Sort variable initializations. If the initialization expression
// for variable A refers directly or indirectly to the initialization
// expression for variable B, then we must initialize B before A.
class Var_init
{
public:
Var_init()
: var_(NULL), init_(NULL_TREE), waiting_(0)
{ }
Var_init(Named_object* var, tree init)
: var_(var), init_(init), waiting_(0)
{ }
// Return the variable.
Named_object*
var() const
{ return this->var_; }
// Return the initialization expression.
tree
init() const
{ return this->init_; }
// Return the number of variables waiting for this one to be
// initialized.
size_t
waiting() const
{ return this->waiting_; }
// Increment the number waiting.
void
increment_waiting()
{ ++this->waiting_; }
private:
// The variable being initialized.
Named_object* var_;
// The initialization expression to run.
tree init_;
// The number of variables which are waiting for this one.
size_t waiting_;
};
typedef std::list<Var_init> Var_inits;
// Sort the variable initializations. The rule we follow is that we
// emit them in the order they appear in the array, except that if the
// initialization expression for a variable V1 depends upon another
// variable V2 then we initialize V1 after V2.
static void
sort_var_inits(Var_inits* var_inits)
{
Var_inits ready;
while (!var_inits->empty())
{
Var_inits::iterator p1 = var_inits->begin();
Named_object* var = p1->var();
Expression* init = var->var_value()->init();
Block* preinit = var->var_value()->preinit();
// Start walking through the list to see which variables VAR
// needs to wait for. We can skip P1->WAITING variables--that
// is the number we've already checked.
Var_inits::iterator p2 = p1;
++p2;
for (size_t i = p1->waiting(); i > 0; --i)
++p2;
for (; p2 != var_inits->end(); ++p2)
{
if (expression_requires(init, preinit, p2->var()))
{
// Check for cycles.
if (expression_requires(p2->var()->var_value()->init(),
p2->var()->var_value()->preinit(),
var))
{
error_at(var->location(),
("initialization expressions for %qs and "
"%qs depend upon each other"),
var->message_name().c_str(),
p2->var()->message_name().c_str());
inform(p2->var()->location(), "%qs defined here",
p2->var()->message_name().c_str());
p2 = var_inits->end();
}
else
{
// We can't emit P1 until P2 is emitted. Move P1.
// Note that the WAITING loop always executes at
// least once, which is what we want.
p2->increment_waiting();
Var_inits::iterator p3 = p2;
for (size_t i = p2->waiting(); i > 0; --i)
++p3;
var_inits->splice(p3, *var_inits, p1);
}
break;
}
}
if (p2 == var_inits->end())
{
// VAR does not depends upon any other initialization expressions.
// Check for a loop of VAR on itself. We only do this if
// INIT is not NULL; when INIT is NULL, it means that
// PREINIT sets VAR, which we will interpret as a loop.
if (init != NULL && expression_requires(init, preinit, var))
error_at(var->location(),
"initialization expression for %qs depends upon itself",
var->message_name().c_str());
ready.splice(ready.end(), *var_inits, p1);
}
}
// Now READY is the list in the desired initialization order.
var_inits->swap(ready);
}
// Write out the global definitions.
void
Gogo::write_globals()
{
this->convert_named_types();
this->build_interface_method_tables();
Bindings* bindings = this->current_bindings();
size_t count = bindings->size_definitions();
tree* vec = new tree[count];
tree init_fndecl = NULL_TREE;
tree init_stmt_list = NULL_TREE;
if (this->is_main_package())
this->init_imports(&init_stmt_list);
// A list of variable initializations.
Var_inits var_inits;
// A list of variables which need to be registered with the garbage
// collector.
std::vector<Named_object*> var_gc;
var_gc.reserve(count);
tree var_init_stmt_list = NULL_TREE;
size_t i = 0;
for (Bindings::const_definitions_iterator p = bindings->begin_definitions();
p != bindings->end_definitions();
++p, ++i)
{
Named_object* no = *p;
gcc_assert(!no->is_type_declaration() && !no->is_function_declaration());
// There is nothing to do for a package.
if (no->is_package())
{
--i;
--count;
continue;
}
// There is nothing to do for an object which was imported from
// a different package into the global scope.
if (no->package() != NULL)
{
--i;
--count;
continue;
}
// There is nothing useful we can output for constants which
// have ideal or non-integeral type.
if (no->is_const())
{
Type* type = no->const_value()->type();
if (type == NULL)
type = no->const_value()->expr()->type();
if (type->is_abstract() || type->integer_type() == NULL)
{
--i;
--count;
continue;
}
}
vec[i] = no->get_tree(this, NULL);
if (vec[i] == error_mark_node)
{
gcc_assert(saw_errors());
--i;
--count;
continue;
}
// If a variable is initialized to a non-constant value, do the
// initialization in an initialization function.
if (TREE_CODE(vec[i]) == VAR_DECL)
{
gcc_assert(no->is_variable());
// Check for a sink variable, which may be used to run
// an initializer purely for its side effects.
bool is_sink = no->name()[0] == '_' && no->name()[1] == '.';
tree var_init_tree = NULL_TREE;
if (!no->var_value()->has_pre_init())
{
tree init = no->var_value()->get_init_tree(this, NULL);
if (init == error_mark_node)
gcc_assert(saw_errors());
else if (init == NULL_TREE)
;
else if (TREE_CONSTANT(init))
DECL_INITIAL(vec[i]) = init;
else if (is_sink)
var_init_tree = init;
else
var_init_tree = fold_build2_loc(no->location(), MODIFY_EXPR,
void_type_node, vec[i], init);
}
else
{
// We are going to create temporary variables which
// means that we need an fndecl.
if (init_fndecl == NULL_TREE)
init_fndecl = this->initialization_function_decl();
current_function_decl = init_fndecl;
if (DECL_STRUCT_FUNCTION(init_fndecl) == NULL)
push_struct_function(init_fndecl);
else
push_cfun(DECL_STRUCT_FUNCTION(init_fndecl));
tree var_decl = is_sink ? NULL_TREE : vec[i];
var_init_tree = no->var_value()->get_init_block(this, NULL,
var_decl);
current_function_decl = NULL_TREE;
pop_cfun();
}
if (var_init_tree != NULL_TREE && var_init_tree != error_mark_node)
{
if (no->var_value()->init() == NULL
&& !no->var_value()->has_pre_init())
append_to_statement_list(var_init_tree, &var_init_stmt_list);
else
var_inits.push_back(Var_init(no, var_init_tree));
}
if (!is_sink && no->var_value()->type()->has_pointer())
var_gc.push_back(no);
}
}
// Register global variables with the garbage collector.
this->register_gc_vars(var_gc, &init_stmt_list);
// Simple variable initializations, after all variables are
// registered.
append_to_statement_list(var_init_stmt_list, &init_stmt_list);
// Complex variable initializations, first sorting them into a
// workable order.
if (!var_inits.empty())
{
sort_var_inits(&var_inits);
for (Var_inits::const_iterator p = var_inits.begin();
p != var_inits.end();
++p)
append_to_statement_list(p->init(), &init_stmt_list);
}
// After all the variables are initialized, call the "init"
// functions if there are any.
for (std::vector<Named_object*>::const_iterator p =
this->init_functions_.begin();
p != this->init_functions_.end();
++p)
{
tree decl = (*p)->get_tree(this, NULL);
tree call = build_call_expr(decl, 0);
append_to_statement_list(call, &init_stmt_list);
}
// Set up a magic function to do all the initialization actions.
// This will be called if this package is imported.
if (init_stmt_list != NULL_TREE
|| this->need_init_fn_
|| this->is_main_package())
this->write_initialization_function(init_fndecl, init_stmt_list);
// Pass everything back to the middle-end.
wrapup_global_declarations(vec, count);
cgraph_finalize_compilation_unit();
check_global_declarations(vec, count);
emit_debug_global_declarations(vec, count);
delete[] vec;
}
// Get a tree for the identifier for a named object.
tree
Named_object::get_id(Gogo* gogo)
{
std::string decl_name;
if (this->is_function_declaration()
&& !this->func_declaration_value()->asm_name().empty())
decl_name = this->func_declaration_value()->asm_name();
else if ((this->is_variable() && !this->var_value()->is_global())
|| (this->is_type()
&& this->type_value()->location() == BUILTINS_LOCATION))
{
// We don't need the package name for local variables or builtin
// types.
decl_name = Gogo::unpack_hidden_name(this->name_);
}
else
{
std::string package_name;
if (this->package_ == NULL)
package_name = gogo->package_name();
else
package_name = this->package_->name();
decl_name = package_name + '.' + Gogo::unpack_hidden_name(this->name_);
Function_type* fntype;
if (this->is_function())
fntype = this->func_value()->type();
else if (this->is_function_declaration())
fntype = this->func_declaration_value()->type();
else
fntype = NULL;
if (fntype != NULL && fntype->is_method())
{
decl_name.push_back('.');
decl_name.append(fntype->receiver()->type()->mangled_name(gogo));
}
}
if (this->is_type())
{
const Named_object* in_function = this->type_value()->in_function();
if (in_function != NULL)
decl_name += '$' + in_function->name();
}
return get_identifier_from_string(decl_name);
}
// Get a tree for a named object.
tree
Named_object::get_tree(Gogo* gogo, Named_object* function)
{
if (this->tree_ != NULL_TREE)
{
// If this is a variable whose address is taken, we must rebuild
// the INDIRECT_REF each time to avoid invalid sharing.
tree ret = this->tree_;
if (((this->classification_ == NAMED_OBJECT_VAR
&& this->var_value()->is_in_heap())
|| (this->classification_ == NAMED_OBJECT_RESULT_VAR
&& this->result_var_value()->is_in_heap()))
&& ret != error_mark_node)
{
gcc_assert(TREE_CODE(ret) == INDIRECT_REF);
ret = build_fold_indirect_ref(TREE_OPERAND(ret, 0));
TREE_THIS_NOTRAP(ret) = 1;
}
return ret;
}
tree name;
if (this->classification_ == NAMED_OBJECT_TYPE)
name = NULL_TREE;
else
name = this->get_id(gogo);
tree decl;
switch (this->classification_)
{
case NAMED_OBJECT_CONST:
{
Named_constant* named_constant = this->u_.const_value;
Translate_context subcontext(gogo, function, NULL, NULL_TREE);
tree expr_tree = named_constant->expr()->get_tree(&subcontext);
if (expr_tree == error_mark_node)
decl = error_mark_node;
else
{
Type* type = named_constant->type();
if (type != NULL && !type->is_abstract())
{
if (!type->is_undefined())
expr_tree = fold_convert(type->get_tree(gogo), expr_tree);
else
{
// Make sure we report the error.
type->base();
expr_tree = error_mark_node;
}
}
if (expr_tree == error_mark_node)
decl = error_mark_node;
else if (INTEGRAL_TYPE_P(TREE_TYPE(expr_tree)))
{
decl = build_decl(named_constant->location(), CONST_DECL,
name, TREE_TYPE(expr_tree));
DECL_INITIAL(decl) = expr_tree;
TREE_CONSTANT(decl) = 1;
TREE_READONLY(decl) = 1;
}
else
{
// A CONST_DECL is only for an enum constant, so we
// shouldn't use for non-integral types. Instead we
// just return the constant itself, rather than a
// decl.
decl = expr_tree;
}
}
}
break;
case NAMED_OBJECT_TYPE:
{
Named_type* named_type = this->u_.type_value;
tree type_tree = named_type->get_tree(gogo);
if (type_tree == error_mark_node)
decl = error_mark_node;
else
{
decl = TYPE_NAME(type_tree);
gcc_assert(decl != NULL_TREE);
// We need to produce a type descriptor for every named
// type, and for a pointer to every named type, since
// other files or packages might refer to them. We need
// to do this even for hidden types, because they might
// still be returned by some function. Simply calling the
// type_descriptor method is enough to create the type
// descriptor, even though we don't do anything with it.
if (this->package_ == NULL)
{
named_type->type_descriptor_pointer(gogo);
Type* pn = Type::make_pointer_type(named_type);
pn->type_descriptor_pointer(gogo);
}
}
}
break;
case NAMED_OBJECT_TYPE_DECLARATION:
error("reference to undefined type %qs",
this->message_name().c_str());
return error_mark_node;
case NAMED_OBJECT_VAR:
{
Variable* var = this->u_.var_value;
Type* type = var->type();
if (type->is_error_type()
|| (type->is_undefined()
&& (!var->is_global() || this->package() == NULL)))
{
// Force the error for an undefined type, just in case.
type->base();
decl = error_mark_node;
}
else
{
tree var_type = type->get_tree(gogo);
bool is_parameter = var->is_parameter();
if (var->is_receiver() && type->points_to() == NULL)
is_parameter = false;
if (var->is_in_heap())
{
is_parameter = false;
var_type = build_pointer_type(var_type);
}
decl = build_decl(var->location(),
is_parameter ? PARM_DECL : VAR_DECL,
name, var_type);
if (!var->is_global())
{
tree fnid = function->get_id(gogo);
tree fndecl = function->func_value()->get_or_make_decl(gogo,
function,
fnid);
DECL_CONTEXT(decl) = fndecl;
}
if (is_parameter)
DECL_ARG_TYPE(decl) = TREE_TYPE(decl);
if (var->is_global())
{
const Package* package = this->package();
if (package == NULL)
TREE_STATIC(decl) = 1;
else
DECL_EXTERNAL(decl) = 1;
if (!Gogo::is_hidden_name(this->name_))
{
TREE_PUBLIC(decl) = 1;
std::string asm_name = (package == NULL
? gogo->unique_prefix()
: package->unique_prefix());
asm_name.append(1, '.');
asm_name.append(IDENTIFIER_POINTER(name),
IDENTIFIER_LENGTH(name));
tree asm_id = get_identifier_from_string(asm_name);
SET_DECL_ASSEMBLER_NAME(decl, asm_id);
}
}
// FIXME: We should only set this for variables which are
// actually used somewhere.
TREE_USED(decl) = 1;
}
}
break;
case NAMED_OBJECT_RESULT_VAR:
{
Result_variable* result = this->u_.result_var_value;
Type* type = result->type();
if (type->is_error_type() || type->is_undefined())
{
// Force the error.
type->base();
decl = error_mark_node;
}
else
{
gcc_assert(result->function() == function->func_value());
source_location loc = function->location();
tree result_type = type->get_tree(gogo);
tree init;
if (!result->is_in_heap())
init = type->get_init_tree(gogo, false);
else
{
tree space = gogo->allocate_memory(type,
TYPE_SIZE_UNIT(result_type),
loc);
result_type = build_pointer_type(result_type);
tree subinit = type->get_init_tree(gogo, true);
if (subinit == NULL_TREE)
init = fold_convert_loc(loc, result_type, space);
else
{
space = save_expr(space);
space = fold_convert_loc(loc, result_type, space);
tree spaceref = build_fold_indirect_ref_loc(loc, space);
TREE_THIS_NOTRAP(spaceref) = 1;
tree set = fold_build2_loc(loc, MODIFY_EXPR, void_type_node,
spaceref, subinit);
init = fold_build2_loc(loc, COMPOUND_EXPR, TREE_TYPE(space),
set, space);
}
}
decl = build_decl(loc, VAR_DECL, name, result_type);
tree fnid = function->get_id(gogo);
tree fndecl = function->func_value()->get_or_make_decl(gogo,
function,
fnid);
DECL_CONTEXT(decl) = fndecl;
DECL_INITIAL(decl) = init;
TREE_USED(decl) = 1;
}
}
break;
case NAMED_OBJECT_SINK:
gcc_unreachable();
case NAMED_OBJECT_FUNC:
{
Function* func = this->u_.func_value;
decl = func->get_or_make_decl(gogo, this, name);
if (decl != error_mark_node)
{
if (func->block() != NULL)
{
if (DECL_STRUCT_FUNCTION(decl) == NULL)
push_struct_function(decl);
else
push_cfun(DECL_STRUCT_FUNCTION(decl));
cfun->function_end_locus = func->block()->end_location();
current_function_decl = decl;
func->build_tree(gogo, this);
gimplify_function_tree(decl);
cgraph_finalize_function(decl, true);
current_function_decl = NULL_TREE;
pop_cfun();
}
}
}
break;
default:
gcc_unreachable();
}
if (TREE_TYPE(decl) == error_mark_node)
decl = error_mark_node;
tree ret = decl;
// If this is a local variable whose address is taken, then we
// actually store it in the heap. For uses of the variable we need
// to return a reference to that heap location.
if (((this->classification_ == NAMED_OBJECT_VAR
&& this->var_value()->is_in_heap())
|| (this->classification_ == NAMED_OBJECT_RESULT_VAR
&& this->result_var_value()->is_in_heap()))
&& ret != error_mark_node)
{
gcc_assert(POINTER_TYPE_P(TREE_TYPE(ret)));
ret = build_fold_indirect_ref(ret);
TREE_THIS_NOTRAP(ret) = 1;
}
this->tree_ = ret;
if (ret != error_mark_node)
go_preserve_from_gc(ret);
return ret;
}
// Get the initial value of a variable as a tree. This does not
// consider whether the variable is in the heap--it returns the
// initial value as though it were always stored in the stack.
tree
Variable::get_init_tree(Gogo* gogo, Named_object* function)
{
gcc_assert(this->preinit_ == NULL);
if (this->init_ == NULL)
{
gcc_assert(!this->is_parameter_);
return this->type_->get_init_tree(gogo, this->is_global_);
}
else
{
Translate_context context(gogo, function, NULL, NULL_TREE);
tree rhs_tree = this->init_->get_tree(&context);
return Expression::convert_for_assignment(&context, this->type(),
this->init_->type(),
rhs_tree, this->location());
}
}
// Get the initial value of a variable when a block is required.
// VAR_DECL is the decl to set; it may be NULL for a sink variable.
tree
Variable::get_init_block(Gogo* gogo, Named_object* function, tree var_decl)
{
gcc_assert(this->preinit_ != NULL);
// We want to add the variable assignment to the end of the preinit
// block. The preinit block may have a TRY_FINALLY_EXPR and a
// TRY_CATCH_EXPR; if it does, we want to add to the end of the
// regular statements.
Translate_context context(gogo, function, NULL, NULL_TREE);
tree block_tree = this->preinit_->get_tree(&context);
if (block_tree == error_mark_node)
return error_mark_node;
gcc_assert(TREE_CODE(block_tree) == BIND_EXPR);
tree statements = BIND_EXPR_BODY(block_tree);
while (statements != NULL_TREE
&& (TREE_CODE(statements) == TRY_FINALLY_EXPR
|| TREE_CODE(statements) == TRY_CATCH_EXPR))
statements = TREE_OPERAND(statements, 0);
// It's possible to have pre-init statements without an initializer
// if the pre-init statements set the variable.
if (this->init_ != NULL)
{
tree rhs_tree = this->init_->get_tree(&context);
if (rhs_tree == error_mark_node)
return error_mark_node;
if (var_decl == NULL_TREE)
append_to_statement_list(rhs_tree, &statements);
else
{
tree val = Expression::convert_for_assignment(&context, this->type(),
this->init_->type(),
rhs_tree,
this->location());
if (val == error_mark_node)
return error_mark_node;
tree set = fold_build2_loc(this->location(), MODIFY_EXPR,
void_type_node, var_decl, val);
append_to_statement_list(set, &statements);
}
}
return block_tree;
}
// Get a tree for a function decl.
tree
Function::get_or_make_decl(Gogo* gogo, Named_object* no, tree id)
{
if (this->fndecl_ == NULL_TREE)
{
tree functype = this->type_->get_tree(gogo);
if (functype == error_mark_node)
this->fndecl_ = error_mark_node;
else
{
// The type of a function comes back as a pointer, but we
// want the real function type for a function declaration.
gcc_assert(POINTER_TYPE_P(functype));
functype = TREE_TYPE(functype);
tree decl = build_decl(this->location(), FUNCTION_DECL, id, functype);
this->fndecl_ = decl;
if (no->package() != NULL)
;
else if (this->enclosing_ != NULL || Gogo::is_thunk(no))
;
else if (Gogo::unpack_hidden_name(no->name()) == "init"
&& !this->type_->is_method())
;
else if (Gogo::unpack_hidden_name(no->name()) == "main"
&& gogo->is_main_package())
TREE_PUBLIC(decl) = 1;
// Methods have to be public even if they are hidden because
// they can be pulled into type descriptors when using
// anonymous fields.
else if (!Gogo::is_hidden_name(no->name())
|| this->type_->is_method())
{
TREE_PUBLIC(decl) = 1;
std::string asm_name = gogo->unique_prefix();
asm_name.append(1, '.');
asm_name.append(IDENTIFIER_POINTER(id), IDENTIFIER_LENGTH(id));
SET_DECL_ASSEMBLER_NAME(decl,
get_identifier_from_string(asm_name));
}
// Why do we have to do this in the frontend?
tree restype = TREE_TYPE(functype);
tree resdecl = build_decl(this->location(), RESULT_DECL, NULL_TREE,
restype);
DECL_ARTIFICIAL(resdecl) = 1;
DECL_IGNORED_P(resdecl) = 1;
DECL_CONTEXT(resdecl) = decl;
DECL_RESULT(decl) = resdecl;
if (this->enclosing_ != NULL)
DECL_STATIC_CHAIN(decl) = 1;
// If a function calls the predeclared recover function, we
// can't inline it, because recover behaves differently in a
// function passed directly to defer.
if (this->calls_recover_ && !this->is_recover_thunk_)
DECL_UNINLINABLE(decl) = 1;
// If this is a thunk created to call a function which calls
// the predeclared recover function, we need to disable
// stack splitting for the thunk.
if (this->is_recover_thunk_)
{
tree attr = get_identifier("__no_split_stack__");
DECL_ATTRIBUTES(decl) = tree_cons(attr, NULL_TREE, NULL_TREE);
}
go_preserve_from_gc(decl);
if (this->closure_var_ != NULL)
{
push_struct_function(decl);
tree closure_decl = this->closure_var_->get_tree(gogo, no);
if (closure_decl == error_mark_node)
this->fndecl_ = error_mark_node;
else
{
DECL_ARTIFICIAL(closure_decl) = 1;
DECL_IGNORED_P(closure_decl) = 1;
TREE_USED(closure_decl) = 1;
DECL_ARG_TYPE(closure_decl) = TREE_TYPE(closure_decl);
TREE_READONLY(closure_decl) = 1;
DECL_STRUCT_FUNCTION(decl)->static_chain_decl = closure_decl;
}
pop_cfun();
}
}
}
return this->fndecl_;
}
// Get a tree for a function declaration.
tree
Function_declaration::get_or_make_decl(Gogo* gogo, Named_object* no, tree id)
{
if (this->fndecl_ == NULL_TREE)
{
// Let Go code use an asm declaration to pick up a builtin
// function.
if (!this->asm_name_.empty())
{
std::map<std::string, tree>::const_iterator p =
builtin_functions.find(this->asm_name_);
if (p != builtin_functions.end())
{
this->fndecl_ = p->second;
return this->fndecl_;
}
}
tree functype = this->fntype_->get_tree(gogo);
tree decl;
if (functype == error_mark_node)
decl = error_mark_node;
else
{
// The type of a function comes back as a pointer, but we
// want the real function type for a function declaration.
gcc_assert(POINTER_TYPE_P(functype));
functype = TREE_TYPE(functype);
decl = build_decl(this->location(), FUNCTION_DECL, id, functype);
TREE_PUBLIC(decl) = 1;
DECL_EXTERNAL(decl) = 1;
if (this->asm_name_.empty())
{
std::string asm_name = (no->package() == NULL
? gogo->unique_prefix()
: no->package()->unique_prefix());
asm_name.append(1, '.');
asm_name.append(IDENTIFIER_POINTER(id), IDENTIFIER_LENGTH(id));
SET_DECL_ASSEMBLER_NAME(decl,
get_identifier_from_string(asm_name));
}
}
this->fndecl_ = decl;
go_preserve_from_gc(decl);
}
return this->fndecl_;
}
// We always pass the receiver to a method as a pointer. If the
// receiver is actually declared as a non-pointer type, then we copy
// the value into a local variable, so that it has the right type. In
// this function we create the real PARM_DECL to use, and set
// DEC_INITIAL of the var_decl to be the value passed in.
tree
Function::make_receiver_parm_decl(Gogo* gogo, Named_object* no, tree var_decl)
{
if (var_decl == error_mark_node)
return error_mark_node;
// If the function takes the address of a receiver which is passed
// by value, then we will have an INDIRECT_REF here. We need to get
// the real variable.
bool is_in_heap = no->var_value()->is_in_heap();
tree val_type;
if (TREE_CODE(var_decl) != INDIRECT_REF)
{
gcc_assert(!is_in_heap);
val_type = TREE_TYPE(var_decl);
}
else
{
gcc_assert(is_in_heap);
var_decl = TREE_OPERAND(var_decl, 0);
if (var_decl == error_mark_node)
return error_mark_node;
gcc_assert(POINTER_TYPE_P(TREE_TYPE(var_decl)));
val_type = TREE_TYPE(TREE_TYPE(var_decl));
}
gcc_assert(TREE_CODE(var_decl) == VAR_DECL);
source_location loc = DECL_SOURCE_LOCATION(var_decl);
std::string name = IDENTIFIER_POINTER(DECL_NAME(var_decl));
name += ".pointer";
tree id = get_identifier_from_string(name);
tree parm_decl = build_decl(loc, PARM_DECL, id, build_pointer_type(val_type));
DECL_CONTEXT(parm_decl) = current_function_decl;
DECL_ARG_TYPE(parm_decl) = TREE_TYPE(parm_decl);
gcc_assert(DECL_INITIAL(var_decl) == NULL_TREE);
// The receiver might be passed as a null pointer.
tree check = fold_build2_loc(loc, NE_EXPR, boolean_type_node, parm_decl,
fold_convert_loc(loc, TREE_TYPE(parm_decl),
null_pointer_node));
tree ind = build_fold_indirect_ref_loc(loc, parm_decl);
TREE_THIS_NOTRAP(ind) = 1;
tree zero_init = no->var_value()->type()->get_init_tree(gogo, false);
tree init = fold_build3_loc(loc, COND_EXPR, TREE_TYPE(ind),
check, ind, zero_init);
if (is_in_heap)
{
tree size = TYPE_SIZE_UNIT(val_type);
tree space = gogo->allocate_memory(no->var_value()->type(), size,
no->location());
space = save_expr(space);
space = fold_convert(build_pointer_type(val_type), space);
tree spaceref = build_fold_indirect_ref_loc(no->location(), space);
TREE_THIS_NOTRAP(spaceref) = 1;
tree check = fold_build2_loc(loc, NE_EXPR, boolean_type_node,
parm_decl,
fold_convert_loc(loc, TREE_TYPE(parm_decl),
null_pointer_node));
tree parmref = build_fold_indirect_ref_loc(no->location(), parm_decl);
TREE_THIS_NOTRAP(parmref) = 1;
tree set = fold_build2_loc(loc, MODIFY_EXPR, void_type_node,
spaceref, parmref);
init = fold_build2_loc(loc, COMPOUND_EXPR, TREE_TYPE(space),
build3(COND_EXPR, void_type_node,
check, set, NULL_TREE),
space);
}
DECL_INITIAL(var_decl) = init;
return parm_decl;
}
// If we take the address of a parameter, then we need to copy it into
// the heap. We will access it as a local variable via an
// indirection.
tree
Function::copy_parm_to_heap(Gogo* gogo, Named_object* no, tree ref)
{
if (ref == error_mark_node)
return error_mark_node;
gcc_assert(TREE_CODE(ref) == INDIRECT_REF);
tree var_decl = TREE_OPERAND(ref, 0);
if (var_decl == error_mark_node)
return error_mark_node;
gcc_assert(TREE_CODE(var_decl) == VAR_DECL);
source_location loc = DECL_SOURCE_LOCATION(var_decl);
std::string name = IDENTIFIER_POINTER(DECL_NAME(var_decl));
name += ".param";
tree id = get_identifier_from_string(name);
tree type = TREE_TYPE(var_decl);
gcc_assert(POINTER_TYPE_P(type));
type = TREE_TYPE(type);
tree parm_decl = build_decl(loc, PARM_DECL, id, type);
DECL_CONTEXT(parm_decl) = current_function_decl;
DECL_ARG_TYPE(parm_decl) = type;
tree size = TYPE_SIZE_UNIT(type);
tree space = gogo->allocate_memory(no->var_value()->type(), size, loc);
space = save_expr(space);
space = fold_convert(TREE_TYPE(var_decl), space);
tree spaceref = build_fold_indirect_ref_loc(loc, space);
TREE_THIS_NOTRAP(spaceref) = 1;
tree init = build2(COMPOUND_EXPR, TREE_TYPE(space),
build2(MODIFY_EXPR, void_type_node, spaceref, parm_decl),
space);
DECL_INITIAL(var_decl) = init;
return parm_decl;
}
// Get a tree for function code.
void
Function::build_tree(Gogo* gogo, Named_object* named_function)
{
tree fndecl = this->fndecl_;
gcc_assert(fndecl != NULL_TREE);
tree params = NULL_TREE;
tree* pp = &params;
tree declare_vars = NULL_TREE;
for (Bindings::const_definitions_iterator p =
this->block_->bindings()->begin_definitions();
p != this->block_->bindings()->end_definitions();
++p)
{
if ((*p)->is_variable() && (*p)->var_value()->is_parameter())
{
*pp = (*p)->get_tree(gogo, named_function);
// We always pass the receiver to a method as a pointer. If
// the receiver is declared as a non-pointer type, then we
// copy the value into a local variable.
if ((*p)->var_value()->is_receiver()
&& (*p)->var_value()->type()->points_to() == NULL)
{
tree parm_decl = this->make_receiver_parm_decl(gogo, *p, *pp);
tree var = *pp;
if (TREE_CODE(var) == INDIRECT_REF)
var = TREE_OPERAND(var, 0);
if (var != error_mark_node)
{
gcc_assert(TREE_CODE(var) == VAR_DECL);
DECL_CHAIN(var) = declare_vars;
declare_vars = var;
}
*pp = parm_decl;
}
else if ((*p)->var_value()->is_in_heap())
{
// If we take the address of a parameter, then we need
// to copy it into the heap.
tree parm_decl = this->copy_parm_to_heap(gogo, *p, *pp);
if (*pp != error_mark_node)
{
gcc_assert(TREE_CODE(*pp) == INDIRECT_REF);
tree var_decl = TREE_OPERAND(*pp, 0);
if (var_decl != error_mark_node)
{
gcc_assert(TREE_CODE(var_decl) == VAR_DECL);
DECL_CHAIN(var_decl) = declare_vars;
declare_vars = var_decl;
}
}
*pp = parm_decl;
}
if (*pp != error_mark_node)
{
gcc_assert(TREE_CODE(*pp) == PARM_DECL);
pp = &DECL_CHAIN(*pp);
}
}
else if ((*p)->is_result_variable())
{
tree var_decl = (*p)->get_tree(gogo, named_function);
if (var_decl != error_mark_node
&& (*p)->result_var_value()->is_in_heap())
{
gcc_assert(TREE_CODE(var_decl) == INDIRECT_REF);
var_decl = TREE_OPERAND(var_decl, 0);
}
if (var_decl != error_mark_node)
{
gcc_assert(TREE_CODE(var_decl) == VAR_DECL);
DECL_CHAIN(var_decl) = declare_vars;
declare_vars = var_decl;
}
}
}
*pp = NULL_TREE;
DECL_ARGUMENTS(fndecl) = params;
if (this->block_ != NULL)
{
gcc_assert(DECL_INITIAL(fndecl) == NULL_TREE);
// Declare variables if necessary.
tree bind = NULL_TREE;
if (declare_vars != NULL_TREE)
{
tree block = make_node(BLOCK);
BLOCK_SUPERCONTEXT(block) = fndecl;
DECL_INITIAL(fndecl) = block;
BLOCK_VARS(block) = declare_vars;
TREE_USED(block) = 1;
bind = build3(BIND_EXPR, void_type_node, BLOCK_VARS(block),
NULL_TREE, block);
TREE_SIDE_EFFECTS(bind) = 1;
}
// Build the trees for all the statements in the function.
Translate_context context(gogo, named_function, NULL, NULL_TREE);
tree code = this->block_->get_tree(&context);
tree init = NULL_TREE;
tree except = NULL_TREE;
tree fini = NULL_TREE;
// Initialize variables if necessary.
for (tree v = declare_vars; v != NULL_TREE; v = DECL_CHAIN(v))
{
tree dv = build1(DECL_EXPR, void_type_node, v);
SET_EXPR_LOCATION(dv, DECL_SOURCE_LOCATION(v));
append_to_statement_list(dv, &init);
}
// If we have a defer stack, initialize it at the start of a
// function.
if (this->defer_stack_ != NULL_TREE)
{
tree defer_init = build1(DECL_EXPR, void_type_node,
this->defer_stack_);
SET_EXPR_LOCATION(defer_init, this->block_->start_location());
append_to_statement_list(defer_init, &init);
// Clean up the defer stack when we leave the function.
this->build_defer_wrapper(gogo, named_function, &except, &fini);
}
if (code != NULL_TREE && code != error_mark_node)
{
if (init != NULL_TREE)
code = build2(COMPOUND_EXPR, void_type_node, init, code);
if (except != NULL_TREE)
code = build2(TRY_CATCH_EXPR, void_type_node, code,
build2(CATCH_EXPR, void_type_node, NULL, except));
if (fini != NULL_TREE)
code = build2(TRY_FINALLY_EXPR, void_type_node, code, fini);
}
// Stick the code into the block we built for the receiver, if
// we built on.
if (bind != NULL_TREE && code != NULL_TREE && code != error_mark_node)
{
BIND_EXPR_BODY(bind) = code;
code = bind;
}
DECL_SAVED_TREE(fndecl) = code;
}
}
// Build the wrappers around function code needed if the function has
// any defer statements. This sets *EXCEPT to an exception handler
// and *FINI to a finally handler.
void
Function::build_defer_wrapper(Gogo* gogo, Named_object* named_function,
tree *except, tree *fini)
{
source_location end_loc = this->block_->end_location();
// Add an exception handler. This is used if a panic occurs. Its
// purpose is to stop the stack unwinding if a deferred function
// calls recover. There are more details in
// libgo/runtime/go-unwind.c.
tree stmt_list = NULL_TREE;
static tree check_fndecl;
tree call = Gogo::call_builtin(&check_fndecl,
end_loc,
"__go_check_defer",
1,
void_type_node,
ptr_type_node,
this->defer_stack(end_loc));
if (call != error_mark_node)
append_to_statement_list(call, &stmt_list);
tree retval = this->return_value(gogo, named_function, end_loc, &stmt_list);
tree set;
if (retval == NULL_TREE)
set = NULL_TREE;
else
set = fold_build2_loc(end_loc, MODIFY_EXPR, void_type_node,
DECL_RESULT(this->fndecl_), retval);
tree ret_stmt = fold_build1_loc(end_loc, RETURN_EXPR, void_type_node, set);
append_to_statement_list(ret_stmt, &stmt_list);
gcc_assert(*except == NULL_TREE);
*except = stmt_list;
// Add some finally code to run the defer functions. This is used
// both in the normal case, when no panic occurs, and also if a
// panic occurs to run any further defer functions. Of course, it
// is possible for a defer function to call panic which should be
// caught by another defer function. To handle that we use a loop.
// finish:
// try { __go_undefer(); } catch { __go_check_defer(); goto finish; }
// if (return values are named) return named_vals;
stmt_list = NULL;
tree label = create_artificial_label(end_loc);
tree define_label = fold_build1_loc(end_loc, LABEL_EXPR, void_type_node,
label);
append_to_statement_list(define_label, &stmt_list);
static tree undefer_fndecl;
tree undefer = Gogo::call_builtin(&undefer_fndecl,
end_loc,
"__go_undefer",
1,
void_type_node,
ptr_type_node,
this->defer_stack(end_loc));
if (undefer_fndecl != NULL_TREE)
TREE_NOTHROW(undefer_fndecl) = 0;
tree defer = Gogo::call_builtin(&check_fndecl,
end_loc,
"__go_check_defer",
1,
void_type_node,
ptr_type_node,
this->defer_stack(end_loc));
tree jump = fold_build1_loc(end_loc, GOTO_EXPR, void_type_node, label);
tree catch_body = build2(COMPOUND_EXPR, void_type_node, defer, jump);
catch_body = build2(CATCH_EXPR, void_type_node, NULL, catch_body);
tree try_catch = build2(TRY_CATCH_EXPR, void_type_node, undefer, catch_body);
append_to_statement_list(try_catch, &stmt_list);
if (this->type_->results() != NULL
&& !this->type_->results()->empty()
&& !this->type_->results()->front().name().empty())
{
// If the result variables are named, we need to return them
// again, because they might have been changed by a defer
// function.
retval = this->return_value(gogo, named_function, end_loc,
&stmt_list);
set = fold_build2_loc(end_loc, MODIFY_EXPR, void_type_node,
DECL_RESULT(this->fndecl_), retval);
ret_stmt = fold_build1_loc(end_loc, RETURN_EXPR, void_type_node, set);
append_to_statement_list(ret_stmt, &stmt_list);
}
gcc_assert(*fini == NULL_TREE);
*fini = stmt_list;
}
// Return the value to assign to DECL_RESULT(this->fndecl_). This may
// also add statements to STMT_LIST, which need to be executed before
// the assignment. This is used for a return statement with no
// explicit values.
tree
Function::return_value(Gogo* gogo, Named_object* named_function,
source_location location, tree* stmt_list) const
{
const Typed_identifier_list* results = this->type_->results();
if (results == NULL || results->empty())
return NULL_TREE;
// In the case of an exception handler created for functions with
// defer statements, the result variables may be unnamed.
bool is_named = !results->front().name().empty();
if (is_named)
{
gcc_assert(this->named_results_ != NULL);
if (this->named_results_->size() != results->size())
{
gcc_assert(saw_errors());
return error_mark_node;
}
}
tree retval;
if (results->size() == 1)
{
if (is_named)
return this->named_results_->front()->get_tree(gogo, named_function);
else
return results->front().type()->get_init_tree(gogo, false);
}
else
{
tree rettype = TREE_TYPE(DECL_RESULT(this->fndecl_));
retval = create_tmp_var(rettype, "RESULT");
tree field = TYPE_FIELDS(rettype);
int index = 0;
for (Typed_identifier_list::const_iterator pr = results->begin();
pr != results->end();
++pr, ++index, field = DECL_CHAIN(field))
{
gcc_assert(field != NULL);
tree val;
if (is_named)
val = (*this->named_results_)[index]->get_tree(gogo,
named_function);
else
val = pr->type()->get_init_tree(gogo, false);
tree set = fold_build2_loc(location, MODIFY_EXPR, void_type_node,
build3(COMPONENT_REF, TREE_TYPE(field),
retval, field, NULL_TREE),
val);
append_to_statement_list(set, stmt_list);
}
return retval;
}
}
// Get the tree for the variable holding the defer stack for this
// function. At least at present, the value of this variable is not
// used. However, a pointer to this variable is used as a marker for
// the functions on the defer stack associated with this function.
// Doing things this way permits inlining a function which uses defer.
tree
Function::defer_stack(source_location location)
{
if (this->defer_stack_ == NULL_TREE)
{
tree var = create_tmp_var(ptr_type_node, "DEFER");
DECL_INITIAL(var) = null_pointer_node;
DECL_SOURCE_LOCATION(var) = location;
TREE_ADDRESSABLE(var) = 1;
this->defer_stack_ = var;
}
return fold_convert_loc(location, ptr_type_node,
build_fold_addr_expr_loc(location,
this->defer_stack_));
}
// Get a tree for the statements in a block.
tree
Block::get_tree(Translate_context* context)
{
Gogo* gogo = context->gogo();
tree block = make_node(BLOCK);
// Put the new block into the block tree.
if (context->block() == NULL)
{
tree fndecl;
if (context->function() != NULL)
fndecl = context->function()->func_value()->get_decl();
else
fndecl = current_function_decl;
gcc_assert(fndecl != NULL_TREE);
// We may have already created a block for the receiver.
if (DECL_INITIAL(fndecl) == NULL_TREE)
{
BLOCK_SUPERCONTEXT(block) = fndecl;
DECL_INITIAL(fndecl) = block;
}
else
{
tree superblock_tree = DECL_INITIAL(fndecl);
BLOCK_SUPERCONTEXT(block) = superblock_tree;
gcc_assert(BLOCK_CHAIN(block) == NULL_TREE);
BLOCK_CHAIN(block) = block;
}
}
else
{
tree superblock_tree = context->block_tree();
BLOCK_SUPERCONTEXT(block) = superblock_tree;
tree* pp;
for (pp = &BLOCK_SUBBLOCKS(superblock_tree);
*pp != NULL_TREE;
pp = &BLOCK_CHAIN(*pp))
;
*pp = block;
}
// Expand local variables in the block.
tree* pp = &BLOCK_VARS(block);
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())
&& !(*pv)->is_result_variable()
&& !(*pv)->is_const())
{
tree var = (*pv)->get_tree(gogo, context->function());
if (var != error_mark_node && TREE_TYPE(var) != error_mark_node)
{
if ((*pv)->is_variable() && (*pv)->var_value()->is_in_heap())
{
gcc_assert(TREE_CODE(var) == INDIRECT_REF);
var = TREE_OPERAND(var, 0);
gcc_assert(TREE_CODE(var) == VAR_DECL);
}
*pp = var;
pp = &DECL_CHAIN(*pp);
}
}
}
*pp = NULL_TREE;
Translate_context subcontext(context->gogo(), context->function(),
this, block);
tree statements = NULL_TREE;
// Expand the statements.
for (std::vector<Statement*>::const_iterator p = this->statements_.begin();
p != this->statements_.end();
++p)
{
tree statement = (*p)->get_tree(&subcontext);
if (statement != error_mark_node)
append_to_statement_list(statement, &statements);
}
TREE_USED(block) = 1;
tree bind = build3(BIND_EXPR, void_type_node, BLOCK_VARS(block), statements,
block);
TREE_SIDE_EFFECTS(bind) = 1;
return bind;
}
// Get the LABEL_DECL for a label.
tree
Label::get_decl()
{
if (this->decl_ == NULL)
{
tree id = get_identifier_from_string(this->name_);
this->decl_ = build_decl(this->location_, LABEL_DECL, id, void_type_node);
DECL_CONTEXT(this->decl_) = current_function_decl;
}
return this->decl_;
}
// Return an expression for the address of this label.
tree
Label::get_addr(source_location location)
{
tree decl = this->get_decl();
TREE_USED(decl) = 1;
TREE_ADDRESSABLE(decl) = 1;
return fold_convert_loc(location, ptr_type_node,
build_fold_addr_expr_loc(location, decl));
}
// Get the LABEL_DECL for an unnamed label.
tree
Unnamed_label::get_decl()
{
if (this->decl_ == NULL)
this->decl_ = create_artificial_label(this->location_);
return this->decl_;
}
// Get the LABEL_EXPR for an unnamed label.
tree
Unnamed_label::get_definition()
{
tree t = build1(LABEL_EXPR, void_type_node, this->get_decl());
SET_EXPR_LOCATION(t, this->location_);
return t;
}
// Return a goto to this label.
tree
Unnamed_label::get_goto(source_location location)
{
tree t = build1(GOTO_EXPR, void_type_node, this->get_decl());
SET_EXPR_LOCATION(t, location);
return t;
}
// Return the integer type to use for a size.
GO_EXTERN_C
tree
go_type_for_size(unsigned int bits, int unsignedp)
{
const char* name;
switch (bits)
{
case 8:
name = unsignedp ? "uint8" : "int8";
break;
case 16:
name = unsignedp ? "uint16" : "int16";
break;
case 32:
name = unsignedp ? "uint32" : "int32";
break;
case 64:
name = unsignedp ? "uint64" : "int64";
break;
default:
if (bits == POINTER_SIZE && unsignedp)
name = "uintptr";
else
return NULL_TREE;
}
Type* type = Type::lookup_integer_type(name);
return type->get_tree(go_get_gogo());
}
// Return the type to use for a mode.
GO_EXTERN_C
tree
go_type_for_mode(enum machine_mode mode, int unsignedp)
{
// FIXME: This static_cast should be in machmode.h.
enum mode_class mc = static_cast<enum mode_class>(GET_MODE_CLASS(mode));
if (mc == MODE_INT)
return go_type_for_size(GET_MODE_BITSIZE(mode), unsignedp);
else if (mc == MODE_FLOAT)
{
Type* type;
switch (GET_MODE_BITSIZE (mode))
{
case 32:
type = Type::lookup_float_type("float32");
break;
case 64:
type = Type::lookup_float_type("float64");
break;
default:
// We have to check for long double in order to support
// i386 excess precision.
if (mode == TYPE_MODE(long_double_type_node))
return long_double_type_node;
return NULL_TREE;
}
return type->float_type()->type_tree();
}
else if (mc == MODE_COMPLEX_FLOAT)
{
Type *type;
switch (GET_MODE_BITSIZE (mode))
{
case 64:
type = Type::lookup_complex_type("complex64");
break;
case 128:
type = Type::lookup_complex_type("complex128");
break;
default:
// We have to check for long double in order to support
// i386 excess precision.
if (mode == TYPE_MODE(complex_long_double_type_node))
return complex_long_double_type_node;
return NULL_TREE;
}
return type->complex_type()->type_tree();
}
else
return NULL_TREE;
}
// Return a tree which allocates SIZE bytes which will holds value of
// type TYPE.
tree
Gogo::allocate_memory(Type* type, tree size, source_location location)
{
// If the package imports unsafe, then it may play games with
// pointers that look like integers.
if (this->imported_unsafe_ || type->has_pointer())
{
static tree new_fndecl;
return Gogo::call_builtin(&new_fndecl,
location,
"__go_new",
1,
ptr_type_node,
sizetype,
size);
}
else
{
static tree new_nopointers_fndecl;
return Gogo::call_builtin(&new_nopointers_fndecl,
location,
"__go_new_nopointers",
1,
ptr_type_node,
sizetype,
size);
}
}
// Build a builtin struct with a list of fields. The name is
// STRUCT_NAME. STRUCT_TYPE is NULL_TREE or an empty RECORD_TYPE
// node; this exists so that the struct can have fields which point to
// itself. If PTYPE is not NULL, store the result in *PTYPE. There
// are NFIELDS fields. Each field is a name (a const char*) followed
// by a type (a tree).
tree
Gogo::builtin_struct(tree* ptype, const char* struct_name, tree struct_type,
int nfields, ...)
{
if (ptype != NULL && *ptype != NULL_TREE)
return *ptype;
va_list ap;
va_start(ap, nfields);
tree fields = NULL_TREE;
for (int i = 0; i < nfields; ++i)
{
const char* field_name = va_arg(ap, const char*);
tree type = va_arg(ap, tree);
if (type == error_mark_node)
{
if (ptype != NULL)
*ptype = error_mark_node;
return error_mark_node;
}
tree field = build_decl(BUILTINS_LOCATION, FIELD_DECL,
get_identifier(field_name), type);
DECL_CHAIN(field) = fields;
fields = field;
}
va_end(ap);
if (struct_type == NULL_TREE)
struct_type = make_node(RECORD_TYPE);
finish_builtin_struct(struct_type, struct_name, fields, NULL_TREE);
if (ptype != NULL)
{
go_preserve_from_gc(struct_type);
*ptype = struct_type;
}
return struct_type;
}
// Return a type to use for pointer to const char for a string.
tree
Gogo::const_char_pointer_type_tree()
{
static tree type;
if (type == NULL_TREE)
{
tree const_char_type = build_qualified_type(unsigned_char_type_node,
TYPE_QUAL_CONST);
type = build_pointer_type(const_char_type);
go_preserve_from_gc(type);
}
return type;
}
// Return a tree for a string constant.
tree
Gogo::string_constant_tree(const std::string& val)
{
tree index_type = build_index_type(size_int(val.length()));
tree const_char_type = build_qualified_type(unsigned_char_type_node,
TYPE_QUAL_CONST);
tree string_type = build_array_type(const_char_type, index_type);
string_type = build_variant_type_copy(string_type);
TYPE_STRING_FLAG(string_type) = 1;
tree string_val = build_string(val.length(), val.data());
TREE_TYPE(string_val) = string_type;
return string_val;
}
// Return a tree for a Go string constant.
tree
Gogo::go_string_constant_tree(const std::string& val)
{
tree string_type = Type::make_string_type()->get_tree(this);
VEC(constructor_elt, gc)* init = VEC_alloc(constructor_elt, gc, 2);
constructor_elt* elt = VEC_quick_push(constructor_elt, init, NULL);
tree field = TYPE_FIELDS(string_type);
gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__data") == 0);
elt->index = field;
tree str = Gogo::string_constant_tree(val);
elt->value = fold_convert(TREE_TYPE(field),
build_fold_addr_expr(str));
elt = VEC_quick_push(constructor_elt, init, NULL);
field = DECL_CHAIN(field);
gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__length") == 0);
elt->index = field;
elt->value = build_int_cst_type(TREE_TYPE(field), val.length());
tree constructor = build_constructor(string_type, init);
TREE_READONLY(constructor) = 1;
TREE_CONSTANT(constructor) = 1;
return constructor;
}
// Return a tree for a pointer to a Go string constant. This is only
// used for type descriptors, so we return a pointer to a constant
// decl.
tree
Gogo::ptr_go_string_constant_tree(const std::string& val)
{
tree pval = this->go_string_constant_tree(val);
tree decl = build_decl(UNKNOWN_LOCATION, VAR_DECL,
create_tmp_var_name("SP"), TREE_TYPE(pval));
DECL_EXTERNAL(decl) = 0;
TREE_PUBLIC(decl) = 0;
TREE_USED(decl) = 1;
TREE_READONLY(decl) = 1;
TREE_CONSTANT(decl) = 1;
TREE_STATIC(decl) = 1;
DECL_ARTIFICIAL(decl) = 1;
DECL_INITIAL(decl) = pval;
rest_of_decl_compilation(decl, 1, 0);
return build_fold_addr_expr(decl);
}
// Build the type of the struct that holds a slice for the given
// element type.
tree
Gogo::slice_type_tree(tree element_type_tree)
{
// We use int for the count and capacity fields in a slice header.
// This matches 6g. The language definition guarantees that we
// can't allocate space of a size which does not fit in int
// anyhow. FIXME: integer_type_node is the the C type "int" but is
// not necessarily the Go type "int". They will differ when the C
// type "int" has fewer than 32 bits.
return Gogo::builtin_struct(NULL, "__go_slice", NULL_TREE, 3,
"__values",
build_pointer_type(element_type_tree),
"__count",
integer_type_node,
"__capacity",
integer_type_node);
}
// Given the tree for a slice type, return the tree for the type of
// the elements of the slice.
tree
Gogo::slice_element_type_tree(tree slice_type_tree)
{
gcc_assert(TREE_CODE(slice_type_tree) == RECORD_TYPE
&& POINTER_TYPE_P(TREE_TYPE(TYPE_FIELDS(slice_type_tree))));
return TREE_TYPE(TREE_TYPE(TYPE_FIELDS(slice_type_tree)));
}
// Build a constructor for a slice. SLICE_TYPE_TREE is the type of
// the slice. VALUES is the value pointer and COUNT is the number of
// entries. If CAPACITY is not NULL, it is the capacity; otherwise
// the capacity and the count are the same.
tree
Gogo::slice_constructor(tree slice_type_tree, tree values, tree count,
tree capacity)
{
gcc_assert(TREE_CODE(slice_type_tree) == RECORD_TYPE);
VEC(constructor_elt,gc)* init = VEC_alloc(constructor_elt, gc, 3);
tree field = TYPE_FIELDS(slice_type_tree);
gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__values") == 0);
constructor_elt* elt = VEC_quick_push(constructor_elt, init, NULL);
elt->index = field;
gcc_assert(TYPE_MAIN_VARIANT(TREE_TYPE(field))
== TYPE_MAIN_VARIANT(TREE_TYPE(values)));
elt->value = values;
count = fold_convert(sizetype, count);
if (capacity == NULL_TREE)
{
count = save_expr(count);
capacity = count;
}
field = DECL_CHAIN(field);
gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__count") == 0);
elt = VEC_quick_push(constructor_elt, init, NULL);
elt->index = field;
elt->value = fold_convert(TREE_TYPE(field), count);
field = DECL_CHAIN(field);
gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__capacity") == 0);
elt = VEC_quick_push(constructor_elt, init, NULL);
elt->index = field;
elt->value = fold_convert(TREE_TYPE(field), capacity);
return build_constructor(slice_type_tree, init);
}
// Build a constructor for an empty slice.
tree
Gogo::empty_slice_constructor(tree slice_type_tree)
{
tree element_field = TYPE_FIELDS(slice_type_tree);
tree ret = Gogo::slice_constructor(slice_type_tree,
fold_convert(TREE_TYPE(element_field),
null_pointer_node),
size_zero_node,
size_zero_node);
TREE_CONSTANT(ret) = 1;
return ret;
}
// Build a map descriptor for a map of type MAPTYPE.
tree
Gogo::map_descriptor(Map_type* maptype)
{
if (this->map_descriptors_ == NULL)
this->map_descriptors_ = new Map_descriptors(10);
std::pair<const Map_type*, tree> val(maptype, NULL);
std::pair<Map_descriptors::iterator, bool> ins =
this->map_descriptors_->insert(val);
Map_descriptors::iterator p = ins.first;
if (!ins.second)
{
if (p->second == error_mark_node)
return error_mark_node;
gcc_assert(p->second != NULL_TREE && DECL_P(p->second));
return build_fold_addr_expr(p->second);
}
Type* keytype = maptype->key_type();
Type* valtype = maptype->val_type();
std::string mangled_name = ("__go_map_" + maptype->mangled_name(this));
tree id = get_identifier_from_string(mangled_name);
// Get the type of the map descriptor. This is __go_map_descriptor
// in libgo/map.h.
tree struct_type = this->map_descriptor_type();
// The map entry type is a struct with three fields. This struct is
// specific to MAPTYPE. Build it.
tree map_entry_type = make_node(RECORD_TYPE);
map_entry_type = Gogo::builtin_struct(NULL, "__map", map_entry_type, 3,
"__next",
build_pointer_type(map_entry_type),
"__key",
keytype->get_tree(this),
"__val",
valtype->get_tree(this));
if (map_entry_type == error_mark_node)
{
p->second = error_mark_node;
return error_mark_node;
}
tree map_entry_key_field = DECL_CHAIN(TYPE_FIELDS(map_entry_type));
gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(map_entry_key_field)),
"__key") == 0);
tree map_entry_val_field = DECL_CHAIN(map_entry_key_field);
gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(map_entry_val_field)),
"__val") == 0);
// Initialize the entries.
tree map_descriptor_field = TYPE_FIELDS(struct_type);
gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(map_descriptor_field)),
"__map_descriptor") == 0);
tree entry_size_field = DECL_CHAIN(map_descriptor_field);
gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(entry_size_field)),
"__entry_size") == 0);
tree key_offset_field = DECL_CHAIN(entry_size_field);
gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(key_offset_field)),
"__key_offset") == 0);
tree val_offset_field = DECL_CHAIN(key_offset_field);
gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(val_offset_field)),
"__val_offset") == 0);
VEC(constructor_elt, gc)* descriptor = VEC_alloc(constructor_elt, gc, 6);
constructor_elt* elt = VEC_quick_push(constructor_elt, descriptor, NULL);
elt->index = map_descriptor_field;
elt->value = maptype->type_descriptor_pointer(this);
elt = VEC_quick_push(constructor_elt, descriptor, NULL);
elt->index = entry_size_field;
elt->value = TYPE_SIZE_UNIT(map_entry_type);
elt = VEC_quick_push(constructor_elt, descriptor, NULL);
elt->index = key_offset_field;
elt->value = byte_position(map_entry_key_field);
elt = VEC_quick_push(constructor_elt, descriptor, NULL);
elt->index = val_offset_field;
elt->value = byte_position(map_entry_val_field);
tree constructor = build_constructor(struct_type, descriptor);
tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL, id, struct_type);
TREE_STATIC(decl) = 1;
TREE_USED(decl) = 1;
TREE_READONLY(decl) = 1;
TREE_CONSTANT(decl) = 1;
DECL_INITIAL(decl) = constructor;
make_decl_one_only(decl, DECL_ASSEMBLER_NAME(decl));
resolve_unique_section(decl, 1, 0);
rest_of_decl_compilation(decl, 1, 0);
go_preserve_from_gc(decl);
p->second = decl;
return build_fold_addr_expr(decl);
}
// Return a tree for the type of a map descriptor. This is struct
// __go_map_descriptor in libgo/runtime/map.h. This is the same for
// all map types.
tree
Gogo::map_descriptor_type()
{
static tree struct_type;
tree dtype = Type::make_type_descriptor_type()->get_tree(this);
dtype = build_qualified_type(dtype, TYPE_QUAL_CONST);
return Gogo::builtin_struct(&struct_type, "__go_map_descriptor", NULL_TREE,
4,
"__map_descriptor",
build_pointer_type(dtype),
"__entry_size",
sizetype,
"__key_offset",
sizetype,
"__val_offset",
sizetype);
}
// Return the name to use for a type descriptor decl for TYPE. This
// is used when TYPE does not have a name.
std::string
Gogo::unnamed_type_descriptor_decl_name(const Type* type)
{
return "__go_td_" + type->mangled_name(this);
}
// Return the name to use for a type descriptor decl for a type named
// NAME, defined in the function IN_FUNCTION. IN_FUNCTION will
// normally be NULL.
std::string
Gogo::type_descriptor_decl_name(const Named_object* no,
const Named_object* in_function)
{
std::string ret = "__go_tdn_";
if (no->type_value()->is_builtin())
gcc_assert(in_function == NULL);
else
{
const std::string& unique_prefix(no->package() == NULL
? this->unique_prefix()
: no->package()->unique_prefix());
const std::string& package_name(no->package() == NULL
? this->package_name()
: no->package()->name());
ret.append(unique_prefix);
ret.append(1, '.');
ret.append(package_name);
ret.append(1, '.');
if (in_function != NULL)
{
ret.append(Gogo::unpack_hidden_name(in_function->name()));
ret.append(1, '.');
}
}
ret.append(no->name());
return ret;
}
// Where a type descriptor decl should be defined.
Gogo::Type_descriptor_location
Gogo::type_descriptor_location(const Type* type)
{
const Named_type* name = type->named_type();
if (name != NULL)
{
if (name->named_object()->package() != NULL)
{
// This is a named type defined in a different package. The
// descriptor should be defined in that package.
return TYPE_DESCRIPTOR_UNDEFINED;
}
else if (name->is_builtin())
{
// We create the descriptor for a builtin type whenever we
// need it.
return TYPE_DESCRIPTOR_COMMON;
}
else
{
// This is a named type defined in this package. The
// descriptor should be defined here.
return TYPE_DESCRIPTOR_DEFINED;
}
}
else
{
if (type->points_to() != NULL
&& type->points_to()->named_type() != NULL
&& type->points_to()->named_type()->named_object()->package() != NULL)
{
// This is an unnamed pointer to a named type defined in a
// different package. The descriptor should be defined in
// that package.
return TYPE_DESCRIPTOR_UNDEFINED;
}
else
{
// This is an unnamed type. The descriptor could be defined
// in any package where it is needed, and the linker will
// pick one descriptor to keep.
return TYPE_DESCRIPTOR_COMMON;
}
}
}
// Build a type descriptor decl for TYPE. INITIALIZER is a struct
// composite literal which initializers the type descriptor.
void
Gogo::build_type_descriptor_decl(const Type* type, Expression* initializer,
tree* pdecl)
{
const Named_type* name = type->named_type();
// We can have multiple instances of unnamed types, but we only want
// to emit the type descriptor once. We use a hash table to handle
// this. This is not necessary for named types, as they are unique,
// and we store the type descriptor decl in the type itself.
tree* phash = NULL;
if (name == NULL)
{
if (this->type_descriptor_decls_ == NULL)
this->type_descriptor_decls_ = new Type_descriptor_decls(10);
std::pair<Type_descriptor_decls::iterator, bool> ins =
this->type_descriptor_decls_->insert(std::make_pair(type, NULL_TREE));
if (!ins.second)
{
// We've already built a type descriptor for this type.
*pdecl = ins.first->second;
return;
}
phash = &ins.first->second;
}
std::string decl_name;
if (name == NULL)
decl_name = this->unnamed_type_descriptor_decl_name(type);
else
decl_name = this->type_descriptor_decl_name(name->named_object(),
name->in_function());
tree id = get_identifier_from_string(decl_name);
tree descriptor_type_tree = initializer->type()->get_tree(this);
if (descriptor_type_tree == error_mark_node)
{
*pdecl = error_mark_node;
return;
}
tree decl = build_decl(name == NULL ? BUILTINS_LOCATION : name->location(),
VAR_DECL, id,
build_qualified_type(descriptor_type_tree,
TYPE_QUAL_CONST));
TREE_READONLY(decl) = 1;
TREE_CONSTANT(decl) = 1;
DECL_ARTIFICIAL(decl) = 1;
go_preserve_from_gc(decl);
if (phash != NULL)
*phash = decl;
// We store the new DECL now because we may need to refer to it when
// expanding INITIALIZER.
*pdecl = decl;
// If appropriate, just refer to the exported type identifier.
Gogo::Type_descriptor_location type_descriptor_location =
this->type_descriptor_location(type);
if (type_descriptor_location == TYPE_DESCRIPTOR_UNDEFINED)
{
TREE_PUBLIC(decl) = 1;
DECL_EXTERNAL(decl) = 1;
return;
}
TREE_STATIC(decl) = 1;
TREE_USED(decl) = 1;
Translate_context context(this, NULL, NULL, NULL);
context.set_is_const();
tree constructor = initializer->get_tree(&context);
if (constructor == error_mark_node)
gcc_assert(saw_errors());
DECL_INITIAL(decl) = constructor;
if (type_descriptor_location == TYPE_DESCRIPTOR_DEFINED)
TREE_PUBLIC(decl) = 1;
else
{
gcc_assert(type_descriptor_location == TYPE_DESCRIPTOR_COMMON);
make_decl_one_only(decl, DECL_ASSEMBLER_NAME(decl));
resolve_unique_section(decl, 1, 0);
}
rest_of_decl_compilation(decl, 1, 0);
}
// Build an interface method table for a type: a list of function
// pointers, one for each interface method. This is used for
// interfaces.
tree
Gogo::interface_method_table_for_type(const Interface_type* interface,
Named_type* type,
bool is_pointer)
{
const Typed_identifier_list* interface_methods = interface->methods();
gcc_assert(!interface_methods->empty());
std::string mangled_name = ((is_pointer ? "__go_pimt__" : "__go_imt_")
+ interface->mangled_name(this)
+ "__"
+ type->mangled_name(this));
tree id = get_identifier_from_string(mangled_name);
// See whether this interface has any hidden methods.
bool has_hidden_methods = false;
for (Typed_identifier_list::const_iterator p = interface_methods->begin();
p != interface_methods->end();
++p)
{
if (Gogo::is_hidden_name(p->name()))
{
has_hidden_methods = true;
break;
}
}
// We already know that the named type is convertible to the
// interface. If the interface has hidden methods, and the named
// type is defined in a different package, then the interface
// conversion table will be defined by that other package.
if (has_hidden_methods && type->named_object()->package() != NULL)
{
tree array_type = build_array_type(const_ptr_type_node, NULL);
tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL, id, array_type);
TREE_READONLY(decl) = 1;
TREE_CONSTANT(decl) = 1;
TREE_PUBLIC(decl) = 1;
DECL_EXTERNAL(decl) = 1;
go_preserve_from_gc(decl);
return decl;
}
size_t count = interface_methods->size();
VEC(constructor_elt, gc)* pointers = VEC_alloc(constructor_elt, gc,
count + 1);
// The first element is the type descriptor.
constructor_elt* elt = VEC_quick_push(constructor_elt, pointers, NULL);
elt->index = size_zero_node;
Type* td_type;
if (!is_pointer)
td_type = type;
else
td_type = Type::make_pointer_type(type);
elt->value = fold_convert(const_ptr_type_node,
td_type->type_descriptor_pointer(this));
size_t i = 1;
for (Typed_identifier_list::const_iterator p = interface_methods->begin();
p != interface_methods->end();
++p, ++i)
{
bool is_ambiguous;
Method* m = type->method_function(p->name(), &is_ambiguous);
gcc_assert(m != NULL);
Named_object* no = m->named_object();
tree fnid = no->get_id(this);
tree fndecl;
if (no->is_function())
fndecl = no->func_value()->get_or_make_decl(this, no, fnid);
else if (no->is_function_declaration())
fndecl = no->func_declaration_value()->get_or_make_decl(this, no,
fnid);
else
gcc_unreachable();
fndecl = build_fold_addr_expr(fndecl);
elt = VEC_quick_push(constructor_elt, pointers, NULL);
elt->index = size_int(i);
elt->value = fold_convert(const_ptr_type_node, fndecl);
}
gcc_assert(i == count + 1);
tree array_type = build_array_type(const_ptr_type_node,
build_index_type(size_int(count)));
tree constructor = build_constructor(array_type, pointers);
tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL, id, array_type);
TREE_STATIC(decl) = 1;
TREE_USED(decl) = 1;
TREE_READONLY(decl) = 1;
TREE_CONSTANT(decl) = 1;
DECL_INITIAL(decl) = constructor;
// If the interface type has hidden methods, then this is the only
// definition of the table. Otherwise it is a comdat table which
// may be defined in multiple packages.
if (has_hidden_methods)
TREE_PUBLIC(decl) = 1;
else
{
make_decl_one_only(decl, DECL_ASSEMBLER_NAME(decl));
resolve_unique_section(decl, 1, 0);
}
rest_of_decl_compilation(decl, 1, 0);
go_preserve_from_gc(decl);
return decl;
}
// Mark a function as a builtin library function.
void
Gogo::mark_fndecl_as_builtin_library(tree fndecl)
{
DECL_EXTERNAL(fndecl) = 1;
TREE_PUBLIC(fndecl) = 1;
DECL_ARTIFICIAL(fndecl) = 1;
TREE_NOTHROW(fndecl) = 1;
DECL_VISIBILITY(fndecl) = VISIBILITY_DEFAULT;
DECL_VISIBILITY_SPECIFIED(fndecl) = 1;
}
// Build a call to a builtin function.
tree
Gogo::call_builtin(tree* pdecl, source_location location, const char* name,
int nargs, tree rettype, ...)
{
if (rettype == error_mark_node)
return error_mark_node;
tree* types = new tree[nargs];
tree* args = new tree[nargs];
va_list ap;
va_start(ap, rettype);
for (int i = 0; i < nargs; ++i)
{
types[i] = va_arg(ap, tree);
args[i] = va_arg(ap, tree);
if (types[i] == error_mark_node || args[i] == error_mark_node)
{
delete[] types;
delete[] args;
return error_mark_node;
}
}
va_end(ap);
if (*pdecl == NULL_TREE)
{
tree fnid = get_identifier(name);
tree argtypes = NULL_TREE;
tree* pp = &argtypes;
for (int i = 0; i < nargs; ++i)
{
*pp = tree_cons(NULL_TREE, types[i], NULL_TREE);
pp = &TREE_CHAIN(*pp);
}
*pp = void_list_node;
tree fntype = build_function_type(rettype, argtypes);
*pdecl = build_decl(BUILTINS_LOCATION, FUNCTION_DECL, fnid, fntype);
Gogo::mark_fndecl_as_builtin_library(*pdecl);
go_preserve_from_gc(*pdecl);
}
tree fnptr = build_fold_addr_expr(*pdecl);
if (CAN_HAVE_LOCATION_P(fnptr))
SET_EXPR_LOCATION(fnptr, location);
tree ret = build_call_array(rettype, fnptr, nargs, args);
SET_EXPR_LOCATION(ret, location);
delete[] types;
delete[] args;
return ret;
}
// Build a call to the runtime error function.
tree
Gogo::runtime_error(int code, source_location location)
{
static tree runtime_error_fndecl;
tree ret = Gogo::call_builtin(&runtime_error_fndecl,
location,
"__go_runtime_error",
1,
void_type_node,
integer_type_node,
build_int_cst(integer_type_node, code));
if (ret == error_mark_node)
return error_mark_node;
// The runtime error function panics and does not return.
TREE_NOTHROW(runtime_error_fndecl) = 0;
TREE_THIS_VOLATILE(runtime_error_fndecl) = 1;
return ret;
}
// Send VAL on CHANNEL. If BLOCKING is true, the resulting tree has a
// void type. If BLOCKING is false, the resulting tree has a boolean
// type, and it will evaluate as true if the value was sent. If
// FOR_SELECT is true, this is being done because it was chosen in a
// select statement.
tree
Gogo::send_on_channel(tree channel, tree val, bool blocking, bool for_select,
source_location location)
{
if (channel == error_mark_node || val == error_mark_node)
return error_mark_node;
if (int_size_in_bytes(TREE_TYPE(val)) <= 8
&& !AGGREGATE_TYPE_P(TREE_TYPE(val))
&& !FLOAT_TYPE_P(TREE_TYPE(val)))
{
val = convert_to_integer(uint64_type_node, val);
if (blocking)
{
static tree send_small_fndecl;
tree ret = Gogo::call_builtin(&send_small_fndecl,
location,
"__go_send_small",
3,
void_type_node,
ptr_type_node,
channel,
uint64_type_node,
val,
boolean_type_node,
(for_select
? boolean_true_node
: boolean_false_node));
if (ret == error_mark_node)
return error_mark_node;
// This can panic if there are too many operations on a
// closed channel.
TREE_NOTHROW(send_small_fndecl) = 0;
return ret;
}
else
{
gcc_assert(!for_select);
static tree send_nonblocking_small_fndecl;
tree ret = Gogo::call_builtin(&send_nonblocking_small_fndecl,
location,
"__go_send_nonblocking_small",
2,
boolean_type_node,
ptr_type_node,
channel,
uint64_type_node,
val);
if (ret == error_mark_node)
return error_mark_node;
// This can panic if there are too many operations on a
// closed channel.
TREE_NOTHROW(send_nonblocking_small_fndecl) = 0;
return ret;
}
}
else
{
tree make_tmp;
if (TREE_ADDRESSABLE(TREE_TYPE(val)) || TREE_CODE(val) == VAR_DECL)
{
make_tmp = NULL_TREE;
val = build_fold_addr_expr(val);
if (DECL_P(val))
TREE_ADDRESSABLE(val) = 1;
}
else
{
tree tmp = create_tmp_var(TREE_TYPE(val), get_name(val));
DECL_IGNORED_P(tmp) = 0;
DECL_INITIAL(tmp) = val;
TREE_ADDRESSABLE(tmp) = 1;
make_tmp = build1(DECL_EXPR, void_type_node, tmp);
SET_EXPR_LOCATION(make_tmp, location);
val = build_fold_addr_expr(tmp);
}
val = fold_convert(ptr_type_node, val);
tree call;
if (blocking)
{
static tree send_big_fndecl;
call = Gogo::call_builtin(&send_big_fndecl,
location,
"__go_send_big",
3,
void_type_node,
ptr_type_node,
channel,
ptr_type_node,
val,
boolean_type_node,
(for_select
? boolean_true_node
: boolean_false_node));
if (call == error_mark_node)
return error_mark_node;
// This can panic if there are too many operations on a
// closed channel.
TREE_NOTHROW(send_big_fndecl) = 0;
}
else
{
gcc_assert(!for_select);
static tree send_nonblocking_big_fndecl;
call = Gogo::call_builtin(&send_nonblocking_big_fndecl,
location,
"__go_send_nonblocking_big",
2,
boolean_type_node,
ptr_type_node,
channel,
ptr_type_node,
val);
if (call == error_mark_node)
return error_mark_node;
// This can panic if there are too many operations on a
// closed channel.
TREE_NOTHROW(send_nonblocking_big_fndecl) = 0;
}
if (make_tmp == NULL_TREE)
return call;
else
{
tree ret = build2(COMPOUND_EXPR, TREE_TYPE(call), make_tmp, call);
SET_EXPR_LOCATION(ret, location);
return ret;
}
}
}
// Return a tree for receiving a value of type TYPE_TREE on CHANNEL.
// This does a blocking receive and returns the value read from the
// channel. If FOR_SELECT is true, this is being done because it was
// chosen in a select statement.
tree
Gogo::receive_from_channel(tree type_tree, tree channel, bool for_select,
source_location location)
{
if (type_tree == error_mark_node || channel == error_mark_node)
return error_mark_node;
if (int_size_in_bytes(type_tree) <= 8
&& !AGGREGATE_TYPE_P(type_tree)
&& !FLOAT_TYPE_P(type_tree))
{
static tree receive_small_fndecl;
tree call = Gogo::call_builtin(&receive_small_fndecl,
location,
"__go_receive_small",
2,
uint64_type_node,
ptr_type_node,
channel,
boolean_type_node,
(for_select
? boolean_true_node
: boolean_false_node));
if (call == error_mark_node)
return error_mark_node;
// This can panic if there are too many operations on a closed
// channel.
TREE_NOTHROW(receive_small_fndecl) = 0;
int bitsize = GET_MODE_BITSIZE(TYPE_MODE(type_tree));
tree int_type_tree = go_type_for_size(bitsize, 1);
return fold_convert_loc(location, type_tree,
fold_convert_loc(location, int_type_tree,
call));
}
else
{
tree tmp = create_tmp_var(type_tree, get_name(type_tree));
DECL_IGNORED_P(tmp) = 0;
TREE_ADDRESSABLE(tmp) = 1;
tree make_tmp = build1(DECL_EXPR, void_type_node, tmp);
SET_EXPR_LOCATION(make_tmp, location);
tree tmpaddr = build_fold_addr_expr(tmp);
tmpaddr = fold_convert(ptr_type_node, tmpaddr);
static tree receive_big_fndecl;
tree call = Gogo::call_builtin(&receive_big_fndecl,
location,
"__go_receive_big",
3,
boolean_type_node,
ptr_type_node,
channel,
ptr_type_node,
tmpaddr,
boolean_type_node,
(for_select
? boolean_true_node
: boolean_false_node));
if (call == error_mark_node)
return error_mark_node;
// This can panic if there are too many operations on a closed
// channel.
TREE_NOTHROW(receive_big_fndecl) = 0;
return build2(COMPOUND_EXPR, type_tree, make_tmp,
build2(COMPOUND_EXPR, type_tree, call, tmp));
}
}
// Return the type of a function trampoline. This is like
// get_trampoline_type in tree-nested.c.
tree
Gogo::trampoline_type_tree()
{
static tree type_tree;
if (type_tree == NULL_TREE)
{
unsigned int size;
unsigned int align;
go_trampoline_info(&size, &align);
tree t = build_index_type(build_int_cst(integer_type_node, size - 1));
t = build_array_type(char_type_node, t);
type_tree = Gogo::builtin_struct(NULL, "__go_trampoline", NULL_TREE, 1,
"__data", t);
t = TYPE_FIELDS(type_tree);
DECL_ALIGN(t) = align;
DECL_USER_ALIGN(t) = 1;
go_preserve_from_gc(type_tree);
}
return type_tree;
}
// Make a trampoline which calls FNADDR passing CLOSURE.
tree
Gogo::make_trampoline(tree fnaddr, tree closure, source_location location)
{
tree trampoline_type = Gogo::trampoline_type_tree();
tree trampoline_size = TYPE_SIZE_UNIT(trampoline_type);
closure = save_expr(closure);
// We allocate the trampoline using a special function which will
// mark it as executable.
static tree trampoline_fndecl;
tree x = Gogo::call_builtin(&trampoline_fndecl,
location,
"__go_allocate_trampoline",
2,
ptr_type_node,
size_type_node,
trampoline_size,
ptr_type_node,
fold_convert_loc(location, ptr_type_node,
closure));
if (x == error_mark_node)
return error_mark_node;
x = save_expr(x);
// Initialize the trampoline.
tree ini = build_call_expr(implicit_built_in_decls[BUILT_IN_INIT_TRAMPOLINE],
3, x, fnaddr, closure);
// On some targets the trampoline address needs to be adjusted. For
// example, when compiling in Thumb mode on the ARM, the address
// needs to have the low bit set.
x = build_call_expr(implicit_built_in_decls[BUILT_IN_ADJUST_TRAMPOLINE],
1, x);
x = fold_convert(TREE_TYPE(fnaddr), x);
return build2(COMPOUND_EXPR, TREE_TYPE(x), ini, x);
}
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
// gogo.h -- Go frontend parsed representation. -*- C++ -*-
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#ifndef GO_GOGO_H
#define GO_GOGO_H
class Traverse;
class Type;
class Type_hash_identical;
class Type_equal;
class Type_identical;
class Typed_identifier;
class Typed_identifier_list;
class Function_type;
class Expression;
class Statement;
class Block;
class Function;
class Bindings;
class Package;
class Variable;
class Pointer_type;
class Struct_type;
class Struct_field;
class Struct_field_list;
class Array_type;
class Map_type;
class Channel_type;
class Interface_type;
class Named_type;
class Forward_declaration_type;
class Method;
class Methods;
class Named_object;
class Label;
class Translate_context;
class Export;
class Import;
// This file declares the basic classes used to hold the internal
// representation of Go which is built by the parser.
// An initialization function for an imported package. This is a
// magic function which initializes variables and runs the "init"
// function.
class Import_init
{
public:
Import_init(const std::string& package_name, const std::string& init_name,
int priority)
: package_name_(package_name), init_name_(init_name), priority_(priority)
{ }
// The name of the package being imported.
const std::string&
package_name() const
{ return this->package_name_; }
// The name of the package's init function.
const std::string&
init_name() const
{ return this->init_name_; }
// The priority of the initialization function. Functions with a
// lower priority number must be run first.
int
priority() const
{ return this->priority_; }
private:
// The name of the package being imported.
std::string package_name_;
// The name of the package's init function.
std::string init_name_;
// The priority.
int priority_;
};
// For sorting purposes.
inline bool
operator<(const Import_init& i1, const Import_init& i2)
{
if (i1.priority() < i2.priority())
return true;
if (i1.priority() > i2.priority())
return false;
if (i1.package_name() != i2.package_name())
return i1.package_name() < i2.package_name();
return i1.init_name() < i2.init_name();
}
// The holder for the internal representation of the entire
// compilation unit.
class Gogo
{
public:
// Create the IR, passing in the sizes of the types "int", "float",
// and "uintptr" in bits.
Gogo(int int_type_size, int float_type_size, int pointer_size);
// Get the package name.
const std::string&
package_name() const;
// Set the package name.
void
set_package_name(const std::string&, source_location);
// If necessary, adjust the name to use for a hidden symbol. We add
// a prefix of the package name, so that hidden symbols in different
// packages do not collide.
std::string
pack_hidden_name(const std::string& name, bool is_exported) const
{
return (is_exported
? name
: ('.' + this->unique_prefix()
+ '.' + this->package_name()
+ '.' + name));
}
// Unpack a name which may have been hidden. Returns the
// user-visible name of the object.
static std::string
unpack_hidden_name(const std::string& name)
{ return name[0] != '.' ? name : name.substr(name.rfind('.') + 1); }
// Return whether a possibly packed name is hidden.
static bool
is_hidden_name(const std::string& name)
{ return name[0] == '.'; }
// Return the package prefix of a hidden name.
static std::string
hidden_name_prefix(const std::string& name)
{
gcc_assert(Gogo::is_hidden_name(name));
return name.substr(1, name.rfind('.') - 1);
}
// Given a name which may or may not have been hidden, return the
// name to use in an error message.
static std::string
message_name(const std::string& name);
// Return whether a name is the blank identifier _.
static bool
is_sink_name(const std::string& name)
{
return (name[0] == '.'
&& name[name.length() - 1] == '_'
&& name[name.length() - 2] == '.');
}
// Return the unique prefix to use for all exported symbols.
const std::string&
unique_prefix() const;
// Set the unique prefix.
void
set_unique_prefix(const std::string&);
// Return the priority to use for the package we are compiling.
// This is two more than the largest priority of any package we
// import.
int
package_priority() const;
// Import a package. FILENAME is the file name argument, LOCAL_NAME
// is the local name to give to the package. If LOCAL_NAME is empty
// the declarations are added to the global scope.
void
import_package(const std::string& filename, const std::string& local_name,
bool is_local_name_exported, source_location);
// Whether we are the global binding level.
bool
in_global_scope() const;
// Look up a name in the current binding contours.
Named_object*
lookup(const std::string&, Named_object** pfunction) const;
// Look up a name in the current block.
Named_object*
lookup_in_block(const std::string&) const;
// Look up a name in the global namespace--the universal scope.
Named_object*
lookup_global(const char*) const;
// Add a new imported package. REAL_NAME is the real name of the
// package. ALIAS is the alias of the package; this may be the same
// as REAL_NAME. This sets *PADD_TO_GLOBALS if symbols added to
// this package should be added to the global namespace; this is
// true if the alias is ".". LOCATION is the location of the import
// statement. This returns the new package, or NULL on error.
Package*
add_imported_package(const std::string& real_name, const std::string& alias,
bool is_alias_exported,
const std::string& unique_prefix,
source_location location,
bool* padd_to_globals);
// Register a package. This package may or may not be imported.
// This returns the Package structure for the package, creating if
// it necessary.
Package*
register_package(const std::string& name, const std::string& unique_prefix,
source_location);
// Start compiling a function. ADD_METHOD_TO_TYPE is true if a
// method function should be added to the type of its receiver.
Named_object*
start_function(const std::string& name, Function_type* type,
bool add_method_to_type, source_location);
// Finish compiling a function.
void
finish_function(source_location);
// Return the current function.
Named_object*
current_function() const;
// Start a new block. This is not initially associated with a
// function.
void
start_block(source_location);
// Finish the current block and return it.
Block*
finish_block(source_location);
// Declare an unknown name. This is used while parsing. The name
// must be resolved by the end of the parse. Unknown names are
// always added at the package level.
Named_object*
add_unknown_name(const std::string& name, source_location);
// Declare a function.
Named_object*
declare_function(const std::string&, Function_type*, source_location);
// Add a label.
Label*
add_label_definition(const std::string&, source_location);
// Add a label reference.
Label*
add_label_reference(const std::string&);
// Add a statement to the current block.
void
add_statement(Statement*);
// Add a block to the current block.
void
add_block(Block*, source_location);
// Add a constant.
Named_object*
add_constant(const Typed_identifier&, Expression*, int iota_value);
// Add a type.
void
add_type(const std::string&, Type*, source_location);
// Add a named type. This is used for builtin types, and to add an
// imported type to the global scope.
void
add_named_type(Named_type*);
// Declare a type.
Named_object*
declare_type(const std::string&, source_location);
// Declare a type at the package level. This is used when the
// parser sees an unknown name where a type name is required.
Named_object*
declare_package_type(const std::string&, source_location);
// Define a type which was already declared.
void
define_type(Named_object*, Named_type*);
// Add a variable.
Named_object*
add_variable(const std::string&, Variable*);
// Add a sink--a reference to the blank identifier _.
Named_object*
add_sink();
// Add a named object to the current namespace. This is used for
// import . "package".
void
add_named_object(Named_object*);
// Return a name to use for a thunk function. A thunk function is
// one we create during the compilation, for a go statement or a
// defer statement or a method expression.
static std::string
thunk_name();
// Return whether an object is a thunk.
static bool
is_thunk(const Named_object*);
// Note that we've seen an interface type. This is used to build
// all required interface method tables.
void
record_interface_type(Interface_type*);
// Clear out all names in file scope. This is called when we start
// parsing a new file.
void
clear_file_scope();
// Traverse the tree. See the Traverse class.
void
traverse(Traverse*);
// Define the predeclared global names.
void
define_global_names();
// Verify and complete all types.
void
verify_types();
// Lower the parse tree.
void
lower_parse_tree();
// Lower an expression.
void
lower_expression(Named_object* function, Expression**);
// Lower a constant.
void
lower_constant(Named_object*);
// Finalize the method lists and build stub methods for named types.
void
finalize_methods();
// Work out the types to use for unspecified variables and
// constants.
void
determine_types();
// Type check the program.
void
check_types();
// Check the types in a single block. This is used for complicated
// go statements.
void
check_types_in_block(Block*);
// Check for return statements.
void
check_return_statements();
// Do all exports.
void
do_exports();
// Add an import control function for an imported package to the
// list.
void
add_import_init_fn(const std::string& package_name,
const std::string& init_name, int prio);
// Turn short-cut operators (&&, ||) into explicit if statements.
void
remove_shortcuts();
// Use temporary variables to force order of evaluation.
void
order_evaluations();
// Build thunks for functions which call recover.
void
build_recover_thunks();
// Simplify statements which might use thunks: go and defer
// statements.
void
simplify_thunk_statements();
// Write out the global values.
void
write_globals();
// Build a call to a builtin function. PDECL should point to a NULL
// initialized static pointer which will hold the fndecl. NAME is
// the name of the function. NARGS is the number of arguments.
// RETTYPE is the return type. It is followed by NARGS pairs of
// type and argument (both trees).
static tree
call_builtin(tree* pdecl, source_location, const char* name, int nargs,
tree rettype, ...);
// Build a call to the runtime error function.
static tree
runtime_error(int code, source_location);
// Build a builtin struct with a list of fields.
static tree
builtin_struct(tree* ptype, const char* struct_name, tree struct_type,
int nfields, ...);
// Mark a function declaration as a builtin library function.
static void
mark_fndecl_as_builtin_library(tree fndecl);
// Build the type of the struct that holds a slice for the given
// element type.
tree
slice_type_tree(tree element_type_tree);
// Given a tree for a slice type, return the tree for the element
// type.
static tree
slice_element_type_tree(tree slice_type_tree);
// Build a constructor for a slice. SLICE_TYPE_TREE is the type of
// the slice. VALUES points to the values. COUNT is the size,
// CAPACITY is the capacity. If CAPACITY is NULL, it is set to
// COUNT.
static tree
slice_constructor(tree slice_type_tree, tree values, tree count,
tree capacity);
// Build a constructor for an empty slice. SLICE_TYPE_TREE is the
// type of the slice.
static tree
empty_slice_constructor(tree slice_type_tree);
// Build a map descriptor.
tree
map_descriptor(Map_type*);
// Return a tree for the type of a map descriptor. This is struct
// __go_map_descriptor in libgo/runtime/map.h. This is the same for
// all map types.
tree
map_descriptor_type();
// Build a type descriptor for TYPE using INITIALIZER as the type
// descriptor. This builds a new decl stored in *PDECL.
void
build_type_descriptor_decl(const Type*, Expression* initializer,
tree* pdecl);
// Build required interface method tables.
void
build_interface_method_tables();
// Build an interface method table for a type: a list of function
// pointers, one for each interface method. This returns a decl.
tree
interface_method_table_for_type(const Interface_type*, Named_type*,
bool is_pointer);
// Return a tree which allocate SIZE bytes to hold values of type
// TYPE.
tree
allocate_memory(Type *type, tree size, source_location);
// Return a type to use for pointer to const char.
static tree
const_char_pointer_type_tree();
// Build a string constant with the right type.
static tree
string_constant_tree(const std::string&);
// Build a Go string constant. This returns a pointer to the
// constant.
tree
go_string_constant_tree(const std::string&);
// Send a value on a channel.
static tree
send_on_channel(tree channel, tree val, bool blocking, bool for_select,
source_location);
// Receive a value from a channel.
static tree
receive_from_channel(tree type_tree, tree channel, bool for_select,
source_location);
// Return a tree for receiving an integer on a channel.
static tree
receive_as_64bit_integer(tree type, tree channel, bool blocking,
bool for_select);
// Make a trampoline which calls FNADDR passing CLOSURE.
tree
make_trampoline(tree fnaddr, tree closure, source_location);
private:
// During parsing, we keep a stack of functions. Each function on
// the stack is one that we are currently parsing. For each
// function, we keep track of the current stack of blocks.
struct Open_function
{
// The function.
Named_object* function;
// The stack of active blocks in the function.
std::vector<Block*> blocks;
};
// The stack of functions.
typedef std::vector<Open_function> Open_functions;
// Create trees for implicit builtin functions.
void
define_builtin_function_trees();
// Set up the built-in unsafe package.
void
import_unsafe(const std::string&, bool is_exported, source_location);
// Add a new imported package.
Named_object*
add_package(const std::string& real_name, const std::string& alias,
const std::string& unique_prefix, source_location location);
// Return the current binding contour.
Bindings*
current_bindings();
const Bindings*
current_bindings() const;
// Return the current block.
Block*
current_block();
// Get the name of the magic initialization function.
const std::string&
get_init_fn_name();
// Get the decl for the magic initialization function.
tree
initialization_function_decl();
// Write the magic initialization function.
void
write_initialization_function(tree fndecl, tree init_stmt_list);
// Initialize imported packages.
void
init_imports(tree*);
// Register variables with the garbage collector.
void
register_gc_vars(const std::vector<Named_object*>&, tree*);
// Build a pointer to a Go string constant. This returns a pointer
// to the pointer.
tree
ptr_go_string_constant_tree(const std::string&);
// Return the name to use for a type descriptor decl for an unnamed
// type.
std::string
unnamed_type_descriptor_decl_name(const Type* type);
// Return the name to use for a type descriptor decl for a type
// named NO, defined in IN_FUNCTION.
std::string
type_descriptor_decl_name(const Named_object* no,
const Named_object* in_function);
// Where a type descriptor should be defined.
enum Type_descriptor_location
{
// Defined in this file.
TYPE_DESCRIPTOR_DEFINED,
// Defined in some other file.
TYPE_DESCRIPTOR_UNDEFINED,
// Common definition which may occur in multiple files.
TYPE_DESCRIPTOR_COMMON
};
// Return where the decl for TYPE should be defined.
Type_descriptor_location
type_descriptor_location(const Type* type);
// Return the type of a trampoline.
static tree
trampoline_type_tree();
// Type used to map import names to packages.
typedef std::map<std::string, Package*> Imports;
// Type used to map package names to packages.
typedef std::map<std::string, Package*> Packages;
// Type used to map special names in the sys package.
typedef std::map<std::string, std::string> Sys_names;
// Hash table mapping map types to map descriptor decls.
typedef Unordered_map_hash(const Map_type*, tree, Type_hash_identical,
Type_identical) Map_descriptors;
// Map unnamed types to type descriptor decls.
typedef Unordered_map_hash(const Type*, tree, Type_hash_identical,
Type_identical) Type_descriptor_decls;
// The package we are compiling.
Package* package_;
// The list of currently open functions during parsing.
Open_functions functions_;
// The global binding contour. This includes the builtin functions
// and the package we are compiling.
Bindings* globals_;
// Mapping from import file names to packages.
Imports imports_;
// Whether the magic unsafe package was imported.
bool imported_unsafe_;
// Mapping from package names we have seen to packages. This does
// not include the package we are compiling.
Packages packages_;
// Mapping from map types to map descriptors.
Map_descriptors* map_descriptors_;
// Mapping from unnamed types to type descriptor decls.
Type_descriptor_decls* type_descriptor_decls_;
// The functions named "init", if there are any.
std::vector<Named_object*> init_functions_;
// Whether we need a magic initialization function.
bool need_init_fn_;
// The name of the magic initialization function.
std::string init_fn_name_;
// A list of import control variables for packages that we import.
std::set<Import_init> imported_init_fns_;
// The unique prefix used for all global symbols.
std::string unique_prefix_;
// A list of interface types defined while parsing.
std::vector<Interface_type*> interface_types_;
};
// A block of statements.
class Block
{
public:
Block(Block* enclosing, source_location);
// Return the enclosing block.
const Block*
enclosing() const
{ return this->enclosing_; }
// Return the bindings of the block.
Bindings*
bindings()
{ return this->bindings_; }
const Bindings*
bindings() const
{ return this->bindings_; }
// Look at the block's statements.
const std::vector<Statement*>*
statements() const
{ return &this->statements_; }
// Return the start location. This is normally the location of the
// left curly brace which starts the block.
source_location
start_location() const
{ return this->start_location_; }
// Return the end location. This is normally the location of the
// right curly brace which ends the block.
source_location
end_location() const
{ return this->end_location_; }
// Add a statement to the block.
void
add_statement(Statement*);
// Add a statement to the front of the block.
void
add_statement_at_front(Statement*);
// Replace a statement in a block.
void
replace_statement(size_t index, Statement*);
// Add a Statement before statement number INDEX.
void
insert_statement_before(size_t index, Statement*);
// Add a Statement after statement number INDEX.
void
insert_statement_after(size_t index, Statement*);
// Set the end location of the block.
void
set_end_location(source_location location)
{ this->end_location_ = location; }
// Traverse the tree.
int
traverse(Traverse*);
// Set final types for unspecified variables and constants.
void
determine_types();
// Return true if execution of this block may fall through to the
// next block.
bool
may_fall_through() const;
// Return a tree of the code in this block.
tree
get_tree(Translate_context*);
// Iterate over statements.
typedef std::vector<Statement*>::iterator iterator;
iterator
begin()
{ return this->statements_.begin(); }
iterator
end()
{ return this->statements_.end(); }
private:
// Enclosing block.
Block* enclosing_;
// Statements in the block.
std::vector<Statement*> statements_;
// Binding contour.
Bindings* bindings_;
// Location of start of block.
source_location start_location_;
// Location of end of block.
source_location end_location_;
};
// A function.
class Function
{
public:
Function(Function_type* type, Function*, Block*, source_location);
// Return the function's type.
Function_type*
type() const
{ return this->type_; }
// Return the enclosing function if there is one.
Function*
enclosing()
{ return this->enclosing_; }
// Set the enclosing function. This is used when building thunks
// for functions which call recover.
void
set_enclosing(Function* enclosing)
{
gcc_assert(this->enclosing_ == NULL);
this->enclosing_ = enclosing;
}
// Create the named result variables in the outer block.
void
create_named_result_variables();
// Add a new field to the closure variable.
void
add_closure_field(Named_object* var, source_location loc)
{ this->closure_fields_.push_back(std::make_pair(var, loc)); }
// Whether this function needs a closure.
bool
needs_closure() const
{ return !this->closure_fields_.empty(); }
// Return the closure variable, creating it if necessary. This is
// passed to the function as a static chain parameter.
Named_object*
closure_var();
// Set the closure variable. This is used when building thunks for
// functions which call recover.
void
set_closure_var(Named_object* v)
{
gcc_assert(this->closure_var_ == NULL);
this->closure_var_ = v;
}
// Return the variable for a reference to field INDEX in the closure
// variable.
Named_object*
enclosing_var(unsigned int index)
{
gcc_assert(index < this->closure_fields_.size());
return closure_fields_[index].first;
}
// Set the type of the closure variable if there is one.
void
set_closure_type();
// Get the block of statements associated with the function.
Block*
block() const
{ return this->block_; }
// Get the location of the start of the function.
source_location
location() const
{ return this->location_; }
// Return whether this function is actually a method.
bool
is_method() const;
// Add a label definition to the function.
Label*
add_label_definition(const std::string& label_name, source_location);
// Add a label reference to a function.
Label*
add_label_reference(const std::string& label_name);
// Whether this function calls the predeclared recover function.
bool
calls_recover() const
{ return this->calls_recover_; }
// Record that this function calls the predeclared recover function.
// This is set during the lowering pass.
void
set_calls_recover()
{ this->calls_recover_ = true; }
// Whether this is a recover thunk function.
bool
is_recover_thunk() const
{ return this->is_recover_thunk_; }
// Record that this is a thunk built for a function which calls
// recover.
void
set_is_recover_thunk()
{ this->is_recover_thunk_ = true; }
// Whether this function already has a recover thunk.
bool
has_recover_thunk() const
{ return this->has_recover_thunk_; }
// Record that this function already has a recover thunk.
void
set_has_recover_thunk()
{ this->has_recover_thunk_ = true; }
// Swap with another function. Used only for the thunk which calls
// recover.
void
swap_for_recover(Function *);
// Traverse the tree.
int
traverse(Traverse*);
// Determine types in the function.
void
determine_types();
// Return the function's decl given an identifier.
tree
get_or_make_decl(Gogo*, Named_object*, tree id);
// Return the function's decl after it has been built.
tree
get_decl() const
{
gcc_assert(this->fndecl_ != NULL);
return this->fndecl_;
}
// Set the function decl to hold a tree of the function code.
void
build_tree(Gogo*, Named_object*);
// Get the value to return when not explicitly specified. May also
// add statements to execute first to STMT_LIST.
tree
return_value(Gogo*, Named_object*, source_location, tree* stmt_list) const;
// Get a tree for the variable holding the defer stack.
tree
defer_stack(source_location);
// Export the function.
void
export_func(Export*, const std::string& name) const;
// Export a function with a type.
static void
export_func_with_type(Export*, const std::string& name,
const Function_type*);
// Import a function.
static void
import_func(Import*, std::string* pname, Typed_identifier** receiver,
Typed_identifier_list** pparameters,
Typed_identifier_list** presults, bool* is_varargs);
private:
// Type for mapping from label names to Label objects.
typedef Unordered_map(std::string, Label*) Labels;
tree
make_receiver_parm_decl(Gogo*, Named_object*, tree);
tree
copy_parm_to_heap(Gogo*, Named_object*, tree);
void
build_defer_wrapper(Gogo*, Named_object*, tree*, tree*);
typedef std::vector<Named_object*> Named_results;
typedef std::vector<std::pair<Named_object*,
source_location> > Closure_fields;
// The function's type.
Function_type* type_;
// The enclosing function. This is NULL when there isn't one, which
// is the normal case.
Function* enclosing_;
// The named result variables, if any.
Named_results* named_results_;
// If there is a closure, this is the list of variables which appear
// in the closure. This is created by the parser, and then resolved
// to a real type when we lower parse trees.
Closure_fields closure_fields_;
// The closure variable, passed as a parameter using the static
// chain parameter. Normally NULL.
Named_object* closure_var_;
// The outer block of statements in the function.
Block* block_;
// The source location of the start of the function.
source_location location_;
// Labels defined or referenced in the function.
Labels labels_;
// The function decl.
tree fndecl_;
// A variable holding the defer stack variable. This is NULL unless
// we actually need a defer stack.
tree defer_stack_;
// True if this function calls the predeclared recover function.
bool calls_recover_;
// True if this a thunk built for a function which calls recover.
bool is_recover_thunk_;
// True if this function already has a recover thunk.
bool has_recover_thunk_;
};
// A function declaration.
class Function_declaration
{
public:
Function_declaration(Function_type* fntype, source_location location)
: fntype_(fntype), location_(location), asm_name_(), fndecl_(NULL)
{ }
Function_type*
type() const
{ return this->fntype_; }
source_location
location() const
{ return this->location_; }
const std::string&
asm_name() const
{ return this->asm_name_; }
// Set the assembler name.
void
set_asm_name(const std::string& asm_name)
{ this->asm_name_ = asm_name; }
// Return a decl for the function given an identifier.
tree
get_or_make_decl(Gogo*, Named_object*, tree id);
// Export a function declaration.
void
export_func(Export* exp, const std::string& name) const
{ Function::export_func_with_type(exp, name, this->fntype_); }
private:
// The type of the function.
Function_type* fntype_;
// The location of the declaration.
source_location location_;
// The assembler name: this is the name to use in references to the
// function. This is normally empty.
std::string asm_name_;
// The function decl if needed.
tree fndecl_;
};
// A variable.
class Variable
{
public:
Variable(Type*, Expression*, bool is_global, bool is_parameter,
bool is_receiver, source_location);
// Get the type of the variable.
Type*
type() const;
// Return whether the type is defined yet.
bool
has_type() const
{ return this->type_ != NULL; }
// Get the initial value.
Expression*
init() const
{ return this->init_; }
// Return whether there are any preinit statements.
bool
has_pre_init() const
{ return this->preinit_ != NULL; }
// Return the preinit statements if any.
Block*
preinit() const
{ return this->preinit_; }
// Return whether this is a global variable.
bool
is_global() const
{ return this->is_global_; }
// Return whether this is a function parameter.
bool
is_parameter() const
{ return this->is_parameter_; }
// Return whether this is the receiver parameter of a method.
bool
is_receiver() const
{ return this->is_receiver_; }
// Change this parameter to be a receiver. This is used when
// creating the thunks created for functions which call recover.
void
set_is_receiver()
{
gcc_assert(this->is_parameter_);
this->is_receiver_ = true;
}
// Change this parameter to not be a receiver. This is used when
// creating the thunks created for functions which call recover.
void
set_is_not_receiver()
{
gcc_assert(this->is_parameter_);
this->is_receiver_ = false;
}
// Return whether this is the varargs parameter of a function.
bool
is_varargs_parameter() const
{ return this->is_varargs_parameter_; }
// Whether this variable's address is taken.
bool
is_address_taken() const
{ return this->is_address_taken_; }
// Whether this variable should live in the heap.
bool
is_in_heap() const
{ return this->is_address_taken_ && !this->is_global_; }
// Get the source location of the variable's declaration.
source_location
location() const
{ return this->location_; }
// Record that this is the varargs parameter of a function.
void
set_is_varargs_parameter()
{
gcc_assert(this->is_parameter_);
this->is_varargs_parameter_ = true;
}
// Clear the initial value; used for error handling.
void
clear_init()
{ this->init_ = NULL; }
// Set the initial value; used for converting shortcuts.
void
set_init(Expression* init)
{ this->init_ = init; }
// Get the preinit block, a block of statements to be run before the
// initialization expression.
Block*
preinit_block();
// Add a statement to be run before the initialization expression.
// This is only used for global variables.
void
add_preinit_statement(Statement*);
// Lower the initialization expression after parsing is complete.
void
lower_init_expression(Gogo*, Named_object*);
// A special case: the init value is used only to determine the
// type. This is used if the variable is defined using := with the
// comma-ok form of a map index or a receive expression. The init
// value is actually the map index expression or receive expression.
// We use this because we may not know the right type at parse time.
void
set_type_from_init_tuple()
{ this->type_from_init_tuple_ = true; }
// Another special case: the init value is used only to determine
// the type. This is used if the variable is defined using := with
// a range clause. The init value is the range expression. The
// type of the variable is the index type of the range expression
// (i.e., the first value returned by a range).
void
set_type_from_range_index()
{ this->type_from_range_index_ = true; }
// Another special case: like set_type_from_range_index, but the
// type is the value type of the range expression (i.e., the second
// value returned by a range).
void
set_type_from_range_value()
{ this->type_from_range_value_ = true; }
// Another special case: the init value is used only to determine
// the type. This is used if the variable is defined using := with
// a case in a select statement. The init value is the channel.
// The type of the variable is the channel's element type.
void
set_type_from_chan_element()
{ this->type_from_chan_element_ = true; }
// After we lower the select statement, we once again set the type
// from the initialization expression.
void
clear_type_from_chan_element()
{
gcc_assert(this->type_from_chan_element_);
this->type_from_chan_element_ = false;
}
// Note that this variable was created for a type switch clause.
void
set_is_type_switch_var()
{ this->is_type_switch_var_ = true; }
// Traverse the initializer expression.
int
traverse_expression(Traverse*);
// Determine the type of the variable if necessary.
void
determine_type();
// Note that something takes the address of this variable.
void
set_address_taken()
{ this->is_address_taken_ = true; }
// Get the initial value of the variable as a tree. This may only
// be called if has_pre_init() returns false.
tree
get_init_tree(Gogo*, Named_object* function);
// Return a series of statements which sets the value of the
// variable in DECL. This should only be called is has_pre_init()
// returns true. DECL may be NULL for a sink variable.
tree
get_init_block(Gogo*, Named_object* function, tree decl);
// Export the variable.
void
export_var(Export*, const std::string& name) const;
// Import a variable.
static void
import_var(Import*, std::string* pname, Type** ptype);
private:
// The type of a tuple.
Type*
type_from_tuple(Expression*, bool) const;
// The type of a range.
Type*
type_from_range(Expression*, bool, bool) const;
// The element type of a channel.
Type*
type_from_chan_element(Expression*, bool) const;
// The variable's type. This may be NULL if the type is set from
// the expression.
Type* type_;
// The initial value. This may be NULL if the variable should be
// initialized to the default value for the type.
Expression* init_;
// Statements to run before the init statement.
Block* preinit_;
// Location of variable definition.
source_location location_;
// Whether this is a global variable.
bool is_global_ : 1;
// Whether this is a function parameter.
bool is_parameter_ : 1;
// Whether this is the receiver parameter of a method.
bool is_receiver_ : 1;
// Whether this is the varargs parameter of a function.
bool is_varargs_parameter_ : 1;
// Whether something takes the address of this variable.
bool is_address_taken_ : 1;
// True if we have lowered the initialization expression.
bool init_is_lowered_ : 1;
// True if init is a tuple used to set the type.
bool type_from_init_tuple_ : 1;
// True if init is a range clause and the type is the index type.
bool type_from_range_index_ : 1;
// True if init is a range clause and the type is the value type.
bool type_from_range_value_ : 1;
// True if init is a channel and the type is the channel's element type.
bool type_from_chan_element_ : 1;
// True if this is a variable created for a type switch case.
bool is_type_switch_var_ : 1;
};
// A variable which is really the name for a function return value, or
// part of one.
class Result_variable
{
public:
Result_variable(Type* type, Function* function, int index)
: type_(type), function_(function), index_(index),
is_address_taken_(false)
{ }
// Get the type of the result variable.
Type*
type() const
{ return this->type_; }
// Get the function that this is associated with.
Function*
function() const
{ return this->function_; }
// Index in the list of function results.
int
index() const
{ return this->index_; }
// Whether this variable's address is taken.
bool
is_address_taken() const
{ return this->is_address_taken_; }
// Note that something takes the address of this variable.
void
set_address_taken()
{ this->is_address_taken_ = true; }
// Whether this variable should live in the heap.
bool
is_in_heap() const
{ return this->is_address_taken_; }
private:
// Type of result variable.
Type* type_;
// Function with which this is associated.
Function* function_;
// Index in list of results.
int index_;
// Whether something takes the address of this variable.
bool is_address_taken_;
};
// The value we keep for a named constant. This lets us hold a type
// and an expression.
class Named_constant
{
public:
Named_constant(Type* type, Expression* expr, int iota_value,
source_location location)
: type_(type), expr_(expr), iota_value_(iota_value), location_(location),
lowering_(false)
{ }
Type*
type() const
{ return this->type_; }
Expression*
expr() const
{ return this->expr_; }
int
iota_value() const
{ return this->iota_value_; }
source_location
location() const
{ return this->location_; }
// Whether we are lowering.
bool
lowering() const
{ return this->lowering_; }
// Set that we are lowering.
void
set_lowering()
{ this->lowering_ = true; }
// We are no longer lowering.
void
clear_lowering()
{ this->lowering_ = false; }
// Traverse the expression.
int
traverse_expression(Traverse*);
// Determine the type of the constant if necessary.
void
determine_type();
// Indicate that we found and reported an error for this constant.
void
set_error();
// Export the constant.
void
export_const(Export*, const std::string& name) const;
// Import a constant.
static void
import_const(Import*, std::string*, Type**, Expression**);
private:
// The type of the constant.
Type* type_;
// The expression for the constant.
Expression* expr_;
// If the predeclared constant iota is used in EXPR_, this is the
// value it will have. We do this because at parse time we don't
// know whether the name "iota" will refer to the predeclared
// constant or to something else. We put in the right value in when
// we lower.
int iota_value_;
// The location of the definition.
source_location location_;
// Whether we are currently lowering this constant.
bool lowering_;
};
// A type declaration.
class Type_declaration
{
public:
Type_declaration(source_location location)
: location_(location), in_function_(NULL), methods_(),
issued_warning_(false)
{ }
// Return the location.
source_location
location() const
{ return this->location_; }
// Return the function in which this type is declared. This will
// return NULL for a type declared in global scope.
Named_object*
in_function()
{ return this->in_function_; }
// Set the function in which this type is declared.
void
set_in_function(Named_object* f)
{ this->in_function_ = f; }
// Add a method to this type. This is used when methods are defined
// before the type.
Named_object*
add_method(const std::string& name, Function* function);
// Add a method declaration to this type.
Named_object*
add_method_declaration(const std::string& name, Function_type* type,
source_location location);
// Return whether any methods were defined.
bool
has_methods() const;
// Define methods when the real type is known.
void
define_methods(Named_type*);
// This is called if we are trying to use this type. It returns
// true if we should issue a warning.
bool
using_type();
private:
typedef std::vector<Named_object*> Methods;
// The location of the type declaration.
source_location location_;
// If this type is declared in a function, a pointer back to the
// function in which it is defined.
Named_object* in_function_;
// Methods defined before the type is defined.
Methods methods_;
// True if we have issued a warning about a use of this type
// declaration when it is undefined.
bool issued_warning_;
};
// An unknown object. These are created by the parser for forward
// references to names which have not been seen before. In a correct
// program, these will always point to a real definition by the end of
// the parse. Because they point to another Named_object, these may
// only be referenced by Unknown_expression objects.
class Unknown_name
{
public:
Unknown_name(source_location location)
: location_(location), real_named_object_(NULL)
{ }
// Return the location where this name was first seen.
source_location
location() const
{ return this->location_; }
// Return the real named object that this points to, or NULL if it
// was never resolved.
Named_object*
real_named_object() const
{ return this->real_named_object_; }
// Set the real named object that this points to.
void
set_real_named_object(Named_object* no);
private:
// The location where this name was first seen.
source_location location_;
// The real named object when it is known.
Named_object*
real_named_object_;
};
// A named object named. This is the result of a declaration. We
// don't use a superclass because they all have to be handled
// differently.
class Named_object
{
public:
enum Classification
{
// An uninitialized Named_object. We should never see this.
NAMED_OBJECT_UNINITIALIZED,
// An unknown name. This is used for forward references. In a
// correct program, these will all be resolved by the end of the
// parse.
NAMED_OBJECT_UNKNOWN,
// A const.
NAMED_OBJECT_CONST,
// A type.
NAMED_OBJECT_TYPE,
// A forward type declaration.
NAMED_OBJECT_TYPE_DECLARATION,
// A var.
NAMED_OBJECT_VAR,
// A result variable in a function.
NAMED_OBJECT_RESULT_VAR,
// The blank identifier--the special variable named _.
NAMED_OBJECT_SINK,
// A func.
NAMED_OBJECT_FUNC,
// A forward func declaration.
NAMED_OBJECT_FUNC_DECLARATION,
// A package.
NAMED_OBJECT_PACKAGE
};
// Return the classification.
Classification
classification() const
{ return this->classification_; }
// Classifiers.
bool
is_unknown() const
{ return this->classification_ == NAMED_OBJECT_UNKNOWN; }
bool
is_const() const
{ return this->classification_ == NAMED_OBJECT_CONST; }
bool
is_type() const
{ return this->classification_ == NAMED_OBJECT_TYPE; }
bool
is_type_declaration() const
{ return this->classification_ == NAMED_OBJECT_TYPE_DECLARATION; }
bool
is_variable() const
{ return this->classification_ == NAMED_OBJECT_VAR; }
bool
is_result_variable() const
{ return this->classification_ == NAMED_OBJECT_RESULT_VAR; }
bool
is_sink() const
{ return this->classification_ == NAMED_OBJECT_SINK; }
bool
is_function() const
{ return this->classification_ == NAMED_OBJECT_FUNC; }
bool
is_function_declaration() const
{ return this->classification_ == NAMED_OBJECT_FUNC_DECLARATION; }
bool
is_package() const
{ return this->classification_ == NAMED_OBJECT_PACKAGE; }
// Creators.
static Named_object*
make_unknown_name(const std::string& name, source_location);
static Named_object*
make_constant(const Typed_identifier&, const Package*, Expression*,
int iota_value);
static Named_object*
make_type(const std::string&, const Package*, Type*, source_location);
static Named_object*
make_type_declaration(const std::string&, const Package*, source_location);
static Named_object*
make_variable(const std::string&, const Package*, Variable*);
static Named_object*
make_result_variable(const std::string&, Result_variable*);
static Named_object*
make_sink();
static Named_object*
make_function(const std::string&, const Package*, Function*);
static Named_object*
make_function_declaration(const std::string&, const Package*, Function_type*,
source_location);
static Named_object*
make_package(const std::string& alias, Package* package);
// Getters.
Unknown_name*
unknown_value()
{
gcc_assert(this->classification_ == NAMED_OBJECT_UNKNOWN);
return this->u_.unknown_value;
}
const Unknown_name*
unknown_value() const
{
gcc_assert(this->classification_ == NAMED_OBJECT_UNKNOWN);
return this->u_.unknown_value;
}
Named_constant*
const_value()
{
gcc_assert(this->classification_ == NAMED_OBJECT_CONST);
return this->u_.const_value;
}
const Named_constant*
const_value() const
{
gcc_assert(this->classification_ == NAMED_OBJECT_CONST);
return this->u_.const_value;
}
Named_type*
type_value()
{
gcc_assert(this->classification_ == NAMED_OBJECT_TYPE);
return this->u_.type_value;
}
const Named_type*
type_value() const
{
gcc_assert(this->classification_ == NAMED_OBJECT_TYPE);
return this->u_.type_value;
}
Type_declaration*
type_declaration_value()
{
gcc_assert(this->classification_ == NAMED_OBJECT_TYPE_DECLARATION);
return this->u_.type_declaration;
}
const Type_declaration*
type_declaration_value() const
{
gcc_assert(this->classification_ == NAMED_OBJECT_TYPE_DECLARATION);
return this->u_.type_declaration;
}
Variable*
var_value()
{
gcc_assert(this->classification_ == NAMED_OBJECT_VAR);
return this->u_.var_value;
}
const Variable*
var_value() const
{
gcc_assert(this->classification_ == NAMED_OBJECT_VAR);
return this->u_.var_value;
}
Result_variable*
result_var_value()
{
gcc_assert(this->classification_ == NAMED_OBJECT_RESULT_VAR);
return this->u_.result_var_value;
}
const Result_variable*
result_var_value() const
{
gcc_assert(this->classification_ == NAMED_OBJECT_RESULT_VAR);
return this->u_.result_var_value;
}
Function*
func_value()
{
gcc_assert(this->classification_ == NAMED_OBJECT_FUNC);
return this->u_.func_value;
}
const Function*
func_value() const
{
gcc_assert(this->classification_ == NAMED_OBJECT_FUNC);
return this->u_.func_value;
}
Function_declaration*
func_declaration_value()
{
gcc_assert(this->classification_ == NAMED_OBJECT_FUNC_DECLARATION);
return this->u_.func_declaration_value;
}
const Function_declaration*
func_declaration_value() const
{
gcc_assert(this->classification_ == NAMED_OBJECT_FUNC_DECLARATION);
return this->u_.func_declaration_value;
}
Package*
package_value()
{
gcc_assert(this->classification_ == NAMED_OBJECT_PACKAGE);
return this->u_.package_value;
}
const Package*
package_value() const
{
gcc_assert(this->classification_ == NAMED_OBJECT_PACKAGE);
return this->u_.package_value;
}
const std::string&
name() const
{ return this->name_; }
// Return the name to use in an error message. The difference is
// that if this Named_object is defined in a different package, this
// will return PACKAGE.NAME.
std::string
message_name() const;
const Package*
package() const
{ return this->package_; }
// Resolve an unknown value if possible. This returns the same
// Named_object or a new one.
Named_object*
resolve()
{
Named_object* ret = this;
if (this->is_unknown())
{
Named_object* r = this->unknown_value()->real_named_object();
if (r != NULL)
ret = r;
}
return ret;
}
const Named_object*
resolve() const
{
const Named_object* ret = this;
if (this->is_unknown())
{
const Named_object* r = this->unknown_value()->real_named_object();
if (r != NULL)
ret = r;
}
return ret;
}
// The location where this object was defined or referenced.
source_location
location() const;
// Return a tree for the external identifier for this object.
tree
get_id(Gogo*);
// Return a tree representing this object.
tree
get_tree(Gogo*, Named_object* function);
// Define a type declaration.
void
set_type_value(Named_type*);
// Define a function declaration.
void
set_function_value(Function*);
// Export this object.
void
export_named_object(Export*) const;
private:
Named_object(const std::string&, const Package*, Classification);
// The name of the object.
std::string name_;
// The package that this object is in. This is NULL if it is in the
// file we are compiling.
const Package* package_;
// The type of object this is.
Classification classification_;
// The real data.
union
{
Unknown_name* unknown_value;
Named_constant* const_value;
Named_type* type_value;
Type_declaration* type_declaration;
Variable* var_value;
Result_variable* result_var_value;
Function* func_value;
Function_declaration* func_declaration_value;
Package* package_value;
} u_;
// The DECL tree for this object if we have already converted it.
tree tree_;
};
// A binding contour. This binds names to objects.
class Bindings
{
public:
// Type for mapping from names to objects.
typedef Unordered_map(std::string, Named_object*) Contour;
Bindings(Bindings* enclosing);
// Add an unknown name.
Named_object*
add_unknown_name(const std::string& name, source_location location)
{
return this->add_named_object(Named_object::make_unknown_name(name,
location));
}
// Add a constant.
Named_object*
add_constant(const Typed_identifier& tid, const Package* package,
Expression* expr, int iota_value)
{
return this->add_named_object(Named_object::make_constant(tid, package,
expr,
iota_value));
}
// Add a type.
Named_object*
add_type(const std::string& name, const Package* package, Type* type,
source_location location)
{
return this->add_named_object(Named_object::make_type(name, package, type,
location));
}
// Add a named type. This is used for builtin types, and to add an
// imported type to the global scope.
Named_object*
add_named_type(Named_type* named_type);
// Add a type declaration.
Named_object*
add_type_declaration(const std::string& name, const Package* package,
source_location location)
{
Named_object* no = Named_object::make_type_declaration(name, package,
location);
return this->add_named_object(no);
}
// Add a variable.
Named_object*
add_variable(const std::string& name, const Package* package,
Variable* variable)
{
return this->add_named_object(Named_object::make_variable(name, package,
variable));
}
// Add a result variable.
Named_object*
add_result_variable(const std::string& name, Result_variable* result)
{
return this->add_named_object(Named_object::make_result_variable(name,
result));
}
// Add a function.
Named_object*
add_function(const std::string& name, const Package*, Function* function);
// Add a function declaration.
Named_object*
add_function_declaration(const std::string& name, const Package* package,
Function_type* type, source_location location);
// Add a package. The location is the location of the import
// statement.
Named_object*
add_package(const std::string& alias, Package* package)
{
Named_object* no = Named_object::make_package(alias, package);
return this->add_named_object(no);
}
// Define a type which was already declared.
void
define_type(Named_object*, Named_type*);
// Add a method to the list of objects. This is not added to the
// lookup table.
void
add_method(Named_object*);
// Add a named object to this binding.
Named_object*
add_named_object(Named_object* no)
{ return this->add_named_object_to_contour(&this->bindings_, no); }
// Clear all names in file scope from the bindings.
void
clear_file_scope();
// Look up a name in this binding contour and in any enclosing
// binding contours. This returns NULL if the name is not found.
Named_object*
lookup(const std::string&) const;
// Look up a name in this binding contour without looking in any
// enclosing binding contours. Returns NULL if the name is not found.
Named_object*
lookup_local(const std::string&) const;
// Remove a name.
void
remove_binding(Named_object*);
// Traverse the tree. See the Traverse class.
int
traverse(Traverse*, bool is_global);
// Iterate over definitions. This does not include things which
// were only declared.
typedef std::vector<Named_object*>::const_iterator
const_definitions_iterator;
const_definitions_iterator
begin_definitions() const
{ return this->named_objects_.begin(); }
const_definitions_iterator
end_definitions() const
{ return this->named_objects_.end(); }
// Return the number of definitions.
size_t
size_definitions() const
{ return this->named_objects_.size(); }
// Return whether there are no definitions.
bool
empty_definitions() const
{ return this->named_objects_.empty(); }
// Iterate over declarations. This is everything that has been
// declared, which includes everything which has been defined.
typedef Contour::const_iterator const_declarations_iterator;
const_declarations_iterator
begin_declarations() const
{ return this->bindings_.begin(); }
const_declarations_iterator
end_declarations() const
{ return this->bindings_.end(); }
// Return the number of declarations.
size_t
size_declarations() const
{ return this->bindings_.size(); }
// Return whether there are no declarations.
bool
empty_declarations() const
{ return this->bindings_.empty(); }
// Return the first declaration.
Named_object*
first_declaration()
{ return this->bindings_.empty() ? NULL : this->bindings_.begin()->second; }
private:
Named_object*
add_named_object_to_contour(Contour*, Named_object*);
Named_object*
new_definition(Named_object*, Named_object*);
// Enclosing bindings.
Bindings* enclosing_;
// The list of objects.
std::vector<Named_object*> named_objects_;
// The mapping from names to objects.
Contour bindings_;
};
// A label.
class Label
{
public:
Label(const std::string& name)
: name_(name), location_(0), decl_(NULL)
{ }
// Return the label's name.
const std::string&
name() const
{ return this->name_; }
// Return whether the label has been defined.
bool
is_defined() const
{ return this->location_ != 0; }
// Return the location of the definition.
source_location
location() const
{ return this->location_; }
// Define the label at LOCATION.
void
define(source_location location)
{
gcc_assert(this->location_ == 0);
this->location_ = location;
}
// Return the LABEL_DECL for this decl.
tree
get_decl();
// Return an expression for the address of this label.
tree
get_addr(source_location location);
private:
// The name of the label.
std::string name_;
// The location of the definition. This is 0 if the label has not
// yet been defined.
source_location location_;
// The LABEL_DECL.
tree decl_;
};
// An unnamed label. These are used when lowering loops.
class Unnamed_label
{
public:
Unnamed_label(source_location location)
: location_(location), decl_(NULL)
{ }
// Get the location where the label is defined.
source_location
location() const
{ return this->location_; }
// Set the location where the label is defined.
void
set_location(source_location location)
{ this->location_ = location; }
// Return a statement which defines this label.
tree
get_definition();
// Return a goto to this label from LOCATION.
tree
get_goto(source_location location);
private:
// Return the LABEL_DECL to use with GOTO_EXPR.
tree
get_decl();
// The location where the label is defined.
source_location location_;
// The LABEL_DECL.
tree decl_;
};
// An imported package.
class Package
{
public:
Package(const std::string& name, const std::string& unique_prefix,
source_location location);
// The real name of this package. This may be different from the
// name in the associated Named_object if the import statement used
// an alias.
const std::string&
name() const
{ return this->name_; }
// Return the location of the import statement.
source_location
location() const
{ return this->location_; }
// Get the unique prefix used for all symbols exported from this
// package.
const std::string&
unique_prefix() const
{
gcc_assert(!this->unique_prefix_.empty());
return this->unique_prefix_;
}
// The priority of this package. The init function of packages with
// lower priority must be run before the init function of packages
// with higher priority.
int
priority() const
{ return this->priority_; }
// Set the priority.
void
set_priority(int priority);
// Return the bindings.
Bindings*
bindings()
{ return this->bindings_; }
// Whether some symbol from the package was used.
bool
used() const
{ return this->used_; }
// Note that some symbol from this package was used.
void
set_used() const
{ this->used_ = true; }
// Clear the used field for the next file.
void
clear_used()
{ this->used_ = false; }
// Whether this package was imported in the current file.
bool
is_imported() const
{ return this->is_imported_; }
// Note that this package was imported in the current file.
void
set_is_imported()
{ this->is_imported_ = true; }
// Clear the imported field for the next file.
void
clear_is_imported()
{ this->is_imported_ = false; }
// Whether this package was imported with a name of "_".
bool
uses_sink_alias() const
{ return this->uses_sink_alias_; }
// Note that this package was imported with a name of "_".
void
set_uses_sink_alias()
{ this->uses_sink_alias_ = true; }
// Clear the sink alias field for the next file.
void
clear_uses_sink_alias()
{ this->uses_sink_alias_ = false; }
// Look up a name in the package. Returns NULL if the name is not
// found.
Named_object*
lookup(const std::string& name) const
{ return this->bindings_->lookup(name); }
// Set the location of the package. This is used if it is seen in a
// different import before it is really imported.
void
set_location(source_location location)
{ this->location_ = location; }
// Add a constant to the package.
Named_object*
add_constant(const Typed_identifier& tid, Expression* expr)
{ return this->bindings_->add_constant(tid, this, expr, 0); }
// Add a type to the package.
Named_object*
add_type(const std::string& name, Type* type, source_location location)
{ return this->bindings_->add_type(name, this, type, location); }
// Add a type declaration to the package.
Named_object*
add_type_declaration(const std::string& name, source_location location)
{ return this->bindings_->add_type_declaration(name, this, location); }
// Add a variable to the package.
Named_object*
add_variable(const std::string& name, Variable* variable)
{ return this->bindings_->add_variable(name, this, variable); }
// Add a function declaration to the package.
Named_object*
add_function_declaration(const std::string& name, Function_type* type,
source_location loc)
{ return this->bindings_->add_function_declaration(name, this, type, loc); }
// Determine types of constants.
void
determine_types();
private:
// The real name of this package.
std::string name_;
// The unique prefix for all exported global symbols.
std::string unique_prefix_;
// The names in this package.
Bindings* bindings_;
// The priority of this package. A package has a priority higher
// than the priority of all of the packages that it imports. This
// is used to run init functions in the right order.
int priority_;
// The location of the import statement.
source_location location_;
// True if some name from this package was used. This is mutable
// because we can use a package even if we have a const pointer to
// it.
mutable bool used_;
// True if this package was imported in the current file.
bool is_imported_;
// True if this package was imported with a name of "_".
bool uses_sink_alias_;
};
// Return codes for the traversal functions. This is not an enum
// because we want to be able to declare traversal functions in other
// header files without including this one.
// Continue traversal as usual.
const int TRAVERSE_CONTINUE = -1;
// Exit traversal.
const int TRAVERSE_EXIT = 0;
// Continue traversal, but skip components of the current object.
// E.g., if this is returned by Traverse::statement, we do not
// traverse the expressions in the statement even if
// traverse_expressions is set in the traverse_mask.
const int TRAVERSE_SKIP_COMPONENTS = 1;
// This class is used when traversing the parse tree. The caller uses
// a subclass which overrides functions as desired.
class Traverse
{
public:
// These bitmasks say what to traverse.
static const unsigned int traverse_variables = 0x1;
static const unsigned int traverse_constants = 0x2;
static const unsigned int traverse_functions = 0x4;
static const unsigned int traverse_blocks = 0x8;
static const unsigned int traverse_statements = 0x10;
static const unsigned int traverse_expressions = 0x20;
static const unsigned int traverse_types = 0x40;
Traverse(unsigned int traverse_mask)
: traverse_mask_(traverse_mask), types_seen_(NULL), expressions_seen_(NULL)
{ }
virtual ~Traverse();
// The bitmask of what to traverse.
unsigned int
traverse_mask() const
{ return this->traverse_mask_; }
// Record that we are going to traverse a type. This returns true
// if the type has already been seen in this traversal. This is
// required because types, unlike expressions, can form a circular
// graph.
bool
remember_type(const Type*);
// Record that we are going to see an expression. This returns true
// if the expression has already been seen in this traversal. This
// is only needed for cases where multiple expressions can point to
// a single one.
bool
remember_expression(const Expression*);
// These functions return one of the TRAVERSE codes defined above.
// If traverse_variables is set in the mask, this is called for
// every variable in the tree.
virtual int
variable(Named_object*);
// If traverse_constants is set in the mask, this is called for
// every named constant in the tree. The bool parameter is true for
// a global constant.
virtual int
constant(Named_object*, bool);
// If traverse_functions is set in the mask, this is called for
// every function in the tree.
virtual int
function(Named_object*);
// If traverse_blocks is set in the mask, this is called for every
// block in the tree.
virtual int
block(Block*);
// If traverse_statements is set in the mask, this is called for
// every statement in the tree.
virtual int
statement(Block*, size_t* index, Statement*);
// If traverse_expressions is set in the mask, this is called for
// every expression in the tree.
virtual int
expression(Expression**);
// If traverse_types is set in the mask, this is called for every
// type in the tree.
virtual int
type(Type*);
private:
typedef Unordered_set_hash(const Type*, Type_hash_identical,
Type_identical) Types_seen;
typedef Unordered_set(const Expression*) Expressions_seen;
// Bitmask of what sort of objects to traverse.
unsigned int traverse_mask_;
// Types which have been seen in this traversal.
Types_seen* types_seen_;
// Expressions which have been seen in this traversal.
Expressions_seen* expressions_seen_;
};
// When translating the gogo IR into trees, this is the context we
// pass down the blocks and statements.
class Translate_context
{
public:
Translate_context(Gogo* gogo, Named_object* function, Block* block,
tree block_tree)
: gogo_(gogo), function_(function), block_(block), block_tree_(block_tree),
is_const_(false)
{ }
// Accessors.
Gogo*
gogo()
{ return this->gogo_; }
Named_object*
function()
{ return this->function_; }
Block*
block()
{ return this->block_; }
tree
block_tree()
{ return this->block_tree_; }
bool
is_const()
{ return this->is_const_; }
// Make a constant context.
void
set_is_const()
{ this->is_const_ = true; }
private:
// The IR for the entire compilation unit.
Gogo* gogo_;
// The function we are currently translating.
Named_object* function_;
// The block we are currently translating.
Block *block_;
// The BLOCK node for the current block.
tree block_tree_;
// Whether this is being evaluated in a constant context. This is
// used for type descriptor initializers.
bool is_const_;
};
// Runtime error codes. These must match the values in
// libgo/runtime/go-runtime-error.c.
// Slice index out of bounds: negative or larger than the length of
// the slice.
static const int RUNTIME_ERROR_SLICE_INDEX_OUT_OF_BOUNDS = 0;
// Array index out of bounds.
static const int RUNTIME_ERROR_ARRAY_INDEX_OUT_OF_BOUNDS = 1;
// String index out of bounds.
static const int RUNTIME_ERROR_STRING_INDEX_OUT_OF_BOUNDS = 2;
// Slice slice out of bounds: negative or larger than the length of
// the slice or high bound less than low bound.
static const int RUNTIME_ERROR_SLICE_SLICE_OUT_OF_BOUNDS = 3;
// Array slice out of bounds.
static const int RUNTIME_ERROR_ARRAY_SLICE_OUT_OF_BOUNDS = 4;
// String slice out of bounds.
static const int RUNTIME_ERROR_STRING_SLICE_OUT_OF_BOUNDS = 5;
// Dereference of nil pointer. This is used when there is a
// dereference of a pointer to a very large struct or array, to ensure
// that a gigantic array is not used a proxy to access random memory
// locations.
static const int RUNTIME_ERROR_NIL_DEREFERENCE = 6;
// Slice length or capacity out of bounds in make: negative or
// overflow or length greater than capacity.
static const int RUNTIME_ERROR_MAKE_SLICE_OUT_OF_BOUNDS = 7;
// Map capacity out of bounds in make: negative or overflow.
static const int RUNTIME_ERROR_MAKE_MAP_OUT_OF_BOUNDS = 8;
// Channel capacity out of bounds in make: negative or overflow.
static const int RUNTIME_ERROR_MAKE_CHAN_OUT_OF_BOUNDS = 9;
// This is used by some of the langhooks.
extern Gogo* go_get_gogo();
// Whether we have seen any errors. FIXME: Replace with a backend
// interface.
extern bool saw_errors();
#endif // !defined(GO_GOGO_H)
// gogo.h -- Go frontend parsed representation. -*- C++ -*-
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#ifndef GO_GOGO_H
#define GO_GOGO_H
class Traverse;
class Type;
class Type_hash_identical;
class Type_equal;
class Type_identical;
class Typed_identifier;
class Typed_identifier_list;
class Function_type;
class Expression;
class Statement;
class Temporary_statement;
class Block;
class Function;
class Bindings;
class Package;
class Variable;
class Pointer_type;
class Struct_type;
class Struct_field;
class Struct_field_list;
class Array_type;
class Map_type;
class Channel_type;
class Interface_type;
class Named_type;
class Forward_declaration_type;
class Method;
class Methods;
class Named_object;
class Label;
class Translate_context;
class Backend;
class Export;
class Import;
class Bexpression;
class Bstatement;
class Bblock;
class Bvariable;
class Blabel;
// This file declares the basic classes used to hold the internal
// representation of Go which is built by the parser.
// An initialization function for an imported package. This is a
// magic function which initializes variables and runs the "init"
// function.
class Import_init
{
public:
Import_init(const std::string& package_name, const std::string& init_name,
int priority)
: package_name_(package_name), init_name_(init_name), priority_(priority)
{ }
// The name of the package being imported.
const std::string&
package_name() const
{ return this->package_name_; }
// The name of the package's init function.
const std::string&
init_name() const
{ return this->init_name_; }
// The priority of the initialization function. Functions with a
// lower priority number must be run first.
int
priority() const
{ return this->priority_; }
private:
// The name of the package being imported.
std::string package_name_;
// The name of the package's init function.
std::string init_name_;
// The priority.
int priority_;
};
// For sorting purposes.
inline bool
operator<(const Import_init& i1, const Import_init& i2)
{
if (i1.priority() < i2.priority())
return true;
if (i1.priority() > i2.priority())
return false;
if (i1.package_name() != i2.package_name())
return i1.package_name() < i2.package_name();
return i1.init_name() < i2.init_name();
}
// The holder for the internal representation of the entire
// compilation unit.
class Gogo
{
public:
// Create the IR, passing in the sizes of the types "int" and
// "uintptr" in bits.
Gogo(Backend* backend, int int_type_size, int pointer_size);
// Get the backend generator.
Backend*
backend()
{ return this->backend_; }
// Get the package name.
const std::string&
package_name() const;
// Set the package name.
void
set_package_name(const std::string&, source_location);
// Return whether this is the "main" package.
bool
is_main_package() const;
// If necessary, adjust the name to use for a hidden symbol. We add
// a prefix of the package name, so that hidden symbols in different
// packages do not collide.
std::string
pack_hidden_name(const std::string& name, bool is_exported) const
{
return (is_exported
? name
: ('.' + this->unique_prefix()
+ '.' + this->package_name()
+ '.' + name));
}
// Unpack a name which may have been hidden. Returns the
// user-visible name of the object.
static std::string
unpack_hidden_name(const std::string& name)
{ return name[0] != '.' ? name : name.substr(name.rfind('.') + 1); }
// Return whether a possibly packed name is hidden.
static bool
is_hidden_name(const std::string& name)
{ return name[0] == '.'; }
// Return the package prefix of a hidden name.
static std::string
hidden_name_prefix(const std::string& name)
{
go_assert(Gogo::is_hidden_name(name));
return name.substr(1, name.rfind('.') - 1);
}
// Given a name which may or may not have been hidden, return the
// name to use in an error message.
static std::string
message_name(const std::string& name);
// Return whether a name is the blank identifier _.
static bool
is_sink_name(const std::string& name)
{
return (name[0] == '.'
&& name[name.length() - 1] == '_'
&& name[name.length() - 2] == '.');
}
// Return the unique prefix to use for all exported symbols.
const std::string&
unique_prefix() const;
// Set the unique prefix.
void
set_unique_prefix(const std::string&);
// Return the priority to use for the package we are compiling.
// This is two more than the largest priority of any package we
// import.
int
package_priority() const;
// Import a package. FILENAME is the file name argument, LOCAL_NAME
// is the local name to give to the package. If LOCAL_NAME is empty
// the declarations are added to the global scope.
void
import_package(const std::string& filename, const std::string& local_name,
bool is_local_name_exported, source_location);
// Whether we are the global binding level.
bool
in_global_scope() const;
// Look up a name in the current binding contours.
Named_object*
lookup(const std::string&, Named_object** pfunction) const;
// Look up a name in the current block.
Named_object*
lookup_in_block(const std::string&) const;
// Look up a name in the global namespace--the universal scope.
Named_object*
lookup_global(const char*) const;
// Add a new imported package. REAL_NAME is the real name of the
// package. ALIAS is the alias of the package; this may be the same
// as REAL_NAME. This sets *PADD_TO_GLOBALS if symbols added to
// this package should be added to the global namespace; this is
// true if the alias is ".". LOCATION is the location of the import
// statement. This returns the new package, or NULL on error.
Package*
add_imported_package(const std::string& real_name, const std::string& alias,
bool is_alias_exported,
const std::string& unique_prefix,
source_location location,
bool* padd_to_globals);
// Register a package. This package may or may not be imported.
// This returns the Package structure for the package, creating if
// it necessary.
Package*
register_package(const std::string& name, const std::string& unique_prefix,
source_location);
// Start compiling a function. ADD_METHOD_TO_TYPE is true if a
// method function should be added to the type of its receiver.
Named_object*
start_function(const std::string& name, Function_type* type,
bool add_method_to_type, source_location);
// Finish compiling a function.
void
finish_function(source_location);
// Return the current function.
Named_object*
current_function() const;
// Start a new block. This is not initially associated with a
// function.
void
start_block(source_location);
// Finish the current block and return it.
Block*
finish_block(source_location);
// Declare an unknown name. This is used while parsing. The name
// must be resolved by the end of the parse. Unknown names are
// always added at the package level.
Named_object*
add_unknown_name(const std::string& name, source_location);
// Declare a function.
Named_object*
declare_function(const std::string&, Function_type*, source_location);
// Add a label.
Label*
add_label_definition(const std::string&, source_location);
// Add a label reference.
Label*
add_label_reference(const std::string&);
// Add a statement to the current block.
void
add_statement(Statement*);
// Add a block to the current block.
void
add_block(Block*, source_location);
// Add a constant.
Named_object*
add_constant(const Typed_identifier&, Expression*, int iota_value);
// Add a type.
void
add_type(const std::string&, Type*, source_location);
// Add a named type. This is used for builtin types, and to add an
// imported type to the global scope.
void
add_named_type(Named_type*);
// Declare a type.
Named_object*
declare_type(const std::string&, source_location);
// Declare a type at the package level. This is used when the
// parser sees an unknown name where a type name is required.
Named_object*
declare_package_type(const std::string&, source_location);
// Define a type which was already declared.
void
define_type(Named_object*, Named_type*);
// Add a variable.
Named_object*
add_variable(const std::string&, Variable*);
// Add a sink--a reference to the blank identifier _.
Named_object*
add_sink();
// Add a named object to the current namespace. This is used for
// import . "package".
void
add_named_object(Named_object*);
// Return a name to use for a thunk function. A thunk function is
// one we create during the compilation, for a go statement or a
// defer statement or a method expression.
static std::string
thunk_name();
// Return whether an object is a thunk.
static bool
is_thunk(const Named_object*);
// Note that we've seen an interface type. This is used to build
// all required interface method tables.
void
record_interface_type(Interface_type*);
// Note that we need an initialization function.
void
set_need_init_fn()
{ this->need_init_fn_ = true; }
// Clear out all names in file scope. This is called when we start
// parsing a new file.
void
clear_file_scope();
// Traverse the tree. See the Traverse class.
void
traverse(Traverse*);
// Define the predeclared global names.
void
define_global_names();
// Verify and complete all types.
void
verify_types();
// Lower the parse tree.
void
lower_parse_tree();
// Lower all the statements in a block.
void
lower_block(Named_object* function, Block*);
// Lower an expression.
void
lower_expression(Named_object* function, Expression**);
// Lower a constant.
void
lower_constant(Named_object*);
// Finalize the method lists and build stub methods for named types.
void
finalize_methods();
// Work out the types to use for unspecified variables and
// constants.
void
determine_types();
// Type check the program.
void
check_types();
// Check the types in a single block. This is used for complicated
// go statements.
void
check_types_in_block(Block*);
// Check for return statements.
void
check_return_statements();
// Do all exports.
void
do_exports();
// Add an import control function for an imported package to the
// list.
void
add_import_init_fn(const std::string& package_name,
const std::string& init_name, int prio);
// Turn short-cut operators (&&, ||) into explicit if statements.
void
remove_shortcuts();
// Use temporary variables to force order of evaluation.
void
order_evaluations();
// Build thunks for functions which call recover.
void
build_recover_thunks();
// Simplify statements which might use thunks: go and defer
// statements.
void
simplify_thunk_statements();
// Convert named types to the backend representation.
void
convert_named_types();
// Convert named types in a list of bindings.
void
convert_named_types_in_bindings(Bindings*);
// True if named types have been converted to the backend
// representation.
bool
named_types_are_converted() const
{ return this->named_types_are_converted_; }
// Write out the global values.
void
write_globals();
// Build a call to a builtin function. PDECL should point to a NULL
// initialized static pointer which will hold the fndecl. NAME is
// the name of the function. NARGS is the number of arguments.
// RETTYPE is the return type. It is followed by NARGS pairs of
// type and argument (both trees).
static tree
call_builtin(tree* pdecl, source_location, const char* name, int nargs,
tree rettype, ...);
// Build a call to the runtime error function.
static tree
runtime_error(int code, source_location);
// Build a builtin struct with a list of fields.
static tree
builtin_struct(tree* ptype, const char* struct_name, tree struct_type,
int nfields, ...);
// Mark a function declaration as a builtin library function.
static void
mark_fndecl_as_builtin_library(tree fndecl);
// Build the type of the struct that holds a slice for the given
// element type.
tree
slice_type_tree(tree element_type_tree);
// Given a tree for a slice type, return the tree for the element
// type.
static tree
slice_element_type_tree(tree slice_type_tree);
// Build a constructor for a slice. SLICE_TYPE_TREE is the type of
// the slice. VALUES points to the values. COUNT is the size,
// CAPACITY is the capacity. If CAPACITY is NULL, it is set to
// COUNT.
static tree
slice_constructor(tree slice_type_tree, tree values, tree count,
tree capacity);
// Build a constructor for an empty slice. SLICE_TYPE_TREE is the
// type of the slice.
static tree
empty_slice_constructor(tree slice_type_tree);
// Build a map descriptor.
tree
map_descriptor(Map_type*);
// Return a tree for the type of a map descriptor. This is struct
// __go_map_descriptor in libgo/runtime/map.h. This is the same for
// all map types.
tree
map_descriptor_type();
// Build a type descriptor for TYPE using INITIALIZER as the type
// descriptor. This builds a new decl stored in *PDECL.
void
build_type_descriptor_decl(const Type*, Expression* initializer,
tree* pdecl);
// Build required interface method tables.
void
build_interface_method_tables();
// Build an interface method table for a type: a list of function
// pointers, one for each interface method. This returns a decl.
tree
interface_method_table_for_type(const Interface_type*, Named_type*,
bool is_pointer);
// Return a tree which allocate SIZE bytes to hold values of type
// TYPE.
tree
allocate_memory(Type *type, tree size, source_location);
// Return a type to use for pointer to const char.
static tree
const_char_pointer_type_tree();
// Build a string constant with the right type.
static tree
string_constant_tree(const std::string&);
// Build a Go string constant. This returns a pointer to the
// constant.
tree
go_string_constant_tree(const std::string&);
// Receive a value from a channel.
static tree
receive_from_channel(tree type_tree, tree channel, bool for_select,
source_location);
// Return a tree for receiving an integer on a channel.
static tree
receive_as_64bit_integer(tree type, tree channel, bool blocking,
bool for_select);
// Make a trampoline which calls FNADDR passing CLOSURE.
tree
make_trampoline(tree fnaddr, tree closure, source_location);
private:
// During parsing, we keep a stack of functions. Each function on
// the stack is one that we are currently parsing. For each
// function, we keep track of the current stack of blocks.
struct Open_function
{
// The function.
Named_object* function;
// The stack of active blocks in the function.
std::vector<Block*> blocks;
};
// The stack of functions.
typedef std::vector<Open_function> Open_functions;
// Create trees for implicit builtin functions.
void
define_builtin_function_trees();
// Set up the built-in unsafe package.
void
import_unsafe(const std::string&, bool is_exported, source_location);
// Add a new imported package.
Named_object*
add_package(const std::string& real_name, const std::string& alias,
const std::string& unique_prefix, source_location location);
// Return the current binding contour.
Bindings*
current_bindings();
const Bindings*
current_bindings() const;
// Return the current block.
Block*
current_block();
// Get the name of the magic initialization function.
const std::string&
get_init_fn_name();
// Get the decl for the magic initialization function.
tree
initialization_function_decl();
// Write the magic initialization function.
void
write_initialization_function(tree fndecl, tree init_stmt_list);
// Initialize imported packages.
void
init_imports(tree*);
// Register variables with the garbage collector.
void
register_gc_vars(const std::vector<Named_object*>&, tree*);
// Build a pointer to a Go string constant. This returns a pointer
// to the pointer.
tree
ptr_go_string_constant_tree(const std::string&);
// Return the name to use for a type descriptor decl for an unnamed
// type.
std::string
unnamed_type_descriptor_decl_name(const Type* type);
// Return the name to use for a type descriptor decl for a type
// named NO, defined in IN_FUNCTION.
std::string
type_descriptor_decl_name(const Named_object* no,
const Named_object* in_function);
// Where a type descriptor should be defined.
enum Type_descriptor_location
{
// Defined in this file.
TYPE_DESCRIPTOR_DEFINED,
// Defined in some other file.
TYPE_DESCRIPTOR_UNDEFINED,
// Common definition which may occur in multiple files.
TYPE_DESCRIPTOR_COMMON
};
// Return where the decl for TYPE should be defined.
Type_descriptor_location
type_descriptor_location(const Type* type);
// Return the type of a trampoline.
static tree
trampoline_type_tree();
// Type used to map import names to packages.
typedef std::map<std::string, Package*> Imports;
// Type used to map package names to packages.
typedef std::map<std::string, Package*> Packages;
// Type used to map special names in the sys package.
typedef std::map<std::string, std::string> Sys_names;
// Hash table mapping map types to map descriptor decls.
typedef Unordered_map_hash(const Map_type*, tree, Type_hash_identical,
Type_identical) Map_descriptors;
// Map unnamed types to type descriptor decls.
typedef Unordered_map_hash(const Type*, tree, Type_hash_identical,
Type_identical) Type_descriptor_decls;
// The backend generator.
Backend* backend_;
// The package we are compiling.
Package* package_;
// The list of currently open functions during parsing.
Open_functions functions_;
// The global binding contour. This includes the builtin functions
// and the package we are compiling.
Bindings* globals_;
// Mapping from import file names to packages.
Imports imports_;
// Whether the magic unsafe package was imported.
bool imported_unsafe_;
// Mapping from package names we have seen to packages. This does
// not include the package we are compiling.
Packages packages_;
// Mapping from map types to map descriptors.
Map_descriptors* map_descriptors_;
// Mapping from unnamed types to type descriptor decls.
Type_descriptor_decls* type_descriptor_decls_;
// The functions named "init", if there are any.
std::vector<Named_object*> init_functions_;
// Whether we need a magic initialization function.
bool need_init_fn_;
// The name of the magic initialization function.
std::string init_fn_name_;
// A list of import control variables for packages that we import.
std::set<Import_init> imported_init_fns_;
// The unique prefix used for all global symbols.
std::string unique_prefix_;
// Whether an explicit unique prefix was set by -fgo-prefix.
bool unique_prefix_specified_;
// A list of interface types defined while parsing.
std::vector<Interface_type*> interface_types_;
// Whether named types have been converted.
bool named_types_are_converted_;
};
// A block of statements.
class Block
{
public:
Block(Block* enclosing, source_location);
// Return the enclosing block.
const Block*
enclosing() const
{ return this->enclosing_; }
// Return the bindings of the block.
Bindings*
bindings()
{ return this->bindings_; }
const Bindings*
bindings() const
{ return this->bindings_; }
// Look at the block's statements.
const std::vector<Statement*>*
statements() const
{ return &this->statements_; }
// Return the start location. This is normally the location of the
// left curly brace which starts the block.
source_location
start_location() const
{ return this->start_location_; }
// Return the end location. This is normally the location of the
// right curly brace which ends the block.
source_location
end_location() const
{ return this->end_location_; }
// Add a statement to the block.
void
add_statement(Statement*);
// Add a statement to the front of the block.
void
add_statement_at_front(Statement*);
// Replace a statement in a block.
void
replace_statement(size_t index, Statement*);
// Add a Statement before statement number INDEX.
void
insert_statement_before(size_t index, Statement*);
// Add a Statement after statement number INDEX.
void
insert_statement_after(size_t index, Statement*);
// Set the end location of the block.
void
set_end_location(source_location location)
{ this->end_location_ = location; }
// Traverse the tree.
int
traverse(Traverse*);
// Set final types for unspecified variables and constants.
void
determine_types();
// Return true if execution of this block may fall through to the
// next block.
bool
may_fall_through() const;
// Convert the block to the backend representation.
Bblock*
get_backend(Translate_context*);
// Iterate over statements.
typedef std::vector<Statement*>::iterator iterator;
iterator
begin()
{ return this->statements_.begin(); }
iterator
end()
{ return this->statements_.end(); }
private:
// Enclosing block.
Block* enclosing_;
// Statements in the block.
std::vector<Statement*> statements_;
// Binding contour.
Bindings* bindings_;
// Location of start of block.
source_location start_location_;
// Location of end of block.
source_location end_location_;
};
// A function.
class Function
{
public:
Function(Function_type* type, Function*, Block*, source_location);
// Return the function's type.
Function_type*
type() const
{ return this->type_; }
// Return the enclosing function if there is one.
Function*
enclosing()
{ return this->enclosing_; }
// Set the enclosing function. This is used when building thunks
// for functions which call recover.
void
set_enclosing(Function* enclosing)
{
go_assert(this->enclosing_ == NULL);
this->enclosing_ = enclosing;
}
// The result variables.
typedef std::vector<Named_object*> Results;
// Create the result variables in the outer block.
void
create_result_variables(Gogo*);
// Update the named result variables when cloning a function which
// calls recover.
void
update_result_variables();
// Return the result variables.
Results*
result_variables()
{ return this->results_; }
// Whether the result variables have names.
bool
results_are_named() const
{ return this->results_are_named_; }
// Add a new field to the closure variable.
void
add_closure_field(Named_object* var, source_location loc)
{ this->closure_fields_.push_back(std::make_pair(var, loc)); }
// Whether this function needs a closure.
bool
needs_closure() const
{ return !this->closure_fields_.empty(); }
// Return the closure variable, creating it if necessary. This is
// passed to the function as a static chain parameter.
Named_object*
closure_var();
// Set the closure variable. This is used when building thunks for
// functions which call recover.
void
set_closure_var(Named_object* v)
{
go_assert(this->closure_var_ == NULL);
this->closure_var_ = v;
}
// Return the variable for a reference to field INDEX in the closure
// variable.
Named_object*
enclosing_var(unsigned int index)
{
go_assert(index < this->closure_fields_.size());
return closure_fields_[index].first;
}
// Set the type of the closure variable if there is one.
void
set_closure_type();
// Get the block of statements associated with the function.
Block*
block() const
{ return this->block_; }
// Get the location of the start of the function.
source_location
location() const
{ return this->location_; }
// Return whether this function is actually a method.
bool
is_method() const;
// Add a label definition to the function.
Label*
add_label_definition(const std::string& label_name, source_location);
// Add a label reference to a function.
Label*
add_label_reference(const std::string& label_name);
// Warn about labels that are defined but not used.
void
check_labels() const;
// Whether this function calls the predeclared recover function.
bool
calls_recover() const
{ return this->calls_recover_; }
// Record that this function calls the predeclared recover function.
// This is set during the lowering pass.
void
set_calls_recover()
{ this->calls_recover_ = true; }
// Whether this is a recover thunk function.
bool
is_recover_thunk() const
{ return this->is_recover_thunk_; }
// Record that this is a thunk built for a function which calls
// recover.
void
set_is_recover_thunk()
{ this->is_recover_thunk_ = true; }
// Whether this function already has a recover thunk.
bool
has_recover_thunk() const
{ return this->has_recover_thunk_; }
// Record that this function already has a recover thunk.
void
set_has_recover_thunk()
{ this->has_recover_thunk_ = true; }
// Swap with another function. Used only for the thunk which calls
// recover.
void
swap_for_recover(Function *);
// Traverse the tree.
int
traverse(Traverse*);
// Determine types in the function.
void
determine_types();
// Return the function's decl given an identifier.
tree
get_or_make_decl(Gogo*, Named_object*, tree id);
// Return the function's decl after it has been built.
tree
get_decl() const
{
go_assert(this->fndecl_ != NULL);
return this->fndecl_;
}
// Set the function decl to hold a tree of the function code.
void
build_tree(Gogo*, Named_object*);
// Get the value to return when not explicitly specified. May also
// add statements to execute first to STMT_LIST.
tree
return_value(Gogo*, Named_object*, source_location, tree* stmt_list) const;
// Get a tree for the variable holding the defer stack.
Expression*
defer_stack(source_location);
// Export the function.
void
export_func(Export*, const std::string& name) const;
// Export a function with a type.
static void
export_func_with_type(Export*, const std::string& name,
const Function_type*);
// Import a function.
static void
import_func(Import*, std::string* pname, Typed_identifier** receiver,
Typed_identifier_list** pparameters,
Typed_identifier_list** presults, bool* is_varargs);
private:
// Type for mapping from label names to Label objects.
typedef Unordered_map(std::string, Label*) Labels;
tree
make_receiver_parm_decl(Gogo*, Named_object*, tree);
tree
copy_parm_to_heap(Gogo*, Named_object*, tree);
void
build_defer_wrapper(Gogo*, Named_object*, tree*, tree*);
typedef std::vector<std::pair<Named_object*,
source_location> > Closure_fields;
// The function's type.
Function_type* type_;
// The enclosing function. This is NULL when there isn't one, which
// is the normal case.
Function* enclosing_;
// The result variables, if any.
Results* results_;
// If there is a closure, this is the list of variables which appear
// in the closure. This is created by the parser, and then resolved
// to a real type when we lower parse trees.
Closure_fields closure_fields_;
// The closure variable, passed as a parameter using the static
// chain parameter. Normally NULL.
Named_object* closure_var_;
// The outer block of statements in the function.
Block* block_;
// The source location of the start of the function.
source_location location_;
// Labels defined or referenced in the function.
Labels labels_;
// The function decl.
tree fndecl_;
// The defer stack variable. A pointer to this variable is used to
// distinguish the defer stack for one function from another. This
// is NULL unless we actually need a defer stack.
Temporary_statement* defer_stack_;
// True if the result variables are named.
bool results_are_named_;
// True if this function calls the predeclared recover function.
bool calls_recover_;
// True if this a thunk built for a function which calls recover.
bool is_recover_thunk_;
// True if this function already has a recover thunk.
bool has_recover_thunk_;
};
// A function declaration.
class Function_declaration
{
public:
Function_declaration(Function_type* fntype, source_location location)
: fntype_(fntype), location_(location), asm_name_(), fndecl_(NULL)
{ }
Function_type*
type() const
{ return this->fntype_; }
source_location
location() const
{ return this->location_; }
const std::string&
asm_name() const
{ return this->asm_name_; }
// Set the assembler name.
void
set_asm_name(const std::string& asm_name)
{ this->asm_name_ = asm_name; }
// Return a decl for the function given an identifier.
tree
get_or_make_decl(Gogo*, Named_object*, tree id);
// Export a function declaration.
void
export_func(Export* exp, const std::string& name) const
{ Function::export_func_with_type(exp, name, this->fntype_); }
private:
// The type of the function.
Function_type* fntype_;
// The location of the declaration.
source_location location_;
// The assembler name: this is the name to use in references to the
// function. This is normally empty.
std::string asm_name_;
// The function decl if needed.
tree fndecl_;
};
// A variable.
class Variable
{
public:
Variable(Type*, Expression*, bool is_global, bool is_parameter,
bool is_receiver, source_location);
// Get the type of the variable.
Type*
type();
Type*
type() const;
// Return whether the type is defined yet.
bool
has_type() const
{ return this->type_ != NULL; }
// Get the initial value.
Expression*
init() const
{ return this->init_; }
// Return whether there are any preinit statements.
bool
has_pre_init() const
{ return this->preinit_ != NULL; }
// Return the preinit statements if any.
Block*
preinit() const
{ return this->preinit_; }
// Return whether this is a global variable.
bool
is_global() const
{ return this->is_global_; }
// Return whether this is a function parameter.
bool
is_parameter() const
{ return this->is_parameter_; }
// Return whether this is the receiver parameter of a method.
bool
is_receiver() const
{ return this->is_receiver_; }
// Change this parameter to be a receiver. This is used when
// creating the thunks created for functions which call recover.
void
set_is_receiver()
{
go_assert(this->is_parameter_);
this->is_receiver_ = true;
}
// Change this parameter to not be a receiver. This is used when
// creating the thunks created for functions which call recover.
void
set_is_not_receiver()
{
go_assert(this->is_parameter_);
this->is_receiver_ = false;
}
// Return whether this is the varargs parameter of a function.
bool
is_varargs_parameter() const
{ return this->is_varargs_parameter_; }
// Whether this variable's address is taken.
bool
is_address_taken() const
{ return this->is_address_taken_; }
// Whether this variable should live in the heap.
bool
is_in_heap() const
{ return this->is_address_taken_ && !this->is_global_; }
// Get the source location of the variable's declaration.
source_location
location() const
{ return this->location_; }
// Record that this is the varargs parameter of a function.
void
set_is_varargs_parameter()
{
go_assert(this->is_parameter_);
this->is_varargs_parameter_ = true;
}
// Clear the initial value; used for error handling.
void
clear_init()
{ this->init_ = NULL; }
// Set the initial value; used for converting shortcuts.
void
set_init(Expression* init)
{ this->init_ = init; }
// Get the preinit block, a block of statements to be run before the
// initialization expression.
Block*
preinit_block(Gogo*);
// Add a statement to be run before the initialization expression.
// This is only used for global variables.
void
add_preinit_statement(Gogo*, Statement*);
// Lower the initialization expression after parsing is complete.
void
lower_init_expression(Gogo*, Named_object*);
// A special case: the init value is used only to determine the
// type. This is used if the variable is defined using := with the
// comma-ok form of a map index or a receive expression. The init
// value is actually the map index expression or receive expression.
// We use this because we may not know the right type at parse time.
void
set_type_from_init_tuple()
{ this->type_from_init_tuple_ = true; }
// Another special case: the init value is used only to determine
// the type. This is used if the variable is defined using := with
// a range clause. The init value is the range expression. The
// type of the variable is the index type of the range expression
// (i.e., the first value returned by a range).
void
set_type_from_range_index()
{ this->type_from_range_index_ = true; }
// Another special case: like set_type_from_range_index, but the
// type is the value type of the range expression (i.e., the second
// value returned by a range).
void
set_type_from_range_value()
{ this->type_from_range_value_ = true; }
// Another special case: the init value is used only to determine
// the type. This is used if the variable is defined using := with
// a case in a select statement. The init value is the channel.
// The type of the variable is the channel's element type.
void
set_type_from_chan_element()
{ this->type_from_chan_element_ = true; }
// After we lower the select statement, we once again set the type
// from the initialization expression.
void
clear_type_from_chan_element()
{
go_assert(this->type_from_chan_element_);
this->type_from_chan_element_ = false;
}
// Note that this variable was created for a type switch clause.
void
set_is_type_switch_var()
{ this->is_type_switch_var_ = true; }
// Traverse the initializer expression.
int
traverse_expression(Traverse*);
// Determine the type of the variable if necessary.
void
determine_type();
// Note that something takes the address of this variable.
void
set_address_taken()
{ this->is_address_taken_ = true; }
// Get the backend representation of the variable.
Bvariable*
get_backend_variable(Gogo*, Named_object*, const Package*,
const std::string&);
// Get the initial value of the variable as a tree. This may only
// be called if has_pre_init() returns false.
tree
get_init_tree(Gogo*, Named_object* function);
// Return a series of statements which sets the value of the
// variable in DECL. This should only be called is has_pre_init()
// returns true. DECL may be NULL for a sink variable.
tree
get_init_block(Gogo*, Named_object* function, tree decl);
// Export the variable.
void
export_var(Export*, const std::string& name) const;
// Import a variable.
static void
import_var(Import*, std::string* pname, Type** ptype);
private:
// The type of a tuple.
Type*
type_from_tuple(Expression*, bool) const;
// The type of a range.
Type*
type_from_range(Expression*, bool, bool) const;
// The element type of a channel.
Type*
type_from_chan_element(Expression*, bool) const;
// The variable's type. This may be NULL if the type is set from
// the expression.
Type* type_;
// The initial value. This may be NULL if the variable should be
// initialized to the default value for the type.
Expression* init_;
// Statements to run before the init statement.
Block* preinit_;
// Location of variable definition.
source_location location_;
// Backend representation.
Bvariable* backend_;
// Whether this is a global variable.
bool is_global_ : 1;
// Whether this is a function parameter.
bool is_parameter_ : 1;
// Whether this is the receiver parameter of a method.
bool is_receiver_ : 1;
// Whether this is the varargs parameter of a function.
bool is_varargs_parameter_ : 1;
// Whether something takes the address of this variable.
bool is_address_taken_ : 1;
// True if we have seen this variable in a traversal.
bool seen_ : 1;
// True if we have lowered the initialization expression.
bool init_is_lowered_ : 1;
// True if init is a tuple used to set the type.
bool type_from_init_tuple_ : 1;
// True if init is a range clause and the type is the index type.
bool type_from_range_index_ : 1;
// True if init is a range clause and the type is the value type.
bool type_from_range_value_ : 1;
// True if init is a channel and the type is the channel's element type.
bool type_from_chan_element_ : 1;
// True if this is a variable created for a type switch case.
bool is_type_switch_var_ : 1;
// True if we have determined types.
bool determined_type_ : 1;
};
// A variable which is really the name for a function return value, or
// part of one.
class Result_variable
{
public:
Result_variable(Type* type, Function* function, int index,
source_location location)
: type_(type), function_(function), index_(index), location_(location),
backend_(NULL), is_address_taken_(false)
{ }
// Get the type of the result variable.
Type*
type() const
{ return this->type_; }
// Get the function that this is associated with.
Function*
function() const
{ return this->function_; }
// Index in the list of function results.
int
index() const
{ return this->index_; }
// The location of the variable definition.
source_location
location() const
{ return this->location_; }
// Whether this variable's address is taken.
bool
is_address_taken() const
{ return this->is_address_taken_; }
// Note that something takes the address of this variable.
void
set_address_taken()
{ this->is_address_taken_ = true; }
// Whether this variable should live in the heap.
bool
is_in_heap() const
{ return this->is_address_taken_; }
// Set the function. This is used when cloning functions which call
// recover.
void
set_function(Function* function)
{ this->function_ = function; }
// Get the backend representation of the variable.
Bvariable*
get_backend_variable(Gogo*, Named_object*, const std::string&);
private:
// Type of result variable.
Type* type_;
// Function with which this is associated.
Function* function_;
// Index in list of results.
int index_;
// Where the result variable is defined.
source_location location_;
// Backend representation.
Bvariable* backend_;
// Whether something takes the address of this variable.
bool is_address_taken_;
};
// The value we keep for a named constant. This lets us hold a type
// and an expression.
class Named_constant
{
public:
Named_constant(Type* type, Expression* expr, int iota_value,
source_location location)
: type_(type), expr_(expr), iota_value_(iota_value), location_(location),
lowering_(false)
{ }
Type*
type() const
{ return this->type_; }
Expression*
expr() const
{ return this->expr_; }
int
iota_value() const
{ return this->iota_value_; }
source_location
location() const
{ return this->location_; }
// Whether we are lowering.
bool
lowering() const
{ return this->lowering_; }
// Set that we are lowering.
void
set_lowering()
{ this->lowering_ = true; }
// We are no longer lowering.
void
clear_lowering()
{ this->lowering_ = false; }
// Traverse the expression.
int
traverse_expression(Traverse*);
// Determine the type of the constant if necessary.
void
determine_type();
// Indicate that we found and reported an error for this constant.
void
set_error();
// Export the constant.
void
export_const(Export*, const std::string& name) const;
// Import a constant.
static void
import_const(Import*, std::string*, Type**, Expression**);
private:
// The type of the constant.
Type* type_;
// The expression for the constant.
Expression* expr_;
// If the predeclared constant iota is used in EXPR_, this is the
// value it will have. We do this because at parse time we don't
// know whether the name "iota" will refer to the predeclared
// constant or to something else. We put in the right value in when
// we lower.
int iota_value_;
// The location of the definition.
source_location location_;
// Whether we are currently lowering this constant.
bool lowering_;
};
// A type declaration.
class Type_declaration
{
public:
Type_declaration(source_location location)
: location_(location), in_function_(NULL), methods_(),
issued_warning_(false)
{ }
// Return the location.
source_location
location() const
{ return this->location_; }
// Return the function in which this type is declared. This will
// return NULL for a type declared in global scope.
Named_object*
in_function()
{ return this->in_function_; }
// Set the function in which this type is declared.
void
set_in_function(Named_object* f)
{ this->in_function_ = f; }
// Add a method to this type. This is used when methods are defined
// before the type.
Named_object*
add_method(const std::string& name, Function* function);
// Add a method declaration to this type.
Named_object*
add_method_declaration(const std::string& name, Function_type* type,
source_location location);
// Return whether any methods were defined.
bool
has_methods() const;
// Define methods when the real type is known.
void
define_methods(Named_type*);
// This is called if we are trying to use this type. It returns
// true if we should issue a warning.
bool
using_type();
private:
typedef std::vector<Named_object*> Methods;
// The location of the type declaration.
source_location location_;
// If this type is declared in a function, a pointer back to the
// function in which it is defined.
Named_object* in_function_;
// Methods defined before the type is defined.
Methods methods_;
// True if we have issued a warning about a use of this type
// declaration when it is undefined.
bool issued_warning_;
};
// An unknown object. These are created by the parser for forward
// references to names which have not been seen before. In a correct
// program, these will always point to a real definition by the end of
// the parse. Because they point to another Named_object, these may
// only be referenced by Unknown_expression objects.
class Unknown_name
{
public:
Unknown_name(source_location location)
: location_(location), real_named_object_(NULL)
{ }
// Return the location where this name was first seen.
source_location
location() const
{ return this->location_; }
// Return the real named object that this points to, or NULL if it
// was never resolved.
Named_object*
real_named_object() const
{ return this->real_named_object_; }
// Set the real named object that this points to.
void
set_real_named_object(Named_object* no);
private:
// The location where this name was first seen.
source_location location_;
// The real named object when it is known.
Named_object*
real_named_object_;
};
// A named object named. This is the result of a declaration. We
// don't use a superclass because they all have to be handled
// differently.
class Named_object
{
public:
enum Classification
{
// An uninitialized Named_object. We should never see this.
NAMED_OBJECT_UNINITIALIZED,
// An unknown name. This is used for forward references. In a
// correct program, these will all be resolved by the end of the
// parse.
NAMED_OBJECT_UNKNOWN,
// A const.
NAMED_OBJECT_CONST,
// A type.
NAMED_OBJECT_TYPE,
// A forward type declaration.
NAMED_OBJECT_TYPE_DECLARATION,
// A var.
NAMED_OBJECT_VAR,
// A result variable in a function.
NAMED_OBJECT_RESULT_VAR,
// The blank identifier--the special variable named _.
NAMED_OBJECT_SINK,
// A func.
NAMED_OBJECT_FUNC,
// A forward func declaration.
NAMED_OBJECT_FUNC_DECLARATION,
// A package.
NAMED_OBJECT_PACKAGE
};
// Return the classification.
Classification
classification() const
{ return this->classification_; }
// Classifiers.
bool
is_unknown() const
{ return this->classification_ == NAMED_OBJECT_UNKNOWN; }
bool
is_const() const
{ return this->classification_ == NAMED_OBJECT_CONST; }
bool
is_type() const
{ return this->classification_ == NAMED_OBJECT_TYPE; }
bool
is_type_declaration() const
{ return this->classification_ == NAMED_OBJECT_TYPE_DECLARATION; }
bool
is_variable() const
{ return this->classification_ == NAMED_OBJECT_VAR; }
bool
is_result_variable() const
{ return this->classification_ == NAMED_OBJECT_RESULT_VAR; }
bool
is_sink() const
{ return this->classification_ == NAMED_OBJECT_SINK; }
bool
is_function() const
{ return this->classification_ == NAMED_OBJECT_FUNC; }
bool
is_function_declaration() const
{ return this->classification_ == NAMED_OBJECT_FUNC_DECLARATION; }
bool
is_package() const
{ return this->classification_ == NAMED_OBJECT_PACKAGE; }
// Creators.
static Named_object*
make_unknown_name(const std::string& name, source_location);
static Named_object*
make_constant(const Typed_identifier&, const Package*, Expression*,
int iota_value);
static Named_object*
make_type(const std::string&, const Package*, Type*, source_location);
static Named_object*
make_type_declaration(const std::string&, const Package*, source_location);
static Named_object*
make_variable(const std::string&, const Package*, Variable*);
static Named_object*
make_result_variable(const std::string&, Result_variable*);
static Named_object*
make_sink();
static Named_object*
make_function(const std::string&, const Package*, Function*);
static Named_object*
make_function_declaration(const std::string&, const Package*, Function_type*,
source_location);
static Named_object*
make_package(const std::string& alias, Package* package);
// Getters.
Unknown_name*
unknown_value()
{
go_assert(this->classification_ == NAMED_OBJECT_UNKNOWN);
return this->u_.unknown_value;
}
const Unknown_name*
unknown_value() const
{
go_assert(this->classification_ == NAMED_OBJECT_UNKNOWN);
return this->u_.unknown_value;
}
Named_constant*
const_value()
{
go_assert(this->classification_ == NAMED_OBJECT_CONST);
return this->u_.const_value;
}
const Named_constant*
const_value() const
{
go_assert(this->classification_ == NAMED_OBJECT_CONST);
return this->u_.const_value;
}
Named_type*
type_value()
{
go_assert(this->classification_ == NAMED_OBJECT_TYPE);
return this->u_.type_value;
}
const Named_type*
type_value() const
{
go_assert(this->classification_ == NAMED_OBJECT_TYPE);
return this->u_.type_value;
}
Type_declaration*
type_declaration_value()
{
go_assert(this->classification_ == NAMED_OBJECT_TYPE_DECLARATION);
return this->u_.type_declaration;
}
const Type_declaration*
type_declaration_value() const
{
go_assert(this->classification_ == NAMED_OBJECT_TYPE_DECLARATION);
return this->u_.type_declaration;
}
Variable*
var_value()
{
go_assert(this->classification_ == NAMED_OBJECT_VAR);
return this->u_.var_value;
}
const Variable*
var_value() const
{
go_assert(this->classification_ == NAMED_OBJECT_VAR);
return this->u_.var_value;
}
Result_variable*
result_var_value()
{
go_assert(this->classification_ == NAMED_OBJECT_RESULT_VAR);
return this->u_.result_var_value;
}
const Result_variable*
result_var_value() const
{
go_assert(this->classification_ == NAMED_OBJECT_RESULT_VAR);
return this->u_.result_var_value;
}
Function*
func_value()
{
go_assert(this->classification_ == NAMED_OBJECT_FUNC);
return this->u_.func_value;
}
const Function*
func_value() const
{
go_assert(this->classification_ == NAMED_OBJECT_FUNC);
return this->u_.func_value;
}
Function_declaration*
func_declaration_value()
{
go_assert(this->classification_ == NAMED_OBJECT_FUNC_DECLARATION);
return this->u_.func_declaration_value;
}
const Function_declaration*
func_declaration_value() const
{
go_assert(this->classification_ == NAMED_OBJECT_FUNC_DECLARATION);
return this->u_.func_declaration_value;
}
Package*
package_value()
{
go_assert(this->classification_ == NAMED_OBJECT_PACKAGE);
return this->u_.package_value;
}
const Package*
package_value() const
{
go_assert(this->classification_ == NAMED_OBJECT_PACKAGE);
return this->u_.package_value;
}
const std::string&
name() const
{ return this->name_; }
// Return the name to use in an error message. The difference is
// that if this Named_object is defined in a different package, this
// will return PACKAGE.NAME.
std::string
message_name() const;
const Package*
package() const
{ return this->package_; }
// Resolve an unknown value if possible. This returns the same
// Named_object or a new one.
Named_object*
resolve()
{
Named_object* ret = this;
if (this->is_unknown())
{
Named_object* r = this->unknown_value()->real_named_object();
if (r != NULL)
ret = r;
}
return ret;
}
const Named_object*
resolve() const
{
const Named_object* ret = this;
if (this->is_unknown())
{
const Named_object* r = this->unknown_value()->real_named_object();
if (r != NULL)
ret = r;
}
return ret;
}
// The location where this object was defined or referenced.
source_location
location() const;
// Convert a variable to the backend representation.
Bvariable*
get_backend_variable(Gogo*, Named_object* function);
// Return a tree for the external identifier for this object.
tree
get_id(Gogo*);
// Return a tree representing this object.
tree
get_tree(Gogo*, Named_object* function);
// Define a type declaration.
void
set_type_value(Named_type*);
// Define a function declaration.
void
set_function_value(Function*);
// Declare an unknown name as a type declaration.
void
declare_as_type();
// Export this object.
void
export_named_object(Export*) const;
private:
Named_object(const std::string&, const Package*, Classification);
// The name of the object.
std::string name_;
// The package that this object is in. This is NULL if it is in the
// file we are compiling.
const Package* package_;
// The type of object this is.
Classification classification_;
// The real data.
union
{
Unknown_name* unknown_value;
Named_constant* const_value;
Named_type* type_value;
Type_declaration* type_declaration;
Variable* var_value;
Result_variable* result_var_value;
Function* func_value;
Function_declaration* func_declaration_value;
Package* package_value;
} u_;
// The DECL tree for this object if we have already converted it.
tree tree_;
};
// A binding contour. This binds names to objects.
class Bindings
{
public:
// Type for mapping from names to objects.
typedef Unordered_map(std::string, Named_object*) Contour;
Bindings(Bindings* enclosing);
// Add an unknown name.
Named_object*
add_unknown_name(const std::string& name, source_location location)
{
return this->add_named_object(Named_object::make_unknown_name(name,
location));
}
// Add a constant.
Named_object*
add_constant(const Typed_identifier& tid, const Package* package,
Expression* expr, int iota_value)
{
return this->add_named_object(Named_object::make_constant(tid, package,
expr,
iota_value));
}
// Add a type.
Named_object*
add_type(const std::string& name, const Package* package, Type* type,
source_location location)
{
return this->add_named_object(Named_object::make_type(name, package, type,
location));
}
// Add a named type. This is used for builtin types, and to add an
// imported type to the global scope.
Named_object*
add_named_type(Named_type* named_type);
// Add a type declaration.
Named_object*
add_type_declaration(const std::string& name, const Package* package,
source_location location)
{
Named_object* no = Named_object::make_type_declaration(name, package,
location);
return this->add_named_object(no);
}
// Add a variable.
Named_object*
add_variable(const std::string& name, const Package* package,
Variable* variable)
{
return this->add_named_object(Named_object::make_variable(name, package,
variable));
}
// Add a result variable.
Named_object*
add_result_variable(const std::string& name, Result_variable* result)
{
return this->add_named_object(Named_object::make_result_variable(name,
result));
}
// Add a function.
Named_object*
add_function(const std::string& name, const Package*, Function* function);
// Add a function declaration.
Named_object*
add_function_declaration(const std::string& name, const Package* package,
Function_type* type, source_location location);
// Add a package. The location is the location of the import
// statement.
Named_object*
add_package(const std::string& alias, Package* package)
{
Named_object* no = Named_object::make_package(alias, package);
return this->add_named_object(no);
}
// Define a type which was already declared.
void
define_type(Named_object*, Named_type*);
// Add a method to the list of objects. This is not added to the
// lookup table.
void
add_method(Named_object*);
// Add a named object to this binding.
Named_object*
add_named_object(Named_object* no)
{ return this->add_named_object_to_contour(&this->bindings_, no); }
// Clear all names in file scope from the bindings.
void
clear_file_scope();
// Look up a name in this binding contour and in any enclosing
// binding contours. This returns NULL if the name is not found.
Named_object*
lookup(const std::string&) const;
// Look up a name in this binding contour without looking in any
// enclosing binding contours. Returns NULL if the name is not found.
Named_object*
lookup_local(const std::string&) const;
// Remove a name.
void
remove_binding(Named_object*);
// Traverse the tree. See the Traverse class.
int
traverse(Traverse*, bool is_global);
// Iterate over definitions. This does not include things which
// were only declared.
typedef std::vector<Named_object*>::const_iterator
const_definitions_iterator;
const_definitions_iterator
begin_definitions() const
{ return this->named_objects_.begin(); }
const_definitions_iterator
end_definitions() const
{ return this->named_objects_.end(); }
// Return the number of definitions.
size_t
size_definitions() const
{ return this->named_objects_.size(); }
// Return whether there are no definitions.
bool
empty_definitions() const
{ return this->named_objects_.empty(); }
// Iterate over declarations. This is everything that has been
// declared, which includes everything which has been defined.
typedef Contour::const_iterator const_declarations_iterator;
const_declarations_iterator
begin_declarations() const
{ return this->bindings_.begin(); }
const_declarations_iterator
end_declarations() const
{ return this->bindings_.end(); }
// Return the number of declarations.
size_t
size_declarations() const
{ return this->bindings_.size(); }
// Return whether there are no declarations.
bool
empty_declarations() const
{ return this->bindings_.empty(); }
// Return the first declaration.
Named_object*
first_declaration()
{ return this->bindings_.empty() ? NULL : this->bindings_.begin()->second; }
private:
Named_object*
add_named_object_to_contour(Contour*, Named_object*);
Named_object*
new_definition(Named_object*, Named_object*);
// Enclosing bindings.
Bindings* enclosing_;
// The list of objects.
std::vector<Named_object*> named_objects_;
// The mapping from names to objects.
Contour bindings_;
};
// A label.
class Label
{
public:
Label(const std::string& name)
: name_(name), location_(0), is_used_(false), blabel_(NULL)
{ }
// Return the label's name.
const std::string&
name() const
{ return this->name_; }
// Return whether the label has been defined.
bool
is_defined() const
{ return this->location_ != 0; }
// Return whether the label has been used.
bool
is_used() const
{ return this->is_used_; }
// Record that the label is used.
void
set_is_used()
{ this->is_used_ = true; }
// Return the location of the definition.
source_location
location() const
{ return this->location_; }
// Define the label at LOCATION.
void
define(source_location location)
{
go_assert(this->location_ == 0);
this->location_ = location;
}
// Return the backend representation for this label.
Blabel*
get_backend_label(Translate_context*);
// Return an expression for the address of this label. This is used
// to get the return address of a deferred function to see whether
// the function may call recover.
Bexpression*
get_addr(Translate_context*, source_location location);
private:
// The name of the label.
std::string name_;
// The location of the definition. This is 0 if the label has not
// yet been defined.
source_location location_;
// Whether the label has been used.
bool is_used_;
// The backend representation.
Blabel* blabel_;
};
// An unnamed label. These are used when lowering loops.
class Unnamed_label
{
public:
Unnamed_label(source_location location)
: location_(location), blabel_(NULL)
{ }
// Get the location where the label is defined.
source_location
location() const
{ return this->location_; }
// Set the location where the label is defined.
void
set_location(source_location location)
{ this->location_ = location; }
// Return a statement which defines this label.
Bstatement*
get_definition(Translate_context*);
// Return a goto to this label from LOCATION.
Bstatement*
get_goto(Translate_context*, source_location location);
private:
// Return the backend representation.
Blabel*
get_blabel(Translate_context*);
// The location where the label is defined.
source_location location_;
// The backend representation of this label.
Blabel* blabel_;
};
// An imported package.
class Package
{
public:
Package(const std::string& name, const std::string& unique_prefix,
source_location location);
// The real name of this package. This may be different from the
// name in the associated Named_object if the import statement used
// an alias.
const std::string&
name() const
{ return this->name_; }
// Return the location of the import statement.
source_location
location() const
{ return this->location_; }
// Get the unique prefix used for all symbols exported from this
// package.
const std::string&
unique_prefix() const
{
go_assert(!this->unique_prefix_.empty());
return this->unique_prefix_;
}
// The priority of this package. The init function of packages with
// lower priority must be run before the init function of packages
// with higher priority.
int
priority() const
{ return this->priority_; }
// Set the priority.
void
set_priority(int priority);
// Return the bindings.
Bindings*
bindings()
{ return this->bindings_; }
// Whether some symbol from the package was used.
bool
used() const
{ return this->used_; }
// Note that some symbol from this package was used.
void
set_used() const
{ this->used_ = true; }
// Clear the used field for the next file.
void
clear_used()
{ this->used_ = false; }
// Whether this package was imported in the current file.
bool
is_imported() const
{ return this->is_imported_; }
// Note that this package was imported in the current file.
void
set_is_imported()
{ this->is_imported_ = true; }
// Clear the imported field for the next file.
void
clear_is_imported()
{ this->is_imported_ = false; }
// Whether this package was imported with a name of "_".
bool
uses_sink_alias() const
{ return this->uses_sink_alias_; }
// Note that this package was imported with a name of "_".
void
set_uses_sink_alias()
{ this->uses_sink_alias_ = true; }
// Clear the sink alias field for the next file.
void
clear_uses_sink_alias()
{ this->uses_sink_alias_ = false; }
// Look up a name in the package. Returns NULL if the name is not
// found.
Named_object*
lookup(const std::string& name) const
{ return this->bindings_->lookup(name); }
// Set the location of the package. This is used if it is seen in a
// different import before it is really imported.
void
set_location(source_location location)
{ this->location_ = location; }
// Add a constant to the package.
Named_object*
add_constant(const Typed_identifier& tid, Expression* expr)
{ return this->bindings_->add_constant(tid, this, expr, 0); }
// Add a type to the package.
Named_object*
add_type(const std::string& name, Type* type, source_location location)
{ return this->bindings_->add_type(name, this, type, location); }
// Add a type declaration to the package.
Named_object*
add_type_declaration(const std::string& name, source_location location)
{ return this->bindings_->add_type_declaration(name, this, location); }
// Add a variable to the package.
Named_object*
add_variable(const std::string& name, Variable* variable)
{ return this->bindings_->add_variable(name, this, variable); }
// Add a function declaration to the package.
Named_object*
add_function_declaration(const std::string& name, Function_type* type,
source_location loc)
{ return this->bindings_->add_function_declaration(name, this, type, loc); }
// Determine types of constants.
void
determine_types();
private:
// The real name of this package.
std::string name_;
// The unique prefix for all exported global symbols.
std::string unique_prefix_;
// The names in this package.
Bindings* bindings_;
// The priority of this package. A package has a priority higher
// than the priority of all of the packages that it imports. This
// is used to run init functions in the right order.
int priority_;
// The location of the import statement.
source_location location_;
// True if some name from this package was used. This is mutable
// because we can use a package even if we have a const pointer to
// it.
mutable bool used_;
// True if this package was imported in the current file.
bool is_imported_;
// True if this package was imported with a name of "_".
bool uses_sink_alias_;
};
// Return codes for the traversal functions. This is not an enum
// because we want to be able to declare traversal functions in other
// header files without including this one.
// Continue traversal as usual.
const int TRAVERSE_CONTINUE = -1;
// Exit traversal.
const int TRAVERSE_EXIT = 0;
// Continue traversal, but skip components of the current object.
// E.g., if this is returned by Traverse::statement, we do not
// traverse the expressions in the statement even if
// traverse_expressions is set in the traverse_mask.
const int TRAVERSE_SKIP_COMPONENTS = 1;
// This class is used when traversing the parse tree. The caller uses
// a subclass which overrides functions as desired.
class Traverse
{
public:
// These bitmasks say what to traverse.
static const unsigned int traverse_variables = 0x1;
static const unsigned int traverse_constants = 0x2;
static const unsigned int traverse_functions = 0x4;
static const unsigned int traverse_blocks = 0x8;
static const unsigned int traverse_statements = 0x10;
static const unsigned int traverse_expressions = 0x20;
static const unsigned int traverse_types = 0x40;
Traverse(unsigned int traverse_mask)
: traverse_mask_(traverse_mask), types_seen_(NULL), expressions_seen_(NULL)
{ }
virtual ~Traverse();
// The bitmask of what to traverse.
unsigned int
traverse_mask() const
{ return this->traverse_mask_; }
// Record that we are going to traverse a type. This returns true
// if the type has already been seen in this traversal. This is
// required because types, unlike expressions, can form a circular
// graph.
bool
remember_type(const Type*);
// Record that we are going to see an expression. This returns true
// if the expression has already been seen in this traversal. This
// is only needed for cases where multiple expressions can point to
// a single one.
bool
remember_expression(const Expression*);
// These functions return one of the TRAVERSE codes defined above.
// If traverse_variables is set in the mask, this is called for
// every variable in the tree.
virtual int
variable(Named_object*);
// If traverse_constants is set in the mask, this is called for
// every named constant in the tree. The bool parameter is true for
// a global constant.
virtual int
constant(Named_object*, bool);
// If traverse_functions is set in the mask, this is called for
// every function in the tree.
virtual int
function(Named_object*);
// If traverse_blocks is set in the mask, this is called for every
// block in the tree.
virtual int
block(Block*);
// If traverse_statements is set in the mask, this is called for
// every statement in the tree.
virtual int
statement(Block*, size_t* index, Statement*);
// If traverse_expressions is set in the mask, this is called for
// every expression in the tree.
virtual int
expression(Expression**);
// If traverse_types is set in the mask, this is called for every
// type in the tree.
virtual int
type(Type*);
private:
typedef Unordered_set_hash(const Type*, Type_hash_identical,
Type_identical) Types_seen;
typedef Unordered_set(const Expression*) Expressions_seen;
// Bitmask of what sort of objects to traverse.
unsigned int traverse_mask_;
// Types which have been seen in this traversal.
Types_seen* types_seen_;
// Expressions which have been seen in this traversal.
Expressions_seen* expressions_seen_;
};
// When translating the gogo IR into the backend data structure, this
// is the context we pass down the blocks and statements.
class Translate_context
{
public:
Translate_context(Gogo* gogo, Named_object* function, Block* block,
Bblock* bblock)
: gogo_(gogo), backend_(gogo->backend()), function_(function),
block_(block), bblock_(bblock), is_const_(false)
{ }
// Accessors.
Gogo*
gogo()
{ return this->gogo_; }
Backend*
backend()
{ return this->backend_; }
Named_object*
function()
{ return this->function_; }
Block*
block()
{ return this->block_; }
Bblock*
bblock()
{ return this->bblock_; }
bool
is_const()
{ return this->is_const_; }
// Make a constant context.
void
set_is_const()
{ this->is_const_ = true; }
private:
// The IR for the entire compilation unit.
Gogo* gogo_;
// The generator for the backend data structures.
Backend* backend_;
// The function we are currently translating. NULL if not in a
// function, e.g., the initializer of a global variable.
Named_object* function_;
// The block we are currently translating. NULL if not in a
// function.
Block *block_;
// The backend representation of the current block. NULL if block_
// is NULL.
Bblock* bblock_;
// Whether this is being evaluated in a constant context. This is
// used for type descriptor initializers.
bool is_const_;
};
// Runtime error codes. These must match the values in
// libgo/runtime/go-runtime-error.c.
// Slice index out of bounds: negative or larger than the length of
// the slice.
static const int RUNTIME_ERROR_SLICE_INDEX_OUT_OF_BOUNDS = 0;
// Array index out of bounds.
static const int RUNTIME_ERROR_ARRAY_INDEX_OUT_OF_BOUNDS = 1;
// String index out of bounds.
static const int RUNTIME_ERROR_STRING_INDEX_OUT_OF_BOUNDS = 2;
// Slice slice out of bounds: negative or larger than the length of
// the slice or high bound less than low bound.
static const int RUNTIME_ERROR_SLICE_SLICE_OUT_OF_BOUNDS = 3;
// Array slice out of bounds.
static const int RUNTIME_ERROR_ARRAY_SLICE_OUT_OF_BOUNDS = 4;
// String slice out of bounds.
static const int RUNTIME_ERROR_STRING_SLICE_OUT_OF_BOUNDS = 5;
// Dereference of nil pointer. This is used when there is a
// dereference of a pointer to a very large struct or array, to ensure
// that a gigantic array is not used a proxy to access random memory
// locations.
static const int RUNTIME_ERROR_NIL_DEREFERENCE = 6;
// Slice length or capacity out of bounds in make: negative or
// overflow or length greater than capacity.
static const int RUNTIME_ERROR_MAKE_SLICE_OUT_OF_BOUNDS = 7;
// Map capacity out of bounds in make: negative or overflow.
static const int RUNTIME_ERROR_MAKE_MAP_OUT_OF_BOUNDS = 8;
// Channel capacity out of bounds in make: negative or overflow.
static const int RUNTIME_ERROR_MAKE_CHAN_OUT_OF_BOUNDS = 9;
// This is used by some of the langhooks.
extern Gogo* go_get_gogo();
// Whether we have seen any errors. FIXME: Replace with a backend
// interface.
extern bool saw_errors();
#endif // !defined(GO_GOGO_H)
// gogo.h -- Go frontend parsed representation. -*- C++ -*-
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#ifndef GO_GOGO_H
#define GO_GOGO_H
class Traverse;
class Type;
class Type_hash_identical;
class Type_equal;
class Type_identical;
class Typed_identifier;
class Typed_identifier_list;
class Function_type;
class Expression;
class Statement;
class Block;
class Function;
class Bindings;
class Package;
class Variable;
class Pointer_type;
class Struct_type;
class Struct_field;
class Struct_field_list;
class Array_type;
class Map_type;
class Channel_type;
class Interface_type;
class Named_type;
class Forward_declaration_type;
class Method;
class Methods;
class Named_object;
class Label;
class Translate_context;
class Export;
class Import;
// This file declares the basic classes used to hold the internal
// representation of Go which is built by the parser.
// An initialization function for an imported package. This is a
// magic function which initializes variables and runs the "init"
// function.
class Import_init
{
public:
Import_init(const std::string& package_name, const std::string& init_name,
int priority)
: package_name_(package_name), init_name_(init_name), priority_(priority)
{ }
// The name of the package being imported.
const std::string&
package_name() const
{ return this->package_name_; }
// The name of the package's init function.
const std::string&
init_name() const
{ return this->init_name_; }
// The priority of the initialization function. Functions with a
// lower priority number must be run first.
int
priority() const
{ return this->priority_; }
private:
// The name of the package being imported.
std::string package_name_;
// The name of the package's init function.
std::string init_name_;
// The priority.
int priority_;
};
// For sorting purposes.
inline bool
operator<(const Import_init& i1, const Import_init& i2)
{
if (i1.priority() < i2.priority())
return true;
if (i1.priority() > i2.priority())
return false;
if (i1.package_name() != i2.package_name())
return i1.package_name() < i2.package_name();
return i1.init_name() < i2.init_name();
}
// The holder for the internal representation of the entire
// compilation unit.
class Gogo
{
public:
// Create the IR, passing in the sizes of the types "int" and
// "uintptr" in bits.
Gogo(int int_type_size, int pointer_size);
// Get the package name.
const std::string&
package_name() const;
// Set the package name.
void
set_package_name(const std::string&, source_location);
// Return whether this is the "main" package.
bool
is_main_package() const;
// If necessary, adjust the name to use for a hidden symbol. We add
// a prefix of the package name, so that hidden symbols in different
// packages do not collide.
std::string
pack_hidden_name(const std::string& name, bool is_exported) const
{
return (is_exported
? name
: ('.' + this->unique_prefix()
+ '.' + this->package_name()
+ '.' + name));
}
// Unpack a name which may have been hidden. Returns the
// user-visible name of the object.
static std::string
unpack_hidden_name(const std::string& name)
{ return name[0] != '.' ? name : name.substr(name.rfind('.') + 1); }
// Return whether a possibly packed name is hidden.
static bool
is_hidden_name(const std::string& name)
{ return name[0] == '.'; }
// Return the package prefix of a hidden name.
static std::string
hidden_name_prefix(const std::string& name)
{
gcc_assert(Gogo::is_hidden_name(name));
return name.substr(1, name.rfind('.') - 1);
}
// Given a name which may or may not have been hidden, return the
// name to use in an error message.
static std::string
message_name(const std::string& name);
// Return whether a name is the blank identifier _.
static bool
is_sink_name(const std::string& name)
{
return (name[0] == '.'
&& name[name.length() - 1] == '_'
&& name[name.length() - 2] == '.');
}
// Return the unique prefix to use for all exported symbols.
const std::string&
unique_prefix() const;
// Set the unique prefix.
void
set_unique_prefix(const std::string&);
// Return the priority to use for the package we are compiling.
// This is two more than the largest priority of any package we
// import.
int
package_priority() const;
// Import a package. FILENAME is the file name argument, LOCAL_NAME
// is the local name to give to the package. If LOCAL_NAME is empty
// the declarations are added to the global scope.
void
import_package(const std::string& filename, const std::string& local_name,
bool is_local_name_exported, source_location);
// Whether we are the global binding level.
bool
in_global_scope() const;
// Look up a name in the current binding contours.
Named_object*
lookup(const std::string&, Named_object** pfunction) const;
// Look up a name in the current block.
Named_object*
lookup_in_block(const std::string&) const;
// Look up a name in the global namespace--the universal scope.
Named_object*
lookup_global(const char*) const;
// Add a new imported package. REAL_NAME is the real name of the
// package. ALIAS is the alias of the package; this may be the same
// as REAL_NAME. This sets *PADD_TO_GLOBALS if symbols added to
// this package should be added to the global namespace; this is
// true if the alias is ".". LOCATION is the location of the import
// statement. This returns the new package, or NULL on error.
Package*
add_imported_package(const std::string& real_name, const std::string& alias,
bool is_alias_exported,
const std::string& unique_prefix,
source_location location,
bool* padd_to_globals);
// Register a package. This package may or may not be imported.
// This returns the Package structure for the package, creating if
// it necessary.
Package*
register_package(const std::string& name, const std::string& unique_prefix,
source_location);
// Start compiling a function. ADD_METHOD_TO_TYPE is true if a
// method function should be added to the type of its receiver.
Named_object*
start_function(const std::string& name, Function_type* type,
bool add_method_to_type, source_location);
// Finish compiling a function.
void
finish_function(source_location);
// Return the current function.
Named_object*
current_function() const;
// Start a new block. This is not initially associated with a
// function.
void
start_block(source_location);
// Finish the current block and return it.
Block*
finish_block(source_location);
// Declare an unknown name. This is used while parsing. The name
// must be resolved by the end of the parse. Unknown names are
// always added at the package level.
Named_object*
add_unknown_name(const std::string& name, source_location);
// Declare a function.
Named_object*
declare_function(const std::string&, Function_type*, source_location);
// Add a label.
Label*
add_label_definition(const std::string&, source_location);
// Add a label reference.
Label*
add_label_reference(const std::string&);
// Add a statement to the current block.
void
add_statement(Statement*);
// Add a block to the current block.
void
add_block(Block*, source_location);
// Add a constant.
Named_object*
add_constant(const Typed_identifier&, Expression*, int iota_value);
// Add a type.
void
add_type(const std::string&, Type*, source_location);
// Add a named type. This is used for builtin types, and to add an
// imported type to the global scope.
void
add_named_type(Named_type*);
// Declare a type.
Named_object*
declare_type(const std::string&, source_location);
// Declare a type at the package level. This is used when the
// parser sees an unknown name where a type name is required.
Named_object*
declare_package_type(const std::string&, source_location);
// Define a type which was already declared.
void
define_type(Named_object*, Named_type*);
// Add a variable.
Named_object*
add_variable(const std::string&, Variable*);
// Add a sink--a reference to the blank identifier _.
Named_object*
add_sink();
// Add a named object to the current namespace. This is used for
// import . "package".
void
add_named_object(Named_object*);
// Return a name to use for a thunk function. A thunk function is
// one we create during the compilation, for a go statement or a
// defer statement or a method expression.
static std::string
thunk_name();
// Return whether an object is a thunk.
static bool
is_thunk(const Named_object*);
// Note that we've seen an interface type. This is used to build
// all required interface method tables.
void
record_interface_type(Interface_type*);
// Note that we need an initialization function.
void
set_need_init_fn()
{ this->need_init_fn_ = true; }
// Clear out all names in file scope. This is called when we start
// parsing a new file.
void
clear_file_scope();
// Traverse the tree. See the Traverse class.
void
traverse(Traverse*);
// Define the predeclared global names.
void
define_global_names();
// Verify and complete all types.
void
verify_types();
// Lower the parse tree.
void
lower_parse_tree();
// Lower all the statements in a block.
void
lower_block(Named_object* function, Block*);
// Lower an expression.
void
lower_expression(Named_object* function, Expression**);
// Lower a constant.
void
lower_constant(Named_object*);
// Finalize the method lists and build stub methods for named types.
void
finalize_methods();
// Work out the types to use for unspecified variables and
// constants.
void
determine_types();
// Type check the program.
void
check_types();
// Check the types in a single block. This is used for complicated
// go statements.
void
check_types_in_block(Block*);
// Check for return statements.
void
check_return_statements();
// Do all exports.
void
do_exports();
// Add an import control function for an imported package to the
// list.
void
add_import_init_fn(const std::string& package_name,
const std::string& init_name, int prio);
// Turn short-cut operators (&&, ||) into explicit if statements.
void
remove_shortcuts();
// Use temporary variables to force order of evaluation.
void
order_evaluations();
// Build thunks for functions which call recover.
void
build_recover_thunks();
// Simplify statements which might use thunks: go and defer
// statements.
void
simplify_thunk_statements();
// Convert named types to the backend representation.
void
convert_named_types();
// Convert named types in a list of bindings.
void
convert_named_types_in_bindings(Bindings*);
// True if named types have been converted to the backend
// representation.
bool
named_types_are_converted() const
{ return this->named_types_are_converted_; }
// Write out the global values.
void
write_globals();
// Build a call to a builtin function. PDECL should point to a NULL
// initialized static pointer which will hold the fndecl. NAME is
// the name of the function. NARGS is the number of arguments.
// RETTYPE is the return type. It is followed by NARGS pairs of
// type and argument (both trees).
static tree
call_builtin(tree* pdecl, source_location, const char* name, int nargs,
tree rettype, ...);
// Build a call to the runtime error function.
static tree
runtime_error(int code, source_location);
// Build a builtin struct with a list of fields.
static tree
builtin_struct(tree* ptype, const char* struct_name, tree struct_type,
int nfields, ...);
// Mark a function declaration as a builtin library function.
static void
mark_fndecl_as_builtin_library(tree fndecl);
// Build the type of the struct that holds a slice for the given
// element type.
tree
slice_type_tree(tree element_type_tree);
// Given a tree for a slice type, return the tree for the element
// type.
static tree
slice_element_type_tree(tree slice_type_tree);
// Build a constructor for a slice. SLICE_TYPE_TREE is the type of
// the slice. VALUES points to the values. COUNT is the size,
// CAPACITY is the capacity. If CAPACITY is NULL, it is set to
// COUNT.
static tree
slice_constructor(tree slice_type_tree, tree values, tree count,
tree capacity);
// Build a constructor for an empty slice. SLICE_TYPE_TREE is the
// type of the slice.
static tree
empty_slice_constructor(tree slice_type_tree);
// Build a map descriptor.
tree
map_descriptor(Map_type*);
// Return a tree for the type of a map descriptor. This is struct
// __go_map_descriptor in libgo/runtime/map.h. This is the same for
// all map types.
tree
map_descriptor_type();
// Build a type descriptor for TYPE using INITIALIZER as the type
// descriptor. This builds a new decl stored in *PDECL.
void
build_type_descriptor_decl(const Type*, Expression* initializer,
tree* pdecl);
// Build required interface method tables.
void
build_interface_method_tables();
// Build an interface method table for a type: a list of function
// pointers, one for each interface method. This returns a decl.
tree
interface_method_table_for_type(const Interface_type*, Named_type*,
bool is_pointer);
// Return a tree which allocate SIZE bytes to hold values of type
// TYPE.
tree
allocate_memory(Type *type, tree size, source_location);
// Return a type to use for pointer to const char.
static tree
const_char_pointer_type_tree();
// Build a string constant with the right type.
static tree
string_constant_tree(const std::string&);
// Build a Go string constant. This returns a pointer to the
// constant.
tree
go_string_constant_tree(const std::string&);
// Send a value on a channel.
static tree
send_on_channel(tree channel, tree val, bool blocking, bool for_select,
source_location);
// Receive a value from a channel.
static tree
receive_from_channel(tree type_tree, tree channel, bool for_select,
source_location);
// Return a tree for receiving an integer on a channel.
static tree
receive_as_64bit_integer(tree type, tree channel, bool blocking,
bool for_select);
// Make a trampoline which calls FNADDR passing CLOSURE.
tree
make_trampoline(tree fnaddr, tree closure, source_location);
private:
// During parsing, we keep a stack of functions. Each function on
// the stack is one that we are currently parsing. For each
// function, we keep track of the current stack of blocks.
struct Open_function
{
// The function.
Named_object* function;
// The stack of active blocks in the function.
std::vector<Block*> blocks;
};
// The stack of functions.
typedef std::vector<Open_function> Open_functions;
// Create trees for implicit builtin functions.
void
define_builtin_function_trees();
// Set up the built-in unsafe package.
void
import_unsafe(const std::string&, bool is_exported, source_location);
// Add a new imported package.
Named_object*
add_package(const std::string& real_name, const std::string& alias,
const std::string& unique_prefix, source_location location);
// Return the current binding contour.
Bindings*
current_bindings();
const Bindings*
current_bindings() const;
// Return the current block.
Block*
current_block();
// Get the name of the magic initialization function.
const std::string&
get_init_fn_name();
// Get the decl for the magic initialization function.
tree
initialization_function_decl();
// Write the magic initialization function.
void
write_initialization_function(tree fndecl, tree init_stmt_list);
// Initialize imported packages.
void
init_imports(tree*);
// Register variables with the garbage collector.
void
register_gc_vars(const std::vector<Named_object*>&, tree*);
// Build a pointer to a Go string constant. This returns a pointer
// to the pointer.
tree
ptr_go_string_constant_tree(const std::string&);
// Return the name to use for a type descriptor decl for an unnamed
// type.
std::string
unnamed_type_descriptor_decl_name(const Type* type);
// Return the name to use for a type descriptor decl for a type
// named NO, defined in IN_FUNCTION.
std::string
type_descriptor_decl_name(const Named_object* no,
const Named_object* in_function);
// Where a type descriptor should be defined.
enum Type_descriptor_location
{
// Defined in this file.
TYPE_DESCRIPTOR_DEFINED,
// Defined in some other file.
TYPE_DESCRIPTOR_UNDEFINED,
// Common definition which may occur in multiple files.
TYPE_DESCRIPTOR_COMMON
};
// Return where the decl for TYPE should be defined.
Type_descriptor_location
type_descriptor_location(const Type* type);
// Return the type of a trampoline.
static tree
trampoline_type_tree();
// Type used to map import names to packages.
typedef std::map<std::string, Package*> Imports;
// Type used to map package names to packages.
typedef std::map<std::string, Package*> Packages;
// Type used to map special names in the sys package.
typedef std::map<std::string, std::string> Sys_names;
// Hash table mapping map types to map descriptor decls.
typedef Unordered_map_hash(const Map_type*, tree, Type_hash_identical,
Type_identical) Map_descriptors;
// Map unnamed types to type descriptor decls.
typedef Unordered_map_hash(const Type*, tree, Type_hash_identical,
Type_identical) Type_descriptor_decls;
// The package we are compiling.
Package* package_;
// The list of currently open functions during parsing.
Open_functions functions_;
// The global binding contour. This includes the builtin functions
// and the package we are compiling.
Bindings* globals_;
// Mapping from import file names to packages.
Imports imports_;
// Whether the magic unsafe package was imported.
bool imported_unsafe_;
// Mapping from package names we have seen to packages. This does
// not include the package we are compiling.
Packages packages_;
// Mapping from map types to map descriptors.
Map_descriptors* map_descriptors_;
// Mapping from unnamed types to type descriptor decls.
Type_descriptor_decls* type_descriptor_decls_;
// The functions named "init", if there are any.
std::vector<Named_object*> init_functions_;
// Whether we need a magic initialization function.
bool need_init_fn_;
// The name of the magic initialization function.
std::string init_fn_name_;
// A list of import control variables for packages that we import.
std::set<Import_init> imported_init_fns_;
// The unique prefix used for all global symbols.
std::string unique_prefix_;
// Whether an explicit unique prefix was set by -fgo-prefix.
bool unique_prefix_specified_;
// A list of interface types defined while parsing.
std::vector<Interface_type*> interface_types_;
// Whether named types have been converted.
bool named_types_are_converted_;
};
// A block of statements.
class Block
{
public:
Block(Block* enclosing, source_location);
// Return the enclosing block.
const Block*
enclosing() const
{ return this->enclosing_; }
// Return the bindings of the block.
Bindings*
bindings()
{ return this->bindings_; }
const Bindings*
bindings() const
{ return this->bindings_; }
// Look at the block's statements.
const std::vector<Statement*>*
statements() const
{ return &this->statements_; }
// Return the start location. This is normally the location of the
// left curly brace which starts the block.
source_location
start_location() const
{ return this->start_location_; }
// Return the end location. This is normally the location of the
// right curly brace which ends the block.
source_location
end_location() const
{ return this->end_location_; }
// Add a statement to the block.
void
add_statement(Statement*);
// Add a statement to the front of the block.
void
add_statement_at_front(Statement*);
// Replace a statement in a block.
void
replace_statement(size_t index, Statement*);
// Add a Statement before statement number INDEX.
void
insert_statement_before(size_t index, Statement*);
// Add a Statement after statement number INDEX.
void
insert_statement_after(size_t index, Statement*);
// Set the end location of the block.
void
set_end_location(source_location location)
{ this->end_location_ = location; }
// Traverse the tree.
int
traverse(Traverse*);
// Set final types for unspecified variables and constants.
void
determine_types();
// Return true if execution of this block may fall through to the
// next block.
bool
may_fall_through() const;
// Return a tree of the code in this block.
tree
get_tree(Translate_context*);
// Iterate over statements.
typedef std::vector<Statement*>::iterator iterator;
iterator
begin()
{ return this->statements_.begin(); }
iterator
end()
{ return this->statements_.end(); }
private:
// Enclosing block.
Block* enclosing_;
// Statements in the block.
std::vector<Statement*> statements_;
// Binding contour.
Bindings* bindings_;
// Location of start of block.
source_location start_location_;
// Location of end of block.
source_location end_location_;
};
// A function.
class Function
{
public:
Function(Function_type* type, Function*, Block*, source_location);
// Return the function's type.
Function_type*
type() const
{ return this->type_; }
// Return the enclosing function if there is one.
Function*
enclosing()
{ return this->enclosing_; }
// Set the enclosing function. This is used when building thunks
// for functions which call recover.
void
set_enclosing(Function* enclosing)
{
gcc_assert(this->enclosing_ == NULL);
this->enclosing_ = enclosing;
}
// Create the named result variables in the outer block.
void
create_named_result_variables(Gogo*);
// Update the named result variables when cloning a function which
// calls recover.
void
update_named_result_variables();
// Add a new field to the closure variable.
void
add_closure_field(Named_object* var, source_location loc)
{ this->closure_fields_.push_back(std::make_pair(var, loc)); }
// Whether this function needs a closure.
bool
needs_closure() const
{ return !this->closure_fields_.empty(); }
// Return the closure variable, creating it if necessary. This is
// passed to the function as a static chain parameter.
Named_object*
closure_var();
// Set the closure variable. This is used when building thunks for
// functions which call recover.
void
set_closure_var(Named_object* v)
{
gcc_assert(this->closure_var_ == NULL);
this->closure_var_ = v;
}
// Return the variable for a reference to field INDEX in the closure
// variable.
Named_object*
enclosing_var(unsigned int index)
{
gcc_assert(index < this->closure_fields_.size());
return closure_fields_[index].first;
}
// Set the type of the closure variable if there is one.
void
set_closure_type();
// Get the block of statements associated with the function.
Block*
block() const
{ return this->block_; }
// Get the location of the start of the function.
source_location
location() const
{ return this->location_; }
// Return whether this function is actually a method.
bool
is_method() const;
// Add a label definition to the function.
Label*
add_label_definition(const std::string& label_name, source_location);
// Add a label reference to a function.
Label*
add_label_reference(const std::string& label_name);
// Whether this function calls the predeclared recover function.
bool
calls_recover() const
{ return this->calls_recover_; }
// Record that this function calls the predeclared recover function.
// This is set during the lowering pass.
void
set_calls_recover()
{ this->calls_recover_ = true; }
// Whether this is a recover thunk function.
bool
is_recover_thunk() const
{ return this->is_recover_thunk_; }
// Record that this is a thunk built for a function which calls
// recover.
void
set_is_recover_thunk()
{ this->is_recover_thunk_ = true; }
// Whether this function already has a recover thunk.
bool
has_recover_thunk() const
{ return this->has_recover_thunk_; }
// Record that this function already has a recover thunk.
void
set_has_recover_thunk()
{ this->has_recover_thunk_ = true; }
// Swap with another function. Used only for the thunk which calls
// recover.
void
swap_for_recover(Function *);
// Traverse the tree.
int
traverse(Traverse*);
// Determine types in the function.
void
determine_types();
// Return the function's decl given an identifier.
tree
get_or_make_decl(Gogo*, Named_object*, tree id);
// Return the function's decl after it has been built.
tree
get_decl() const
{
gcc_assert(this->fndecl_ != NULL);
return this->fndecl_;
}
// Set the function decl to hold a tree of the function code.
void
build_tree(Gogo*, Named_object*);
// Get the value to return when not explicitly specified. May also
// add statements to execute first to STMT_LIST.
tree
return_value(Gogo*, Named_object*, source_location, tree* stmt_list) const;
// Get a tree for the variable holding the defer stack.
tree
defer_stack(source_location);
// Export the function.
void
export_func(Export*, const std::string& name) const;
// Export a function with a type.
static void
export_func_with_type(Export*, const std::string& name,
const Function_type*);
// Import a function.
static void
import_func(Import*, std::string* pname, Typed_identifier** receiver,
Typed_identifier_list** pparameters,
Typed_identifier_list** presults, bool* is_varargs);
private:
// Type for mapping from label names to Label objects.
typedef Unordered_map(std::string, Label*) Labels;
tree
make_receiver_parm_decl(Gogo*, Named_object*, tree);
tree
copy_parm_to_heap(Gogo*, Named_object*, tree);
void
build_defer_wrapper(Gogo*, Named_object*, tree*, tree*);
typedef std::vector<Named_object*> Named_results;
typedef std::vector<std::pair<Named_object*,
source_location> > Closure_fields;
// The function's type.
Function_type* type_;
// The enclosing function. This is NULL when there isn't one, which
// is the normal case.
Function* enclosing_;
// The named result variables, if any.
Named_results* named_results_;
// If there is a closure, this is the list of variables which appear
// in the closure. This is created by the parser, and then resolved
// to a real type when we lower parse trees.
Closure_fields closure_fields_;
// The closure variable, passed as a parameter using the static
// chain parameter. Normally NULL.
Named_object* closure_var_;
// The outer block of statements in the function.
Block* block_;
// The source location of the start of the function.
source_location location_;
// Labels defined or referenced in the function.
Labels labels_;
// The function decl.
tree fndecl_;
// A variable holding the defer stack variable. This is NULL unless
// we actually need a defer stack.
tree defer_stack_;
// True if this function calls the predeclared recover function.
bool calls_recover_;
// True if this a thunk built for a function which calls recover.
bool is_recover_thunk_;
// True if this function already has a recover thunk.
bool has_recover_thunk_;
};
// A function declaration.
class Function_declaration
{
public:
Function_declaration(Function_type* fntype, source_location location)
: fntype_(fntype), location_(location), asm_name_(), fndecl_(NULL)
{ }
Function_type*
type() const
{ return this->fntype_; }
source_location
location() const
{ return this->location_; }
const std::string&
asm_name() const
{ return this->asm_name_; }
// Set the assembler name.
void
set_asm_name(const std::string& asm_name)
{ this->asm_name_ = asm_name; }
// Return a decl for the function given an identifier.
tree
get_or_make_decl(Gogo*, Named_object*, tree id);
// Export a function declaration.
void
export_func(Export* exp, const std::string& name) const
{ Function::export_func_with_type(exp, name, this->fntype_); }
private:
// The type of the function.
Function_type* fntype_;
// The location of the declaration.
source_location location_;
// The assembler name: this is the name to use in references to the
// function. This is normally empty.
std::string asm_name_;
// The function decl if needed.
tree fndecl_;
};
// A variable.
class Variable
{
public:
Variable(Type*, Expression*, bool is_global, bool is_parameter,
bool is_receiver, source_location);
// Get the type of the variable.
Type*
type();
Type*
type() const;
// Return whether the type is defined yet.
bool
has_type() const
{ return this->type_ != NULL; }
// Get the initial value.
Expression*
init() const
{ return this->init_; }
// Return whether there are any preinit statements.
bool
has_pre_init() const
{ return this->preinit_ != NULL; }
// Return the preinit statements if any.
Block*
preinit() const
{ return this->preinit_; }
// Return whether this is a global variable.
bool
is_global() const
{ return this->is_global_; }
// Return whether this is a function parameter.
bool
is_parameter() const
{ return this->is_parameter_; }
// Return whether this is the receiver parameter of a method.
bool
is_receiver() const
{ return this->is_receiver_; }
// Change this parameter to be a receiver. This is used when
// creating the thunks created for functions which call recover.
void
set_is_receiver()
{
gcc_assert(this->is_parameter_);
this->is_receiver_ = true;
}
// Change this parameter to not be a receiver. This is used when
// creating the thunks created for functions which call recover.
void
set_is_not_receiver()
{
gcc_assert(this->is_parameter_);
this->is_receiver_ = false;
}
// Return whether this is the varargs parameter of a function.
bool
is_varargs_parameter() const
{ return this->is_varargs_parameter_; }
// Whether this variable's address is taken.
bool
is_address_taken() const
{ return this->is_address_taken_; }
// Whether this variable should live in the heap.
bool
is_in_heap() const
{ return this->is_address_taken_ && !this->is_global_; }
// Get the source location of the variable's declaration.
source_location
location() const
{ return this->location_; }
// Record that this is the varargs parameter of a function.
void
set_is_varargs_parameter()
{
gcc_assert(this->is_parameter_);
this->is_varargs_parameter_ = true;
}
// Clear the initial value; used for error handling.
void
clear_init()
{ this->init_ = NULL; }
// Set the initial value; used for converting shortcuts.
void
set_init(Expression* init)
{ this->init_ = init; }
// Get the preinit block, a block of statements to be run before the
// initialization expression.
Block*
preinit_block(Gogo*);
// Add a statement to be run before the initialization expression.
// This is only used for global variables.
void
add_preinit_statement(Gogo*, Statement*);
// Lower the initialization expression after parsing is complete.
void
lower_init_expression(Gogo*, Named_object*);
// A special case: the init value is used only to determine the
// type. This is used if the variable is defined using := with the
// comma-ok form of a map index or a receive expression. The init
// value is actually the map index expression or receive expression.
// We use this because we may not know the right type at parse time.
void
set_type_from_init_tuple()
{ this->type_from_init_tuple_ = true; }
// Another special case: the init value is used only to determine
// the type. This is used if the variable is defined using := with
// a range clause. The init value is the range expression. The
// type of the variable is the index type of the range expression
// (i.e., the first value returned by a range).
void
set_type_from_range_index()
{ this->type_from_range_index_ = true; }
// Another special case: like set_type_from_range_index, but the
// type is the value type of the range expression (i.e., the second
// value returned by a range).
void
set_type_from_range_value()
{ this->type_from_range_value_ = true; }
// Another special case: the init value is used only to determine
// the type. This is used if the variable is defined using := with
// a case in a select statement. The init value is the channel.
// The type of the variable is the channel's element type.
void
set_type_from_chan_element()
{ this->type_from_chan_element_ = true; }
// After we lower the select statement, we once again set the type
// from the initialization expression.
void
clear_type_from_chan_element()
{
gcc_assert(this->type_from_chan_element_);
this->type_from_chan_element_ = false;
}
// Note that this variable was created for a type switch clause.
void
set_is_type_switch_var()
{ this->is_type_switch_var_ = true; }
// Traverse the initializer expression.
int
traverse_expression(Traverse*);
// Determine the type of the variable if necessary.
void
determine_type();
// Note that something takes the address of this variable.
void
set_address_taken()
{ this->is_address_taken_ = true; }
// Get the initial value of the variable as a tree. This may only
// be called if has_pre_init() returns false.
tree
get_init_tree(Gogo*, Named_object* function);
// Return a series of statements which sets the value of the
// variable in DECL. This should only be called is has_pre_init()
// returns true. DECL may be NULL for a sink variable.
tree
get_init_block(Gogo*, Named_object* function, tree decl);
// Export the variable.
void
export_var(Export*, const std::string& name) const;
// Import a variable.
static void
import_var(Import*, std::string* pname, Type** ptype);
private:
// The type of a tuple.
Type*
type_from_tuple(Expression*, bool) const;
// The type of a range.
Type*
type_from_range(Expression*, bool, bool) const;
// The element type of a channel.
Type*
type_from_chan_element(Expression*, bool) const;
// The variable's type. This may be NULL if the type is set from
// the expression.
Type* type_;
// The initial value. This may be NULL if the variable should be
// initialized to the default value for the type.
Expression* init_;
// Statements to run before the init statement.
Block* preinit_;
// Location of variable definition.
source_location location_;
// Whether this is a global variable.
bool is_global_ : 1;
// Whether this is a function parameter.
bool is_parameter_ : 1;
// Whether this is the receiver parameter of a method.
bool is_receiver_ : 1;
// Whether this is the varargs parameter of a function.
bool is_varargs_parameter_ : 1;
// Whether something takes the address of this variable.
bool is_address_taken_ : 1;
// True if we have seen this variable in a traversal.
bool seen_ : 1;
// True if we have lowered the initialization expression.
bool init_is_lowered_ : 1;
// True if init is a tuple used to set the type.
bool type_from_init_tuple_ : 1;
// True if init is a range clause and the type is the index type.
bool type_from_range_index_ : 1;
// True if init is a range clause and the type is the value type.
bool type_from_range_value_ : 1;
// True if init is a channel and the type is the channel's element type.
bool type_from_chan_element_ : 1;
// True if this is a variable created for a type switch case.
bool is_type_switch_var_ : 1;
// True if we have determined types.
bool determined_type_ : 1;
};
// A variable which is really the name for a function return value, or
// part of one.
class Result_variable
{
public:
Result_variable(Type* type, Function* function, int index)
: type_(type), function_(function), index_(index),
is_address_taken_(false)
{ }
// Get the type of the result variable.
Type*
type() const
{ return this->type_; }
// Get the function that this is associated with.
Function*
function() const
{ return this->function_; }
// Index in the list of function results.
int
index() const
{ return this->index_; }
// Whether this variable's address is taken.
bool
is_address_taken() const
{ return this->is_address_taken_; }
// Note that something takes the address of this variable.
void
set_address_taken()
{ this->is_address_taken_ = true; }
// Whether this variable should live in the heap.
bool
is_in_heap() const
{ return this->is_address_taken_; }
// Set the function. This is used when cloning functions which call
// recover.
void
set_function(Function* function)
{ this->function_ = function; }
private:
// Type of result variable.
Type* type_;
// Function with which this is associated.
Function* function_;
// Index in list of results.
int index_;
// Whether something takes the address of this variable.
bool is_address_taken_;
};
// The value we keep for a named constant. This lets us hold a type
// and an expression.
class Named_constant
{
public:
Named_constant(Type* type, Expression* expr, int iota_value,
source_location location)
: type_(type), expr_(expr), iota_value_(iota_value), location_(location),
lowering_(false)
{ }
Type*
type() const
{ return this->type_; }
Expression*
expr() const
{ return this->expr_; }
int
iota_value() const
{ return this->iota_value_; }
source_location
location() const
{ return this->location_; }
// Whether we are lowering.
bool
lowering() const
{ return this->lowering_; }
// Set that we are lowering.
void
set_lowering()
{ this->lowering_ = true; }
// We are no longer lowering.
void
clear_lowering()
{ this->lowering_ = false; }
// Traverse the expression.
int
traverse_expression(Traverse*);
// Determine the type of the constant if necessary.
void
determine_type();
// Indicate that we found and reported an error for this constant.
void
set_error();
// Export the constant.
void
export_const(Export*, const std::string& name) const;
// Import a constant.
static void
import_const(Import*, std::string*, Type**, Expression**);
private:
// The type of the constant.
Type* type_;
// The expression for the constant.
Expression* expr_;
// If the predeclared constant iota is used in EXPR_, this is the
// value it will have. We do this because at parse time we don't
// know whether the name "iota" will refer to the predeclared
// constant or to something else. We put in the right value in when
// we lower.
int iota_value_;
// The location of the definition.
source_location location_;
// Whether we are currently lowering this constant.
bool lowering_;
};
// A type declaration.
class Type_declaration
{
public:
Type_declaration(source_location location)
: location_(location), in_function_(NULL), methods_(),
issued_warning_(false)
{ }
// Return the location.
source_location
location() const
{ return this->location_; }
// Return the function in which this type is declared. This will
// return NULL for a type declared in global scope.
Named_object*
in_function()
{ return this->in_function_; }
// Set the function in which this type is declared.
void
set_in_function(Named_object* f)
{ this->in_function_ = f; }
// Add a method to this type. This is used when methods are defined
// before the type.
Named_object*
add_method(const std::string& name, Function* function);
// Add a method declaration to this type.
Named_object*
add_method_declaration(const std::string& name, Function_type* type,
source_location location);
// Return whether any methods were defined.
bool
has_methods() const;
// Define methods when the real type is known.
void
define_methods(Named_type*);
// This is called if we are trying to use this type. It returns
// true if we should issue a warning.
bool
using_type();
private:
typedef std::vector<Named_object*> Methods;
// The location of the type declaration.
source_location location_;
// If this type is declared in a function, a pointer back to the
// function in which it is defined.
Named_object* in_function_;
// Methods defined before the type is defined.
Methods methods_;
// True if we have issued a warning about a use of this type
// declaration when it is undefined.
bool issued_warning_;
};
// An unknown object. These are created by the parser for forward
// references to names which have not been seen before. In a correct
// program, these will always point to a real definition by the end of
// the parse. Because they point to another Named_object, these may
// only be referenced by Unknown_expression objects.
class Unknown_name
{
public:
Unknown_name(source_location location)
: location_(location), real_named_object_(NULL)
{ }
// Return the location where this name was first seen.
source_location
location() const
{ return this->location_; }
// Return the real named object that this points to, or NULL if it
// was never resolved.
Named_object*
real_named_object() const
{ return this->real_named_object_; }
// Set the real named object that this points to.
void
set_real_named_object(Named_object* no);
private:
// The location where this name was first seen.
source_location location_;
// The real named object when it is known.
Named_object*
real_named_object_;
};
// A named object named. This is the result of a declaration. We
// don't use a superclass because they all have to be handled
// differently.
class Named_object
{
public:
enum Classification
{
// An uninitialized Named_object. We should never see this.
NAMED_OBJECT_UNINITIALIZED,
// An unknown name. This is used for forward references. In a
// correct program, these will all be resolved by the end of the
// parse.
NAMED_OBJECT_UNKNOWN,
// A const.
NAMED_OBJECT_CONST,
// A type.
NAMED_OBJECT_TYPE,
// A forward type declaration.
NAMED_OBJECT_TYPE_DECLARATION,
// A var.
NAMED_OBJECT_VAR,
// A result variable in a function.
NAMED_OBJECT_RESULT_VAR,
// The blank identifier--the special variable named _.
NAMED_OBJECT_SINK,
// A func.
NAMED_OBJECT_FUNC,
// A forward func declaration.
NAMED_OBJECT_FUNC_DECLARATION,
// A package.
NAMED_OBJECT_PACKAGE
};
// Return the classification.
Classification
classification() const
{ return this->classification_; }
// Classifiers.
bool
is_unknown() const
{ return this->classification_ == NAMED_OBJECT_UNKNOWN; }
bool
is_const() const
{ return this->classification_ == NAMED_OBJECT_CONST; }
bool
is_type() const
{ return this->classification_ == NAMED_OBJECT_TYPE; }
bool
is_type_declaration() const
{ return this->classification_ == NAMED_OBJECT_TYPE_DECLARATION; }
bool
is_variable() const
{ return this->classification_ == NAMED_OBJECT_VAR; }
bool
is_result_variable() const
{ return this->classification_ == NAMED_OBJECT_RESULT_VAR; }
bool
is_sink() const
{ return this->classification_ == NAMED_OBJECT_SINK; }
bool
is_function() const
{ return this->classification_ == NAMED_OBJECT_FUNC; }
bool
is_function_declaration() const
{ return this->classification_ == NAMED_OBJECT_FUNC_DECLARATION; }
bool
is_package() const
{ return this->classification_ == NAMED_OBJECT_PACKAGE; }
// Creators.
static Named_object*
make_unknown_name(const std::string& name, source_location);
static Named_object*
make_constant(const Typed_identifier&, const Package*, Expression*,
int iota_value);
static Named_object*
make_type(const std::string&, const Package*, Type*, source_location);
static Named_object*
make_type_declaration(const std::string&, const Package*, source_location);
static Named_object*
make_variable(const std::string&, const Package*, Variable*);
static Named_object*
make_result_variable(const std::string&, Result_variable*);
static Named_object*
make_sink();
static Named_object*
make_function(const std::string&, const Package*, Function*);
static Named_object*
make_function_declaration(const std::string&, const Package*, Function_type*,
source_location);
static Named_object*
make_package(const std::string& alias, Package* package);
// Getters.
Unknown_name*
unknown_value()
{
gcc_assert(this->classification_ == NAMED_OBJECT_UNKNOWN);
return this->u_.unknown_value;
}
const Unknown_name*
unknown_value() const
{
gcc_assert(this->classification_ == NAMED_OBJECT_UNKNOWN);
return this->u_.unknown_value;
}
Named_constant*
const_value()
{
gcc_assert(this->classification_ == NAMED_OBJECT_CONST);
return this->u_.const_value;
}
const Named_constant*
const_value() const
{
gcc_assert(this->classification_ == NAMED_OBJECT_CONST);
return this->u_.const_value;
}
Named_type*
type_value()
{
gcc_assert(this->classification_ == NAMED_OBJECT_TYPE);
return this->u_.type_value;
}
const Named_type*
type_value() const
{
gcc_assert(this->classification_ == NAMED_OBJECT_TYPE);
return this->u_.type_value;
}
Type_declaration*
type_declaration_value()
{
gcc_assert(this->classification_ == NAMED_OBJECT_TYPE_DECLARATION);
return this->u_.type_declaration;
}
const Type_declaration*
type_declaration_value() const
{
gcc_assert(this->classification_ == NAMED_OBJECT_TYPE_DECLARATION);
return this->u_.type_declaration;
}
Variable*
var_value()
{
gcc_assert(this->classification_ == NAMED_OBJECT_VAR);
return this->u_.var_value;
}
const Variable*
var_value() const
{
gcc_assert(this->classification_ == NAMED_OBJECT_VAR);
return this->u_.var_value;
}
Result_variable*
result_var_value()
{
gcc_assert(this->classification_ == NAMED_OBJECT_RESULT_VAR);
return this->u_.result_var_value;
}
const Result_variable*
result_var_value() const
{
gcc_assert(this->classification_ == NAMED_OBJECT_RESULT_VAR);
return this->u_.result_var_value;
}
Function*
func_value()
{
gcc_assert(this->classification_ == NAMED_OBJECT_FUNC);
return this->u_.func_value;
}
const Function*
func_value() const
{
gcc_assert(this->classification_ == NAMED_OBJECT_FUNC);
return this->u_.func_value;
}
Function_declaration*
func_declaration_value()
{
gcc_assert(this->classification_ == NAMED_OBJECT_FUNC_DECLARATION);
return this->u_.func_declaration_value;
}
const Function_declaration*
func_declaration_value() const
{
gcc_assert(this->classification_ == NAMED_OBJECT_FUNC_DECLARATION);
return this->u_.func_declaration_value;
}
Package*
package_value()
{
gcc_assert(this->classification_ == NAMED_OBJECT_PACKAGE);
return this->u_.package_value;
}
const Package*
package_value() const
{
gcc_assert(this->classification_ == NAMED_OBJECT_PACKAGE);
return this->u_.package_value;
}
const std::string&
name() const
{ return this->name_; }
// Return the name to use in an error message. The difference is
// that if this Named_object is defined in a different package, this
// will return PACKAGE.NAME.
std::string
message_name() const;
const Package*
package() const
{ return this->package_; }
// Resolve an unknown value if possible. This returns the same
// Named_object or a new one.
Named_object*
resolve()
{
Named_object* ret = this;
if (this->is_unknown())
{
Named_object* r = this->unknown_value()->real_named_object();
if (r != NULL)
ret = r;
}
return ret;
}
const Named_object*
resolve() const
{
const Named_object* ret = this;
if (this->is_unknown())
{
const Named_object* r = this->unknown_value()->real_named_object();
if (r != NULL)
ret = r;
}
return ret;
}
// The location where this object was defined or referenced.
source_location
location() const;
// Return a tree for the external identifier for this object.
tree
get_id(Gogo*);
// Return a tree representing this object.
tree
get_tree(Gogo*, Named_object* function);
// Define a type declaration.
void
set_type_value(Named_type*);
// Define a function declaration.
void
set_function_value(Function*);
// Declare an unknown name as a type declaration.
void
declare_as_type();
// Export this object.
void
export_named_object(Export*) const;
private:
Named_object(const std::string&, const Package*, Classification);
// The name of the object.
std::string name_;
// The package that this object is in. This is NULL if it is in the
// file we are compiling.
const Package* package_;
// The type of object this is.
Classification classification_;
// The real data.
union
{
Unknown_name* unknown_value;
Named_constant* const_value;
Named_type* type_value;
Type_declaration* type_declaration;
Variable* var_value;
Result_variable* result_var_value;
Function* func_value;
Function_declaration* func_declaration_value;
Package* package_value;
} u_;
// The DECL tree for this object if we have already converted it.
tree tree_;
};
// A binding contour. This binds names to objects.
class Bindings
{
public:
// Type for mapping from names to objects.
typedef Unordered_map(std::string, Named_object*) Contour;
Bindings(Bindings* enclosing);
// Add an unknown name.
Named_object*
add_unknown_name(const std::string& name, source_location location)
{
return this->add_named_object(Named_object::make_unknown_name(name,
location));
}
// Add a constant.
Named_object*
add_constant(const Typed_identifier& tid, const Package* package,
Expression* expr, int iota_value)
{
return this->add_named_object(Named_object::make_constant(tid, package,
expr,
iota_value));
}
// Add a type.
Named_object*
add_type(const std::string& name, const Package* package, Type* type,
source_location location)
{
return this->add_named_object(Named_object::make_type(name, package, type,
location));
}
// Add a named type. This is used for builtin types, and to add an
// imported type to the global scope.
Named_object*
add_named_type(Named_type* named_type);
// Add a type declaration.
Named_object*
add_type_declaration(const std::string& name, const Package* package,
source_location location)
{
Named_object* no = Named_object::make_type_declaration(name, package,
location);
return this->add_named_object(no);
}
// Add a variable.
Named_object*
add_variable(const std::string& name, const Package* package,
Variable* variable)
{
return this->add_named_object(Named_object::make_variable(name, package,
variable));
}
// Add a result variable.
Named_object*
add_result_variable(const std::string& name, Result_variable* result)
{
return this->add_named_object(Named_object::make_result_variable(name,
result));
}
// Add a function.
Named_object*
add_function(const std::string& name, const Package*, Function* function);
// Add a function declaration.
Named_object*
add_function_declaration(const std::string& name, const Package* package,
Function_type* type, source_location location);
// Add a package. The location is the location of the import
// statement.
Named_object*
add_package(const std::string& alias, Package* package)
{
Named_object* no = Named_object::make_package(alias, package);
return this->add_named_object(no);
}
// Define a type which was already declared.
void
define_type(Named_object*, Named_type*);
// Add a method to the list of objects. This is not added to the
// lookup table.
void
add_method(Named_object*);
// Add a named object to this binding.
Named_object*
add_named_object(Named_object* no)
{ return this->add_named_object_to_contour(&this->bindings_, no); }
// Clear all names in file scope from the bindings.
void
clear_file_scope();
// Look up a name in this binding contour and in any enclosing
// binding contours. This returns NULL if the name is not found.
Named_object*
lookup(const std::string&) const;
// Look up a name in this binding contour without looking in any
// enclosing binding contours. Returns NULL if the name is not found.
Named_object*
lookup_local(const std::string&) const;
// Remove a name.
void
remove_binding(Named_object*);
// Traverse the tree. See the Traverse class.
int
traverse(Traverse*, bool is_global);
// Iterate over definitions. This does not include things which
// were only declared.
typedef std::vector<Named_object*>::const_iterator
const_definitions_iterator;
const_definitions_iterator
begin_definitions() const
{ return this->named_objects_.begin(); }
const_definitions_iterator
end_definitions() const
{ return this->named_objects_.end(); }
// Return the number of definitions.
size_t
size_definitions() const
{ return this->named_objects_.size(); }
// Return whether there are no definitions.
bool
empty_definitions() const
{ return this->named_objects_.empty(); }
// Iterate over declarations. This is everything that has been
// declared, which includes everything which has been defined.
typedef Contour::const_iterator const_declarations_iterator;
const_declarations_iterator
begin_declarations() const
{ return this->bindings_.begin(); }
const_declarations_iterator
end_declarations() const
{ return this->bindings_.end(); }
// Return the number of declarations.
size_t
size_declarations() const
{ return this->bindings_.size(); }
// Return whether there are no declarations.
bool
empty_declarations() const
{ return this->bindings_.empty(); }
// Return the first declaration.
Named_object*
first_declaration()
{ return this->bindings_.empty() ? NULL : this->bindings_.begin()->second; }
private:
Named_object*
add_named_object_to_contour(Contour*, Named_object*);
Named_object*
new_definition(Named_object*, Named_object*);
// Enclosing bindings.
Bindings* enclosing_;
// The list of objects.
std::vector<Named_object*> named_objects_;
// The mapping from names to objects.
Contour bindings_;
};
// A label.
class Label
{
public:
Label(const std::string& name)
: name_(name), location_(0), decl_(NULL)
{ }
// Return the label's name.
const std::string&
name() const
{ return this->name_; }
// Return whether the label has been defined.
bool
is_defined() const
{ return this->location_ != 0; }
// Return the location of the definition.
source_location
location() const
{ return this->location_; }
// Define the label at LOCATION.
void
define(source_location location)
{
gcc_assert(this->location_ == 0);
this->location_ = location;
}
// Return the LABEL_DECL for this decl.
tree
get_decl();
// Return an expression for the address of this label.
tree
get_addr(source_location location);
private:
// The name of the label.
std::string name_;
// The location of the definition. This is 0 if the label has not
// yet been defined.
source_location location_;
// The LABEL_DECL.
tree decl_;
};
// An unnamed label. These are used when lowering loops.
class Unnamed_label
{
public:
Unnamed_label(source_location location)
: location_(location), decl_(NULL)
{ }
// Get the location where the label is defined.
source_location
location() const
{ return this->location_; }
// Set the location where the label is defined.
void
set_location(source_location location)
{ this->location_ = location; }
// Return a statement which defines this label.
tree
get_definition();
// Return a goto to this label from LOCATION.
tree
get_goto(source_location location);
private:
// Return the LABEL_DECL to use with GOTO_EXPR.
tree
get_decl();
// The location where the label is defined.
source_location location_;
// The LABEL_DECL.
tree decl_;
};
// An imported package.
class Package
{
public:
Package(const std::string& name, const std::string& unique_prefix,
source_location location);
// The real name of this package. This may be different from the
// name in the associated Named_object if the import statement used
// an alias.
const std::string&
name() const
{ return this->name_; }
// Return the location of the import statement.
source_location
location() const
{ return this->location_; }
// Get the unique prefix used for all symbols exported from this
// package.
const std::string&
unique_prefix() const
{
gcc_assert(!this->unique_prefix_.empty());
return this->unique_prefix_;
}
// The priority of this package. The init function of packages with
// lower priority must be run before the init function of packages
// with higher priority.
int
priority() const
{ return this->priority_; }
// Set the priority.
void
set_priority(int priority);
// Return the bindings.
Bindings*
bindings()
{ return this->bindings_; }
// Whether some symbol from the package was used.
bool
used() const
{ return this->used_; }
// Note that some symbol from this package was used.
void
set_used() const
{ this->used_ = true; }
// Clear the used field for the next file.
void
clear_used()
{ this->used_ = false; }
// Whether this package was imported in the current file.
bool
is_imported() const
{ return this->is_imported_; }
// Note that this package was imported in the current file.
void
set_is_imported()
{ this->is_imported_ = true; }
// Clear the imported field for the next file.
void
clear_is_imported()
{ this->is_imported_ = false; }
// Whether this package was imported with a name of "_".
bool
uses_sink_alias() const
{ return this->uses_sink_alias_; }
// Note that this package was imported with a name of "_".
void
set_uses_sink_alias()
{ this->uses_sink_alias_ = true; }
// Clear the sink alias field for the next file.
void
clear_uses_sink_alias()
{ this->uses_sink_alias_ = false; }
// Look up a name in the package. Returns NULL if the name is not
// found.
Named_object*
lookup(const std::string& name) const
{ return this->bindings_->lookup(name); }
// Set the location of the package. This is used if it is seen in a
// different import before it is really imported.
void
set_location(source_location location)
{ this->location_ = location; }
// Add a constant to the package.
Named_object*
add_constant(const Typed_identifier& tid, Expression* expr)
{ return this->bindings_->add_constant(tid, this, expr, 0); }
// Add a type to the package.
Named_object*
add_type(const std::string& name, Type* type, source_location location)
{ return this->bindings_->add_type(name, this, type, location); }
// Add a type declaration to the package.
Named_object*
add_type_declaration(const std::string& name, source_location location)
{ return this->bindings_->add_type_declaration(name, this, location); }
// Add a variable to the package.
Named_object*
add_variable(const std::string& name, Variable* variable)
{ return this->bindings_->add_variable(name, this, variable); }
// Add a function declaration to the package.
Named_object*
add_function_declaration(const std::string& name, Function_type* type,
source_location loc)
{ return this->bindings_->add_function_declaration(name, this, type, loc); }
// Determine types of constants.
void
determine_types();
private:
// The real name of this package.
std::string name_;
// The unique prefix for all exported global symbols.
std::string unique_prefix_;
// The names in this package.
Bindings* bindings_;
// The priority of this package. A package has a priority higher
// than the priority of all of the packages that it imports. This
// is used to run init functions in the right order.
int priority_;
// The location of the import statement.
source_location location_;
// True if some name from this package was used. This is mutable
// because we can use a package even if we have a const pointer to
// it.
mutable bool used_;
// True if this package was imported in the current file.
bool is_imported_;
// True if this package was imported with a name of "_".
bool uses_sink_alias_;
};
// Return codes for the traversal functions. This is not an enum
// because we want to be able to declare traversal functions in other
// header files without including this one.
// Continue traversal as usual.
const int TRAVERSE_CONTINUE = -1;
// Exit traversal.
const int TRAVERSE_EXIT = 0;
// Continue traversal, but skip components of the current object.
// E.g., if this is returned by Traverse::statement, we do not
// traverse the expressions in the statement even if
// traverse_expressions is set in the traverse_mask.
const int TRAVERSE_SKIP_COMPONENTS = 1;
// This class is used when traversing the parse tree. The caller uses
// a subclass which overrides functions as desired.
class Traverse
{
public:
// These bitmasks say what to traverse.
static const unsigned int traverse_variables = 0x1;
static const unsigned int traverse_constants = 0x2;
static const unsigned int traverse_functions = 0x4;
static const unsigned int traverse_blocks = 0x8;
static const unsigned int traverse_statements = 0x10;
static const unsigned int traverse_expressions = 0x20;
static const unsigned int traverse_types = 0x40;
Traverse(unsigned int traverse_mask)
: traverse_mask_(traverse_mask), types_seen_(NULL), expressions_seen_(NULL)
{ }
virtual ~Traverse();
// The bitmask of what to traverse.
unsigned int
traverse_mask() const
{ return this->traverse_mask_; }
// Record that we are going to traverse a type. This returns true
// if the type has already been seen in this traversal. This is
// required because types, unlike expressions, can form a circular
// graph.
bool
remember_type(const Type*);
// Record that we are going to see an expression. This returns true
// if the expression has already been seen in this traversal. This
// is only needed for cases where multiple expressions can point to
// a single one.
bool
remember_expression(const Expression*);
// These functions return one of the TRAVERSE codes defined above.
// If traverse_variables is set in the mask, this is called for
// every variable in the tree.
virtual int
variable(Named_object*);
// If traverse_constants is set in the mask, this is called for
// every named constant in the tree. The bool parameter is true for
// a global constant.
virtual int
constant(Named_object*, bool);
// If traverse_functions is set in the mask, this is called for
// every function in the tree.
virtual int
function(Named_object*);
// If traverse_blocks is set in the mask, this is called for every
// block in the tree.
virtual int
block(Block*);
// If traverse_statements is set in the mask, this is called for
// every statement in the tree.
virtual int
statement(Block*, size_t* index, Statement*);
// If traverse_expressions is set in the mask, this is called for
// every expression in the tree.
virtual int
expression(Expression**);
// If traverse_types is set in the mask, this is called for every
// type in the tree.
virtual int
type(Type*);
private:
typedef Unordered_set_hash(const Type*, Type_hash_identical,
Type_identical) Types_seen;
typedef Unordered_set(const Expression*) Expressions_seen;
// Bitmask of what sort of objects to traverse.
unsigned int traverse_mask_;
// Types which have been seen in this traversal.
Types_seen* types_seen_;
// Expressions which have been seen in this traversal.
Expressions_seen* expressions_seen_;
};
// When translating the gogo IR into trees, this is the context we
// pass down the blocks and statements.
class Translate_context
{
public:
Translate_context(Gogo* gogo, Named_object* function, Block* block,
tree block_tree)
: gogo_(gogo), function_(function), block_(block), block_tree_(block_tree),
is_const_(false)
{ }
// Accessors.
Gogo*
gogo()
{ return this->gogo_; }
Named_object*
function()
{ return this->function_; }
Block*
block()
{ return this->block_; }
tree
block_tree()
{ return this->block_tree_; }
bool
is_const()
{ return this->is_const_; }
// Make a constant context.
void
set_is_const()
{ this->is_const_ = true; }
private:
// The IR for the entire compilation unit.
Gogo* gogo_;
// The function we are currently translating.
Named_object* function_;
// The block we are currently translating.
Block *block_;
// The BLOCK node for the current block.
tree block_tree_;
// Whether this is being evaluated in a constant context. This is
// used for type descriptor initializers.
bool is_const_;
};
// Runtime error codes. These must match the values in
// libgo/runtime/go-runtime-error.c.
// Slice index out of bounds: negative or larger than the length of
// the slice.
static const int RUNTIME_ERROR_SLICE_INDEX_OUT_OF_BOUNDS = 0;
// Array index out of bounds.
static const int RUNTIME_ERROR_ARRAY_INDEX_OUT_OF_BOUNDS = 1;
// String index out of bounds.
static const int RUNTIME_ERROR_STRING_INDEX_OUT_OF_BOUNDS = 2;
// Slice slice out of bounds: negative or larger than the length of
// the slice or high bound less than low bound.
static const int RUNTIME_ERROR_SLICE_SLICE_OUT_OF_BOUNDS = 3;
// Array slice out of bounds.
static const int RUNTIME_ERROR_ARRAY_SLICE_OUT_OF_BOUNDS = 4;
// String slice out of bounds.
static const int RUNTIME_ERROR_STRING_SLICE_OUT_OF_BOUNDS = 5;
// Dereference of nil pointer. This is used when there is a
// dereference of a pointer to a very large struct or array, to ensure
// that a gigantic array is not used a proxy to access random memory
// locations.
static const int RUNTIME_ERROR_NIL_DEREFERENCE = 6;
// Slice length or capacity out of bounds in make: negative or
// overflow or length greater than capacity.
static const int RUNTIME_ERROR_MAKE_SLICE_OUT_OF_BOUNDS = 7;
// Map capacity out of bounds in make: negative or overflow.
static const int RUNTIME_ERROR_MAKE_MAP_OUT_OF_BOUNDS = 8;
// Channel capacity out of bounds in make: negative or overflow.
static const int RUNTIME_ERROR_MAKE_CHAN_OUT_OF_BOUNDS = 9;
// This is used by some of the langhooks.
extern Gogo* go_get_gogo();
// Whether we have seen any errors. FIXME: Replace with a backend
// interface.
extern bool saw_errors();
#endif // !defined(GO_GOGO_H)
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
// parse.h -- Go frontend parser. -*- C++ -*-
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#ifndef GO_PARSE_H
#define GO_PARSE_H
class Set_iota_traverse;
class Lex;
class Gogo;
class Named_object;
class Type;
class Typed_identifier;
class Typed_identifier_list;
class Function_type;
class Block;
class Expression;
class Expression_list;
class Struct_field_list;
class Case_clauses;
class Type_case_clauses;
class Select_clauses;
class Statement;
class Label;
// Parse the program.
class Parse
{
public:
Parse(Lex*, Gogo*);
// Parse a program.
void
program();
private:
// Precedence values.
enum Precedence
{
PRECEDENCE_INVALID = -1,
PRECEDENCE_NORMAL = 0,
PRECEDENCE_OROR,
PRECEDENCE_ANDAND,
PRECEDENCE_CHANOP,
PRECEDENCE_RELOP,
PRECEDENCE_ADDOP,
PRECEDENCE_MULOP
};
// We use this when parsing the range clause of a for statement.
struct Range_clause
{
// Set to true if we found a range clause.
bool found;
// The index expression.
Expression* index;
// The value expression.
Expression* value;
// The range expression.
Expression* range;
Range_clause()
: found(false), index(NULL), value(NULL), range(NULL)
{ }
};
// We use this when parsing the statement at the start of a switch,
// in order to recognize type switches.
struct Type_switch
{
// Set to true if we find a type switch.
bool found;
// The variable name.
std::string name;
// The location of the variable.
source_location location;
// The expression.
Expression* expr;
Type_switch()
: found(false), name(), location(UNKNOWN_LOCATION), expr(NULL)
{ }
};
// A variable defined in an enclosing function referenced by the
// current function.
class Enclosing_var
{
public:
Enclosing_var(Named_object* var, Named_object* in_function,
unsigned int index)
: var_(var), in_function_(in_function), index_(index)
{ }
// We put these in a vector, so we need a default constructor.
Enclosing_var()
: var_(NULL), in_function_(NULL), index_(-1U)
{ }
Named_object*
var() const
{ return this->var_; }
Named_object*
in_function() const
{ return this->in_function_; }
unsigned int
index() const
{ return this->index_; }
private:
// The variable which is being referred to.
Named_object* var_;
// The function where the variable is defined.
Named_object* in_function_;
// The index of the field in this function's closure struct for
// this variable.
unsigned int index_;
};
// We store Enclosing_var entries in a set, so we need a comparator.
struct Enclosing_var_comparison
{
bool
operator()(const Enclosing_var&, const Enclosing_var&);
};
// A set of Enclosing_var entries.
typedef std::set<Enclosing_var, Enclosing_var_comparison> Enclosing_vars;
// Peek at the current token from the lexer.
const Token*
peek_token();
// Consume the current token, return the next one.
const Token*
advance_token();
// Push a token back on the input stream.
void
unget_token(const Token&);
// The location of the current token.
source_location
location();
// For break and continue we keep a stack of statements with
// associated labels (if any). The top of the stack is used for a
// break or continue statement with no label.
typedef std::vector<std::pair<Statement*, const Label*> > Bc_stack;
// Parser nonterminals.
void identifier_list(Typed_identifier_list*);
Expression_list* expression_list(Expression*, bool may_be_sink);
bool qualified_ident(std::string*, Named_object**);
Type* type();
bool type_may_start_here();
Type* type_name(bool issue_error);
Type* array_type(bool may_use_ellipsis);
Type* map_type();
Type* struct_type();
void field_decl(Struct_field_list*);
Type* pointer_type();
Type* channel_type();
Function_type* signature(Typed_identifier*, source_location);
Typed_identifier_list* parameters(bool* is_varargs);
Typed_identifier_list* parameter_list(bool* is_varargs);
void parameter_decl(bool, Typed_identifier_list*, bool*, bool*);
Typed_identifier_list* result();
source_location block();
Type* interface_type();
bool method_spec(Typed_identifier_list*);
void declaration();
bool declaration_may_start_here();
void decl(void (Parse::*)(void*), void*);
void list(void (Parse::*)(void*), void*, bool);
void const_decl();
void const_spec(Type**, Expression_list**);
void type_decl();
void type_spec(void*);
void var_decl();
void var_spec(void*);
void init_vars(const Typed_identifier_list*, Type*, Expression_list*,
bool is_coloneq, source_location);
bool init_vars_from_call(const Typed_identifier_list*, Type*, Expression*,
bool is_coloneq, source_location);
bool init_vars_from_map(const Typed_identifier_list*, Type*, Expression*,
bool is_coloneq, source_location);
bool init_vars_from_receive(const Typed_identifier_list*, Type*,
Expression*, bool is_coloneq, source_location);
bool init_vars_from_type_guard(const Typed_identifier_list*, Type*,
Expression*, bool is_coloneq,
source_location);
Named_object* init_var(const Typed_identifier&, Type*, Expression*,
bool is_coloneq, bool type_from_init, bool* is_new);
void simple_var_decl_or_assignment(const std::string&, source_location,
Range_clause*, Type_switch*);
void function_decl();
Typed_identifier* receiver();
Expression* operand(bool may_be_sink);
Expression* enclosing_var_reference(Named_object*, Named_object*,
source_location);
Expression* composite_lit(Type*, int depth, source_location);
Expression* function_lit();
Expression* create_closure(Named_object* function, Enclosing_vars*,
source_location);
Expression* primary_expr(bool may_be_sink, bool may_be_composite_lit,
bool* is_type_switch);
Expression* selector(Expression*, bool* is_type_switch);
Expression* index(Expression*);
Expression* call(Expression*);
Expression* expression(Precedence, bool may_be_sink,
bool may_be_composite_lit, bool* is_type_switch);
bool expression_may_start_here();
Expression* unary_expr(bool may_be_sink, bool may_be_composite_lit,
bool* is_type_switch);
Expression* qualified_expr(Expression*, source_location);
Expression* id_to_expression(const std::string&, source_location);
void statement(const Label*);
bool statement_may_start_here();
void labeled_stmt(const std::string&, source_location);
Expression* simple_stat(bool, bool, Range_clause*, Type_switch*);
bool simple_stat_may_start_here();
void statement_list();
bool statement_list_may_start_here();
void expression_stat(Expression*);
void inc_dec_stat(Expression*);
void assignment(Expression*, Range_clause*);
void tuple_assignment(Expression_list*, Range_clause*);
void send();
void go_or_defer_stat();
void return_stat();
void if_stat();
void switch_stat(const Label*);
Statement* expr_switch_body(const Label*, Expression*, source_location);
void expr_case_clause(Case_clauses*);
Expression_list* expr_switch_case(bool*);
Statement* type_switch_body(const Label*, const Type_switch&,
source_location);
void type_case_clause(Named_object*, Type_case_clauses*);
void type_switch_case(std::vector<Type*>*, bool*);
void select_stat(const Label*);
void comm_clause(Select_clauses*);
bool comm_case(bool*, Expression**, Expression**, std::string*, bool*);
bool send_or_recv_expr(bool*, Expression**, Expression**, std::string*);
void for_stat(const Label*);
void for_clause(Expression**, Block**);
void range_clause_decl(const Typed_identifier_list*, Range_clause*);
void range_clause_expr(const Expression_list*, Range_clause*);
void push_break_statement(Statement*, const Label*);
void push_continue_statement(Statement*, const Label*);
void pop_break_statement();
void pop_continue_statement();
Statement* find_bc_statement(const Bc_stack*, const std::string&);
void break_stat();
void continue_stat();
void goto_stat();
void package_clause();
void import_decl();
void import_spec(void*);
void reset_iota();
int iota_value();
void increment_iota();
// Skip past an error looking for a semicolon or OP. Return true if
// all is well, false if we found EOF.
bool
skip_past_error(Operator op);
// Verify that an expression is not a sink, and return either the
// expression or an error.
Expression*
verify_not_sink(Expression*);
// Return the statement associated with a label in a Bc_stack, or
// NULL.
Statement*
find_bc_statement(const Bc_stack*, const std::string&) const;
// The lexer output we are parsing.
Lex* lex_;
// The current token.
Token token_;
// A token pushed back on the input stream.
Token unget_token_;
// Whether unget_token_ is valid.
bool unget_token_valid_;
// The code we are generating.
Gogo* gogo_;
// A stack of statements for which break may be used.
Bc_stack break_stack_;
// A stack of statements for which continue may be used.
Bc_stack continue_stack_;
// The current iota value.
int iota_;
// References from the local function to variables defined in
// enclosing functions.
Enclosing_vars enclosing_vars_;
};
#endif // !defined(GO_PARSE_H)
// parse.h -- Go frontend parser. -*- C++ -*-
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#ifndef GO_PARSE_H
#define GO_PARSE_H
class Set_iota_traverse;
class Lex;
class Gogo;
class Named_object;
class Type;
class Typed_identifier;
class Typed_identifier_list;
class Function_type;
class Block;
class Expression;
class Expression_list;
class Struct_field_list;
class Case_clauses;
class Type_case_clauses;
class Select_clauses;
class Statement;
class Label;
// Parse the program.
class Parse
{
public:
Parse(Lex*, Gogo*);
// Parse a program.
void
program();
private:
// Precedence values.
enum Precedence
{
PRECEDENCE_INVALID = -1,
PRECEDENCE_NORMAL = 0,
PRECEDENCE_OROR,
PRECEDENCE_ANDAND,
PRECEDENCE_RELOP,
PRECEDENCE_ADDOP,
PRECEDENCE_MULOP
};
// We use this when parsing the range clause of a for statement.
struct Range_clause
{
// Set to true if we found a range clause.
bool found;
// The index expression.
Expression* index;
// The value expression.
Expression* value;
// The range expression.
Expression* range;
Range_clause()
: found(false), index(NULL), value(NULL), range(NULL)
{ }
};
// We use this when parsing the statement at the start of a switch,
// in order to recognize type switches.
struct Type_switch
{
// Set to true if we find a type switch.
bool found;
// The variable name.
std::string name;
// The location of the variable.
source_location location;
// The expression.
Expression* expr;
Type_switch()
: found(false), name(), location(UNKNOWN_LOCATION), expr(NULL)
{ }
};
// A variable defined in an enclosing function referenced by the
// current function.
class Enclosing_var
{
public:
Enclosing_var(Named_object* var, Named_object* in_function,
unsigned int index)
: var_(var), in_function_(in_function), index_(index)
{ }
// We put these in a vector, so we need a default constructor.
Enclosing_var()
: var_(NULL), in_function_(NULL), index_(-1U)
{ }
Named_object*
var() const
{ return this->var_; }
Named_object*
in_function() const
{ return this->in_function_; }
unsigned int
index() const
{ return this->index_; }
private:
// The variable which is being referred to.
Named_object* var_;
// The function where the variable is defined.
Named_object* in_function_;
// The index of the field in this function's closure struct for
// this variable.
unsigned int index_;
};
// We store Enclosing_var entries in a set, so we need a comparator.
struct Enclosing_var_comparison
{
bool
operator()(const Enclosing_var&, const Enclosing_var&);
};
// A set of Enclosing_var entries.
typedef std::set<Enclosing_var, Enclosing_var_comparison> Enclosing_vars;
// Peek at the current token from the lexer.
const Token*
peek_token();
// Consume the current token, return the next one.
const Token*
advance_token();
// Push a token back on the input stream.
void
unget_token(const Token&);
// The location of the current token.
source_location
location();
// For break and continue we keep a stack of statements with
// associated labels (if any). The top of the stack is used for a
// break or continue statement with no label.
typedef std::vector<std::pair<Statement*, Label*> > Bc_stack;
// Parser nonterminals.
void identifier_list(Typed_identifier_list*);
Expression_list* expression_list(Expression*, bool may_be_sink);
bool qualified_ident(std::string*, Named_object**);
Type* type();
bool type_may_start_here();
Type* type_name(bool issue_error);
Type* array_type(bool may_use_ellipsis);
Type* map_type();
Type* struct_type();
void field_decl(Struct_field_list*);
Type* pointer_type();
Type* channel_type();
Function_type* signature(Typed_identifier*, source_location);
bool parameters(Typed_identifier_list**, bool* is_varargs);
Typed_identifier_list* parameter_list(bool* is_varargs);
void parameter_decl(bool, Typed_identifier_list*, bool*, bool*);
bool result(Typed_identifier_list**);
source_location block();
Type* interface_type();
void method_spec(Typed_identifier_list*);
void declaration();
bool declaration_may_start_here();
void decl(void (Parse::*)(void*), void*);
void list(void (Parse::*)(void*), void*, bool);
void const_decl();
void const_spec(Type**, Expression_list**);
void type_decl();
void type_spec(void*);
void var_decl();
void var_spec(void*);
void init_vars(const Typed_identifier_list*, Type*, Expression_list*,
bool is_coloneq, source_location);
bool init_vars_from_call(const Typed_identifier_list*, Type*, Expression*,
bool is_coloneq, source_location);
bool init_vars_from_map(const Typed_identifier_list*, Type*, Expression*,
bool is_coloneq, source_location);
bool init_vars_from_receive(const Typed_identifier_list*, Type*,
Expression*, bool is_coloneq, source_location);
bool init_vars_from_type_guard(const Typed_identifier_list*, Type*,
Expression*, bool is_coloneq,
source_location);
Named_object* init_var(const Typed_identifier&, Type*, Expression*,
bool is_coloneq, bool type_from_init, bool* is_new);
Named_object* create_dummy_global(Type*, Expression*, source_location);
void simple_var_decl_or_assignment(const std::string&, source_location,
Range_clause*, Type_switch*);
void function_decl();
Typed_identifier* receiver();
Expression* operand(bool may_be_sink);
Expression* enclosing_var_reference(Named_object*, Named_object*,
source_location);
Expression* composite_lit(Type*, int depth, source_location);
Expression* function_lit();
Expression* create_closure(Named_object* function, Enclosing_vars*,
source_location);
Expression* primary_expr(bool may_be_sink, bool may_be_composite_lit,
bool* is_type_switch);
Expression* selector(Expression*, bool* is_type_switch);
Expression* index(Expression*);
Expression* call(Expression*);
Expression* expression(Precedence, bool may_be_sink,
bool may_be_composite_lit, bool* is_type_switch);
bool expression_may_start_here();
Expression* unary_expr(bool may_be_sink, bool may_be_composite_lit,
bool* is_type_switch);
Expression* qualified_expr(Expression*, source_location);
Expression* id_to_expression(const std::string&, source_location);
void statement(Label*);
bool statement_may_start_here();
void labeled_stmt(const std::string&, source_location);
Expression* simple_stat(bool, bool*, Range_clause*, Type_switch*);
bool simple_stat_may_start_here();
void statement_list();
bool statement_list_may_start_here();
void expression_stat(Expression*);
void send_stmt(Expression*);
void inc_dec_stat(Expression*);
void assignment(Expression*, Range_clause*);
void tuple_assignment(Expression_list*, Range_clause*);
void send();
void go_or_defer_stat();
void return_stat();
void if_stat();
void switch_stat(Label*);
Statement* expr_switch_body(Label*, Expression*, source_location);
void expr_case_clause(Case_clauses*, bool* saw_default);
Expression_list* expr_switch_case(bool*);
Statement* type_switch_body(Label*, const Type_switch&, source_location);
void type_case_clause(Named_object*, Type_case_clauses*, bool* saw_default);
void type_switch_case(std::vector<Type*>*, bool*);
void select_stat(Label*);
void comm_clause(Select_clauses*, bool* saw_default);
bool comm_case(bool*, Expression**, Expression**, Expression**,
std::string*, std::string*, bool*);
bool send_or_recv_stmt(bool*, Expression**, Expression**, Expression**,
std::string*, std::string*);
void for_stat(Label*);
void for_clause(Expression**, Block**);
void range_clause_decl(const Typed_identifier_list*, Range_clause*);
void range_clause_expr(const Expression_list*, Range_clause*);
void push_break_statement(Statement*, Label*);
void push_continue_statement(Statement*, Label*);
void pop_break_statement();
void pop_continue_statement();
Statement* find_bc_statement(const Bc_stack*, const std::string&);
void break_stat();
void continue_stat();
void goto_stat();
void package_clause();
void import_decl();
void import_spec(void*);
void reset_iota();
int iota_value();
void increment_iota();
// Skip past an error looking for a semicolon or OP. Return true if
// all is well, false if we found EOF.
bool
skip_past_error(Operator op);
// Verify that an expression is not a sink, and return either the
// expression or an error.
Expression*
verify_not_sink(Expression*);
// Return the statement associated with a label in a Bc_stack, or
// NULL.
Statement*
find_bc_statement(const Bc_stack*, const std::string&) const;
// The lexer output we are parsing.
Lex* lex_;
// The current token.
Token token_;
// A token pushed back on the input stream.
Token unget_token_;
// Whether unget_token_ is valid.
bool unget_token_valid_;
// The code we are generating.
Gogo* gogo_;
// A stack of statements for which break may be used.
Bc_stack* break_stack_;
// A stack of statements for which continue may be used.
Bc_stack* continue_stack_;
// The current iota value.
int iota_;
// References from the local function to variables defined in
// enclosing functions.
Enclosing_vars enclosing_vars_;
};
#endif // !defined(GO_PARSE_H)
// parse.h -- Go frontend parser. -*- C++ -*-
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#ifndef GO_PARSE_H
#define GO_PARSE_H
class Set_iota_traverse;
class Lex;
class Gogo;
class Named_object;
class Type;
class Typed_identifier;
class Typed_identifier_list;
class Function_type;
class Block;
class Expression;
class Expression_list;
class Struct_field_list;
class Case_clauses;
class Type_case_clauses;
class Select_clauses;
class Statement;
class Label;
// Parse the program.
class Parse
{
public:
Parse(Lex*, Gogo*);
// Parse a program.
void
program();
private:
// Precedence values.
enum Precedence
{
PRECEDENCE_INVALID = -1,
PRECEDENCE_NORMAL = 0,
PRECEDENCE_OROR,
PRECEDENCE_ANDAND,
PRECEDENCE_RELOP,
PRECEDENCE_ADDOP,
PRECEDENCE_MULOP
};
// We use this when parsing the range clause of a for statement.
struct Range_clause
{
// Set to true if we found a range clause.
bool found;
// The index expression.
Expression* index;
// The value expression.
Expression* value;
// The range expression.
Expression* range;
Range_clause()
: found(false), index(NULL), value(NULL), range(NULL)
{ }
};
// We use this when parsing the statement at the start of a switch,
// in order to recognize type switches.
struct Type_switch
{
// Set to true if we find a type switch.
bool found;
// The variable name.
std::string name;
// The location of the variable.
source_location location;
// The expression.
Expression* expr;
Type_switch()
: found(false), name(), location(UNKNOWN_LOCATION), expr(NULL)
{ }
};
// A variable defined in an enclosing function referenced by the
// current function.
class Enclosing_var
{
public:
Enclosing_var(Named_object* var, Named_object* in_function,
unsigned int index)
: var_(var), in_function_(in_function), index_(index)
{ }
// We put these in a vector, so we need a default constructor.
Enclosing_var()
: var_(NULL), in_function_(NULL), index_(-1U)
{ }
Named_object*
var() const
{ return this->var_; }
Named_object*
in_function() const
{ return this->in_function_; }
unsigned int
index() const
{ return this->index_; }
private:
// The variable which is being referred to.
Named_object* var_;
// The function where the variable is defined.
Named_object* in_function_;
// The index of the field in this function's closure struct for
// this variable.
unsigned int index_;
};
// We store Enclosing_var entries in a set, so we need a comparator.
struct Enclosing_var_comparison
{
bool
operator()(const Enclosing_var&, const Enclosing_var&);
};
// A set of Enclosing_var entries.
typedef std::set<Enclosing_var, Enclosing_var_comparison> Enclosing_vars;
// Peek at the current token from the lexer.
const Token*
peek_token();
// Consume the current token, return the next one.
const Token*
advance_token();
// Push a token back on the input stream.
void
unget_token(const Token&);
// The location of the current token.
source_location
location();
// For break and continue we keep a stack of statements with
// associated labels (if any). The top of the stack is used for a
// break or continue statement with no label.
typedef std::vector<std::pair<Statement*, const Label*> > Bc_stack;
// Parser nonterminals.
void identifier_list(Typed_identifier_list*);
Expression_list* expression_list(Expression*, bool may_be_sink);
bool qualified_ident(std::string*, Named_object**);
Type* type();
bool type_may_start_here();
Type* type_name(bool issue_error);
Type* array_type(bool may_use_ellipsis);
Type* map_type();
Type* struct_type();
void field_decl(Struct_field_list*);
Type* pointer_type();
Type* channel_type();
Function_type* signature(Typed_identifier*, source_location);
bool parameters(Typed_identifier_list**, bool* is_varargs);
Typed_identifier_list* parameter_list(bool* is_varargs);
void parameter_decl(bool, Typed_identifier_list*, bool*, bool*);
bool result(Typed_identifier_list**);
source_location block();
Type* interface_type();
void method_spec(Typed_identifier_list*);
void declaration();
bool declaration_may_start_here();
void decl(void (Parse::*)(void*), void*);
void list(void (Parse::*)(void*), void*, bool);
void const_decl();
void const_spec(Type**, Expression_list**);
void type_decl();
void type_spec(void*);
void var_decl();
void var_spec(void*);
void init_vars(const Typed_identifier_list*, Type*, Expression_list*,
bool is_coloneq, source_location);
bool init_vars_from_call(const Typed_identifier_list*, Type*, Expression*,
bool is_coloneq, source_location);
bool init_vars_from_map(const Typed_identifier_list*, Type*, Expression*,
bool is_coloneq, source_location);
bool init_vars_from_receive(const Typed_identifier_list*, Type*,
Expression*, bool is_coloneq, source_location);
bool init_vars_from_type_guard(const Typed_identifier_list*, Type*,
Expression*, bool is_coloneq,
source_location);
Named_object* init_var(const Typed_identifier&, Type*, Expression*,
bool is_coloneq, bool type_from_init, bool* is_new);
Named_object* create_dummy_global(Type*, Expression*, source_location);
void simple_var_decl_or_assignment(const std::string&, source_location,
Range_clause*, Type_switch*);
void function_decl();
Typed_identifier* receiver();
Expression* operand(bool may_be_sink);
Expression* enclosing_var_reference(Named_object*, Named_object*,
source_location);
Expression* composite_lit(Type*, int depth, source_location);
Expression* function_lit();
Expression* create_closure(Named_object* function, Enclosing_vars*,
source_location);
Expression* primary_expr(bool may_be_sink, bool may_be_composite_lit,
bool* is_type_switch);
Expression* selector(Expression*, bool* is_type_switch);
Expression* index(Expression*);
Expression* call(Expression*);
Expression* expression(Precedence, bool may_be_sink,
bool may_be_composite_lit, bool* is_type_switch);
bool expression_may_start_here();
Expression* unary_expr(bool may_be_sink, bool may_be_composite_lit,
bool* is_type_switch);
Expression* qualified_expr(Expression*, source_location);
Expression* id_to_expression(const std::string&, source_location);
void statement(const Label*);
bool statement_may_start_here();
void labeled_stmt(const std::string&, source_location);
Expression* simple_stat(bool, bool, Range_clause*, Type_switch*);
bool simple_stat_may_start_here();
void statement_list();
bool statement_list_may_start_here();
void expression_stat(Expression*);
void send_stmt(Expression*);
void inc_dec_stat(Expression*);
void assignment(Expression*, Range_clause*);
void tuple_assignment(Expression_list*, Range_clause*);
void send();
void go_or_defer_stat();
void return_stat();
void if_stat();
void switch_stat(const Label*);
Statement* expr_switch_body(const Label*, Expression*, source_location);
void expr_case_clause(Case_clauses*, bool* saw_default);
Expression_list* expr_switch_case(bool*);
Statement* type_switch_body(const Label*, const Type_switch&,
source_location);
void type_case_clause(Named_object*, Type_case_clauses*, bool* saw_default);
void type_switch_case(std::vector<Type*>*, bool*);
void select_stat(const Label*);
void comm_clause(Select_clauses*, bool* saw_default);
bool comm_case(bool*, Expression**, Expression**, Expression**,
std::string*, std::string*, bool*);
bool send_or_recv_stmt(bool*, Expression**, Expression**, Expression**,
std::string*, std::string*);
void for_stat(const Label*);
void for_clause(Expression**, Block**);
void range_clause_decl(const Typed_identifier_list*, Range_clause*);
void range_clause_expr(const Expression_list*, Range_clause*);
void push_break_statement(Statement*, const Label*);
void push_continue_statement(Statement*, const Label*);
void pop_break_statement();
void pop_continue_statement();
Statement* find_bc_statement(const Bc_stack*, const std::string&);
void break_stat();
void continue_stat();
void goto_stat();
void package_clause();
void import_decl();
void import_spec(void*);
void reset_iota();
int iota_value();
void increment_iota();
// Skip past an error looking for a semicolon or OP. Return true if
// all is well, false if we found EOF.
bool
skip_past_error(Operator op);
// Verify that an expression is not a sink, and return either the
// expression or an error.
Expression*
verify_not_sink(Expression*);
// Return the statement associated with a label in a Bc_stack, or
// NULL.
Statement*
find_bc_statement(const Bc_stack*, const std::string&) const;
// The lexer output we are parsing.
Lex* lex_;
// The current token.
Token token_;
// A token pushed back on the input stream.
Token unget_token_;
// Whether unget_token_ is valid.
bool unget_token_valid_;
// The code we are generating.
Gogo* gogo_;
// A stack of statements for which break may be used.
Bc_stack* break_stack_;
// A stack of statements for which continue may be used.
Bc_stack* continue_stack_;
// The current iota value.
int iota_;
// References from the local function to variables defined in
// enclosing functions.
Enclosing_vars enclosing_vars_;
};
#endif // !defined(GO_PARSE_H)
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
// statements.h -- Go frontend statements. -*- C++ -*-
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#ifndef GO_STATEMENTS_H
#define GO_STATEMENTS_H
#include "operator.h"
class Gogo;
class Traverse;
class Block;
class Function;
class Unnamed_label;
class Temporary_statement;
class Variable_declaration_statement;
class Return_statement;
class Thunk_statement;
class Label_statement;
class For_statement;
class For_range_statement;
class Switch_statement;
class Type_switch_statement;
class Select_statement;
class Variable;
class Named_object;
class Label;
class Translate_context;
class Expression;
class Expression_list;
class Struct_type;
class Call_expression;
class Map_index_expression;
class Receive_expression;
class Case_clauses;
class Type_case_clauses;
class Select_clauses;
class Typed_identifier_list;
// This class is used to traverse assignments made by a statement
// which makes assignments.
class Traverse_assignments
{
public:
Traverse_assignments()
{ }
virtual ~Traverse_assignments()
{ }
// This is called for a variable initialization.
virtual void
initialize_variable(Named_object*) = 0;
// This is called for each assignment made by the statement. PLHS
// points to the left hand side, and PRHS points to the right hand
// side. PRHS may be NULL if there is no associated expression, as
// in the bool set by a non-blocking receive.
virtual void
assignment(Expression** plhs, Expression** prhs) = 0;
// This is called for each expression which is not passed to the
// assignment function. This is used for some of the statements
// which assign two values, for which there is no expression which
// describes the value. For ++ and -- the value is passed to both
// the assignment method and the rhs method. IS_STORED is true if
// this value is being stored directly. It is false if the value is
// computed but not stored. IS_LOCAL is true if the value is being
// stored in a local variable or this is being called by a return
// statement.
virtual void
value(Expression**, bool is_stored, bool is_local) = 0;
};
// A single statement.
class Statement
{
public:
// The types of statements.
enum Statement_classification
{
STATEMENT_ERROR,
STATEMENT_VARIABLE_DECLARATION,
STATEMENT_TEMPORARY,
STATEMENT_ASSIGNMENT,
STATEMENT_EXPRESSION,
STATEMENT_BLOCK,
STATEMENT_GO,
STATEMENT_DEFER,
STATEMENT_RETURN,
STATEMENT_BREAK_OR_CONTINUE,
STATEMENT_GOTO,
STATEMENT_GOTO_UNNAMED,
STATEMENT_LABEL,
STATEMENT_UNNAMED_LABEL,
STATEMENT_IF,
STATEMENT_CONSTANT_SWITCH,
STATEMENT_SELECT,
// These statements types are created by the parser, but they
// disappear during the lowering pass.
STATEMENT_ASSIGNMENT_OPERATION,
STATEMENT_TUPLE_ASSIGNMENT,
STATEMENT_TUPLE_MAP_ASSIGNMENT,
STATEMENT_MAP_ASSIGNMENT,
STATEMENT_TUPLE_RECEIVE_ASSIGNMENT,
STATEMENT_TUPLE_TYPE_GUARD_ASSIGNMENT,
STATEMENT_INCDEC,
STATEMENT_FOR,
STATEMENT_FOR_RANGE,
STATEMENT_SWITCH,
STATEMENT_TYPE_SWITCH
};
Statement(Statement_classification, source_location);
virtual ~Statement();
// Make a variable declaration.
static Statement*
make_variable_declaration(Named_object*);
// Make a statement which creates a temporary variable and
// initializes it to an expression. The block is used if the
// temporary variable has to be explicitly destroyed; the variable
// must still be added to the block. References to the temporary
// variable may be constructed using make_temporary_reference.
// Either the type or the initialization expression may be NULL, but
// not both.
static Temporary_statement*
make_temporary(Type*, Expression*, source_location);
// Make an assignment statement.
static Statement*
make_assignment(Expression*, Expression*, source_location);
// Make an assignment operation (+=, etc.).
static Statement*
make_assignment_operation(Operator, Expression*, Expression*,
source_location);
// Make a tuple assignment statement.
static Statement*
make_tuple_assignment(Expression_list*, Expression_list*, source_location);
// Make an assignment from a map index to a pair of variables.
static Statement*
make_tuple_map_assignment(Expression* val, Expression* present,
Expression*, source_location);
// Make a statement which assigns a pair of values to a map.
static Statement*
make_map_assignment(Expression*, Expression* val,
Expression* should_set, source_location);
// Make an assignment from a nonblocking receive to a pair of
// variables.
static Statement*
make_tuple_receive_assignment(Expression* val, Expression* success,
Expression* channel, source_location);
// Make an assignment from a type guard to a pair of variables.
static Statement*
make_tuple_type_guard_assignment(Expression* val, Expression* ok,
Expression* expr, Type* type,
source_location);
// Make an expression statement from an Expression.
static Statement*
make_statement(Expression*);
// Make a block statement from a Block. This is an embedded list of
// statements which may also include variable definitions.
static Statement*
make_block_statement(Block*, source_location);
// Make an increment statement.
static Statement*
make_inc_statement(Expression*);
// Make a decrement statement.
static Statement*
make_dec_statement(Expression*);
// Make a go statement.
static Statement*
make_go_statement(Call_expression* call, source_location);
// Make a defer statement.
static Statement*
make_defer_statement(Call_expression* call, source_location);
// Make a return statement.
static Statement*
make_return_statement(const Typed_identifier_list*, Expression_list*,
source_location);
// Make a break statement.
static Statement*
make_break_statement(Unnamed_label* label, source_location);
// Make a continue statement.
static Statement*
make_continue_statement(Unnamed_label* label, source_location);
// Make a goto statement.
static Statement*
make_goto_statement(Label* label, source_location);
// Make a goto statement to an unnamed label.
static Statement*
make_goto_unnamed_statement(Unnamed_label* label, source_location);
// Make a label statement--where the label is defined.
static Statement*
make_label_statement(Label* label, source_location);
// Make an unnamed label statement--where the label is defined.
static Statement*
make_unnamed_label_statement(Unnamed_label* label);
// Make an if statement.
static Statement*
make_if_statement(Expression* cond, Block* then_block, Block* else_block,
source_location);
// Make a switch statement.
static Switch_statement*
make_switch_statement(Expression* switch_val, source_location);
// Make a type switch statement.
static Type_switch_statement*
make_type_switch_statement(Named_object* var, Expression*, source_location);
// Make a select statement.
static Select_statement*
make_select_statement(source_location);
// Make a for statement.
static For_statement*
make_for_statement(Block* init, Expression* cond, Block* post,
source_location location);
// Make a for statement with a range clause.
static For_range_statement*
make_for_range_statement(Expression* index_var, Expression* value_var,
Expression* range, source_location);
// Return the statement classification.
Statement_classification
classification() const
{ return this->classification_; }
// Get the statement location.
source_location
location() const
{ return this->location_; }
// Traverse the tree.
int
traverse(Block*, size_t* index, Traverse*);
// Traverse the contents of this statement--the expressions and
// statements which it contains.
int
traverse_contents(Traverse*);
// If this statement assigns some values, it calls a function for
// each value to which this statement assigns a value, and returns
// true. If this statement does not assign any values, it returns
// false.
bool
traverse_assignments(Traverse_assignments* tassign);
// Lower a statement. This is called immediately after parsing to
// simplify statements for further processing. It returns the same
// Statement or a new one. BLOCK is the block containing this
// statement.
Statement*
lower(Gogo* gogo, Block* block)
{ return this->do_lower(gogo, block); }
// Set type information for unnamed constants.
void
determine_types();
// Check types in a statement. This simply checks that any
// expressions used by the statement have the right type.
void
check_types(Gogo* gogo)
{ this->do_check_types(gogo); }
// Return whether this is a block statement.
bool
is_block_statement() const
{ return this->classification_ == STATEMENT_BLOCK; }
// If this is a variable declaration statement, return it.
// Otherwise return NULL.
Variable_declaration_statement*
variable_declaration_statement()
{
return this->convert<Variable_declaration_statement,
STATEMENT_VARIABLE_DECLARATION>();
}
// If this is a return statement, return it. Otherwise return NULL.
Return_statement*
return_statement()
{ return this->convert<Return_statement, STATEMENT_RETURN>(); }
// If this is a thunk statement (a go or defer statement), return
// it. Otherwise return NULL.
Thunk_statement*
thunk_statement();
// If this is a label statement, return it. Otherwise return NULL.
Label_statement*
label_statement()
{ return this->convert<Label_statement, STATEMENT_LABEL>(); }
// If this is a for statement, return it. Otherwise return NULL.
For_statement*
for_statement()
{ return this->convert<For_statement, STATEMENT_FOR>(); }
// If this is a for statement over a range clause, return it.
// Otherwise return NULL.
For_range_statement*
for_range_statement()
{ return this->convert<For_range_statement, STATEMENT_FOR_RANGE>(); }
// If this is a switch statement, return it. Otherwise return NULL.
Switch_statement*
switch_statement()
{ return this->convert<Switch_statement, STATEMENT_SWITCH>(); }
// If this is a type switch statement, return it. Otherwise return
// NULL.
Type_switch_statement*
type_switch_statement()
{ return this->convert<Type_switch_statement, STATEMENT_TYPE_SWITCH>(); }
// If this is a select statement, return it. Otherwise return NULL.
Select_statement*
select_statement()
{ return this->convert<Select_statement, STATEMENT_SELECT>(); }
// Return true if this statement may fall through--if after
// executing this statement we may go on to execute the following
// statement, if any.
bool
may_fall_through() const
{ return this->do_may_fall_through(); }
// Return the tree for a statement. BLOCK is the enclosing block.
tree
get_tree(Translate_context*);
protected:
// Implemented by child class: traverse the tree.
virtual int
do_traverse(Traverse*) = 0;
// Implemented by child class: traverse assignments. Any statement
// which includes an assignment should implement this.
virtual bool
do_traverse_assignments(Traverse_assignments*)
{ return false; }
// Implemented by the child class: lower this statement to a simpler
// one.
virtual Statement*
do_lower(Gogo*, Block*)
{ return this; }
// Implemented by child class: set type information for unnamed
// constants. Any statement which includes an expression needs to
// implement this.
virtual void
do_determine_types()
{ }
// Implemented by child class: check types of expressions used in a
// statement.
virtual void
do_check_types(Gogo*)
{ }
// Implemented by child class: return true if this statement may
// fall through.
virtual bool
do_may_fall_through() const
{ return true; }
// Implemented by child class: return a tree.
virtual tree
do_get_tree(Translate_context*) = 0;
// Traverse an expression in a statement.
int
traverse_expression(Traverse*, Expression**);
// Traverse an expression list in a statement. The Expression_list
// may be NULL.
int
traverse_expression_list(Traverse*, Expression_list*);
// Traverse a type in a statement.
int
traverse_type(Traverse*, Type*);
// Build a tree node with one operand, setting the location. The
// first operand really has type "enum tree_code", but that enum is
// not defined here.
tree
build_stmt_1(int tree_code_value, tree);
// For children to call when they detect that they are in error.
void
set_is_error();
// For children to call to report an error conveniently.
void
report_error(const char*);
// For children to return an error statement from lower().
static Statement*
make_error_statement(source_location);
private:
// Convert to the desired statement classification, or return NULL.
// This is a controlled dynamic cast.
template<typename Statement_class, Statement_classification sc>
Statement_class*
convert()
{
return (this->classification_ == sc
? static_cast<Statement_class*>(this)
: NULL);
}
template<typename Statement_class, Statement_classification sc>
const Statement_class*
convert() const
{
return (this->classification_ == sc
? static_cast<const Statement_class*>(this)
: NULL);
}
// The statement classification.
Statement_classification classification_;
// The location in the input file of the start of this statement.
source_location location_;
};
// A statement which creates and initializes a temporary variable.
class Temporary_statement : public Statement
{
public:
Temporary_statement(Type* type, Expression* init, source_location location)
: Statement(STATEMENT_TEMPORARY, location),
type_(type), init_(init), decl_(NULL), is_address_taken_(false)
{ }
// Return the type of the temporary variable.
Type*
type() const;
// Return the initialization expression.
Expression*
init() const
{ return this->init_; }
// Record that something takes the address of this temporary
// variable.
void
set_is_address_taken()
{ this->is_address_taken_ = true; }
// Return the tree for the temporary variable itself. This should
// not be called until after the statement itself has been expanded.
tree
get_decl() const
{
gcc_assert(this->decl_ != NULL);
return this->decl_;
}
protected:
int
do_traverse(Traverse*);
bool
do_traverse_assignments(Traverse_assignments*);
void
do_determine_types();
void
do_check_types(Gogo*);
tree
do_get_tree(Translate_context*);
private:
// The type of the temporary variable.
Type* type_;
// The initial value of the temporary variable. This may be NULL.
Expression* init_;
// The DECL for the temporary variable.
tree decl_;
// True if something takes the address of this temporary variable.
bool is_address_taken_;
};
// A variable declaration. This marks the point in the code where a
// variable is declared. The Variable is also attached to a Block.
class Variable_declaration_statement : public Statement
{
public:
Variable_declaration_statement(Named_object* var);
// The variable being declared.
Named_object*
var()
{ return this->var_; }
protected:
int
do_traverse(Traverse*);
bool
do_traverse_assignments(Traverse_assignments*);
tree
do_get_tree(Translate_context*);
private:
Named_object* var_;
};
// A return statement.
class Return_statement : public Statement
{
public:
Return_statement(const Typed_identifier_list* results, Expression_list* vals,
source_location location)
: Statement(STATEMENT_RETURN, location),
results_(results), vals_(vals)
{ }
// The list of values being returned. This may be NULL.
const Expression_list*
vals() const
{ return this->vals_; }
protected:
int
do_traverse(Traverse* traverse)
{ return this->traverse_expression_list(traverse, this->vals_); }
bool
do_traverse_assignments(Traverse_assignments*);
Statement*
do_lower(Gogo*, Block*);
void
do_determine_types();
void
do_check_types(Gogo*);
bool
do_may_fall_through() const
{ return false; }
tree
do_get_tree(Translate_context*);
private:
// The result types of the function we are returning from. This is
// here because in some of the traversals it is inconvenient to get
// it.
const Typed_identifier_list* results_;
// Return values. This may be NULL.
Expression_list* vals_;
};
// Select_clauses holds the clauses of a select statement. This is
// built by the parser.
class Select_clauses
{
public:
Select_clauses()
: clauses_()
{ }
// Add a new clause. IS_SEND is true if this is a send clause,
// false for a receive clause. For a send clause CHANNEL is the
// channel and VAL is the value to send. For a receive clause
// CHANNEL is the channel and VAL is either NULL or a Var_expression
// for the variable to set; if VAL is NULL, VAR may be a variable
// which is initialized with the received value. IS_DEFAULT is true
// if this is the default clause. STATEMENTS is the list of
// statements to execute.
void
add(bool is_send, Expression* channel, Expression* val, Named_object* var,
bool is_default, Block* statements, source_location location)
{
this->clauses_.push_back(Select_clause(is_send, channel, val, var,
is_default, statements, location));
}
// Traverse the select clauses.
int
traverse(Traverse*);
// Lower statements.
void
lower(Block*);
// Determine types.
void
determine_types();
// Whether the select clauses may fall through to the statement
// which follows the overall select statement.
bool
may_fall_through() const;
// Return a tree implementing the select statement.
tree
get_tree(Translate_context*, Unnamed_label* break_label, source_location);
private:
// A single clause.
class Select_clause
{
public:
Select_clause()
: channel_(NULL), val_(NULL), var_(NULL), statements_(NULL),
is_send_(false), is_default_(false)
{ }
Select_clause(bool is_send, Expression* channel, Expression* val,
Named_object* var, bool is_default, Block* statements,
source_location location)
: channel_(channel), val_(val), var_(var), statements_(statements),
location_(location), is_send_(is_send), is_default_(is_default),
is_lowered_(false)
{ gcc_assert(is_default ? channel == NULL : channel != NULL); }
// Traverse the select clause.
int
traverse(Traverse*);
// Lower statements.
void
lower(Block*);
// Determine types.
void
determine_types();
// Return true if this is the default clause.
bool
is_default() const
{ return this->is_default_; }
// Return the channel. This will return NULL for the default
// clause.
Expression*
channel() const
{ return this->channel_; }
// Return the value. This will return NULL for the default
// clause, or for a receive clause for which no value was given.
Expression*
val() const
{ return this->val_; }
// Return the variable to set when a receive clause is also a
// variable definition (v := <- ch). This will return NULL for
// the default case, or for a send clause, or for a receive clause
// which does not define a variable.
Named_object*
var() const
{ return this->var_; }
// Return true for a send, false for a receive.
bool
is_send() const
{
gcc_assert(!this->is_default_);
return this->is_send_;
}
// Return the statements.
const Block*
statements() const
{ return this->statements_; }
// Return the location.
source_location
location() const
{ return this->location_; }
// Whether this clause may fall through to the statement which
// follows the overall select statement.
bool
may_fall_through() const;
// Return a tree for the statements to execute.
tree
get_statements_tree(Translate_context*);
private:
// The channel.
Expression* channel_;
// The value to send or the variable to set.
Expression* val_;
// The variable to initialize, for "case a := <- ch".
Named_object* var_;
// The statements to execute.
Block* statements_;
// The location of this clause.
source_location location_;
// Whether this is a send or a receive.
bool is_send_;
// Whether this is the default.
bool is_default_;
// Whether this has been lowered.
bool is_lowered_;
};
void
add_clause_tree(Translate_context*, int, Select_clause*, Unnamed_label*,
tree*);
typedef std::vector<Select_clause> Clauses;
Clauses clauses_;
};
// A select statement.
class Select_statement : public Statement
{
public:
Select_statement(source_location location)
: Statement(STATEMENT_SELECT, location),
clauses_(NULL), break_label_(NULL), is_lowered_(false)
{ }
// Add the clauses.
void
add_clauses(Select_clauses* clauses)
{
gcc_assert(this->clauses_ == NULL);
this->clauses_ = clauses;
}
// Return the break label for this select statement.
Unnamed_label*
break_label();
protected:
int
do_traverse(Traverse* traverse)
{ return this->clauses_->traverse(traverse); }
Statement*
do_lower(Gogo*, Block*);
void
do_determine_types()
{ this->clauses_->determine_types(); }
bool
do_may_fall_through() const
{ return this->clauses_->may_fall_through(); }
tree
do_get_tree(Translate_context*);
private:
// The select clauses.
Select_clauses* clauses_;
// The break label.
Unnamed_label* break_label_;
// Whether this statement has been lowered.
bool is_lowered_;
};
// A statement which requires a thunk: go or defer.
class Thunk_statement : public Statement
{
public:
Thunk_statement(Statement_classification, Call_expression*,
source_location);
// Return the call expression.
Expression*
call()
{ return this->call_; }
// Simplify a go or defer statement so that it only uses a single
// parameter.
bool
simplify_statement(Gogo*, Block*);
protected:
int
do_traverse(Traverse* traverse);
bool
do_traverse_assignments(Traverse_assignments*);
void
do_determine_types();
void
do_check_types(Gogo*);
// Return the function and argument trees for the call.
void
get_fn_and_arg(Translate_context*, tree* pfn, tree* parg);
private:
// Return whether this is a simple go statement.
bool
is_simple(Function_type*) const;
// Build the struct to use for a complex case.
Struct_type*
build_struct(Function_type* fntype);
// Build the thunk.
void
build_thunk(Gogo*, const std::string&, Function_type* fntype);
// The field name used in the thunk structure for the function
// pointer.
static const char* const thunk_field_fn;
// The field name used in the thunk structure for the receiver, if
// there is one.
static const char* const thunk_field_receiver;
// Set the name to use for thunk field N.
void
thunk_field_param(int n, char* buf, size_t buflen);
// The function call to be executed in a separate thread (go) or
// later (defer).
Expression* call_;
// The type used for a struct to pass to a thunk, if this is not a
// simple call.
Struct_type* struct_type_;
};
// A go statement.
class Go_statement : public Thunk_statement
{
public:
Go_statement(Call_expression* call, source_location location)
: Thunk_statement(STATEMENT_GO, call, location)
{ }
protected:
tree
do_get_tree(Translate_context*);
};
// A defer statement.
class Defer_statement : public Thunk_statement
{
public:
Defer_statement(Call_expression* call, source_location location)
: Thunk_statement(STATEMENT_DEFER, call, location)
{ }
protected:
tree
do_get_tree(Translate_context*);
};
// A label statement.
class Label_statement : public Statement
{
public:
Label_statement(Label* label, source_location location)
: Statement(STATEMENT_LABEL, location),
label_(label)
{ }
// Return the label itself.
const Label*
label() const
{ return this->label_; }
protected:
int
do_traverse(Traverse*);
tree
do_get_tree(Translate_context*);
private:
// The label.
Label* label_;
};
// A for statement.
class For_statement : public Statement
{
public:
For_statement(Block* init, Expression* cond, Block* post,
source_location location)
: Statement(STATEMENT_FOR, location),
init_(init), cond_(cond), post_(post), statements_(NULL),
break_label_(NULL), continue_label_(NULL)
{ }
// Add the statements.
void
add_statements(Block* statements)
{
gcc_assert(this->statements_ == NULL);
this->statements_ = statements;
}
// Return the break label for this for statement.
Unnamed_label*
break_label();
// Return the continue label for this for statement.
Unnamed_label*
continue_label();
// Set the break and continue labels for this statement.
void
set_break_continue_labels(Unnamed_label* break_label,
Unnamed_label* continue_label);
protected:
int
do_traverse(Traverse*);
bool
do_traverse_assignments(Traverse_assignments*)
{ gcc_unreachable(); }
Statement*
do_lower(Gogo*, Block*);
tree
do_get_tree(Translate_context*)
{ gcc_unreachable(); }
private:
// The initialization statements. This may be NULL.
Block* init_;
// The condition. This may be NULL.
Expression* cond_;
// The statements to run after each iteration. This may be NULL.
Block* post_;
// The statements in the loop itself.
Block* statements_;
// The break label, if needed.
Unnamed_label* break_label_;
// The continue label, if needed.
Unnamed_label* continue_label_;
};
// A for statement over a range clause.
class For_range_statement : public Statement
{
public:
For_range_statement(Expression* index_var, Expression* value_var,
Expression* range, source_location location)
: Statement(STATEMENT_FOR_RANGE, location),
index_var_(index_var), value_var_(value_var), range_(range),
statements_(NULL), break_label_(NULL), continue_label_(NULL)
{ }
// Add the statements.
void
add_statements(Block* statements)
{
gcc_assert(this->statements_ == NULL);
this->statements_ = statements;
}
// Return the break label for this for statement.
Unnamed_label*
break_label();
// Return the continue label for this for statement.
Unnamed_label*
continue_label();
protected:
int
do_traverse(Traverse*);
bool
do_traverse_assignments(Traverse_assignments*)
{ gcc_unreachable(); }
Statement*
do_lower(Gogo*, Block*);
tree
do_get_tree(Translate_context*)
{ gcc_unreachable(); }
private:
Expression*
make_range_ref(Named_object*, Temporary_statement*, source_location);
Expression*
call_builtin(Gogo*, const char* funcname, Expression* arg, source_location);
void
lower_range_array(Gogo*, Block*, Block*, Named_object*, Temporary_statement*,
Temporary_statement*, Temporary_statement*,
Block**, Expression**, Block**, Block**);
void
lower_range_string(Gogo*, Block*, Block*, Named_object*, Temporary_statement*,
Temporary_statement*, Temporary_statement*,
Block**, Expression**, Block**, Block**);
void
lower_range_map(Gogo*, Block*, Block*, Named_object*, Temporary_statement*,
Temporary_statement*, Temporary_statement*,
Block**, Expression**, Block**, Block**);
void
lower_range_channel(Gogo*, Block*, Block*, Named_object*,
Temporary_statement*, Temporary_statement*,
Temporary_statement*, Block**, Expression**, Block**,
Block**);
// The variable which is set to the index value.
Expression* index_var_;
// The variable which is set to the element value. This may be
// NULL.
Expression* value_var_;
// The expression we are ranging over.
Expression* range_;
// The statements in the block.
Block* statements_;
// The break label, if needed.
Unnamed_label* break_label_;
// The continue label, if needed.
Unnamed_label* continue_label_;
};
// Class Case_clauses holds the clauses of a switch statement. This
// is built by the parser.
class Case_clauses
{
public:
Case_clauses()
: clauses_()
{ }
// Add a new clause. CASES is a list of case expressions; it may be
// NULL. IS_DEFAULT is true if this is the default case.
// STATEMENTS is a block of statements. IS_FALLTHROUGH is true if
// after the statements the case clause should fall through to the
// next clause.
void
add(Expression_list* cases, bool is_default, Block* statements,
bool is_fallthrough, source_location location)
{
this->clauses_.push_back(Case_clause(cases, is_default, statements,
is_fallthrough, location));
}
// Return whether there are no clauses.
bool
empty() const
{ return this->clauses_.empty(); }
// Traverse the case clauses.
int
traverse(Traverse*);
// Lower for a nonconstant switch.
void
lower(Block*, Temporary_statement*, Unnamed_label*) const;
// Determine types of expressions. The Type parameter is the type
// of the switch value.
void
determine_types(Type*);
// Check types. The Type parameter is the type of the switch value.
bool
check_types(Type*);
// Return true if all the clauses are constant values.
bool
is_constant() const;
// Return true if these clauses may fall through to the statements
// following the switch statement.
bool
may_fall_through() const;
// Return the body of a SWITCH_EXPR when all the clauses are
// constants.
tree
get_constant_tree(Translate_context*, Unnamed_label* break_label) const;
private:
// For a constant tree we need to keep a record of constants we have
// already seen. Note that INTEGER_CST trees are interned.
typedef Unordered_set(tree) Case_constants;
// One case clause.
class Case_clause
{
public:
Case_clause()
: cases_(NULL), statements_(NULL), is_default_(false),
is_fallthrough_(false), location_(UNKNOWN_LOCATION)
{ }
Case_clause(Expression_list* cases, bool is_default, Block* statements,
bool is_fallthrough, source_location location)
: cases_(cases), statements_(statements), is_default_(is_default),
is_fallthrough_(is_fallthrough), location_(location)
{ }
// Whether this clause falls through to the next clause.
bool
is_fallthrough() const
{ return this->is_fallthrough_; }
// Whether this is the default.
bool
is_default() const
{ return this->is_default_; }
// The location of this clause.
source_location
location() const
{ return this->location_; }
// Traversal.
int
traverse(Traverse*);
// Lower for a nonconstant switch.
void
lower(Block*, Temporary_statement*, Unnamed_label*, Unnamed_label*) const;
// Determine types.
void
determine_types(Type*);
// Check types.
bool
check_types(Type*);
// Return true if all the case expressions are constant.
bool
is_constant() const;
// Return true if this clause may fall through to execute the
// statements following the switch statement. This is not the
// same as whether this clause falls through to the next clause.
bool
may_fall_through() const;
// Build up the body of a SWITCH_EXPR when the case expressions
// are constant.
void
get_constant_tree(Translate_context*, Unnamed_label* break_label,
Case_constants* case_constants, tree* stmt_list) const;
private:
// The list of case expressions.
Expression_list* cases_;
// The statements to execute.
Block* statements_;
// Whether this is the default case.
bool is_default_;
// Whether this falls through after the statements.
bool is_fallthrough_;
// The location of this case clause.
source_location location_;
};
friend class Case_clause;
// The type of the list of clauses.
typedef std::vector<Case_clause> Clauses;
// All the case clauses.
Clauses clauses_;
};
// A switch statement.
class Switch_statement : public Statement
{
public:
Switch_statement(Expression* val, source_location location)
: Statement(STATEMENT_SWITCH, location),
val_(val), clauses_(NULL), break_label_(NULL)
{ }
// Add the clauses.
void
add_clauses(Case_clauses* clauses)
{
gcc_assert(this->clauses_ == NULL);
this->clauses_ = clauses;
}
// Return the break label for this switch statement.
Unnamed_label*
break_label();
protected:
int
do_traverse(Traverse*);
Statement*
do_lower(Gogo*, Block*);
tree
do_get_tree(Translate_context*)
{ gcc_unreachable(); }
private:
// The value to switch on. This may be NULL.
Expression* val_;
// The case clauses.
Case_clauses* clauses_;
// The break label, if needed.
Unnamed_label* break_label_;
};
// Class Type_case_clauses holds the clauses of a type switch
// statement. This is built by the parser.
class Type_case_clauses
{
public:
Type_case_clauses()
: clauses_()
{ }
// Add a new clause. TYPE is the type for this clause; it may be
// NULL. IS_FALLTHROUGH is true if this falls through to the next
// clause; in this case STATEMENTS will be NULL. IS_DEFAULT is true
// if this is the default case. STATEMENTS is a block of
// statements; it may be NULL.
void
add(Type* type, bool is_fallthrough, bool is_default, Block* statements,
source_location location)
{
this->clauses_.push_back(Type_case_clause(type, is_fallthrough, is_default,
statements, location));
}
// Return whether there are no clauses.
bool
empty() const
{ return this->clauses_.empty(); }
// Traverse the type case clauses.
int
traverse(Traverse*);
// Check for duplicates.
void
check_duplicates() const;
// Lower to if and goto statements.
void
lower(Block*, Temporary_statement* descriptor_temp,
Unnamed_label* break_label) const;
private:
// One type case clause.
class Type_case_clause
{
public:
Type_case_clause()
: type_(NULL), statements_(NULL), is_default_(false),
location_(UNKNOWN_LOCATION)
{ }
Type_case_clause(Type* type, bool is_fallthrough, bool is_default,
Block* statements, source_location location)
: type_(type), statements_(statements), is_fallthrough_(is_fallthrough),
is_default_(is_default), location_(location)
{ }
// The type.
Type*
type() const
{ return this->type_; }
// Whether this is the default.
bool
is_default() const
{ return this->is_default_; }
// The location of this type clause.
source_location
location() const
{ return this->location_; }
// Traversal.
int
traverse(Traverse*);
// Lower to if and goto statements.
void
lower(Block*, Temporary_statement* descriptor_temp,
Unnamed_label* break_label, Unnamed_label** stmts_label) const;
private:
// The type for this type clause.
Type* type_;
// The statements to execute.
Block* statements_;
// Whether this falls through--this is true for "case T1, T2".
bool is_fallthrough_;
// Whether this is the default case.
bool is_default_;
// The location of this type case clause.
source_location location_;
};
friend class Type_case_clause;
// The type of the list of type clauses.
typedef std::vector<Type_case_clause> Type_clauses;
// All the type case clauses.
Type_clauses clauses_;
};
// A type switch statement.
class Type_switch_statement : public Statement
{
public:
Type_switch_statement(Named_object* var, Expression* expr,
source_location location)
: Statement(STATEMENT_TYPE_SWITCH, location),
var_(var), expr_(expr), clauses_(NULL), break_label_(NULL)
{ gcc_assert(var == NULL || expr == NULL); }
// Add the clauses.
void
add_clauses(Type_case_clauses* clauses)
{
gcc_assert(this->clauses_ == NULL);
this->clauses_ = clauses;
}
// Return the break label for this type switch statement.
Unnamed_label*
break_label();
protected:
int
do_traverse(Traverse*);
Statement*
do_lower(Gogo*, Block*);
tree
do_get_tree(Translate_context*)
{ gcc_unreachable(); }
private:
// Get the type descriptor.
tree
get_type_descriptor(Translate_context*, Type*, tree);
// The variable holding the value we are switching on.
Named_object* var_;
// The expression we are switching on if there is no variable.
Expression* expr_;
// The type case clauses.
Type_case_clauses* clauses_;
// The break label, if needed.
Unnamed_label* break_label_;
};
#endif // !defined(GO_STATEMENTS_H)
// statements.h -- Go frontend statements. -*- C++ -*-
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#ifndef GO_STATEMENTS_H
#define GO_STATEMENTS_H
#include "operator.h"
class Gogo;
class Traverse;
class Block;
class Function;
class Unnamed_label;
class Temporary_statement;
class Variable_declaration_statement;
class Return_statement;
class Thunk_statement;
class Label_statement;
class For_statement;
class For_range_statement;
class Switch_statement;
class Type_switch_statement;
class Send_statement;
class Select_statement;
class Variable;
class Named_object;
class Label;
class Translate_context;
class Expression;
class Expression_list;
class Struct_type;
class Call_expression;
class Map_index_expression;
class Receive_expression;
class Case_clauses;
class Type_case_clauses;
class Select_clauses;
class Typed_identifier_list;
class Bexpression;
class Bstatement;
class Bvariable;
// This class is used to traverse assignments made by a statement
// which makes assignments.
class Traverse_assignments
{
public:
Traverse_assignments()
{ }
virtual ~Traverse_assignments()
{ }
// This is called for a variable initialization.
virtual void
initialize_variable(Named_object*) = 0;
// This is called for each assignment made by the statement. PLHS
// points to the left hand side, and PRHS points to the right hand
// side. PRHS may be NULL if there is no associated expression, as
// in the bool set by a non-blocking receive.
virtual void
assignment(Expression** plhs, Expression** prhs) = 0;
// This is called for each expression which is not passed to the
// assignment function. This is used for some of the statements
// which assign two values, for which there is no expression which
// describes the value. For ++ and -- the value is passed to both
// the assignment method and the rhs method. IS_STORED is true if
// this value is being stored directly. It is false if the value is
// computed but not stored. IS_LOCAL is true if the value is being
// stored in a local variable or this is being called by a return
// statement.
virtual void
value(Expression**, bool is_stored, bool is_local) = 0;
};
// A single statement.
class Statement
{
public:
// The types of statements.
enum Statement_classification
{
STATEMENT_ERROR,
STATEMENT_VARIABLE_DECLARATION,
STATEMENT_TEMPORARY,
STATEMENT_ASSIGNMENT,
STATEMENT_EXPRESSION,
STATEMENT_BLOCK,
STATEMENT_GO,
STATEMENT_DEFER,
STATEMENT_RETURN,
STATEMENT_BREAK_OR_CONTINUE,
STATEMENT_GOTO,
STATEMENT_GOTO_UNNAMED,
STATEMENT_LABEL,
STATEMENT_UNNAMED_LABEL,
STATEMENT_IF,
STATEMENT_CONSTANT_SWITCH,
STATEMENT_SEND,
STATEMENT_SELECT,
// These statements types are created by the parser, but they
// disappear during the lowering pass.
STATEMENT_ASSIGNMENT_OPERATION,
STATEMENT_TUPLE_ASSIGNMENT,
STATEMENT_TUPLE_MAP_ASSIGNMENT,
STATEMENT_MAP_ASSIGNMENT,
STATEMENT_TUPLE_RECEIVE_ASSIGNMENT,
STATEMENT_TUPLE_TYPE_GUARD_ASSIGNMENT,
STATEMENT_INCDEC,
STATEMENT_FOR,
STATEMENT_FOR_RANGE,
STATEMENT_SWITCH,
STATEMENT_TYPE_SWITCH
};
Statement(Statement_classification, source_location);
virtual ~Statement();
// Make a variable declaration.
static Statement*
make_variable_declaration(Named_object*);
// Make a statement which creates a temporary variable and
// initializes it to an expression. The block is used if the
// temporary variable has to be explicitly destroyed; the variable
// must still be added to the block. References to the temporary
// variable may be constructed using make_temporary_reference.
// Either the type or the initialization expression may be NULL, but
// not both.
static Temporary_statement*
make_temporary(Type*, Expression*, source_location);
// Make an assignment statement.
static Statement*
make_assignment(Expression*, Expression*, source_location);
// Make an assignment operation (+=, etc.).
static Statement*
make_assignment_operation(Operator, Expression*, Expression*,
source_location);
// Make a tuple assignment statement.
static Statement*
make_tuple_assignment(Expression_list*, Expression_list*, source_location);
// Make an assignment from a map index to a pair of variables.
static Statement*
make_tuple_map_assignment(Expression* val, Expression* present,
Expression*, source_location);
// Make a statement which assigns a pair of values to a map.
static Statement*
make_map_assignment(Expression*, Expression* val,
Expression* should_set, source_location);
// Make an assignment from a nonblocking receive to a pair of
// variables. FOR_SELECT is true is this is being created for a
// case x, ok := <-c in a select statement.
static Statement*
make_tuple_receive_assignment(Expression* val, Expression* closed,
Expression* channel, bool for_select,
source_location);
// Make an assignment from a type guard to a pair of variables.
static Statement*
make_tuple_type_guard_assignment(Expression* val, Expression* ok,
Expression* expr, Type* type,
source_location);
// Make an expression statement from an Expression.
static Statement*
make_statement(Expression*);
// Make a block statement from a Block. This is an embedded list of
// statements which may also include variable definitions.
static Statement*
make_block_statement(Block*, source_location);
// Make an increment statement.
static Statement*
make_inc_statement(Expression*);
// Make a decrement statement.
static Statement*
make_dec_statement(Expression*);
// Make a go statement.
static Statement*
make_go_statement(Call_expression* call, source_location);
// Make a defer statement.
static Statement*
make_defer_statement(Call_expression* call, source_location);
// Make a return statement.
static Statement*
make_return_statement(Expression_list*, source_location);
// Make a break statement.
static Statement*
make_break_statement(Unnamed_label* label, source_location);
// Make a continue statement.
static Statement*
make_continue_statement(Unnamed_label* label, source_location);
// Make a goto statement.
static Statement*
make_goto_statement(Label* label, source_location);
// Make a goto statement to an unnamed label.
static Statement*
make_goto_unnamed_statement(Unnamed_label* label, source_location);
// Make a label statement--where the label is defined.
static Statement*
make_label_statement(Label* label, source_location);
// Make an unnamed label statement--where the label is defined.
static Statement*
make_unnamed_label_statement(Unnamed_label* label);
// Make an if statement.
static Statement*
make_if_statement(Expression* cond, Block* then_block, Block* else_block,
source_location);
// Make a switch statement.
static Switch_statement*
make_switch_statement(Expression* switch_val, source_location);
// Make a type switch statement.
static Type_switch_statement*
make_type_switch_statement(Named_object* var, Expression*, source_location);
// Make a send statement.
static Send_statement*
make_send_statement(Expression* channel, Expression* val, source_location);
// Make a select statement.
static Select_statement*
make_select_statement(source_location);
// Make a for statement.
static For_statement*
make_for_statement(Block* init, Expression* cond, Block* post,
source_location location);
// Make a for statement with a range clause.
static For_range_statement*
make_for_range_statement(Expression* index_var, Expression* value_var,
Expression* range, source_location);
// Return the statement classification.
Statement_classification
classification() const
{ return this->classification_; }
// Get the statement location.
source_location
location() const
{ return this->location_; }
// Traverse the tree.
int
traverse(Block*, size_t* index, Traverse*);
// Traverse the contents of this statement--the expressions and
// statements which it contains.
int
traverse_contents(Traverse*);
// If this statement assigns some values, it calls a function for
// each value to which this statement assigns a value, and returns
// true. If this statement does not assign any values, it returns
// false.
bool
traverse_assignments(Traverse_assignments* tassign);
// Lower a statement. This is called immediately after parsing to
// simplify statements for further processing. It returns the same
// Statement or a new one. FUNCTION is the function containing this
// statement. BLOCK is the block containing this statement.
Statement*
lower(Gogo* gogo, Named_object* function, Block* block)
{ return this->do_lower(gogo, function, block); }
// Set type information for unnamed constants.
void
determine_types();
// Check types in a statement. This simply checks that any
// expressions used by the statement have the right type.
void
check_types(Gogo* gogo)
{ this->do_check_types(gogo); }
// Return whether this is a block statement.
bool
is_block_statement() const
{ return this->classification_ == STATEMENT_BLOCK; }
// If this is a variable declaration statement, return it.
// Otherwise return NULL.
Variable_declaration_statement*
variable_declaration_statement()
{
return this->convert<Variable_declaration_statement,
STATEMENT_VARIABLE_DECLARATION>();
}
// If this is a return statement, return it. Otherwise return NULL.
Return_statement*
return_statement()
{ return this->convert<Return_statement, STATEMENT_RETURN>(); }
// If this is a thunk statement (a go or defer statement), return
// it. Otherwise return NULL.
Thunk_statement*
thunk_statement();
// If this is a label statement, return it. Otherwise return NULL.
Label_statement*
label_statement()
{ return this->convert<Label_statement, STATEMENT_LABEL>(); }
// If this is a for statement, return it. Otherwise return NULL.
For_statement*
for_statement()
{ return this->convert<For_statement, STATEMENT_FOR>(); }
// If this is a for statement over a range clause, return it.
// Otherwise return NULL.
For_range_statement*
for_range_statement()
{ return this->convert<For_range_statement, STATEMENT_FOR_RANGE>(); }
// If this is a switch statement, return it. Otherwise return NULL.
Switch_statement*
switch_statement()
{ return this->convert<Switch_statement, STATEMENT_SWITCH>(); }
// If this is a type switch statement, return it. Otherwise return
// NULL.
Type_switch_statement*
type_switch_statement()
{ return this->convert<Type_switch_statement, STATEMENT_TYPE_SWITCH>(); }
// If this is a select statement, return it. Otherwise return NULL.
Select_statement*
select_statement()
{ return this->convert<Select_statement, STATEMENT_SELECT>(); }
// Return true if this statement may fall through--if after
// executing this statement we may go on to execute the following
// statement, if any.
bool
may_fall_through() const
{ return this->do_may_fall_through(); }
// Convert the statement to the backend representation.
Bstatement*
get_backend(Translate_context*);
protected:
// Implemented by child class: traverse the tree.
virtual int
do_traverse(Traverse*) = 0;
// Implemented by child class: traverse assignments. Any statement
// which includes an assignment should implement this.
virtual bool
do_traverse_assignments(Traverse_assignments*)
{ return false; }
// Implemented by the child class: lower this statement to a simpler
// one.
virtual Statement*
do_lower(Gogo*, Named_object*, Block*)
{ return this; }
// Implemented by child class: set type information for unnamed
// constants. Any statement which includes an expression needs to
// implement this.
virtual void
do_determine_types()
{ }
// Implemented by child class: check types of expressions used in a
// statement.
virtual void
do_check_types(Gogo*)
{ }
// Implemented by child class: return true if this statement may
// fall through.
virtual bool
do_may_fall_through() const
{ return true; }
// Implemented by child class: convert to backend representation.
virtual Bstatement*
do_get_backend(Translate_context*) = 0;
// Traverse an expression in a statement.
int
traverse_expression(Traverse*, Expression**);
// Traverse an expression list in a statement. The Expression_list
// may be NULL.
int
traverse_expression_list(Traverse*, Expression_list*);
// Traverse a type in a statement.
int
traverse_type(Traverse*, Type*);
// For children to call when they detect that they are in error.
void
set_is_error();
// For children to call to report an error conveniently.
void
report_error(const char*);
// For children to return an error statement from lower().
static Statement*
make_error_statement(source_location);
private:
// Convert to the desired statement classification, or return NULL.
// This is a controlled dynamic cast.
template<typename Statement_class, Statement_classification sc>
Statement_class*
convert()
{
return (this->classification_ == sc
? static_cast<Statement_class*>(this)
: NULL);
}
template<typename Statement_class, Statement_classification sc>
const Statement_class*
convert() const
{
return (this->classification_ == sc
? static_cast<const Statement_class*>(this)
: NULL);
}
// The statement classification.
Statement_classification classification_;
// The location in the input file of the start of this statement.
source_location location_;
};
// A statement which creates and initializes a temporary variable.
class Temporary_statement : public Statement
{
public:
Temporary_statement(Type* type, Expression* init, source_location location)
: Statement(STATEMENT_TEMPORARY, location),
type_(type), init_(init), bvariable_(NULL), is_address_taken_(false)
{ }
// Return the type of the temporary variable.
Type*
type() const;
// Record that something takes the address of this temporary
// variable.
void
set_is_address_taken()
{ this->is_address_taken_ = true; }
// Return the temporary variable. This should not be called until
// after the statement itself has been converted.
Bvariable*
get_backend_variable(Translate_context*) const;
protected:
int
do_traverse(Traverse*);
bool
do_traverse_assignments(Traverse_assignments*);
void
do_determine_types();
void
do_check_types(Gogo*);
Bstatement*
do_get_backend(Translate_context*);
private:
// The type of the temporary variable.
Type* type_;
// The initial value of the temporary variable. This may be NULL.
Expression* init_;
// The backend representation of the temporary variable.
Bvariable* bvariable_;
// True if something takes the address of this temporary variable.
bool is_address_taken_;
};
// A variable declaration. This marks the point in the code where a
// variable is declared. The Variable is also attached to a Block.
class Variable_declaration_statement : public Statement
{
public:
Variable_declaration_statement(Named_object* var);
// The variable being declared.
Named_object*
var()
{ return this->var_; }
protected:
int
do_traverse(Traverse*);
bool
do_traverse_assignments(Traverse_assignments*);
Bstatement*
do_get_backend(Translate_context*);
private:
Named_object* var_;
};
// A return statement.
class Return_statement : public Statement
{
public:
Return_statement(Expression_list* vals, source_location location)
: Statement(STATEMENT_RETURN, location),
vals_(vals), is_lowered_(false)
{ }
// The list of values being returned. This may be NULL.
const Expression_list*
vals() const
{ return this->vals_; }
protected:
int
do_traverse(Traverse* traverse)
{ return this->traverse_expression_list(traverse, this->vals_); }
bool
do_traverse_assignments(Traverse_assignments*);
Statement*
do_lower(Gogo*, Named_object*, Block*);
bool
do_may_fall_through() const
{ return false; }
Bstatement*
do_get_backend(Translate_context*);
private:
// Return values. This may be NULL.
Expression_list* vals_;
// True if this statement has been lowered.
bool is_lowered_;
};
// A send statement.
class Send_statement : public Statement
{
public:
Send_statement(Expression* channel, Expression* val,
source_location location)
: Statement(STATEMENT_SEND, location),
channel_(channel), val_(val), for_select_(false)
{ }
// Note that this is for a select statement.
void
set_for_select()
{ this->for_select_ = true; }
protected:
int
do_traverse(Traverse* traverse);
void
do_determine_types();
void
do_check_types(Gogo*);
Bstatement*
do_get_backend(Translate_context*);
private:
// The channel on which to send the value.
Expression* channel_;
// The value to send.
Expression* val_;
// Whether this is for a select statement.
bool for_select_;
};
// Select_clauses holds the clauses of a select statement. This is
// built by the parser.
class Select_clauses
{
public:
Select_clauses()
: clauses_()
{ }
// Add a new clause. IS_SEND is true if this is a send clause,
// false for a receive clause. For a send clause CHANNEL is the
// channel and VAL is the value to send. For a receive clause
// CHANNEL is the channel, VAL is either NULL or a Var_expression
// for the variable to set, and CLOSED is either NULL or a
// Var_expression to set to whether the channel is closed. If VAL
// is NULL, VAR may be a variable to be initialized with the
// received value, and CLOSEDVAR ma be a variable to be initialized
// with whether the channel is closed. IS_DEFAULT is true if this
// is the default clause. STATEMENTS is the list of statements to
// execute.
void
add(bool is_send, Expression* channel, Expression* val, Expression* closed,
Named_object* var, Named_object* closedvar, bool is_default,
Block* statements, source_location location)
{
this->clauses_.push_back(Select_clause(is_send, channel, val, closed, var,
closedvar, is_default, statements,
location));
}
// Traverse the select clauses.
int
traverse(Traverse*);
// Lower statements.
void
lower(Gogo*, Named_object*, Block*);
// Determine types.
void
determine_types();
// Whether the select clauses may fall through to the statement
// which follows the overall select statement.
bool
may_fall_through() const;
// Convert to the backend representation.
Bstatement*
get_backend(Translate_context*, Unnamed_label* break_label, source_location);
private:
// A single clause.
class Select_clause
{
public:
Select_clause()
: channel_(NULL), val_(NULL), closed_(NULL), var_(NULL),
closedvar_(NULL), statements_(NULL), is_send_(false),
is_default_(false)
{ }
Select_clause(bool is_send, Expression* channel, Expression* val,
Expression* closed, Named_object* var,
Named_object* closedvar, bool is_default, Block* statements,
source_location location)
: channel_(channel), val_(val), closed_(closed), var_(var),
closedvar_(closedvar), statements_(statements), location_(location),
is_send_(is_send), is_default_(is_default), is_lowered_(false)
{ go_assert(is_default ? channel == NULL : channel != NULL); }
// Traverse the select clause.
int
traverse(Traverse*);
// Lower statements.
void
lower(Gogo*, Named_object*, Block*);
// Determine types.
void
determine_types();
// Return true if this is the default clause.
bool
is_default() const
{ return this->is_default_; }
// Return the channel. This will return NULL for the default
// clause.
Expression*
channel() const
{ return this->channel_; }
// Return true for a send, false for a receive.
bool
is_send() const
{
go_assert(!this->is_default_);
return this->is_send_;
}
// Return the statements.
const Block*
statements() const
{ return this->statements_; }
// Return the location.
source_location
location() const
{ return this->location_; }
// Whether this clause may fall through to the statement which
// follows the overall select statement.
bool
may_fall_through() const;
// Convert the statements to the backend representation.
Bstatement*
get_statements_backend(Translate_context*);
private:
// The channel.
Expression* channel_;
// The value to send or the lvalue to receive into.
Expression* val_;
// The lvalue to set to whether the channel is closed on a
// receive.
Expression* closed_;
// The variable to initialize, for "case a := <-ch".
Named_object* var_;
// The variable to initialize to whether the channel is closed,
// for "case a, c := <-ch".
Named_object* closedvar_;
// The statements to execute.
Block* statements_;
// The location of this clause.
source_location location_;
// Whether this is a send or a receive.
bool is_send_;
// Whether this is the default.
bool is_default_;
// Whether this has been lowered.
bool is_lowered_;
};
void
add_clause_backend(Translate_context*, source_location, int index,
int case_value, Select_clause*, Unnamed_label*,
std::vector<std::vector<Bexpression*> >* cases,
std::vector<Bstatement*>* clauses);
typedef std::vector<Select_clause> Clauses;
Clauses clauses_;
};
// A select statement.
class Select_statement : public Statement
{
public:
Select_statement(source_location location)
: Statement(STATEMENT_SELECT, location),
clauses_(NULL), break_label_(NULL), is_lowered_(false)
{ }
// Add the clauses.
void
add_clauses(Select_clauses* clauses)
{
go_assert(this->clauses_ == NULL);
this->clauses_ = clauses;
}
// Return the break label for this select statement.
Unnamed_label*
break_label();
protected:
int
do_traverse(Traverse* traverse)
{ return this->clauses_->traverse(traverse); }
Statement*
do_lower(Gogo*, Named_object*, Block*);
void
do_determine_types()
{ this->clauses_->determine_types(); }
bool
do_may_fall_through() const
{ return this->clauses_->may_fall_through(); }
Bstatement*
do_get_backend(Translate_context*);
private:
// The select clauses.
Select_clauses* clauses_;
// The break label.
Unnamed_label* break_label_;
// Whether this statement has been lowered.
bool is_lowered_;
};
// A statement which requires a thunk: go or defer.
class Thunk_statement : public Statement
{
public:
Thunk_statement(Statement_classification, Call_expression*,
source_location);
// Return the call expression.
Expression*
call()
{ return this->call_; }
// Simplify a go or defer statement so that it only uses a single
// parameter.
bool
simplify_statement(Gogo*, Named_object*, Block*);
protected:
int
do_traverse(Traverse* traverse);
bool
do_traverse_assignments(Traverse_assignments*);
void
do_determine_types();
void
do_check_types(Gogo*);
// Return the function and argument for the call.
bool
get_fn_and_arg(Expression** pfn, Expression** parg);
private:
// Return whether this is a simple go statement.
bool
is_simple(Function_type*) const;
// Build the struct to use for a complex case.
Struct_type*
build_struct(Function_type* fntype);
// Build the thunk.
void
build_thunk(Gogo*, const std::string&, Function_type* fntype);
// The field name used in the thunk structure for the function
// pointer.
static const char* const thunk_field_fn;
// The field name used in the thunk structure for the receiver, if
// there is one.
static const char* const thunk_field_receiver;
// Set the name to use for thunk field N.
void
thunk_field_param(int n, char* buf, size_t buflen);
// The function call to be executed in a separate thread (go) or
// later (defer).
Expression* call_;
// The type used for a struct to pass to a thunk, if this is not a
// simple call.
Struct_type* struct_type_;
};
// A go statement.
class Go_statement : public Thunk_statement
{
public:
Go_statement(Call_expression* call, source_location location)
: Thunk_statement(STATEMENT_GO, call, location)
{ }
protected:
Bstatement*
do_get_backend(Translate_context*);
};
// A defer statement.
class Defer_statement : public Thunk_statement
{
public:
Defer_statement(Call_expression* call, source_location location)
: Thunk_statement(STATEMENT_DEFER, call, location)
{ }
protected:
Bstatement*
do_get_backend(Translate_context*);
};
// A label statement.
class Label_statement : public Statement
{
public:
Label_statement(Label* label, source_location location)
: Statement(STATEMENT_LABEL, location),
label_(label)
{ }
// Return the label itself.
const Label*
label() const
{ return this->label_; }
protected:
int
do_traverse(Traverse*);
Bstatement*
do_get_backend(Translate_context*);
private:
// The label.
Label* label_;
};
// A for statement.
class For_statement : public Statement
{
public:
For_statement(Block* init, Expression* cond, Block* post,
source_location location)
: Statement(STATEMENT_FOR, location),
init_(init), cond_(cond), post_(post), statements_(NULL),
break_label_(NULL), continue_label_(NULL)
{ }
// Add the statements.
void
add_statements(Block* statements)
{
go_assert(this->statements_ == NULL);
this->statements_ = statements;
}
// Return the break label for this for statement.
Unnamed_label*
break_label();
// Return the continue label for this for statement.
Unnamed_label*
continue_label();
// Set the break and continue labels for this statement.
void
set_break_continue_labels(Unnamed_label* break_label,
Unnamed_label* continue_label);
protected:
int
do_traverse(Traverse*);
bool
do_traverse_assignments(Traverse_assignments*)
{ go_unreachable(); }
Statement*
do_lower(Gogo*, Named_object*, Block*);
Bstatement*
do_get_backend(Translate_context*)
{ go_unreachable(); }
private:
// The initialization statements. This may be NULL.
Block* init_;
// The condition. This may be NULL.
Expression* cond_;
// The statements to run after each iteration. This may be NULL.
Block* post_;
// The statements in the loop itself.
Block* statements_;
// The break label, if needed.
Unnamed_label* break_label_;
// The continue label, if needed.
Unnamed_label* continue_label_;
};
// A for statement over a range clause.
class For_range_statement : public Statement
{
public:
For_range_statement(Expression* index_var, Expression* value_var,
Expression* range, source_location location)
: Statement(STATEMENT_FOR_RANGE, location),
index_var_(index_var), value_var_(value_var), range_(range),
statements_(NULL), break_label_(NULL), continue_label_(NULL)
{ }
// Add the statements.
void
add_statements(Block* statements)
{
go_assert(this->statements_ == NULL);
this->statements_ = statements;
}
// Return the break label for this for statement.
Unnamed_label*
break_label();
// Return the continue label for this for statement.
Unnamed_label*
continue_label();
protected:
int
do_traverse(Traverse*);
bool
do_traverse_assignments(Traverse_assignments*)
{ go_unreachable(); }
Statement*
do_lower(Gogo*, Named_object*, Block*);
Bstatement*
do_get_backend(Translate_context*)
{ go_unreachable(); }
private:
Expression*
make_range_ref(Named_object*, Temporary_statement*, source_location);
Expression*
call_builtin(Gogo*, const char* funcname, Expression* arg, source_location);
void
lower_range_array(Gogo*, Block*, Block*, Named_object*, Temporary_statement*,
Temporary_statement*, Temporary_statement*,
Block**, Expression**, Block**, Block**);
void
lower_range_string(Gogo*, Block*, Block*, Named_object*, Temporary_statement*,
Temporary_statement*, Temporary_statement*,
Block**, Expression**, Block**, Block**);
void
lower_range_map(Gogo*, Block*, Block*, Named_object*, Temporary_statement*,
Temporary_statement*, Temporary_statement*,
Block**, Expression**, Block**, Block**);
void
lower_range_channel(Gogo*, Block*, Block*, Named_object*,
Temporary_statement*, Temporary_statement*,
Temporary_statement*, Block**, Expression**, Block**,
Block**);
// The variable which is set to the index value.
Expression* index_var_;
// The variable which is set to the element value. This may be
// NULL.
Expression* value_var_;
// The expression we are ranging over.
Expression* range_;
// The statements in the block.
Block* statements_;
// The break label, if needed.
Unnamed_label* break_label_;
// The continue label, if needed.
Unnamed_label* continue_label_;
};
// Class Case_clauses holds the clauses of a switch statement. This
// is built by the parser.
class Case_clauses
{
public:
Case_clauses()
: clauses_()
{ }
// Add a new clause. CASES is a list of case expressions; it may be
// NULL. IS_DEFAULT is true if this is the default case.
// STATEMENTS is a block of statements. IS_FALLTHROUGH is true if
// after the statements the case clause should fall through to the
// next clause.
void
add(Expression_list* cases, bool is_default, Block* statements,
bool is_fallthrough, source_location location)
{
this->clauses_.push_back(Case_clause(cases, is_default, statements,
is_fallthrough, location));
}
// Return whether there are no clauses.
bool
empty() const
{ return this->clauses_.empty(); }
// Traverse the case clauses.
int
traverse(Traverse*);
// Lower for a nonconstant switch.
void
lower(Block*, Temporary_statement*, Unnamed_label*) const;
// Determine types of expressions. The Type parameter is the type
// of the switch value.
void
determine_types(Type*);
// Check types. The Type parameter is the type of the switch value.
bool
check_types(Type*);
// Return true if all the clauses are constant values.
bool
is_constant() const;
// Return true if these clauses may fall through to the statements
// following the switch statement.
bool
may_fall_through() const;
// Return the body of a SWITCH_EXPR when all the clauses are
// constants.
void
get_backend(Translate_context*, Unnamed_label* break_label,
std::vector<std::vector<Bexpression*> >* all_cases,
std::vector<Bstatement*>* all_statements) const;
private:
// For a constant switch we need to keep a record of constants we
// have already seen.
class Hash_integer_value;
class Eq_integer_value;
typedef Unordered_set_hash(Expression*, Hash_integer_value,
Eq_integer_value) Case_constants;
// One case clause.
class Case_clause
{
public:
Case_clause()
: cases_(NULL), statements_(NULL), is_default_(false),
is_fallthrough_(false), location_(UNKNOWN_LOCATION)
{ }
Case_clause(Expression_list* cases, bool is_default, Block* statements,
bool is_fallthrough, source_location location)
: cases_(cases), statements_(statements), is_default_(is_default),
is_fallthrough_(is_fallthrough), location_(location)
{ }
// Whether this clause falls through to the next clause.
bool
is_fallthrough() const
{ return this->is_fallthrough_; }
// Whether this is the default.
bool
is_default() const
{ return this->is_default_; }
// The location of this clause.
source_location
location() const
{ return this->location_; }
// Traversal.
int
traverse(Traverse*);
// Lower for a nonconstant switch.
void
lower(Block*, Temporary_statement*, Unnamed_label*, Unnamed_label*) const;
// Determine types.
void
determine_types(Type*);
// Check types.
bool
check_types(Type*);
// Return true if all the case expressions are constant.
bool
is_constant() const;
// Return true if this clause may fall through to execute the
// statements following the switch statement. This is not the
// same as whether this clause falls through to the next clause.
bool
may_fall_through() const;
// Convert the case values and statements to the backend
// representation.
Bstatement*
get_backend(Translate_context*, Unnamed_label* break_label,
Case_constants*, std::vector<Bexpression*>* cases) const;
private:
// The list of case expressions.
Expression_list* cases_;
// The statements to execute.
Block* statements_;
// Whether this is the default case.
bool is_default_;
// Whether this falls through after the statements.
bool is_fallthrough_;
// The location of this case clause.
source_location location_;
};
friend class Case_clause;
// The type of the list of clauses.
typedef std::vector<Case_clause> Clauses;
// All the case clauses.
Clauses clauses_;
};
// A switch statement.
class Switch_statement : public Statement
{
public:
Switch_statement(Expression* val, source_location location)
: Statement(STATEMENT_SWITCH, location),
val_(val), clauses_(NULL), break_label_(NULL)
{ }
// Add the clauses.
void
add_clauses(Case_clauses* clauses)
{
go_assert(this->clauses_ == NULL);
this->clauses_ = clauses;
}
// Return the break label for this switch statement.
Unnamed_label*
break_label();
protected:
int
do_traverse(Traverse*);
Statement*
do_lower(Gogo*, Named_object*, Block*);
Bstatement*
do_get_backend(Translate_context*)
{ go_unreachable(); }
private:
// The value to switch on. This may be NULL.
Expression* val_;
// The case clauses.
Case_clauses* clauses_;
// The break label, if needed.
Unnamed_label* break_label_;
};
// Class Type_case_clauses holds the clauses of a type switch
// statement. This is built by the parser.
class Type_case_clauses
{
public:
Type_case_clauses()
: clauses_()
{ }
// Add a new clause. TYPE is the type for this clause; it may be
// NULL. IS_FALLTHROUGH is true if this falls through to the next
// clause; in this case STATEMENTS will be NULL. IS_DEFAULT is true
// if this is the default case. STATEMENTS is a block of
// statements; it may be NULL.
void
add(Type* type, bool is_fallthrough, bool is_default, Block* statements,
source_location location)
{
this->clauses_.push_back(Type_case_clause(type, is_fallthrough, is_default,
statements, location));
}
// Return whether there are no clauses.
bool
empty() const
{ return this->clauses_.empty(); }
// Traverse the type case clauses.
int
traverse(Traverse*);
// Check for duplicates.
void
check_duplicates() const;
// Lower to if and goto statements.
void
lower(Block*, Temporary_statement* descriptor_temp,
Unnamed_label* break_label) const;
private:
// One type case clause.
class Type_case_clause
{
public:
Type_case_clause()
: type_(NULL), statements_(NULL), is_default_(false),
location_(UNKNOWN_LOCATION)
{ }
Type_case_clause(Type* type, bool is_fallthrough, bool is_default,
Block* statements, source_location location)
: type_(type), statements_(statements), is_fallthrough_(is_fallthrough),
is_default_(is_default), location_(location)
{ }
// The type.
Type*
type() const
{ return this->type_; }
// Whether this is the default.
bool
is_default() const
{ return this->is_default_; }
// The location of this type clause.
source_location
location() const
{ return this->location_; }
// Traversal.
int
traverse(Traverse*);
// Lower to if and goto statements.
void
lower(Block*, Temporary_statement* descriptor_temp,
Unnamed_label* break_label, Unnamed_label** stmts_label) const;
private:
// The type for this type clause.
Type* type_;
// The statements to execute.
Block* statements_;
// Whether this falls through--this is true for "case T1, T2".
bool is_fallthrough_;
// Whether this is the default case.
bool is_default_;
// The location of this type case clause.
source_location location_;
};
friend class Type_case_clause;
// The type of the list of type clauses.
typedef std::vector<Type_case_clause> Type_clauses;
// All the type case clauses.
Type_clauses clauses_;
};
// A type switch statement.
class Type_switch_statement : public Statement
{
public:
Type_switch_statement(Named_object* var, Expression* expr,
source_location location)
: Statement(STATEMENT_TYPE_SWITCH, location),
var_(var), expr_(expr), clauses_(NULL), break_label_(NULL)
{ go_assert(var == NULL || expr == NULL); }
// Add the clauses.
void
add_clauses(Type_case_clauses* clauses)
{
go_assert(this->clauses_ == NULL);
this->clauses_ = clauses;
}
// Return the break label for this type switch statement.
Unnamed_label*
break_label();
protected:
int
do_traverse(Traverse*);
Statement*
do_lower(Gogo*, Named_object*, Block*);
Bstatement*
do_get_backend(Translate_context*)
{ go_unreachable(); }
private:
// The variable holding the value we are switching on.
Named_object* var_;
// The expression we are switching on if there is no variable.
Expression* expr_;
// The type case clauses.
Type_case_clauses* clauses_;
// The break label, if needed.
Unnamed_label* break_label_;
};
#endif // !defined(GO_STATEMENTS_H)
// statements.h -- Go frontend statements. -*- C++ -*-
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#ifndef GO_STATEMENTS_H
#define GO_STATEMENTS_H
#include "operator.h"
class Gogo;
class Traverse;
class Block;
class Function;
class Unnamed_label;
class Temporary_statement;
class Variable_declaration_statement;
class Return_statement;
class Thunk_statement;
class Label_statement;
class For_statement;
class For_range_statement;
class Switch_statement;
class Type_switch_statement;
class Send_statement;
class Select_statement;
class Variable;
class Named_object;
class Label;
class Translate_context;
class Expression;
class Expression_list;
class Struct_type;
class Call_expression;
class Map_index_expression;
class Receive_expression;
class Case_clauses;
class Type_case_clauses;
class Select_clauses;
class Typed_identifier_list;
// This class is used to traverse assignments made by a statement
// which makes assignments.
class Traverse_assignments
{
public:
Traverse_assignments()
{ }
virtual ~Traverse_assignments()
{ }
// This is called for a variable initialization.
virtual void
initialize_variable(Named_object*) = 0;
// This is called for each assignment made by the statement. PLHS
// points to the left hand side, and PRHS points to the right hand
// side. PRHS may be NULL if there is no associated expression, as
// in the bool set by a non-blocking receive.
virtual void
assignment(Expression** plhs, Expression** prhs) = 0;
// This is called for each expression which is not passed to the
// assignment function. This is used for some of the statements
// which assign two values, for which there is no expression which
// describes the value. For ++ and -- the value is passed to both
// the assignment method and the rhs method. IS_STORED is true if
// this value is being stored directly. It is false if the value is
// computed but not stored. IS_LOCAL is true if the value is being
// stored in a local variable or this is being called by a return
// statement.
virtual void
value(Expression**, bool is_stored, bool is_local) = 0;
};
// A single statement.
class Statement
{
public:
// The types of statements.
enum Statement_classification
{
STATEMENT_ERROR,
STATEMENT_VARIABLE_DECLARATION,
STATEMENT_TEMPORARY,
STATEMENT_ASSIGNMENT,
STATEMENT_EXPRESSION,
STATEMENT_BLOCK,
STATEMENT_GO,
STATEMENT_DEFER,
STATEMENT_RETURN,
STATEMENT_BREAK_OR_CONTINUE,
STATEMENT_GOTO,
STATEMENT_GOTO_UNNAMED,
STATEMENT_LABEL,
STATEMENT_UNNAMED_LABEL,
STATEMENT_IF,
STATEMENT_CONSTANT_SWITCH,
STATEMENT_SEND,
STATEMENT_SELECT,
// These statements types are created by the parser, but they
// disappear during the lowering pass.
STATEMENT_ASSIGNMENT_OPERATION,
STATEMENT_TUPLE_ASSIGNMENT,
STATEMENT_TUPLE_MAP_ASSIGNMENT,
STATEMENT_MAP_ASSIGNMENT,
STATEMENT_TUPLE_RECEIVE_ASSIGNMENT,
STATEMENT_TUPLE_TYPE_GUARD_ASSIGNMENT,
STATEMENT_INCDEC,
STATEMENT_FOR,
STATEMENT_FOR_RANGE,
STATEMENT_SWITCH,
STATEMENT_TYPE_SWITCH
};
Statement(Statement_classification, source_location);
virtual ~Statement();
// Make a variable declaration.
static Statement*
make_variable_declaration(Named_object*);
// Make a statement which creates a temporary variable and
// initializes it to an expression. The block is used if the
// temporary variable has to be explicitly destroyed; the variable
// must still be added to the block. References to the temporary
// variable may be constructed using make_temporary_reference.
// Either the type or the initialization expression may be NULL, but
// not both.
static Temporary_statement*
make_temporary(Type*, Expression*, source_location);
// Make an assignment statement.
static Statement*
make_assignment(Expression*, Expression*, source_location);
// Make an assignment operation (+=, etc.).
static Statement*
make_assignment_operation(Operator, Expression*, Expression*,
source_location);
// Make a tuple assignment statement.
static Statement*
make_tuple_assignment(Expression_list*, Expression_list*, source_location);
// Make an assignment from a map index to a pair of variables.
static Statement*
make_tuple_map_assignment(Expression* val, Expression* present,
Expression*, source_location);
// Make a statement which assigns a pair of values to a map.
static Statement*
make_map_assignment(Expression*, Expression* val,
Expression* should_set, source_location);
// Make an assignment from a nonblocking receive to a pair of
// variables. FOR_SELECT is true is this is being created for a
// case x, ok := <-c in a select statement.
static Statement*
make_tuple_receive_assignment(Expression* val, Expression* closed,
Expression* channel, bool for_select,
source_location);
// Make an assignment from a type guard to a pair of variables.
static Statement*
make_tuple_type_guard_assignment(Expression* val, Expression* ok,
Expression* expr, Type* type,
source_location);
// Make an expression statement from an Expression.
static Statement*
make_statement(Expression*);
// Make a block statement from a Block. This is an embedded list of
// statements which may also include variable definitions.
static Statement*
make_block_statement(Block*, source_location);
// Make an increment statement.
static Statement*
make_inc_statement(Expression*);
// Make a decrement statement.
static Statement*
make_dec_statement(Expression*);
// Make a go statement.
static Statement*
make_go_statement(Call_expression* call, source_location);
// Make a defer statement.
static Statement*
make_defer_statement(Call_expression* call, source_location);
// Make a return statement.
static Statement*
make_return_statement(const Typed_identifier_list*, Expression_list*,
source_location);
// Make a break statement.
static Statement*
make_break_statement(Unnamed_label* label, source_location);
// Make a continue statement.
static Statement*
make_continue_statement(Unnamed_label* label, source_location);
// Make a goto statement.
static Statement*
make_goto_statement(Label* label, source_location);
// Make a goto statement to an unnamed label.
static Statement*
make_goto_unnamed_statement(Unnamed_label* label, source_location);
// Make a label statement--where the label is defined.
static Statement*
make_label_statement(Label* label, source_location);
// Make an unnamed label statement--where the label is defined.
static Statement*
make_unnamed_label_statement(Unnamed_label* label);
// Make an if statement.
static Statement*
make_if_statement(Expression* cond, Block* then_block, Block* else_block,
source_location);
// Make a switch statement.
static Switch_statement*
make_switch_statement(Expression* switch_val, source_location);
// Make a type switch statement.
static Type_switch_statement*
make_type_switch_statement(Named_object* var, Expression*, source_location);
// Make a send statement.
static Send_statement*
make_send_statement(Expression* channel, Expression* val, source_location);
// Make a select statement.
static Select_statement*
make_select_statement(source_location);
// Make a for statement.
static For_statement*
make_for_statement(Block* init, Expression* cond, Block* post,
source_location location);
// Make a for statement with a range clause.
static For_range_statement*
make_for_range_statement(Expression* index_var, Expression* value_var,
Expression* range, source_location);
// Return the statement classification.
Statement_classification
classification() const
{ return this->classification_; }
// Get the statement location.
source_location
location() const
{ return this->location_; }
// Traverse the tree.
int
traverse(Block*, size_t* index, Traverse*);
// Traverse the contents of this statement--the expressions and
// statements which it contains.
int
traverse_contents(Traverse*);
// If this statement assigns some values, it calls a function for
// each value to which this statement assigns a value, and returns
// true. If this statement does not assign any values, it returns
// false.
bool
traverse_assignments(Traverse_assignments* tassign);
// Lower a statement. This is called immediately after parsing to
// simplify statements for further processing. It returns the same
// Statement or a new one. FUNCTION is the function containing this
// statement. BLOCK is the block containing this statement.
Statement*
lower(Gogo* gogo, Named_object* function, Block* block)
{ return this->do_lower(gogo, function, block); }
// Set type information for unnamed constants.
void
determine_types();
// Check types in a statement. This simply checks that any
// expressions used by the statement have the right type.
void
check_types(Gogo* gogo)
{ this->do_check_types(gogo); }
// Return whether this is a block statement.
bool
is_block_statement() const
{ return this->classification_ == STATEMENT_BLOCK; }
// If this is a variable declaration statement, return it.
// Otherwise return NULL.
Variable_declaration_statement*
variable_declaration_statement()
{
return this->convert<Variable_declaration_statement,
STATEMENT_VARIABLE_DECLARATION>();
}
// If this is a return statement, return it. Otherwise return NULL.
Return_statement*
return_statement()
{ return this->convert<Return_statement, STATEMENT_RETURN>(); }
// If this is a thunk statement (a go or defer statement), return
// it. Otherwise return NULL.
Thunk_statement*
thunk_statement();
// If this is a label statement, return it. Otherwise return NULL.
Label_statement*
label_statement()
{ return this->convert<Label_statement, STATEMENT_LABEL>(); }
// If this is a for statement, return it. Otherwise return NULL.
For_statement*
for_statement()
{ return this->convert<For_statement, STATEMENT_FOR>(); }
// If this is a for statement over a range clause, return it.
// Otherwise return NULL.
For_range_statement*
for_range_statement()
{ return this->convert<For_range_statement, STATEMENT_FOR_RANGE>(); }
// If this is a switch statement, return it. Otherwise return NULL.
Switch_statement*
switch_statement()
{ return this->convert<Switch_statement, STATEMENT_SWITCH>(); }
// If this is a type switch statement, return it. Otherwise return
// NULL.
Type_switch_statement*
type_switch_statement()
{ return this->convert<Type_switch_statement, STATEMENT_TYPE_SWITCH>(); }
// If this is a select statement, return it. Otherwise return NULL.
Select_statement*
select_statement()
{ return this->convert<Select_statement, STATEMENT_SELECT>(); }
// Return true if this statement may fall through--if after
// executing this statement we may go on to execute the following
// statement, if any.
bool
may_fall_through() const
{ return this->do_may_fall_through(); }
// Return the tree for a statement. BLOCK is the enclosing block.
tree
get_tree(Translate_context*);
protected:
// Implemented by child class: traverse the tree.
virtual int
do_traverse(Traverse*) = 0;
// Implemented by child class: traverse assignments. Any statement
// which includes an assignment should implement this.
virtual bool
do_traverse_assignments(Traverse_assignments*)
{ return false; }
// Implemented by the child class: lower this statement to a simpler
// one.
virtual Statement*
do_lower(Gogo*, Named_object*, Block*)
{ return this; }
// Implemented by child class: set type information for unnamed
// constants. Any statement which includes an expression needs to
// implement this.
virtual void
do_determine_types()
{ }
// Implemented by child class: check types of expressions used in a
// statement.
virtual void
do_check_types(Gogo*)
{ }
// Implemented by child class: return true if this statement may
// fall through.
virtual bool
do_may_fall_through() const
{ return true; }
// Implemented by child class: return a tree.
virtual tree
do_get_tree(Translate_context*) = 0;
// Traverse an expression in a statement.
int
traverse_expression(Traverse*, Expression**);
// Traverse an expression list in a statement. The Expression_list
// may be NULL.
int
traverse_expression_list(Traverse*, Expression_list*);
// Traverse a type in a statement.
int
traverse_type(Traverse*, Type*);
// Build a tree node with one operand, setting the location. The
// first operand really has type "enum tree_code", but that enum is
// not defined here.
tree
build_stmt_1(int tree_code_value, tree);
// For children to call when they detect that they are in error.
void
set_is_error();
// For children to call to report an error conveniently.
void
report_error(const char*);
// For children to return an error statement from lower().
static Statement*
make_error_statement(source_location);
private:
// Convert to the desired statement classification, or return NULL.
// This is a controlled dynamic cast.
template<typename Statement_class, Statement_classification sc>
Statement_class*
convert()
{
return (this->classification_ == sc
? static_cast<Statement_class*>(this)
: NULL);
}
template<typename Statement_class, Statement_classification sc>
const Statement_class*
convert() const
{
return (this->classification_ == sc
? static_cast<const Statement_class*>(this)
: NULL);
}
// The statement classification.
Statement_classification classification_;
// The location in the input file of the start of this statement.
source_location location_;
};
// A statement which creates and initializes a temporary variable.
class Temporary_statement : public Statement
{
public:
Temporary_statement(Type* type, Expression* init, source_location location)
: Statement(STATEMENT_TEMPORARY, location),
type_(type), init_(init), decl_(NULL), is_address_taken_(false)
{ }
// Return the type of the temporary variable.
Type*
type() const;
// Return the initialization expression.
Expression*
init() const
{ return this->init_; }
// Record that something takes the address of this temporary
// variable.
void
set_is_address_taken()
{ this->is_address_taken_ = true; }
// Return the tree for the temporary variable itself. This should
// not be called until after the statement itself has been expanded.
tree
get_decl() const;
protected:
int
do_traverse(Traverse*);
bool
do_traverse_assignments(Traverse_assignments*);
void
do_determine_types();
void
do_check_types(Gogo*);
tree
do_get_tree(Translate_context*);
private:
// The type of the temporary variable.
Type* type_;
// The initial value of the temporary variable. This may be NULL.
Expression* init_;
// The DECL for the temporary variable.
tree decl_;
// True if something takes the address of this temporary variable.
bool is_address_taken_;
};
// A variable declaration. This marks the point in the code where a
// variable is declared. The Variable is also attached to a Block.
class Variable_declaration_statement : public Statement
{
public:
Variable_declaration_statement(Named_object* var);
// The variable being declared.
Named_object*
var()
{ return this->var_; }
protected:
int
do_traverse(Traverse*);
bool
do_traverse_assignments(Traverse_assignments*);
tree
do_get_tree(Translate_context*);
private:
Named_object* var_;
};
// A return statement.
class Return_statement : public Statement
{
public:
Return_statement(const Typed_identifier_list* results, Expression_list* vals,
source_location location)
: Statement(STATEMENT_RETURN, location),
results_(results), vals_(vals)
{ }
// The list of values being returned. This may be NULL.
const Expression_list*
vals() const
{ return this->vals_; }
protected:
int
do_traverse(Traverse* traverse)
{ return this->traverse_expression_list(traverse, this->vals_); }
bool
do_traverse_assignments(Traverse_assignments*);
Statement*
do_lower(Gogo*, Named_object*, Block*);
void
do_determine_types();
void
do_check_types(Gogo*);
bool
do_may_fall_through() const
{ return false; }
tree
do_get_tree(Translate_context*);
private:
// The result types of the function we are returning from. This is
// here because in some of the traversals it is inconvenient to get
// it.
const Typed_identifier_list* results_;
// Return values. This may be NULL.
Expression_list* vals_;
};
// A send statement.
class Send_statement : public Statement
{
public:
Send_statement(Expression* channel, Expression* val,
source_location location)
: Statement(STATEMENT_SEND, location),
channel_(channel), val_(val), for_select_(false)
{ }
// Note that this is for a select statement.
void
set_for_select()
{ this->for_select_ = true; }
protected:
int
do_traverse(Traverse* traverse);
void
do_determine_types();
void
do_check_types(Gogo*);
tree
do_get_tree(Translate_context*);
private:
// The channel on which to send the value.
Expression* channel_;
// The value to send.
Expression* val_;
// Whether this is for a select statement.
bool for_select_;
};
// Select_clauses holds the clauses of a select statement. This is
// built by the parser.
class Select_clauses
{
public:
Select_clauses()
: clauses_()
{ }
// Add a new clause. IS_SEND is true if this is a send clause,
// false for a receive clause. For a send clause CHANNEL is the
// channel and VAL is the value to send. For a receive clause
// CHANNEL is the channel, VAL is either NULL or a Var_expression
// for the variable to set, and CLOSED is either NULL or a
// Var_expression to set to whether the channel is closed. If VAL
// is NULL, VAR may be a variable to be initialized with the
// received value, and CLOSEDVAR ma be a variable to be initialized
// with whether the channel is closed. IS_DEFAULT is true if this
// is the default clause. STATEMENTS is the list of statements to
// execute.
void
add(bool is_send, Expression* channel, Expression* val, Expression* closed,
Named_object* var, Named_object* closedvar, bool is_default,
Block* statements, source_location location)
{
this->clauses_.push_back(Select_clause(is_send, channel, val, closed, var,
closedvar, is_default, statements,
location));
}
// Traverse the select clauses.
int
traverse(Traverse*);
// Lower statements.
void
lower(Gogo*, Named_object*, Block*);
// Determine types.
void
determine_types();
// Whether the select clauses may fall through to the statement
// which follows the overall select statement.
bool
may_fall_through() const;
// Return a tree implementing the select statement.
tree
get_tree(Translate_context*, Unnamed_label* break_label, source_location);
private:
// A single clause.
class Select_clause
{
public:
Select_clause()
: channel_(NULL), val_(NULL), closed_(NULL), var_(NULL),
closedvar_(NULL), statements_(NULL), is_send_(false),
is_default_(false)
{ }
Select_clause(bool is_send, Expression* channel, Expression* val,
Expression* closed, Named_object* var,
Named_object* closedvar, bool is_default, Block* statements,
source_location location)
: channel_(channel), val_(val), closed_(closed), var_(var),
closedvar_(closedvar), statements_(statements), location_(location),
is_send_(is_send), is_default_(is_default), is_lowered_(false)
{ gcc_assert(is_default ? channel == NULL : channel != NULL); }
// Traverse the select clause.
int
traverse(Traverse*);
// Lower statements.
void
lower(Gogo*, Named_object*, Block*);
// Determine types.
void
determine_types();
// Return true if this is the default clause.
bool
is_default() const
{ return this->is_default_; }
// Return the channel. This will return NULL for the default
// clause.
Expression*
channel() const
{ return this->channel_; }
// Return true for a send, false for a receive.
bool
is_send() const
{
gcc_assert(!this->is_default_);
return this->is_send_;
}
// Return the statements.
const Block*
statements() const
{ return this->statements_; }
// Return the location.
source_location
location() const
{ return this->location_; }
// Whether this clause may fall through to the statement which
// follows the overall select statement.
bool
may_fall_through() const;
// Return a tree for the statements to execute.
tree
get_statements_tree(Translate_context*);
private:
// The channel.
Expression* channel_;
// The value to send or the lvalue to receive into.
Expression* val_;
// The lvalue to set to whether the channel is closed on a
// receive.
Expression* closed_;
// The variable to initialize, for "case a := <-ch".
Named_object* var_;
// The variable to initialize to whether the channel is closed,
// for "case a, c := <-ch".
Named_object* closedvar_;
// The statements to execute.
Block* statements_;
// The location of this clause.
source_location location_;
// Whether this is a send or a receive.
bool is_send_;
// Whether this is the default.
bool is_default_;
// Whether this has been lowered.
bool is_lowered_;
};
void
add_clause_tree(Translate_context*, int, Select_clause*, Unnamed_label*,
tree*);
typedef std::vector<Select_clause> Clauses;
Clauses clauses_;
};
// A select statement.
class Select_statement : public Statement
{
public:
Select_statement(source_location location)
: Statement(STATEMENT_SELECT, location),
clauses_(NULL), break_label_(NULL), is_lowered_(false)
{ }
// Add the clauses.
void
add_clauses(Select_clauses* clauses)
{
gcc_assert(this->clauses_ == NULL);
this->clauses_ = clauses;
}
// Return the break label for this select statement.
Unnamed_label*
break_label();
protected:
int
do_traverse(Traverse* traverse)
{ return this->clauses_->traverse(traverse); }
Statement*
do_lower(Gogo*, Named_object*, Block*);
void
do_determine_types()
{ this->clauses_->determine_types(); }
bool
do_may_fall_through() const
{ return this->clauses_->may_fall_through(); }
tree
do_get_tree(Translate_context*);
private:
// The select clauses.
Select_clauses* clauses_;
// The break label.
Unnamed_label* break_label_;
// Whether this statement has been lowered.
bool is_lowered_;
};
// A statement which requires a thunk: go or defer.
class Thunk_statement : public Statement
{
public:
Thunk_statement(Statement_classification, Call_expression*,
source_location);
// Return the call expression.
Expression*
call()
{ return this->call_; }
// Simplify a go or defer statement so that it only uses a single
// parameter.
bool
simplify_statement(Gogo*, Block*);
protected:
int
do_traverse(Traverse* traverse);
bool
do_traverse_assignments(Traverse_assignments*);
void
do_determine_types();
void
do_check_types(Gogo*);
// Return the function and argument trees for the call.
void
get_fn_and_arg(Translate_context*, tree* pfn, tree* parg);
private:
// Return whether this is a simple go statement.
bool
is_simple(Function_type*) const;
// Build the struct to use for a complex case.
Struct_type*
build_struct(Function_type* fntype);
// Build the thunk.
void
build_thunk(Gogo*, const std::string&, Function_type* fntype);
// The field name used in the thunk structure for the function
// pointer.
static const char* const thunk_field_fn;
// The field name used in the thunk structure for the receiver, if
// there is one.
static const char* const thunk_field_receiver;
// Set the name to use for thunk field N.
void
thunk_field_param(int n, char* buf, size_t buflen);
// The function call to be executed in a separate thread (go) or
// later (defer).
Expression* call_;
// The type used for a struct to pass to a thunk, if this is not a
// simple call.
Struct_type* struct_type_;
};
// A go statement.
class Go_statement : public Thunk_statement
{
public:
Go_statement(Call_expression* call, source_location location)
: Thunk_statement(STATEMENT_GO, call, location)
{ }
protected:
tree
do_get_tree(Translate_context*);
};
// A defer statement.
class Defer_statement : public Thunk_statement
{
public:
Defer_statement(Call_expression* call, source_location location)
: Thunk_statement(STATEMENT_DEFER, call, location)
{ }
protected:
tree
do_get_tree(Translate_context*);
};
// A label statement.
class Label_statement : public Statement
{
public:
Label_statement(Label* label, source_location location)
: Statement(STATEMENT_LABEL, location),
label_(label)
{ }
// Return the label itself.
const Label*
label() const
{ return this->label_; }
protected:
int
do_traverse(Traverse*);
tree
do_get_tree(Translate_context*);
private:
// The label.
Label* label_;
};
// A for statement.
class For_statement : public Statement
{
public:
For_statement(Block* init, Expression* cond, Block* post,
source_location location)
: Statement(STATEMENT_FOR, location),
init_(init), cond_(cond), post_(post), statements_(NULL),
break_label_(NULL), continue_label_(NULL)
{ }
// Add the statements.
void
add_statements(Block* statements)
{
gcc_assert(this->statements_ == NULL);
this->statements_ = statements;
}
// Return the break label for this for statement.
Unnamed_label*
break_label();
// Return the continue label for this for statement.
Unnamed_label*
continue_label();
// Set the break and continue labels for this statement.
void
set_break_continue_labels(Unnamed_label* break_label,
Unnamed_label* continue_label);
protected:
int
do_traverse(Traverse*);
bool
do_traverse_assignments(Traverse_assignments*)
{ gcc_unreachable(); }
Statement*
do_lower(Gogo*, Named_object*, Block*);
tree
do_get_tree(Translate_context*)
{ gcc_unreachable(); }
private:
// The initialization statements. This may be NULL.
Block* init_;
// The condition. This may be NULL.
Expression* cond_;
// The statements to run after each iteration. This may be NULL.
Block* post_;
// The statements in the loop itself.
Block* statements_;
// The break label, if needed.
Unnamed_label* break_label_;
// The continue label, if needed.
Unnamed_label* continue_label_;
};
// A for statement over a range clause.
class For_range_statement : public Statement
{
public:
For_range_statement(Expression* index_var, Expression* value_var,
Expression* range, source_location location)
: Statement(STATEMENT_FOR_RANGE, location),
index_var_(index_var), value_var_(value_var), range_(range),
statements_(NULL), break_label_(NULL), continue_label_(NULL)
{ }
// Add the statements.
void
add_statements(Block* statements)
{
gcc_assert(this->statements_ == NULL);
this->statements_ = statements;
}
// Return the break label for this for statement.
Unnamed_label*
break_label();
// Return the continue label for this for statement.
Unnamed_label*
continue_label();
protected:
int
do_traverse(Traverse*);
bool
do_traverse_assignments(Traverse_assignments*)
{ gcc_unreachable(); }
Statement*
do_lower(Gogo*, Named_object*, Block*);
tree
do_get_tree(Translate_context*)
{ gcc_unreachable(); }
private:
Expression*
make_range_ref(Named_object*, Temporary_statement*, source_location);
Expression*
call_builtin(Gogo*, const char* funcname, Expression* arg, source_location);
void
lower_range_array(Gogo*, Block*, Block*, Named_object*, Temporary_statement*,
Temporary_statement*, Temporary_statement*,
Block**, Expression**, Block**, Block**);
void
lower_range_string(Gogo*, Block*, Block*, Named_object*, Temporary_statement*,
Temporary_statement*, Temporary_statement*,
Block**, Expression**, Block**, Block**);
void
lower_range_map(Gogo*, Block*, Block*, Named_object*, Temporary_statement*,
Temporary_statement*, Temporary_statement*,
Block**, Expression**, Block**, Block**);
void
lower_range_channel(Gogo*, Block*, Block*, Named_object*,
Temporary_statement*, Temporary_statement*,
Temporary_statement*, Block**, Expression**, Block**,
Block**);
// The variable which is set to the index value.
Expression* index_var_;
// The variable which is set to the element value. This may be
// NULL.
Expression* value_var_;
// The expression we are ranging over.
Expression* range_;
// The statements in the block.
Block* statements_;
// The break label, if needed.
Unnamed_label* break_label_;
// The continue label, if needed.
Unnamed_label* continue_label_;
};
// Class Case_clauses holds the clauses of a switch statement. This
// is built by the parser.
class Case_clauses
{
public:
Case_clauses()
: clauses_()
{ }
// Add a new clause. CASES is a list of case expressions; it may be
// NULL. IS_DEFAULT is true if this is the default case.
// STATEMENTS is a block of statements. IS_FALLTHROUGH is true if
// after the statements the case clause should fall through to the
// next clause.
void
add(Expression_list* cases, bool is_default, Block* statements,
bool is_fallthrough, source_location location)
{
this->clauses_.push_back(Case_clause(cases, is_default, statements,
is_fallthrough, location));
}
// Return whether there are no clauses.
bool
empty() const
{ return this->clauses_.empty(); }
// Traverse the case clauses.
int
traverse(Traverse*);
// Lower for a nonconstant switch.
void
lower(Block*, Temporary_statement*, Unnamed_label*) const;
// Determine types of expressions. The Type parameter is the type
// of the switch value.
void
determine_types(Type*);
// Check types. The Type parameter is the type of the switch value.
bool
check_types(Type*);
// Return true if all the clauses are constant values.
bool
is_constant() const;
// Return true if these clauses may fall through to the statements
// following the switch statement.
bool
may_fall_through() const;
// Return the body of a SWITCH_EXPR when all the clauses are
// constants.
tree
get_constant_tree(Translate_context*, Unnamed_label* break_label) const;
private:
// For a constant tree we need to keep a record of constants we have
// already seen. Note that INTEGER_CST trees are interned.
typedef Unordered_set(tree) Case_constants;
// One case clause.
class Case_clause
{
public:
Case_clause()
: cases_(NULL), statements_(NULL), is_default_(false),
is_fallthrough_(false), location_(UNKNOWN_LOCATION)
{ }
Case_clause(Expression_list* cases, bool is_default, Block* statements,
bool is_fallthrough, source_location location)
: cases_(cases), statements_(statements), is_default_(is_default),
is_fallthrough_(is_fallthrough), location_(location)
{ }
// Whether this clause falls through to the next clause.
bool
is_fallthrough() const
{ return this->is_fallthrough_; }
// Whether this is the default.
bool
is_default() const
{ return this->is_default_; }
// The location of this clause.
source_location
location() const
{ return this->location_; }
// Traversal.
int
traverse(Traverse*);
// Lower for a nonconstant switch.
void
lower(Block*, Temporary_statement*, Unnamed_label*, Unnamed_label*) const;
// Determine types.
void
determine_types(Type*);
// Check types.
bool
check_types(Type*);
// Return true if all the case expressions are constant.
bool
is_constant() const;
// Return true if this clause may fall through to execute the
// statements following the switch statement. This is not the
// same as whether this clause falls through to the next clause.
bool
may_fall_through() const;
// Build up the body of a SWITCH_EXPR when the case expressions
// are constant.
void
get_constant_tree(Translate_context*, Unnamed_label* break_label,
Case_constants* case_constants, tree* stmt_list) const;
private:
// The list of case expressions.
Expression_list* cases_;
// The statements to execute.
Block* statements_;
// Whether this is the default case.
bool is_default_;
// Whether this falls through after the statements.
bool is_fallthrough_;
// The location of this case clause.
source_location location_;
};
friend class Case_clause;
// The type of the list of clauses.
typedef std::vector<Case_clause> Clauses;
// All the case clauses.
Clauses clauses_;
};
// A switch statement.
class Switch_statement : public Statement
{
public:
Switch_statement(Expression* val, source_location location)
: Statement(STATEMENT_SWITCH, location),
val_(val), clauses_(NULL), break_label_(NULL)
{ }
// Add the clauses.
void
add_clauses(Case_clauses* clauses)
{
gcc_assert(this->clauses_ == NULL);
this->clauses_ = clauses;
}
// Return the break label for this switch statement.
Unnamed_label*
break_label();
protected:
int
do_traverse(Traverse*);
Statement*
do_lower(Gogo*, Named_object*, Block*);
tree
do_get_tree(Translate_context*)
{ gcc_unreachable(); }
private:
// The value to switch on. This may be NULL.
Expression* val_;
// The case clauses.
Case_clauses* clauses_;
// The break label, if needed.
Unnamed_label* break_label_;
};
// Class Type_case_clauses holds the clauses of a type switch
// statement. This is built by the parser.
class Type_case_clauses
{
public:
Type_case_clauses()
: clauses_()
{ }
// Add a new clause. TYPE is the type for this clause; it may be
// NULL. IS_FALLTHROUGH is true if this falls through to the next
// clause; in this case STATEMENTS will be NULL. IS_DEFAULT is true
// if this is the default case. STATEMENTS is a block of
// statements; it may be NULL.
void
add(Type* type, bool is_fallthrough, bool is_default, Block* statements,
source_location location)
{
this->clauses_.push_back(Type_case_clause(type, is_fallthrough, is_default,
statements, location));
}
// Return whether there are no clauses.
bool
empty() const
{ return this->clauses_.empty(); }
// Traverse the type case clauses.
int
traverse(Traverse*);
// Check for duplicates.
void
check_duplicates() const;
// Lower to if and goto statements.
void
lower(Block*, Temporary_statement* descriptor_temp,
Unnamed_label* break_label) const;
private:
// One type case clause.
class Type_case_clause
{
public:
Type_case_clause()
: type_(NULL), statements_(NULL), is_default_(false),
location_(UNKNOWN_LOCATION)
{ }
Type_case_clause(Type* type, bool is_fallthrough, bool is_default,
Block* statements, source_location location)
: type_(type), statements_(statements), is_fallthrough_(is_fallthrough),
is_default_(is_default), location_(location)
{ }
// The type.
Type*
type() const
{ return this->type_; }
// Whether this is the default.
bool
is_default() const
{ return this->is_default_; }
// The location of this type clause.
source_location
location() const
{ return this->location_; }
// Traversal.
int
traverse(Traverse*);
// Lower to if and goto statements.
void
lower(Block*, Temporary_statement* descriptor_temp,
Unnamed_label* break_label, Unnamed_label** stmts_label) const;
private:
// The type for this type clause.
Type* type_;
// The statements to execute.
Block* statements_;
// Whether this falls through--this is true for "case T1, T2".
bool is_fallthrough_;
// Whether this is the default case.
bool is_default_;
// The location of this type case clause.
source_location location_;
};
friend class Type_case_clause;
// The type of the list of type clauses.
typedef std::vector<Type_case_clause> Type_clauses;
// All the type case clauses.
Type_clauses clauses_;
};
// A type switch statement.
class Type_switch_statement : public Statement
{
public:
Type_switch_statement(Named_object* var, Expression* expr,
source_location location)
: Statement(STATEMENT_TYPE_SWITCH, location),
var_(var), expr_(expr), clauses_(NULL), break_label_(NULL)
{ gcc_assert(var == NULL || expr == NULL); }
// Add the clauses.
void
add_clauses(Type_case_clauses* clauses)
{
gcc_assert(this->clauses_ == NULL);
this->clauses_ = clauses;
}
// Return the break label for this type switch statement.
Unnamed_label*
break_label();
protected:
int
do_traverse(Traverse*);
Statement*
do_lower(Gogo*, Named_object*, Block*);
tree
do_get_tree(Translate_context*)
{ gcc_unreachable(); }
private:
// Get the type descriptor.
tree
get_type_descriptor(Translate_context*, Type*, tree);
// The variable holding the value we are switching on.
Named_object* var_;
// The expression we are switching on if there is no variable.
Expression* expr_;
// The type case clauses.
Type_case_clauses* clauses_;
// The break label, if needed.
Unnamed_label* break_label_;
};
#endif // !defined(GO_STATEMENTS_H)
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
// unsafe.cc -- Go frontend builtin unsafe package.
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "go-system.h"
#include "types.h"
#include "gogo.h"
// Set up the builtin unsafe package. This should probably be driven
// by a table.
void
Gogo::import_unsafe(const std::string& local_name, bool is_local_name_exported,
source_location location)
{
location_t bloc = BUILTINS_LOCATION;
bool add_to_globals;
Package* package = this->add_imported_package("unsafe", local_name,
is_local_name_exported,
"libgo_unsafe",
location, &add_to_globals);
package->set_is_imported();
Bindings* bindings = package->bindings();
// The type may have already been created by an import.
Named_object* no = package->bindings()->lookup("Pointer");
if (no == NULL)
{
Type* type = Type::make_pointer_type(Type::make_void_type());
no = bindings->add_type("Pointer", package, type, UNKNOWN_LOCATION);
}
else
{
gcc_assert(no->package() == package);
gcc_assert(no->is_type());
gcc_assert(no->type_value()->is_unsafe_pointer_type());
no->type_value()->set_is_visible();
}
Named_type* pointer_type = no->type_value();
if (add_to_globals)
this->add_named_type(pointer_type);
Type* int_type = this->lookup_global("int")->type_value();
// Sizeof.
Typed_identifier_list* results = new Typed_identifier_list;
results->push_back(Typed_identifier("", int_type, bloc));
Function_type* fntype = Type::make_function_type(NULL, NULL, results, bloc);
fntype->set_is_builtin();
no = bindings->add_function_declaration("Sizeof", package, fntype, bloc);
if (add_to_globals)
this->add_named_object(no);
// Offsetof.
results = new Typed_identifier_list;
results->push_back(Typed_identifier("", int_type, bloc));
fntype = Type::make_function_type(NULL, NULL, results, bloc);
fntype->set_is_varargs();
fntype->set_is_builtin();
no = bindings->add_function_declaration("Offsetof", package, fntype, bloc);
if (add_to_globals)
this->add_named_object(no);
// Alignof.
results = new Typed_identifier_list;
results->push_back(Typed_identifier("", int_type, bloc));
fntype = Type::make_function_type(NULL, NULL, results, bloc);
fntype->set_is_varargs();
fntype->set_is_builtin();
no = bindings->add_function_declaration("Alignof", package, fntype, bloc);
if (add_to_globals)
this->add_named_object(no);
// Typeof.
Type* empty_interface = Type::make_interface_type(NULL, bloc);
Typed_identifier_list* parameters = new Typed_identifier_list;
parameters->push_back(Typed_identifier("i", empty_interface, bloc));
results = new Typed_identifier_list;
results->push_back(Typed_identifier("", empty_interface, bloc));
fntype = Type::make_function_type(NULL, parameters, results, bloc);
no = bindings->add_function_declaration("Typeof", package, fntype, bloc);
if (add_to_globals)
this->add_named_object(no);
// Reflect.
parameters = new Typed_identifier_list;
parameters->push_back(Typed_identifier("it", empty_interface, bloc));
results = new Typed_identifier_list;
results->push_back(Typed_identifier("", empty_interface, bloc));
results->push_back(Typed_identifier("", pointer_type, bloc));
fntype = Type::make_function_type(NULL, parameters, results, bloc);
no = bindings->add_function_declaration("Reflect", package, fntype, bloc);
if (add_to_globals)
this->add_named_object(no);
// Unreflect.
parameters = new Typed_identifier_list;
parameters->push_back(Typed_identifier("typ", empty_interface, bloc));
parameters->push_back(Typed_identifier("addr", pointer_type, bloc));
results = new Typed_identifier_list;
results->push_back(Typed_identifier("", empty_interface, bloc));
fntype = Type::make_function_type(NULL, parameters, results, bloc);
no = bindings->add_function_declaration("Unreflect", package, fntype, bloc);
if (add_to_globals)
this->add_named_object(no);
// New.
parameters = new Typed_identifier_list;
parameters->push_back(Typed_identifier("typ", empty_interface, bloc));
results = new Typed_identifier_list;
results->push_back(Typed_identifier("", pointer_type, bloc));
fntype = Type::make_function_type(NULL, parameters, results, bloc);
no = bindings->add_function_declaration("New", package, fntype, bloc);
if (add_to_globals)
this->add_named_object(no);
// NewArray.
parameters = new Typed_identifier_list;
parameters->push_back(Typed_identifier("typ", empty_interface, bloc));
parameters->push_back(Typed_identifier("n", int_type, bloc));
results = new Typed_identifier_list;
results->push_back(Typed_identifier("", pointer_type, bloc));
fntype = Type::make_function_type(NULL, parameters, results, bloc);
no = bindings->add_function_declaration("NewArray", package, fntype, bloc);
if (add_to_globals)
this->add_named_object(no);
this->imported_unsafe_ = true;
}
// unsafe.cc -- Go frontend builtin unsafe package.
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "go-system.h"
#include "go-c.h"
#include "types.h"
#include "gogo.h"
// Set up the builtin unsafe package. This should probably be driven
// by a table.
void
Gogo::import_unsafe(const std::string& local_name, bool is_local_name_exported,
source_location location)
{
location_t bloc = BUILTINS_LOCATION;
bool add_to_globals;
Package* package = this->add_imported_package("unsafe", local_name,
is_local_name_exported,
"libgo_unsafe",
location, &add_to_globals);
if (package == NULL)
{
go_assert(saw_errors());
return;
}
package->set_is_imported();
Bindings* bindings = package->bindings();
// The type may have already been created by an import.
Named_object* no = package->bindings()->lookup("Pointer");
if (no == NULL)
{
Type* type = Type::make_pointer_type(Type::make_void_type());
no = bindings->add_type("Pointer", package, type, UNKNOWN_LOCATION);
}
else
{
go_assert(no->package() == package);
go_assert(no->is_type());
go_assert(no->type_value()->is_unsafe_pointer_type());
no->type_value()->set_is_visible();
}
Named_type* pointer_type = no->type_value();
if (add_to_globals)
this->add_named_type(pointer_type);
Type* int_type = this->lookup_global("int")->type_value();
// Sizeof.
Typed_identifier_list* results = new Typed_identifier_list;
results->push_back(Typed_identifier("", int_type, bloc));
Function_type* fntype = Type::make_function_type(NULL, NULL, results, bloc);
fntype->set_is_builtin();
no = bindings->add_function_declaration("Sizeof", package, fntype, bloc);
if (add_to_globals)
this->add_named_object(no);
// Offsetof.
results = new Typed_identifier_list;
results->push_back(Typed_identifier("", int_type, bloc));
fntype = Type::make_function_type(NULL, NULL, results, bloc);
fntype->set_is_varargs();
fntype->set_is_builtin();
no = bindings->add_function_declaration("Offsetof", package, fntype, bloc);
if (add_to_globals)
this->add_named_object(no);
// Alignof.
results = new Typed_identifier_list;
results->push_back(Typed_identifier("", int_type, bloc));
fntype = Type::make_function_type(NULL, NULL, results, bloc);
fntype->set_is_varargs();
fntype->set_is_builtin();
no = bindings->add_function_declaration("Alignof", package, fntype, bloc);
if (add_to_globals)
this->add_named_object(no);
// Typeof.
Type* empty_interface = Type::make_interface_type(NULL, bloc);
Typed_identifier_list* parameters = new Typed_identifier_list;
parameters->push_back(Typed_identifier("i", empty_interface, bloc));
results = new Typed_identifier_list;
results->push_back(Typed_identifier("", empty_interface, bloc));
fntype = Type::make_function_type(NULL, parameters, results, bloc);
no = bindings->add_function_declaration("Typeof", package, fntype, bloc);
if (add_to_globals)
this->add_named_object(no);
// Reflect.
parameters = new Typed_identifier_list;
parameters->push_back(Typed_identifier("it", empty_interface, bloc));
results = new Typed_identifier_list;
results->push_back(Typed_identifier("", empty_interface, bloc));
results->push_back(Typed_identifier("", pointer_type, bloc));
fntype = Type::make_function_type(NULL, parameters, results, bloc);
no = bindings->add_function_declaration("Reflect", package, fntype, bloc);
if (add_to_globals)
this->add_named_object(no);
// Unreflect.
parameters = new Typed_identifier_list;
parameters->push_back(Typed_identifier("typ", empty_interface, bloc));
parameters->push_back(Typed_identifier("addr", pointer_type, bloc));
results = new Typed_identifier_list;
results->push_back(Typed_identifier("", empty_interface, bloc));
fntype = Type::make_function_type(NULL, parameters, results, bloc);
no = bindings->add_function_declaration("Unreflect", package, fntype, bloc);
if (add_to_globals)
this->add_named_object(no);
// New.
parameters = new Typed_identifier_list;
parameters->push_back(Typed_identifier("typ", empty_interface, bloc));
results = new Typed_identifier_list;
results->push_back(Typed_identifier("", pointer_type, bloc));
fntype = Type::make_function_type(NULL, parameters, results, bloc);
no = bindings->add_function_declaration("New", package, fntype, bloc);
if (add_to_globals)
this->add_named_object(no);
// NewArray.
parameters = new Typed_identifier_list;
parameters->push_back(Typed_identifier("typ", empty_interface, bloc));
parameters->push_back(Typed_identifier("n", int_type, bloc));
results = new Typed_identifier_list;
results->push_back(Typed_identifier("", pointer_type, bloc));
fntype = Type::make_function_type(NULL, parameters, results, bloc);
no = bindings->add_function_declaration("NewArray", package, fntype, bloc);
if (add_to_globals)
this->add_named_object(no);
if (!this->imported_unsafe_)
{
go_imported_unsafe();
this->imported_unsafe_ = true;
}
}
// unsafe.cc -- Go frontend builtin unsafe package.
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "go-system.h"
#include "go-c.h"
#include "types.h"
#include "gogo.h"
// Set up the builtin unsafe package. This should probably be driven
// by a table.
void
Gogo::import_unsafe(const std::string& local_name, bool is_local_name_exported,
source_location location)
{
location_t bloc = BUILTINS_LOCATION;
bool add_to_globals;
Package* package = this->add_imported_package("unsafe", local_name,
is_local_name_exported,
"libgo_unsafe",
location, &add_to_globals);
if (package == NULL)
{
gcc_assert(saw_errors());
return;
}
package->set_is_imported();
Bindings* bindings = package->bindings();
// The type may have already been created by an import.
Named_object* no = package->bindings()->lookup("Pointer");
if (no == NULL)
{
Type* type = Type::make_pointer_type(Type::make_void_type());
no = bindings->add_type("Pointer", package, type, UNKNOWN_LOCATION);
}
else
{
gcc_assert(no->package() == package);
gcc_assert(no->is_type());
gcc_assert(no->type_value()->is_unsafe_pointer_type());
no->type_value()->set_is_visible();
}
Named_type* pointer_type = no->type_value();
if (add_to_globals)
this->add_named_type(pointer_type);
Type* int_type = this->lookup_global("int")->type_value();
// Sizeof.
Typed_identifier_list* results = new Typed_identifier_list;
results->push_back(Typed_identifier("", int_type, bloc));
Function_type* fntype = Type::make_function_type(NULL, NULL, results, bloc);
fntype->set_is_builtin();
no = bindings->add_function_declaration("Sizeof", package, fntype, bloc);
if (add_to_globals)
this->add_named_object(no);
// Offsetof.
results = new Typed_identifier_list;
results->push_back(Typed_identifier("", int_type, bloc));
fntype = Type::make_function_type(NULL, NULL, results, bloc);
fntype->set_is_varargs();
fntype->set_is_builtin();
no = bindings->add_function_declaration("Offsetof", package, fntype, bloc);
if (add_to_globals)
this->add_named_object(no);
// Alignof.
results = new Typed_identifier_list;
results->push_back(Typed_identifier("", int_type, bloc));
fntype = Type::make_function_type(NULL, NULL, results, bloc);
fntype->set_is_varargs();
fntype->set_is_builtin();
no = bindings->add_function_declaration("Alignof", package, fntype, bloc);
if (add_to_globals)
this->add_named_object(no);
// Typeof.
Type* empty_interface = Type::make_interface_type(NULL, bloc);
Typed_identifier_list* parameters = new Typed_identifier_list;
parameters->push_back(Typed_identifier("i", empty_interface, bloc));
results = new Typed_identifier_list;
results->push_back(Typed_identifier("", empty_interface, bloc));
fntype = Type::make_function_type(NULL, parameters, results, bloc);
no = bindings->add_function_declaration("Typeof", package, fntype, bloc);
if (add_to_globals)
this->add_named_object(no);
// Reflect.
parameters = new Typed_identifier_list;
parameters->push_back(Typed_identifier("it", empty_interface, bloc));
results = new Typed_identifier_list;
results->push_back(Typed_identifier("", empty_interface, bloc));
results->push_back(Typed_identifier("", pointer_type, bloc));
fntype = Type::make_function_type(NULL, parameters, results, bloc);
no = bindings->add_function_declaration("Reflect", package, fntype, bloc);
if (add_to_globals)
this->add_named_object(no);
// Unreflect.
parameters = new Typed_identifier_list;
parameters->push_back(Typed_identifier("typ", empty_interface, bloc));
parameters->push_back(Typed_identifier("addr", pointer_type, bloc));
results = new Typed_identifier_list;
results->push_back(Typed_identifier("", empty_interface, bloc));
fntype = Type::make_function_type(NULL, parameters, results, bloc);
no = bindings->add_function_declaration("Unreflect", package, fntype, bloc);
if (add_to_globals)
this->add_named_object(no);
// New.
parameters = new Typed_identifier_list;
parameters->push_back(Typed_identifier("typ", empty_interface, bloc));
results = new Typed_identifier_list;
results->push_back(Typed_identifier("", pointer_type, bloc));
fntype = Type::make_function_type(NULL, parameters, results, bloc);
no = bindings->add_function_declaration("New", package, fntype, bloc);
if (add_to_globals)
this->add_named_object(no);
// NewArray.
parameters = new Typed_identifier_list;
parameters->push_back(Typed_identifier("typ", empty_interface, bloc));
parameters->push_back(Typed_identifier("n", int_type, bloc));
results = new Typed_identifier_list;
results->push_back(Typed_identifier("", pointer_type, bloc));
fntype = Type::make_function_type(NULL, parameters, results, bloc);
no = bindings->add_function_declaration("NewArray", package, fntype, bloc);
if (add_to_globals)
this->add_named_object(no);
if (!this->imported_unsafe_)
{
go_imported_unsafe();
this->imported_unsafe_ = true;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment