Viewing file: gogo.h (108.1 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
// 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_equal; 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 Export_function_body; class Import; class Import_function_body; 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.
// The name of some backend object. Backend objects have a // user-visible name and an assembler name. The user visible name // might include arbitrary Unicode characters. The assembler name // will not.
class Backend_name { public: Backend_name() : prefix_(NULL), components_(), count_(0), suffix_(), is_asm_name_(false), is_non_identifier_(false) {}
// Set the prefix. Prefixes are always constant strings. void set_prefix(const char* p) { go_assert(this->prefix_ == NULL && !this->is_asm_name_); this->prefix_ = p; }
// Set the suffix. void set_suffix(const std::string& s) { go_assert(this->suffix_.empty() && !this->is_asm_name_); this->suffix_ = s; }
// Append to the suffix. void append_suffix(const std::string& s) { if (this->is_asm_name_) this->components_[0].append(s); else this->suffix_.append(s); }
// Add a component. void add(const std::string& c) { go_assert(this->count_ < Backend_name::max_components && !this->is_asm_name_); this->components_[this->count_] = c; ++this->count_; }
// Set an assembler name specified by the user. This overrides both // the user-visible name and the assembler name. No further // encoding is applied. void set_asm_name(const std::string& n) { go_assert(this->prefix_ == NULL && this->count_ == 0 && this->suffix_.empty() && !this->is_asm_name_); this->components_[0] = n; this->is_asm_name_ = true; }
// Whether some component includes some characters that can't appear // in an identifier. bool is_non_identifier() const { return this->is_non_identifier_; }
// Record that some component includes some character that can't // appear in an identifier. void set_is_non_identifier() { this->is_non_identifier_ = true; }
// Get the user visible name. std::string name() const;
// Get the assembler name. This may be the same as the user visible // name. std::string asm_name() const;
// Get an optional assembler name: if it would be the same as the // user visible name, this returns the empty string. std::string optional_asm_name() const;
private: // The maximum number of components. static const int max_components = 4;
// An optional prefix that does not require encoding. const char *prefix_; // Up to four components. The name will include these components // separated by dots. Each component will be underscore-encoded // (see the long comment near the top of names.cc). std::string components_[Backend_name::max_components]; // Number of components. int count_; // An optional suffix that does not require encoding. std::string suffix_; // True if components_[0] is an assembler name specified by the user. bool is_asm_name_; // True if some component includes some character that can't // normally appear in an identifier. bool is_non_identifier_; };
// 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<std::string>& precursors() const { return this->precursor_functions_; }
// Whether this is a dummy init, which is used only to record transitive import. bool is_dummy() const { return this->init_name_[0] == '~'; }
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<std::string> 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<Import_init*, Import_init_lt> { };
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, append the // appropriate version of the name to the result string. static void append_possibly_hidden_name(std::string *result, const std::string& name);
// 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] == '.') || (name[0] == '_' && name.length() == 1); }
// Helper used when adding parameters (including receiver param) to the // bindings of a function. If the specified parameter name is empty or // corresponds to the sink name, param name is replaced with a new unique // name. PNAME is the address of a string containing the parameter variable // name to be checked/updated; TAG is a descriptive tag to be used in // manufacturing the new unique name, and COUNT is the address of a counter // holding the number of params renamed so far with the tag in question. static void rename_if_empty(std::string* pname, const char* tag, unsigned* count);
// Convert a pkgpath into a string suitable for a symbol static std::string pkgpath_for_symbol(const std::string& pkgpath);
// Compute a hash code for a string, given a seed. static unsigned int hash_string(const std::string&, unsigned int);
// 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; }
// Read an embedcfg file. void read_embedcfg(const char* filename);
// Build an initializer for a variable with a go:embed directive. Expression* initializer_for_embeds(Type*, const std::vector<std::string>*, Location);
// 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 whether to output optimization diagnostics. bool debug_optimization() const { return this->debug_optimization_; }
// Set the option to output optimization diagnostics. void set_debug_optimization(bool b) { this->debug_optimization_ = b; }
// Dump to stderr for debugging void debug_dump();
// 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; }
// Return whether runtime.eqtype calls are needed when comparing // type descriptors. bool need_eqtype() const { return this->need_eqtype_; }
// Set if calls to runtime.eqtype are needed. void set_need_eqtype(bool b) { this->need_eqtype_ = b; }
// 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);
// Add the unsafe bindings to the unsafe package. void add_unsafe_bindings(Package*);
// 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 function declaration to the list of functions we may want // to inline. void add_imported_inlinable_function(Named_object*);
// Add a function to the list of functions that we do want to // inline. void add_imported_inline_function(Named_object* no) { this->imported_inline_functions_.push_back(no); }
// 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<std::vector<Named_object*>, bool> Analysis_set;
// Add a GROUP of possibly RECURSIVE functions to the Analysis_set for this // package. void add_analysis_set(const std::vector<Named_object*>& 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. // If EXT_NAME is the empty string, GO_NAME is unchanged, but the // symbol is made publicly visible. 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_; }
// Return whether the current file imported the embed package. bool current_file_imported_embed() const { return this->current_file_imported_embed_; }
// 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 hash function to be written out. This // is used when a type-specific hash function is needed when not at // top level. void queue_hash_function(Type* type, int64_t size, Backend_name*, Function_type* hash_fntype);
// Queue up a type-specific equal function to be written out. This // is used when a type-specific equal function is needed when not at // top level. void queue_equal_function(Type* type, Named_type* name, int64_t size, Backend_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); }
// Add a type to the descriptor list. void add_type_descriptor(Type* type) { this->type_descriptors_.push_back(type); }
// 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();
// Finalize the method list for one type. void finalize_methods_for_type(Type*);
// 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();
// Remove deadcode. void remove_deadcode();
// Make implicit type conversions explicit. void add_conversions();
// Make implicit type conversions explicit in a block. void add_conversions_in_block(Block*);
// 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();
// Turn short-cut operators into explicit if statements in a block. void remove_shortcuts_in_block(Block*);
// Use temporary variables to force order of evaluation. void order_evaluations();
// Order evaluations in a block. void order_block(Block*);
// 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, Unordered_set(const Named_object*)*);
// Return whether EXPR is the address of a variable that can be set // without a write barrier. That is, if this returns true, then an // assignment to *EXPR does not require a write barrier. bool is_nonwb_pointer(Expression* expr, Unordered_set(const Named_object*)*);
// 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);
// Return a statement that tests whether write barriers are enabled // and executes either the efficient code (WITHOUT) or the write // barrier function call (WITH), depending. Statement* check_write_barrier(Block*, Statement* without, Statement* with);
// Flatten parse tree. void flatten();
// 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();
// 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 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);
// Get the backend name to use for an exported function, a method, // or a function/method declaration. void function_backend_name(const std::string& go_name, const Package*, const Type* receiver, Backend_name*);
// Return the name to use for a function descriptor. void function_descriptor_backend_name(Named_object*, Backend_name*);
// Return the name to use for a generated stub method. std::string stub_method_name(const Package*, const std::string& method_name);
// Get the backend name of the hash function for TYPE. void hash_function_name(const Type*, Backend_name*);
// Get the backend name of the equal function for TYPE. void equal_function_name(const Type*, const Named_type*, Backend_name*);
// Get the backend name to use for a global variable. void global_var_backend_name(const std::string& go_name, const Package*, Backend_name*);
// 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 dummy init function, which is not a real // function but only for tracking transitive import. std::string dummy_init_fn_name();
// Return the package path symbol from an init function name, which // can be a real init function or a dummy one. std::string pkgpath_symbol_from_init_fn_name(std::string);
// Get the backend name for a type descriptor symbol. void type_descriptor_backend_name(const Type*, Named_type*, Backend_name*);
// Return the name of the type descriptor list symbol of a package. // The argument is an encoded pkgpath, as with pkgpath_symbol. std::string type_descriptor_list_symbol(const std::string&);
// Return the name of the list of all type descriptor lists. std::string typelists_symbol();
// 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);
// If NAME is a special name used as a Go identifier, return the // position within the string where the special part of the name // occurs. static size_t special_name_pos(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<Block*> blocks; };
// The stack of functions. typedef std::vector<Open_function> 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<Bstatement*>&, Bfunction* bfunction);
// Register variables with the garbage collector. void register_gc_vars(const std::vector<Named_object*>&, std::vector<Bstatement*>&, Bfunction* init_bfunction);
// Build the list of type descriptors. void build_type_descriptor_list();
// Register the type descriptors with the runtime. void register_type_descriptors(std::vector<Bstatement*>&, Bfunction* init_bfunction);
void propagate_writebarrierrec();
Named_object* write_barrier_variable();
static bool is_digits(const std::string&);
// Type used to map go:embed patterns to a list of files. typedef Unordered_map(std::string, std::vector<std::string>) Embed_patterns;
// Type used to map go:embed file names to their full path. typedef Unordered_map(std::string, std::string) Embed_files;
// 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 variables to the function calls that set them. // This is used for initialization dependency analysis. typedef std::map<Variable*, Named_object*> 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 { enum Specific_type_function_kind { SPECIFIC_HASH, SPECIFIC_EQUAL };
Type* type; Named_type* name; int64_t size; Specific_type_function_kind kind; Backend_name bname; Function_type* fntype;
Specific_type_function(Type* atype, Named_type* aname, int64_t asize, Specific_type_function_kind akind, Backend_name* abname, Function_type* afntype) : type(atype), name(aname), size(asize), kind(akind), bname(*abname), fntype(afntype) { } };
// Recompute init priorities. void recompute_init_priorities();
// Recursive helper used by the routine above. void update_init_priority(Import_init* ii, std::set<const Import_init *>* 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_; // Whether the embed package was imported by the current file. bool current_file_imported_embed_; // 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<Named_object*> 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_; // Patterns from an embedcfg file. Embed_patterns embed_patterns_; // Mapping from file to full path from an embedcfg file. Embed_files embed_files_; // 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_; // Whether to output optimization diagnostics, from the // -fgo-debug-optimization option. bool debug_optimization_; // Nil-check size threshhold. int64_t nil_check_size_threshold_; // Whether runtime.eqtype calls are needed when comparing type // descriptors. bool need_eqtype_; // A list of types to verify. std::vector<Type*> verify_types_; // A list of interface types defined while parsing. std::vector<Interface_type*> interface_types_; // Type specific functions to write out. std::vector<Specific_type_function*> 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_set> analysis_sets_; // A list of objects to add to the GC roots. std::vector<Expression*> gc_roots_; // A list of type descriptors that we need to register. std::vector<Type*> type_descriptors_; // A list of function declarations with imported bodies that we may // want to inline. std::vector<Named_object*> imported_inlinable_functions_; // A list of functions that we want to inline. These will be sent // to the backend. std::vector<Named_object*> imported_inline_functions_; };
// 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<Statement*>* 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;
// Write the export data for the block's statements to the string. void export_block(Export_function_body*);
// Turn exported block data into a block. static bool import_block(Block*, Import_function_body*, Location);
// 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. 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<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_; }
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; }
// Mark this symbol as exported by a linkname directive. void set_is_exported_by_linkname() { this->is_exported_by_linkname_ = true; }
// 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; }
// Return whether this function should be exported for inlining. bool export_for_inlining() const { return this->export_for_inlining_; }
// Mark the function to be exported for inlining. void set_export_for_inlining() { this->export_for_inlining_ = true; }
// Return whether this function is inline only. bool is_inline_only() const { return this->is_inline_only_; }
// Mark the function as inline only: the body should not be emitted // if it is not inlined. void set_is_inline_only() { this->is_inline_only_ = true; }
// Report whether the function is referenced by an inline body. bool is_referenced_by_inline() const { return this->is_referenced_by_inline_; }
// Mark the function as referenced by an inline body. void set_is_referenced_by_inline() { this->is_referenced_by_inline_ = true; }
// Set the receiver type. This is used to remove aliases. void set_receiver_type(Type* rtype);
// 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 the backend name of this function. void backend_name(Gogo*, Named_object*, Backend_name*);
// Get an expression for the variable holding the defer stack. Expression* defer_stack(Location);
// Export the function. void export_func(Export*, const Named_object*) const;
// Export a function with a type. static void export_func_with_type(Export*, const Named_object*, const Function_type*, Results*, bool nointerface, const std::string& asm_name, Block* block, Location);
// Import a function. Reports whether the import succeeded. static bool import_func(Import*, std::string* pname, Package** pkg, bool* is_exported, Typed_identifier** receiver, Typed_identifier_list** pparameters, Typed_identifier_list** presults, bool* is_varargs, bool* nointerface, std::string* asm_name, std::string* body);
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<std::pair<Named_object*, 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. 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; // True if we should export the body of this function for // cross-package inlining. bool export_for_inlining_ : 1; // True if this function is inline only: if it should not be emitted // if it is not inlined. bool is_inline_only_ : 1; // True if this function is referenced from an inlined body that // will be put into the export data. bool is_referenced_by_inline_ : 1; // True if we should make this function visible to other packages // because of a go:linkname directive. bool is_exported_by_linkname_ : 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<size_t> 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), imported_body_(), is_on_inlinable_list_(false) { }
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();
// Whether we have an imported function body. bool has_imported_body() const { return !this->imported_body_.empty(); }
// Record the imported body of this function. void set_imported_body(Import* imp, const std::string& imported_body) { this->imp_ = imp; this->imported_body_ = imported_body; }
// Whether this declaration is on the list of inlinable functions. bool is_on_inlinable_list() const { return this->is_on_inlinable_list_; }
// Set that this function is on the list of inlinable functions. void set_is_on_inlinable_list() { this->is_on_inlinable_list_ = true; }
// Set the receiver type. This is used to remove aliases. void set_receiver_type(Type* rtype);
// Import the function body, creating a function. void import_function_body(Gogo*, Named_object*);
// 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*);
// Get the backend name of this function declaration. void backend_name(Gogo*, Named_object*, Backend_name*);
// Export a function declaration. void export_func(Export* exp, const Named_object* no) const { Function::export_func_with_type(exp, no, this->fntype_, NULL, this->is_method() && this->nointerface(), this->asm_name_, NULL, this->location_); }
// 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_; // Importer for function body if imported from a different package. Import* imp_; // Export data for function body if imported from a different package. std::string imported_body_; // Whether this declaration is already on the list of inlinable functions. bool is_on_inlinable_list_; };
// 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_; }
// Return whether this is a global sink variable, created only to // run an initializer. bool is_global_sink() const { return this->is_global_sink_; }
// Record that this is a global sink variable. void set_is_global_sink() { go_assert(this->is_global_); this->is_global_sink_ = true; }
// 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_; }
// 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; }
// 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; }
// Mark the variable as referenced by an inline body. void set_is_referenced_by_inline() { go_assert(this->is_global_); this->is_referenced_by_inline_ = true; }
// Attach any go:embed comments for this variable. void set_embeds(std::vector<std::string>* embeds) { go_assert(this->is_global_ && this->init_ == NULL && this->preinit_ == NULL); this->embeds_ = embeds; }
// 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 Named_object*) const;
// Import a variable. Reports whether the import succeeded. static bool import_var(Import*, std::string* pname, Package** pkg, bool* is_exported, 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_; // Any associated go:embed comments. std::vector<std::string>* embeds_; // 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 is a global sink variable created to run an // initializer. bool is_global_sink_ : 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; // True if this variable is referenced from an inlined body that // will be put into the export data. bool is_referenced_by_inline_ : 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) { }
// 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; }
// 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. 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_; };
// 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<Named_object*>* 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<Named_object*> 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;
// Traverse a Named_object. int traverse(Traverse*, bool is_global);
// Convert a variable to the backend representation. Bvariable* get_backend_variable(Gogo*, Named_object* function);
// Get the backend representation of this object. void get_backend(Gogo*, std::vector<Bexpression*>&, std::vector<Btype*>&, std::vector<Bfunction*>&);
// 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<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; }
// Dump to stderr for debugging void debug_dump();
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_(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<Bindings_snapshot*>& 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<Bindings_snapshot*> 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() const { return this->bindings_; }
// Type used to map import names to package aliases. typedef std::map<std::string, Package_alias*> 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<Expression*> 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_; };
// This class looks for interface types to finalize methods of inherited // interfaces.
class Finalize_methods : public Traverse { public: Finalize_methods(Gogo* gogo) : Traverse(traverse_types), gogo_(gogo) { }
int type(Type*);
private: Gogo* gogo_; };
// A class which makes it easier to insert new statements before the // current statement during a traversal.
class Statement_inserter { public: typedef Unordered_set(Statement*) Statements;
// Empty constructor. Statement_inserter() : block_(NULL), pindex_(NULL), gogo_(NULL), var_(NULL), statements_added_(NULL) { }
// Constructor for a statement in a block. Statement_inserter(Block* block, size_t *pindex, Statements *added = NULL) : block_(block), pindex_(pindex), gogo_(NULL), var_(NULL), statements_added_(added) { }
// Constructor for a global variable. Statement_inserter(Gogo* gogo, Variable* var, Statements *added = NULL) : block_(NULL), pindex_(NULL), gogo_(gogo), var_(var), statements_added_(added) { 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_; // If non-null, a set to record new statements inserted (non-owned). Statements* statements_added_; };
// 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_; };
// 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();
// For use in the debugger extern void debug_go_gogo(Gogo*); extern void debug_go_named_object(Named_object*); extern void debug_go_bindings(Bindings*);
#endif // !defined(GO_GOGO_H)
|