// 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 #include "go-linemap.h" class Traverse; class Statement_inserter; 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 Expression_list; class Statement; class Temporary_statement; class Block; class Function; class Bindings; class Bindings_snapshot; 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 Named_object; class Label; class Translate_context; class Backend; class Export; class Import; class Bexpression; class Btype; class Bstatement; class Bblock; class Bvariable; class Blabel; class Bfunction; class Escape_context; class Node; // 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_; } // Older V1 export data uses a priority scheme to order // initialization functions; functions with a lower priority number // must be run first. This value will be set to -1 for current // generation objects, and will take on a non-negative value only // when importing a V1-vintage object. int priority() const { return this->priority_; } // Reset priority. void set_priority(int new_priority) { this->priority_ = new_priority; } // Record the fact that some other init fcn must be run before this init fcn. void record_precursor_fcn(std::string init_fcn_name) { this->precursor_functions_.insert(init_fcn_name); } // Return the list of precursor fcns for this fcn (must be run before it). const std::set& precursors() const { return this->precursor_functions_; } private: // The name of the package being imported. std::string package_name_; // The name of the package's init function. std::string init_name_; // Names of init functions that must be run before this fcn. std::set precursor_functions_; // Priority for this function. See note above on obsolescence. int priority_; }; // For sorting purposes. struct Import_init_lt { bool operator()(const Import_init* i1, const Import_init* i2) const { return i1->init_name() < i2->init_name(); } }; // Set of import init objects. class Import_init_set : public std::set { }; inline bool priority_compare(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, Linemap *linemap, int int_type_size, int pointer_size); // Get the backend generator. Backend* backend() { return this->backend_; } // Get the Location generator. Linemap* linemap() { return this->linemap_; } // Get the package name. const std::string& package_name() const; // Set the package name. void set_package_name(const std::string&, 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 // 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->pkgpath() + '.' + 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 path of a hidden name. static std::string hidden_name_pkgpath(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 within a mangled symbol name. static std::string mangle_possibly_hidden_name(const std::string& name) { // FIXME: This adds in pkgpath twice for hidden symbols, which is // less than ideal. std::string n; if (!Gogo::is_hidden_name(name)) n = name; else { n = "."; std::string pkgpath = Gogo::hidden_name_pkgpath(name); n.append(Gogo::pkgpath_for_symbol(pkgpath)); n.append(1, '.'); n.append(Gogo::unpack_hidden_name(name)); } return n; } // 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] == '.'); } // Convert a pkgpath into a string suitable for a symbol static std::string pkgpath_for_symbol(const std::string& pkgpath); // Return the package path to use for reflect.Type.PkgPath. const std::string& pkgpath() const; // Return the package path to use for a symbol name. const std::string& pkgpath_symbol() const; // Set the package path from a command line option. void set_pkgpath(const std::string&); // Set the prefix from a command line option. void set_prefix(const std::string&); // Return whether pkgpath was set from a command line option. bool pkgpath_from_option() const { return this->pkgpath_from_option_; } // Return the relative import path as set from the command line. // Returns an empty string if it was not set. const std::string& relative_import_path() const { return this->relative_import_path_; } // Set the relative import path from a command line option. void set_relative_import_path(const std::string& s) { this->relative_import_path_ = s; } // Set the C header file to write. This is used for the runtime // package. void set_c_header(const std::string& s) { this->c_header_ = s; } // Return whether to check for division by zero in binary operations. bool check_divide_by_zero() const { return this->check_divide_by_zero_; } // Set the option to check division by zero from a command line option. void set_check_divide_by_zero(bool b) { this->check_divide_by_zero_ = b; } // Return whether to check for division overflow in binary operations. bool check_divide_overflow() const { return this->check_divide_overflow_; } // Set the option to check division overflow from a command line option. void set_check_divide_overflow(bool b) { this->check_divide_overflow_ = b; } // Return whether we are compiling the runtime package. bool compiling_runtime() const { return this->compiling_runtime_; } // Set whether we are compiling the runtime package. void set_compiling_runtime(bool b) { this->compiling_runtime_ = b; } // Return the level of escape analysis debug information to emit. int debug_escape_level() const { return this->debug_escape_level_; } // Set the level of escape analysis debugging from a command line option. void set_debug_escape_level(int level) { this->debug_escape_level_ = level; } // Return the hash for debug escape analysis. std::string debug_escape_hash() const { return this->debug_escape_hash_; } // Set the hash value for debug escape analysis. void set_debug_escape_hash(const std::string& s) { this->debug_escape_hash_ = s; } // Return the size threshold used to determine whether to issue // a nil-check for a given pointer dereference. A threshold of -1 // implies that all potentially faulting dereference ops should // be nil-checked. A positive threshold of N implies that a deref // of *P where P has size less than N doesn't need a nil check. int64_t nil_check_size_threshold() const { return this->nil_check_size_threshold_; } // Set the nil-check size threshold, as described above. void set_nil_check_size_threshold(int64_t bytes) { this->nil_check_size_threshold_ = bytes; } // 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, bool must_exist, 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& pkgpath, const std::string& pkgpath_symbol, 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& pkgpath, const std::string& pkgpath_symbol, Location); // Look up a package by pkgpath, and return its pkgpath_symbol. std::string pkgpath_symbol_for_package(const std::string&); // 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, Location); // Finish compiling a function. void finish_function(Location); // Return the current function. Named_object* current_function() const; // Return the current block. Block* current_block(); // Start a new block. This is not initially associated with a // function. void start_block(Location); // Finish the current block and return it. Block* finish_block(Location); // Declare an erroneous name. This is used to avoid knock-on errors // after a parsing error. Named_object* add_erroneous_name(const std::string& name); // 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, Location); // Declare a function. Named_object* declare_function(const std::string&, Function_type*, Location); // Declare a function at the package level. This is used for // functions generated for a type. Named_object* declare_package_function(const std::string&, Function_type*, Location); // Add a label. Label* add_label_definition(const std::string&, Location); // Add a label reference. ISSUE_GOTO_ERRORS is true if we should // report errors for a goto from the current location to the label // location. Label* add_label_reference(const std::string&, Location, bool issue_goto_errors); // An analysis set is a list of functions paired with a boolean that indicates // whether the list of functions are recursive. typedef std::pair, bool> Analysis_set; // Add a GROUP of possibly RECURSIVE functions to the Analysis_set for this // package. void add_analysis_set(const std::vector& group, bool recursive) { this->analysis_sets_.push_back(std::make_pair(group, recursive)); } // Return a snapshot of the current binding state. Bindings_snapshot* bindings_snapshot(Location); // Add a statement to the current block. void add_statement(Statement*); // Add a block to the current block. void add_block(Block*, 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*, 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&, 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&, 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 type which needs to be verified. This is used for sink // types, just to give appropriate error messages. void add_type_to_verify(Type* type); // Add a named object to the current namespace. This is used for // import . "package". void add_dot_import_object(Named_object*); // Add an identifier to the list of names seen in the file block. void add_file_block_name(const std::string& name, Location location) { this->file_block_names_[name] = location; } // Add a linkname, from the go:linkname compiler directive. This // changes the externally visible name of go_name to be ext_name. void add_linkname(const std::string& go_name, bool is_exported, const std::string& ext_name, Location location); // Mark all local variables in current bindings as used. This is // used when there is a parse error to avoid useless errors. void mark_locals_used(); // 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; } // Return whether the current file imported the unsafe package. bool current_file_imported_unsafe() const { return this->current_file_imported_unsafe_; } // Clear out all names in file scope. This is called when we start // parsing a new file. void clear_file_scope(); // Record that VAR1 must be initialized after VAR2. This is used // when VAR2 does not appear in VAR1's INIT or PREINIT. void record_var_depends_on(Variable* var1, Named_object* var2) { go_assert(this->var_deps_.find(var1) == this->var_deps_.end()); this->var_deps_[var1] = var2; } // Return the variable that VAR depends on, or NULL if none. Named_object* var_depends_on(Variable* var) const { Var_deps::const_iterator p = this->var_deps_.find(var); return p != this->var_deps_.end() ? p->second : NULL; } // Queue up a type-specific function to be written out. This is // used when a type-specific function is needed when not at the top // level. void queue_specific_type_function(Type* type, Named_type* name, int64_t size, const std::string& hash_name, Function_type* hash_fntype, const std::string& equal_name, Function_type* equal_fntype); // Write out queued specific type functions. void write_specific_type_functions(); // Whether we are done writing out specific type functions. bool specific_type_functions_are_written() const { return this->specific_type_functions_are_written_; } // Add a pointer that needs to be added to the list of objects // traversed by the garbage collector. This should be an expression // of pointer type that points to static storage. It's not // necessary to add global variables to this list, just global // variable initializers that would otherwise not be seen. void add_gc_root(Expression* expr) { this->set_need_init_fn(); this->gc_roots_.push_back(expr); } // 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, Statement_inserter*, Expression**); // Lower a constant. void lower_constant(Named_object*); // Flatten all the statements in a block. void flatten_block(Named_object* function, Block*); // Flatten an expression. void flatten_expression(Named_object* function, Statement_inserter*, Expression**); // Create all necessary function descriptors. void create_function_descriptors(); // 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(); // Analyze the program flow for escape information. void analyze_escape(); // Discover the groups of possibly recursive functions in this package. void discover_analysis_sets(); // Build a connectivity graph between the objects in each analyzed function. void assign_connectivity(Escape_context*, Named_object*); // Traverse the objects in the connecitivty graph from the sink, adjusting the // escape levels of each object. void propagate_escape(Escape_context*, Node*); // Add notes about the escape level of a function's input and output // parameters for exporting and importing top level functions. void tag_function(Escape_context*, Named_object*); // Reclaim memory of escape analysis Nodes. void reclaim_escape_nodes(); // 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); // Return the Import_init for a given init name. Import_init* lookup_init(const std::string& init_name); // Turn short-cut operators (&&, ||) into explicit if statements. void remove_shortcuts(); // Use temporary variables to force order of evaluation. void order_evaluations(); // Add write barriers as needed. void add_write_barriers(); // Return whether an assignment that sets LHS to RHS needs a write // barrier. bool assign_needs_write_barrier(Expression* lhs); // Return an assignment that sets LHS to RHS using a write barrier. // This returns an if statement that checks whether write barriers // are enabled. If not, it does LHS = RHS, otherwise it calls the // appropriate write barrier function. Statement* assign_with_write_barrier(Function*, Block*, Statement_inserter*, Expression* lhs, Expression* rhs, Location); // Flatten parse tree. void flatten(); // Build thunks for functions which call recover. void build_recover_thunks(); // Return a declaration for __builtin_return_address or // __builtin_frame_address. static Named_object* declare_builtin_rf_address(const char* name); // Simplify statements which might use thunks: go and defer // statements. void simplify_thunk_statements(); // Dump AST if -fgo-dump-ast is set void dump_ast(const char* basename); // Dump Call Graph if -fgo-dump-calls is set. void dump_call_graph(const char* basename); // Dump Connection Graphs if -fgo-dump-connections is set. void dump_connection_graphs(const char* basename); // 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_; } // Give an error if the initialization of VAR depends on itself. void check_self_dep(Named_object*); // Write out the global values. void write_globals(); // Build a call to the runtime error function. Expression* runtime_error(int code, Location); // Build required interface method tables. void build_interface_method_tables(); // Return an expression which allocates memory to hold values of type TYPE. Expression* allocate_memory(Type *type, Location); // Return the assembler name to use for an exported function, a // method, or a function/method declaration. std::string function_asm_name(const std::string& go_name, const Package*, const Type* receiver); // Return the name to use for a function descriptor. std::string function_descriptor_name(Named_object*); // Return the name to use for a generated stub method. std::string stub_method_name(const Package*, const std::string& method_name); // Return the names of the hash and equality functions for TYPE. void specific_type_function_names(const Type*, const Named_type*, std::string* hash_name, std::string* equal_name); // Return the assembler name to use for a global variable. std::string global_var_asm_name(const std::string& go_name, const Package*); // Return a name to use for an error case. This should only be used // after reporting an error, and is used to avoid useless knockon // errors. static std::string erroneous_name(); // Return whether the name indicates an error. static bool is_erroneous_name(const std::string&); // 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. std::string thunk_name(); // Return whether an object is a thunk. static bool is_thunk(const Named_object*); // Return the name to use for an init function. std::string init_function_name(); // Return the name to use for a nested function. std::string nested_function_name(Named_object* enclosing); // Return the name to use for a sink funciton. std::string sink_function_name(); // Return the name to use for an (erroneous) redefined function. std::string redefined_function_name(); // Return the name for use for a recover thunk. std::string recover_thunk_name(const std::string& name, const Type* rtype); // Return the name to use for the GC root variable. std::string gc_root_name(); // Return the name to use for a composite literal or string // initializer. std::string initializer_name(); // Return the name of the variable used to represent the zero value // of a map. std::string map_zero_value_name(); // Get the name of the magic initialization function. const std::string& get_init_fn_name(); // Return the name for a type descriptor symbol. std::string type_descriptor_name(Type*, Named_type*); // Return the assembler name for the GC symbol for a type. std::string gc_symbol_name(Type*); // Return the assembler name for a ptrmask variable. std::string ptrmask_symbol_name(const std::string& ptrmask_sym_name); // Return the name to use for an interface method table. std::string interface_method_table_name(Interface_type*, Type*, bool is_pointer); // Return whether NAME is a special name that can not be passed to // unpack_hidden_name. This is needed because various special names // use "..SUFFIX", but unpack_hidden_name just looks for '.'. static bool is_special_name(const std::string& name); 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 blocks; }; // The stack of functions. typedef std::vector Open_functions; // Set up the built-in unsafe package. void import_unsafe(const std::string&, bool is_exported, Location); // Return the current binding contour. Bindings* current_bindings(); const Bindings* current_bindings() const; void write_c_header(); // Get the decl for the magic initialization function. Named_object* initialization_function_decl(); // Create the magic initialization function. Named_object* create_initialization_function(Named_object* fndecl, Bstatement* code_stmt); // Initialize imported packages. BFUNCTION is the function // into which the package init calls will be placed. void init_imports(std::vector&, Bfunction* bfunction); // Register variables with the garbage collector. void register_gc_vars(const std::vector&, std::vector&, Bfunction* init_bfunction); Named_object* write_barrier_variable(); Statement* check_write_barrier(Block*, Statement*, Statement*); // Type used to map import names to packages. typedef std::map Imports; // Type used to map package names to packages. typedef std::map Packages; // Type used to map variables to the function calls that set them. // This is used for initialization dependency analysis. typedef std::map Var_deps; // Type used to map identifiers in the file block to the location // where they were defined. typedef Unordered_map(std::string, Location) File_block_names; // Type used to queue writing a type specific function. struct Specific_type_function { Type* type; Named_type* name; int64_t size; std::string hash_name; Function_type* hash_fntype; std::string equal_name; Function_type* equal_fntype; Specific_type_function(Type* atype, Named_type* aname, int64_t asize, const std::string& ahash_name, Function_type* ahash_fntype, const std::string& aequal_name, Function_type* aequal_fntype) : type(atype), name(aname), size(asize), hash_name(ahash_name), hash_fntype(ahash_fntype), equal_name(aequal_name), equal_fntype(aequal_fntype) { } }; // Recompute init priorities. void recompute_init_priorities(); // Recursive helper used by the routine above. void update_init_priority(Import_init* ii, std::set* visited); // The backend generator. Backend* backend_; // The object used to keep track of file names and line numbers. Linemap* linemap_; // 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_; // The list of names we have seen in the file block. File_block_names file_block_names_; // Mapping from import file names to packages. Imports imports_; // Whether the magic unsafe package was imported. bool imported_unsafe_; // Whether the magic unsafe package was imported by the current file. bool current_file_imported_unsafe_; // Mapping from package names we have seen to packages. This does // not include the package we are compiling. Packages packages_; // The functions named "init", if there are any. std::vector init_functions_; // A mapping from variables to the function calls that initialize // them, if it is not stored in the variable's init or preinit. // This is used for dependency analysis. Var_deps var_deps_; // 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. Import_init_set imported_init_fns_; // The package path used for reflection data. std::string pkgpath_; // The package path to use for a symbol name. std::string pkgpath_symbol_; // The prefix to use for symbols, from the -fgo-prefix option. std::string prefix_; // Whether pkgpath_ has been set. bool pkgpath_set_; // Whether an explicit package path was set by -fgo-pkgpath. bool pkgpath_from_option_; // Whether an explicit prefix was set by -fgo-prefix. bool prefix_from_option_; // The relative import path, from the -fgo-relative-import-path // option. std::string relative_import_path_; // The C header file to write, from the -fgo-c-header option. std::string c_header_; // Whether or not to check for division by zero, from the // -fgo-check-divide-zero option. bool check_divide_by_zero_; // Whether or not to check for division overflow, from the // -fgo-check-divide-overflow option. bool check_divide_overflow_; // Whether we are compiling the runtime package, from the // -fgo-compiling-runtime option. bool compiling_runtime_; // The level of escape analysis debug information to emit, from the // -fgo-debug-escape option. int debug_escape_level_; // A hash value for debug escape analysis, from the // -fgo-debug-escape-hash option. The analysis is run only on // functions with names that hash to the matching value. std::string debug_escape_hash_; // Nil-check size threshhold. int64_t nil_check_size_threshold_; // A list of types to verify. std::vector verify_types_; // A list of interface types defined while parsing. std::vector interface_types_; // Type specific functions to write out. std::vector specific_type_functions_; // Whether we are done writing out specific type functions. bool specific_type_functions_are_written_; // Whether named types have been converted. bool named_types_are_converted_; // A list containing groups of possibly mutually recursive functions to be // considered during escape analysis. std::vector analysis_sets_; // A list of objects to add to the GC roots. std::vector gc_roots_; }; // A block of statements. class Block { public: Block(Block* enclosing, 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* statements() const { return &this->statements_; } // Return the start location. This is normally the location of the // left curly brace which starts the block. 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. 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(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::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 statements_; // Binding contour. Bindings* bindings_; // Location of start of block. Location start_location_; // Location of end of block. Location end_location_; }; // A function. class Function { public: Function(Function_type* type, Named_object*, Block*, Location); // Return the function's type. Function_type* type() const { return this->type_; } // Return the enclosing function if there is one. Named_object* enclosing() const { return this->enclosing_; } // Set the enclosing function. This is used when building thunks // for functions which call recover. void set_enclosing(Named_object* enclosing) { go_assert(this->enclosing_ == NULL); this->enclosing_ = enclosing; } // The result variables. typedef std::vector 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_; } bool is_sink() const { return this->is_sink_; } void set_is_sink() { this->is_sink_ = true; } // Whether the result variables have names. bool results_are_named() const { return this->results_are_named_; } // Return the assembler name. 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 the pragmas for this function. unsigned int pragmas() const { return this->pragmas_; } // Set the pragmas for this function. void set_pragmas(unsigned int pragmas) { this->pragmas_ = pragmas; } // Return the index to use for a nested function. unsigned int next_nested_function_index() { ++this->nested_functions_; return this->nested_functions_; } // Whether this method should not be included in the type // descriptor. bool nointerface() const; // Record that this method should not be included in the type // descriptor. void set_nointerface(); // Record that this function is a stub method created for an unnamed // type. void set_is_unnamed_type_stub_method() { go_assert(this->is_method()); this->is_unnamed_type_stub_method_ = true; } // Return the amount of enclosed variables in this closure. size_t closure_field_count() const { return this->closure_fields_.size(); } // Add a new field to the closure variable. void add_closure_field(Named_object* var, 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. 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(Gogo*, const std::string& label_name, Location); // Add a label reference to a function. ISSUE_GOTO_ERRORS is true // if we should report errors for a goto from the current location // to the label location. Label* add_label_reference(Gogo*, const std::string& label_name, Location, bool issue_goto_errors); // Warn about labels that are defined but not used. void check_labels() const; // Note that a new local type has been added. Return its index. unsigned int new_local_type_index() { return this->local_type_count_++; } // 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; } // Record that this function is a thunk created for a defer // statement that calls the __go_set_defer_retaddr runtime function. void set_calls_defer_retaddr() { this->calls_defer_retaddr_ = true; } // Whether this is a type hash or equality function created by the // compiler. bool is_type_specific_function() { return this->is_type_specific_function_; } // Record that this function is a type hash or equality function // created by the compiler. void set_is_type_specific_function() { this->is_type_specific_function_ = true; } // Mark the function as going into a unique section. void set_in_unique_section() { this->in_unique_section_ = 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 an expression for the function descriptor, given the named // object for this function. This may only be called for functions // without a closure. This will be an immutable struct with one // field that points to the function's code. Expression* descriptor(Gogo*, Named_object*); // Set the descriptor for this function. This is used when a // function declaration is followed by a function definition. void set_descriptor(Expression* descriptor) { go_assert(this->descriptor_ == NULL); this->descriptor_ = descriptor; } // Return the backend representation. Bfunction* get_or_make_decl(Gogo*, Named_object*); // Return the function's decl after it has been built. Bfunction* get_decl() const; // Set the function decl to hold a backend representation of the function // code. void build(Gogo*, Named_object*); // Get the statement that assigns values to this function's result struct. Bstatement* return_value(Gogo*, Named_object*, Location) const; // Get an expression for the variable holding the defer stack. Expression* defer_stack(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*, bool nointerface); // 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, bool* nointerface); private: // Type for mapping from label names to Label objects. typedef Unordered_map(std::string, Label*) Labels; void build_defer_wrapper(Gogo*, Named_object*, Bstatement**, Bstatement**); typedef std::vector > 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. Named_object* 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. Location location_; // Labels defined or referenced in the function. Labels labels_; // The number of local types defined in this function. unsigned int local_type_count_; // The assembler name: this is the name that will be put in the object file. // Set by the go:linkname compiler directive. This is normally empty. std::string asm_name_; // The function descriptor, if any. Expression* descriptor_; // The function decl. Bfunction* 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_; // Pragmas for this function. This is a set of GOPRAGMA bits. unsigned int pragmas_; // Number of nested functions defined within this function. unsigned int nested_functions_; // True if this function is sink-named. No code is generated. bool is_sink_ : 1; // True if the result variables are named. bool results_are_named_ : 1; // True if this function is a stub method created for an unnamed // type. bool is_unnamed_type_stub_method_ : 1; // True if this function calls the predeclared recover function. bool calls_recover_ : 1; // True if this a thunk built for a function which calls recover. bool is_recover_thunk_ : 1; // True if this function already has a recover thunk. bool has_recover_thunk_ : 1; // True if this is a thunk built for a defer statement that calls // the __go_set_defer_retaddr runtime function. bool calls_defer_retaddr_ : 1; // True if this is a function built by the compiler to as a hash or // equality function for some type. bool is_type_specific_function_ : 1; // True if this function should be put in a unique section. This is // turned on for field tracking. bool in_unique_section_ : 1; }; // A snapshot of the current binding state. class Bindings_snapshot { public: Bindings_snapshot(const Block*, Location); // Report any errors appropriate for a goto from the current binding // state of B to this one. void check_goto_from(const Block* b, Location); // Report any errors appropriate for a goto from this binding state // to the current state of B. void check_goto_to(const Block* b); private: bool check_goto_block(Location, const Block*, const Block*, size_t*); void check_goto_defs(Location, const Block*, size_t, size_t); // The current block. const Block* block_; // The number of names currently defined in each open block. // Element 0 is this->block_, element 1 is // this->block_->enclosing(), etc. std::vector counts_; // The location where this snapshot was taken. Location location_; }; // A function declaration. class Function_declaration { public: Function_declaration(Function_type* fntype, Location location) : fntype_(fntype), location_(location), asm_name_(), descriptor_(NULL), fndecl_(NULL), pragmas_(0) { } Function_type* type() const { return this->fntype_; } Location location() const { return this->location_; } // Return whether this function declaration is a method. bool is_method() const; 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 the pragmas for this function. unsigned int pragmas() const { return this->pragmas_; } // Set the pragmas for this function. void set_pragmas(unsigned int pragmas) { this->pragmas_ = pragmas; } // Whether this method should not be included in the type // descriptor. bool nointerface() const; // Record that this method should not be included in the type // descriptor. void set_nointerface(); // Return an expression for the function descriptor, given the named // object for this function. This may only be called for functions // without a closure. This will be an immutable struct with one // field that points to the function's code. Expression* descriptor(Gogo*, Named_object*); // Return true if we have created a descriptor for this declaration. bool has_descriptor() const { return this->descriptor_ != NULL; } // Return a backend representation. Bfunction* get_or_make_decl(Gogo*, Named_object*); // If there is a descriptor, build it into the backend // representation. void build_backend_descriptor(Gogo*); // Export a function declaration. void export_func(Export* exp, const std::string& name) const { Function::export_func_with_type(exp, name, this->fntype_, this->is_method() && this->nointerface()); } // Check that the types used in this declaration's signature are defined. void check_types() const; private: // The type of the function. Function_type* fntype_; // The location of the declaration. 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 descriptor, if any. Expression* descriptor_; // The function decl if needed. Bfunction* fndecl_; // Pragmas for this function. This is a set of GOPRAGMA bits. unsigned int pragmas_; }; // A variable. class Variable { public: Variable(Type*, Expression*, bool is_global, bool is_parameter, bool is_receiver, Location); // Get the type of the variable. Type* type(); Type* type() const; // Return whether the type is defined yet. bool has_type() const; // 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 a closure (static chain) parameter. bool is_closure() const { return this->is_closure_; } // Change this parameter to be a closure. void set_is_closure() { this->is_closure_ = true; } // 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->escapes_ && !this->is_global_; } // Note that something takes the address of this variable. void set_address_taken() { this->is_address_taken_ = true; } // Return whether the address is taken but does not escape. bool is_non_escaping_address_taken() const { return this->is_non_escaping_address_taken_; } // Note that something takes the address of this variable such that // the address does not escape the function. void set_non_escaping_address_taken() { this->is_non_escaping_address_taken_ = true; } // Return whether this variable escapes the function it is declared in. bool escapes() { return this->escapes_; } // Note that this variable does not escape the function it is declared in. void set_does_not_escape() { this->escapes_ = false; } // Get the source location of the variable's declaration. 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; } // Return whether the variable has been used. bool is_used() const { return this->is_used_; } // Mark that the variable has been used. void set_is_used() { this->is_used_ = true; } // Clear the initial value; used for error handling and write barriers. 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*, Statement_inserter*); // Flatten the initialization expression after ordering evaluations. void flatten_init_expression(Gogo*, Named_object*, Statement_inserter*); // 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; } // TRUE if this variable was created for a type switch clause. bool is_type_switch_var() const { return this->is_type_switch_var_; } // Note that this variable was created for a type switch clause. void set_is_type_switch_var() { this->is_type_switch_var_ = true; } // Mark the variable as going into a unique section. void set_in_unique_section() { go_assert(this->is_global_); this->in_unique_section_ = true; } // Return the top-level declaration for this variable. Statement* toplevel_decl() { return this->toplevel_decl_; } // Set the top-level declaration for this variable. Only used for local // variables void set_toplevel_decl(Statement* s) { go_assert(!this->is_global_ && !this->is_parameter_ && !this->is_receiver_); this->toplevel_decl_ = s; } // Traverse the initializer expression. int traverse_expression(Traverse*, unsigned int traverse_mask); // Determine the type of the variable if necessary. void determine_type(); // 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. This may only // be called if has_pre_init() returns false. Bexpression* get_init(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. Bstatement* get_init_block(Gogo*, Named_object* function, Bvariable* 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. 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 a closure parameter. bool is_closure_ : 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 this variable is ever referenced. bool is_used_ : 1; // Whether something takes the address of this variable. For a // local variable this implies that the variable has to be on the // heap if it escapes from its function. bool is_address_taken_ : 1; // Whether something takes the address of this variable such that // the address does not escape the function. bool is_non_escaping_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 we have flattened the initialization expression. bool init_is_flattened_ : 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; // True if this variable should be put in a unique section. This is // used for field tracking. bool in_unique_section_ : 1; // Whether this variable escapes the function it is created in. This is // true until shown otherwise. bool escapes_ : 1; // The top-level declaration for this variable. Only used for local // variables. Must be a Temporary_statement if not NULL. Statement* toplevel_decl_; }; // 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, Location location) : type_(type), function_(function), index_(index), location_(location), backend_(NULL), is_address_taken_(false), is_non_escaping_address_taken_(false), escapes_(true) { } // 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. 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; } // Return whether the address is taken but does not escape. bool is_non_escaping_address_taken() const { return this->is_non_escaping_address_taken_; } // Note that something takes the address of this variable such that // the address does not escape the function. void set_non_escaping_address_taken() { this->is_non_escaping_address_taken_ = true; } // Return whether this variable escapes the function it is declared in. bool escapes() { return this->escapes_; } // Note that this variable does not escape the function it is declared in. void set_does_not_escape() { this->escapes_ = false; } // Whether this variable should live in the heap. bool is_in_heap() const { return this->is_address_taken_ && this->escapes_; } // 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. Location location_; // Backend representation. Bvariable* backend_; // Whether something takes the address of this variable. bool is_address_taken_; // Whether something takes the address of this variable such that // the address does not escape the function. bool is_non_escaping_address_taken_; // Whether this variable escapes the function it is created in. This is // true until shown otherwise. bool escapes_; }; // 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, Location location) : type_(type), expr_(expr), iota_value_(iota_value), location_(location), lowering_(false), is_sink_(false), bconst_(NULL) { } Type* type() const { return this->type_; } void set_type(Type* t); Expression* expr() const { return this->expr_; } int iota_value() const { return this->iota_value_; } 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; } bool is_sink() const { return this->is_sink_; } void set_is_sink() { this->is_sink_ = true; } // 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**); // Get the backend representation of the constant value. Bexpression* get_backend(Gogo*, Named_object*); 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. Location location_; // Whether we are currently lowering this constant. bool lowering_; // Whether this constant is blank named and needs only type checking. bool is_sink_; // The backend representation of the constant value. Bexpression* bconst_; }; // A type declaration. class Type_declaration { public: Type_declaration(Location location) : location_(location), in_function_(NULL), in_function_index_(0), methods_(), issued_warning_(false) { } // Return the location. 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(unsigned int* pindex) { *pindex = this->in_function_index_; return this->in_function_; } // Set the function in which this type is declared. void set_in_function(Named_object* f, unsigned int index) { this->in_function_ = f; this->in_function_index_ = index; } // 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, Package*, Function_type* type, Location location); // Add an already created object as a method. void add_existing_method(Named_object* no) { this->methods_.push_back(no); } // Return whether any methods were defined. bool has_methods() const; // Return the methods. const std::vector* methods() const { return &this->methods_; } // 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: // The location of the type declaration. 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_; // The index of this type in IN_FUNCTION_. unsigned int in_function_index_; // Methods defined before the type is defined. std::vector 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(Location location) : location_(location), real_named_object_(NULL) { } // Return the location where this name was first seen. 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. 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 erroneous name. This indicates a parse error, to avoid // later errors about undefined references. NAMED_OBJECT_ERRONEOUS, // 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_erroneous() const { return this->classification_ == NAMED_OBJECT_ERRONEOUS; } 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_erroneous_name(const std::string& name) { return new Named_object(name, NULL, NAMED_OBJECT_ERRONEOUS); } static Named_object* make_unknown_name(const std::string& name, 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*, Location); static Named_object* make_type_declaration(const std::string&, const Package*, 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*, 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. Location location() const; // Convert a variable to the backend representation. Bvariable* get_backend_variable(Gogo*, Named_object* function); // Return the external identifier for this object. std::string get_id(Gogo*); // Get the backend representation of this object. void get_backend(Gogo*, std::vector&, std::vector&, std::vector&); // 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; // Mark this named object as an invalid redefinition of another object. void set_is_redefinition() { this->is_redefinition_ = true; } // Return whether or not this object is a invalid redefinition of another // object. bool is_redefinition() const { return this->is_redefinition_; } 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_; // True if this object is an invalid redefinition of another object. bool is_redefinition_; }; // 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 erroneous name. Named_object* add_erroneous_name(const std::string& name) { return this->add_named_object(Named_object::make_erroneous_name(name)); } // Add an unknown name. Named_object* add_unknown_name(const std::string& name, 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, 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, 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, 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(Gogo*); // 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*); // Mark all variables as used. This is used for some types of parse // error. void mark_locals_used(); // 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::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_objects_; // The mapping from names to objects. Contour bindings_; }; // A label. class Label { public: Label(const std::string& name) : name_(name), location_(Linemap::unknown_location()), snapshot_(NULL), refs_(), is_used_(false), blabel_(NULL), depth_(DEPTH_UNKNOWN) { } // 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 !Linemap::is_unknown_location(this->location_); } // 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 whether this label is looping. bool looping() const { return this->depth_ == DEPTH_LOOPING; } // Set this label as looping. void set_looping() { this->depth_ = DEPTH_LOOPING; } // Return whether this label is nonlooping. bool nonlooping() const { return this->depth_ == DEPTH_NONLOOPING; } // Set this label as nonlooping. void set_nonlooping() { this->depth_ = DEPTH_NONLOOPING; } // Return the location of the definition. Location location() const { return this->location_; } // Return the bindings snapshot. Bindings_snapshot* snapshot() const { return this->snapshot_; } // Add a snapshot of a goto which refers to this label. void add_snapshot_ref(Bindings_snapshot* snapshot) { go_assert(Linemap::is_unknown_location(this->location_)); this->refs_.push_back(snapshot); } // Return the list of snapshots of goto statements which refer to // this label. const std::vector& refs() const { return this->refs_; } // Clear the references. void clear_refs(); // Define the label at LOCATION with the given bindings snapshot. void define(Location location, Bindings_snapshot* snapshot) { if (this->is_dummy_label()) return; go_assert(Linemap::is_unknown_location(this->location_) && this->snapshot_ == NULL); this->location_ = location; this->snapshot_ = snapshot; } // 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*, Location location); // Return a dummy label, representing any instance of the blank label. static Label* create_dummy_label(); // Return TRUE if this is a dummy label. bool is_dummy_label() const { return this->name_ == "_"; } // A classification of a label's looping depth. enum Loop_depth { DEPTH_UNKNOWN, // A label never jumped to. DEPTH_NONLOOPING, // A label jumped to. DEPTH_LOOPING }; 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. Location location_; // A snapshot of the set of bindings defined at this label, used to // issue errors about invalid goto statements. Bindings_snapshot* snapshot_; // A list of snapshots of goto statements which refer to this label. std::vector refs_; // Whether the label has been used. bool is_used_; // The backend representation. Blabel* blabel_; // The looping depth of this label, for escape analysis. Loop_depth depth_; }; // An unnamed label. These are used when lowering loops. class Unnamed_label { public: Unnamed_label(Location location) : location_(location), derived_from_(NULL), blabel_(NULL) { } // Get the location where the label is defined. Location location() const { return this->location_; } // Set the location where the label is defined. void set_location(Location location) { this->location_ = location; } // Get the top level statement this unnamed label is derived from. Statement* derived_from() const { return this->derived_from_; } // Set the top level statement this unnamed label is derived from. void set_derived_from(Statement* s) { this->derived_from_ = s; } // 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*, Location location); private: // Return the backend representation. Blabel* get_blabel(Translate_context*); // The location where the label is defined. Location location_; // The top-level statement this unnamed label was derived/lowered from. // This is NULL is this label is not the top-level of a lowered statement. Statement* derived_from_; // The backend representation of this label. Blabel* blabel_; }; // An alias for an imported package. class Package_alias { public: Package_alias(Location location) : location_(location), used_(0) { } // The location of the import statement. Location location() { return this->location_; } // How many symbols from the package were used under this alias. size_t used() const { return this->used_; } // Note that some symbol was used under this alias. void note_usage() { this->used_++; } private: // The location of the import statement. Location location_; // The amount of times some name from this package was used under this alias. size_t used_; }; // An imported package. class Package { public: Package(const std::string& pkgpath, const std::string& pkgpath_symbol, Location location); // Get the package path used for all symbols exported from this // package. const std::string& pkgpath() const { return this->pkgpath_; } // Return the package path to use for a symbol name. std::string pkgpath_symbol() const; // Set the package path symbol. void set_pkgpath_symbol(const std::string&); // Return the location of the most recent import statement. Location location() const { return this->location_; } // Return whether we know the name of this package yet. bool has_package_name() const { return !this->package_name_.empty(); } // The name that this package uses in its package clause. This may // be different from the name in the associated Named_object if the // import statement used an alias. const std::string& package_name() const { go_assert(!this->package_name_.empty()); return this->package_name_; } // Return the bindings. Bindings* bindings() { return this->bindings_; } // Type used to map import names to package aliases. typedef std::map Aliases; // Return the set of package aliases. const Aliases& aliases() const { return this->aliases_; } // Note that some symbol from this package was used and qualified by ALIAS. // For dot imports, the ALIAS should be ".PACKAGE_NAME". void note_usage(const std::string& alias) const; // Note that USAGE might be a fake usage of this package. void note_fake_usage(Expression* usage) const { this->fake_uses_.insert(usage); } // Forget a given USAGE of this package. void forget_usage(Expression* usage) const; // Clear the used field for the next file. void clear_used(); // 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 name of the package. void set_package_name(const std::string& name, Location); // Set the location of the package. This is used to record the most // recent import location. void set_location(Location location) { this->location_ = location; } // Add a package name as an ALIAS for this package. Package_alias* add_alias(const std::string& alias, 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, 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, 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, Location loc) { return this->bindings_->add_function_declaration(name, this, type, loc); } // Determine types of constants. void determine_types(); private: // The package path for type reflection data. std::string pkgpath_; // The package path for symbol names. std::string pkgpath_symbol_; // The name that this package uses in the package clause. This may // be the empty string if it is not yet known. std::string package_name_; // The names in this package. Bindings* bindings_; // The location of the most recent import statement. Location location_; // The set of aliases associated with this package. Aliases aliases_; // A set of possibly fake uses of this package. This is mutable because we // can track fake uses of a package even if we have a const pointer to it. mutable std::set fake_uses_; }; // 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; static const unsigned int traverse_func_declarations = 0x80; 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*); // If traverse_func_declarations is set in the mask, this is called // for every function declarations in the tree. virtual int function_declaration(Named_object*); private: // A hash table for types we have seen during this traversal. Note // that this uses the default hash functions for pointers rather // than Type_hash_identical and Type_identical. This is because for // traversal we care about seeing a specific type structure. If // there are two separate instances of identical types, we want to // traverse both. typedef Unordered_set(const Type*) 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_; }; // A class which makes it easier to insert new statements before the // current statement during a traversal. class Statement_inserter { public: // Empty constructor. Statement_inserter() : block_(NULL), pindex_(NULL), gogo_(NULL), var_(NULL) { } // Constructor for a statement in a block. Statement_inserter(Block* block, size_t *pindex) : block_(block), pindex_(pindex), gogo_(NULL), var_(NULL) { } // Constructor for a global variable. Statement_inserter(Gogo* gogo, Variable* var) : block_(NULL), pindex_(NULL), gogo_(gogo), var_(var) { go_assert(var->is_global()); } // We use the default copy constructor and assignment operator. // Insert S before the statement we are traversing, or before the // initialization expression of a global variable. void insert(Statement* s); private: // The block that the statement is in. Block* block_; // The index of the statement that we are traversing. size_t* pindex_; // The IR, needed when looking at an initializer expression for a // global variable. Gogo* gogo_; // The global variable, when looking at an initializer expression. Variable* var_; }; // 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; // Division by zero. static const int RUNTIME_ERROR_DIVISION_BY_ZERO = 10; // Go statement with nil function. static const int RUNTIME_ERROR_GO_NIL = 11; // 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)