|  | // statements.h -- Go frontend statements.     -*- C++ -*- | 
|  |  | 
|  | // Copyright 2009 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | #ifndef GO_STATEMENTS_H | 
|  | #define GO_STATEMENTS_H | 
|  |  | 
|  | #include "operator.h" | 
|  |  | 
|  | class Gogo; | 
|  | class Traverse; | 
|  | class Statement_inserter; | 
|  | class Block; | 
|  | class Function; | 
|  | class Unnamed_label; | 
|  | class Export_function_body; | 
|  | class Import_function_body; | 
|  | class Assignment_statement; | 
|  | class Temporary_statement; | 
|  | class Variable_declaration_statement; | 
|  | class Expression_statement; | 
|  | class Block_statement; | 
|  | class Return_statement; | 
|  | class Thunk_statement; | 
|  | class Defer_statement; | 
|  | class Goto_statement; | 
|  | class Goto_unnamed_statement; | 
|  | class Label_statement; | 
|  | class Unnamed_label_statement; | 
|  | class If_statement; | 
|  | class For_statement; | 
|  | class For_range_statement; | 
|  | class Switch_statement; | 
|  | class Type_switch_statement; | 
|  | class Send_statement; | 
|  | class Select_statement; | 
|  | class Variable; | 
|  | class Named_object; | 
|  | class Label; | 
|  | class Translate_context; | 
|  | class Expression; | 
|  | class Expression_list; | 
|  | class Struct_type; | 
|  | class Call_expression; | 
|  | class Map_index_expression; | 
|  | class Receive_expression; | 
|  | class Case_clauses; | 
|  | class Type_case_clauses; | 
|  | class Select_clauses; | 
|  | class Typed_identifier_list; | 
|  | class Bexpression; | 
|  | class Bstatement; | 
|  | class Bvariable; | 
|  | class Ast_dump_context; | 
|  |  | 
|  | // This class is used to traverse assignments made by a statement | 
|  | // which makes assignments. | 
|  |  | 
|  | class Traverse_assignments | 
|  | { | 
|  | public: | 
|  | Traverse_assignments() | 
|  | { } | 
|  |  | 
|  | virtual ~Traverse_assignments() | 
|  | { } | 
|  |  | 
|  | // This is called for a variable initialization. | 
|  | virtual void | 
|  | initialize_variable(Named_object*) = 0; | 
|  |  | 
|  | // This is called for each assignment made by the statement.  PLHS | 
|  | // points to the left hand side, and PRHS points to the right hand | 
|  | // side.  PRHS may be NULL if there is no associated expression, as | 
|  | // in the bool set by a non-blocking receive. | 
|  | virtual void | 
|  | assignment(Expression** plhs, Expression** prhs) = 0; | 
|  |  | 
|  | // This is called for each expression which is not passed to the | 
|  | // assignment function.  This is used for some of the statements | 
|  | // which assign two values, for which there is no expression which | 
|  | // describes the value.  For ++ and -- the value is passed to both | 
|  | // the assignment method and the rhs method.  IS_STORED is true if | 
|  | // this value is being stored directly.  It is false if the value is | 
|  | // computed but not stored.  IS_LOCAL is true if the value is being | 
|  | // stored in a local variable or this is being called by a return | 
|  | // statement. | 
|  | virtual void | 
|  | value(Expression**, bool is_stored, bool is_local) = 0; | 
|  | }; | 
|  |  | 
|  | // A single statement. | 
|  |  | 
|  | class Statement | 
|  | { | 
|  | public: | 
|  | // The types of statements. | 
|  | enum Statement_classification | 
|  | { | 
|  | STATEMENT_ERROR, | 
|  | STATEMENT_VARIABLE_DECLARATION, | 
|  | STATEMENT_TEMPORARY, | 
|  | STATEMENT_ASSIGNMENT, | 
|  | STATEMENT_EXPRESSION, | 
|  | STATEMENT_BLOCK, | 
|  | STATEMENT_GO, | 
|  | STATEMENT_DEFER, | 
|  | STATEMENT_RETURN, | 
|  | STATEMENT_BREAK_OR_CONTINUE, | 
|  | STATEMENT_GOTO, | 
|  | STATEMENT_GOTO_UNNAMED, | 
|  | STATEMENT_LABEL, | 
|  | STATEMENT_UNNAMED_LABEL, | 
|  | STATEMENT_IF, | 
|  | STATEMENT_CONSTANT_SWITCH, | 
|  | STATEMENT_SEND, | 
|  | STATEMENT_SELECT, | 
|  |  | 
|  | // These statements types are created by the parser, but they | 
|  | // disappear during the lowering pass. | 
|  | STATEMENT_ASSIGNMENT_OPERATION, | 
|  | STATEMENT_TUPLE_ASSIGNMENT, | 
|  | STATEMENT_TUPLE_MAP_ASSIGNMENT, | 
|  | STATEMENT_TUPLE_RECEIVE_ASSIGNMENT, | 
|  | STATEMENT_TUPLE_TYPE_GUARD_ASSIGNMENT, | 
|  | STATEMENT_INCDEC, | 
|  | STATEMENT_FOR, | 
|  | STATEMENT_FOR_RANGE, | 
|  | STATEMENT_SWITCH, | 
|  | STATEMENT_TYPE_SWITCH | 
|  | }; | 
|  |  | 
|  | Statement(Statement_classification, Location); | 
|  |  | 
|  | virtual ~Statement(); | 
|  |  | 
|  | // Make a variable declaration. | 
|  | static Statement* | 
|  | make_variable_declaration(Named_object*); | 
|  |  | 
|  | // Make a statement which creates a temporary variable and | 
|  | // initializes it to an expression.  The block is used if the | 
|  | // temporary variable has to be explicitly destroyed; the variable | 
|  | // must still be added to the block.  References to the temporary | 
|  | // variable may be constructed using make_temporary_reference. | 
|  | // Either the type or the initialization expression may be NULL, but | 
|  | // not both. | 
|  | static Temporary_statement* | 
|  | make_temporary(Type*, Expression*, Location); | 
|  |  | 
|  | // Make an assignment statement. | 
|  | static Assignment_statement* | 
|  | make_assignment(Expression*, Expression*, Location); | 
|  |  | 
|  | // Make an assignment operation (+=, etc.). | 
|  | static Statement* | 
|  | make_assignment_operation(Operator, Expression*, Expression*, | 
|  | Location); | 
|  |  | 
|  | // Make a tuple assignment statement. | 
|  | static Statement* | 
|  | make_tuple_assignment(Expression_list*, Expression_list*, Location); | 
|  |  | 
|  | // Make an assignment from a map index to a pair of variables. | 
|  | static Statement* | 
|  | make_tuple_map_assignment(Expression* val, Expression* present, | 
|  | Expression*, Location); | 
|  |  | 
|  | // Make an assignment from a nonblocking receive to a pair of | 
|  | // variables. | 
|  | static Statement* | 
|  | make_tuple_receive_assignment(Expression* val, Expression* closed, | 
|  | Expression* channel, Location); | 
|  |  | 
|  | // Make an assignment from a type guard to a pair of variables. | 
|  | static Statement* | 
|  | make_tuple_type_guard_assignment(Expression* val, Expression* ok, | 
|  | Expression* expr, Type* type, | 
|  | Location); | 
|  |  | 
|  | // Make an expression statement from an Expression.  IS_IGNORED is | 
|  | // true if the value is being explicitly ignored, as in an | 
|  | // assignment to _. | 
|  | static Statement* | 
|  | make_statement(Expression*, bool is_ignored); | 
|  |  | 
|  | // Make a block statement from a Block.  This is an embedded list of | 
|  | // statements which may also include variable definitions. | 
|  | static Block_statement* | 
|  | make_block_statement(Block*, Location); | 
|  |  | 
|  | // Make an increment statement. | 
|  | static Statement* | 
|  | make_inc_statement(Expression*); | 
|  |  | 
|  | // Make a decrement statement. | 
|  | static Statement* | 
|  | make_dec_statement(Expression*); | 
|  |  | 
|  | // Make a go statement. | 
|  | static Statement* | 
|  | make_go_statement(Call_expression* call, Location); | 
|  |  | 
|  | // Make a defer statement. | 
|  | static Statement* | 
|  | make_defer_statement(Call_expression* call, Location); | 
|  |  | 
|  | // Make a return statement. | 
|  | static Return_statement* | 
|  | make_return_statement(Expression_list*, Location); | 
|  |  | 
|  | // Make a statement that returns the result of a call expression. | 
|  | // If the call does not return any results, this just returns the | 
|  | // call expression as a statement, assuming that the function will | 
|  | // end immediately afterward. | 
|  | static Statement* | 
|  | make_return_from_call(Call_expression*, Location); | 
|  |  | 
|  | // Make a break statement. | 
|  | static Statement* | 
|  | make_break_statement(Unnamed_label* label, Location); | 
|  |  | 
|  | // Make a continue statement. | 
|  | static Statement* | 
|  | make_continue_statement(Unnamed_label* label, Location); | 
|  |  | 
|  | // Make a goto statement. | 
|  | static Statement* | 
|  | make_goto_statement(Label* label, Location); | 
|  |  | 
|  | // Make a goto statement to an unnamed label. | 
|  | static Statement* | 
|  | make_goto_unnamed_statement(Unnamed_label* label, Location); | 
|  |  | 
|  | // Make a label statement--where the label is defined. | 
|  | static Statement* | 
|  | make_label_statement(Label* label, Location); | 
|  |  | 
|  | // Make an unnamed label statement--where the label is defined. | 
|  | static Statement* | 
|  | make_unnamed_label_statement(Unnamed_label* label); | 
|  |  | 
|  | // Make an if statement. | 
|  | static Statement* | 
|  | make_if_statement(Expression* cond, Block* then_block, Block* else_block, | 
|  | Location); | 
|  |  | 
|  | // Make a switch statement. | 
|  | static Switch_statement* | 
|  | make_switch_statement(Expression* switch_val, Location); | 
|  |  | 
|  | // Make a type switch statement. | 
|  | static Type_switch_statement* | 
|  | make_type_switch_statement(const std::string&, Expression*, Location); | 
|  |  | 
|  | // Make a send statement. | 
|  | static Send_statement* | 
|  | make_send_statement(Expression* channel, Expression* val, Location); | 
|  |  | 
|  | // Make a select statement. | 
|  | static Select_statement* | 
|  | make_select_statement(Location); | 
|  |  | 
|  | // Make a for statement. | 
|  | static For_statement* | 
|  | make_for_statement(Block* init, Expression* cond, Block* post, | 
|  | Location location); | 
|  |  | 
|  | // Make a for statement with a range clause. | 
|  | static For_range_statement* | 
|  | make_for_range_statement(Expression* index_var, Expression* value_var, | 
|  | Expression* range, Location); | 
|  |  | 
|  | // Return the statement classification. | 
|  | Statement_classification | 
|  | classification() const | 
|  | { return this->classification_; } | 
|  |  | 
|  | // Get the statement location. | 
|  | Location | 
|  | location() const | 
|  | { return this->location_; } | 
|  |  | 
|  | // Traverse the tree. | 
|  | int | 
|  | traverse(Block*, size_t* index, Traverse*); | 
|  |  | 
|  | // Traverse the contents of this statement--the expressions and | 
|  | // statements which it contains. | 
|  | int | 
|  | traverse_contents(Traverse*); | 
|  |  | 
|  | // If this statement assigns some values, it calls a function for | 
|  | // each value to which this statement assigns a value, and returns | 
|  | // true.  If this statement does not assign any values, it returns | 
|  | // false. | 
|  | bool | 
|  | traverse_assignments(Traverse_assignments* tassign); | 
|  |  | 
|  | // Lower a statement.  This is called immediately after parsing to | 
|  | // simplify statements for further processing.  It returns the same | 
|  | // Statement or a new one.  FUNCTION is the function containing this | 
|  | // statement.  BLOCK is the block containing this statement. | 
|  | // INSERTER can be used to insert new statements before this one. | 
|  | Statement* | 
|  | lower(Gogo* gogo, Named_object* function, Block* block, | 
|  | Statement_inserter* inserter) | 
|  | { return this->do_lower(gogo, function, block, inserter); } | 
|  |  | 
|  | // Flatten a statement.  This is called immediately after the order of | 
|  | // evaluation rules are applied to statements.  It returns the same | 
|  | // Statement or a new one.  FUNCTION is the function containing this | 
|  | // statement.  BLOCK is the block containing this statement. | 
|  | // INSERTER can be used to insert new statements before this one. | 
|  | Statement* | 
|  | flatten(Gogo* gogo, Named_object* function, Block* block, | 
|  | Statement_inserter* inserter) | 
|  | { return this->do_flatten(gogo, function, block, inserter); } | 
|  |  | 
|  | // Set type information for unnamed constants. | 
|  | void | 
|  | determine_types(); | 
|  |  | 
|  | // Check types in a statement.  This simply checks that any | 
|  | // expressions used by the statement have the right type. | 
|  | void | 
|  | check_types(Gogo* gogo) | 
|  | { this->do_check_types(gogo); } | 
|  |  | 
|  | // Return the cost of this statement for inlining purposes. | 
|  | int | 
|  | inlining_cost() | 
|  | { return this->do_inlining_cost(); } | 
|  |  | 
|  | // Export data for this statement to BODY. | 
|  | void | 
|  | export_statement(Export_function_body* efb) | 
|  | { this->do_export_statement(efb); } | 
|  |  | 
|  | // Make implicit type conversions explicit. | 
|  | void | 
|  | add_conversions() | 
|  | { this->do_add_conversions(); } | 
|  |  | 
|  | // Read a statement from export data.  The location should be used | 
|  | // for the returned statement.  Errors should be reported using the | 
|  | // Import_function_body's location method. | 
|  | static Statement* | 
|  | import_statement(Import_function_body*, Location); | 
|  |  | 
|  | // Return whether this is a block statement. | 
|  | bool | 
|  | is_block_statement() const | 
|  | { return this->classification_ == STATEMENT_BLOCK; } | 
|  |  | 
|  | // If this is an assignment statement, return it.  Otherwise return | 
|  | // NULL. | 
|  | Assignment_statement* | 
|  | assignment_statement() | 
|  | { | 
|  | return this->convert<Assignment_statement, STATEMENT_ASSIGNMENT>(); | 
|  | } | 
|  |  | 
|  | // If this is an temporary statement, return it.  Otherwise return | 
|  | // NULL. | 
|  | Temporary_statement* | 
|  | temporary_statement() | 
|  | { | 
|  | return this->convert<Temporary_statement, STATEMENT_TEMPORARY>(); | 
|  | } | 
|  |  | 
|  | // If this is a variable declaration statement, return it. | 
|  | // Otherwise return NULL. | 
|  | Variable_declaration_statement* | 
|  | variable_declaration_statement() | 
|  | { | 
|  | return this->convert<Variable_declaration_statement, | 
|  | STATEMENT_VARIABLE_DECLARATION>(); | 
|  | } | 
|  |  | 
|  | // If this is an expression statement, return it.  Otherwise return | 
|  | // NULL. | 
|  | Expression_statement* | 
|  | expression_statement() | 
|  | { | 
|  | return this->convert<Expression_statement, STATEMENT_EXPRESSION>(); | 
|  | } | 
|  |  | 
|  | // If this is an block statement, return it.  Otherwise return | 
|  | // NULL. | 
|  | Block_statement* | 
|  | block_statement() | 
|  | { return this->convert<Block_statement, STATEMENT_BLOCK>(); } | 
|  |  | 
|  | // If this is a return statement, return it.  Otherwise return NULL. | 
|  | Return_statement* | 
|  | return_statement() | 
|  | { return this->convert<Return_statement, STATEMENT_RETURN>(); } | 
|  |  | 
|  | // If this is a thunk statement (a go or defer statement), return | 
|  | // it.  Otherwise return NULL. | 
|  | Thunk_statement* | 
|  | thunk_statement(); | 
|  |  | 
|  | // If this is a defer statement, return it.  Otherwise return NULL. | 
|  | Defer_statement* | 
|  | defer_statement() | 
|  | { return this->convert<Defer_statement, STATEMENT_DEFER>(); } | 
|  |  | 
|  | // If this is a goto statement, return it.  Otherwise return NULL. | 
|  | Goto_statement* | 
|  | goto_statement() | 
|  | { return this->convert<Goto_statement, STATEMENT_GOTO>(); } | 
|  |  | 
|  | // If this is a goto_unnamed statement, return it.  Otherwise return NULL. | 
|  | Goto_unnamed_statement* | 
|  | goto_unnamed_statement() | 
|  | { return this->convert<Goto_unnamed_statement, STATEMENT_GOTO_UNNAMED>(); } | 
|  |  | 
|  | // If this is a label statement, return it.  Otherwise return NULL. | 
|  | Label_statement* | 
|  | label_statement() | 
|  | { return this->convert<Label_statement, STATEMENT_LABEL>(); } | 
|  |  | 
|  | // If this is an unnamed_label statement, return it.  Otherwise return NULL. | 
|  | Unnamed_label_statement* | 
|  | unnamed_label_statement() | 
|  | { return this->convert<Unnamed_label_statement, STATEMENT_UNNAMED_LABEL>(); } | 
|  |  | 
|  | // If this is an if statement, return it.  Otherwise return NULL. | 
|  | If_statement* | 
|  | if_statement() | 
|  | { return this->convert<If_statement, STATEMENT_IF>(); } | 
|  |  | 
|  | // If this is a for statement, return it.  Otherwise return NULL. | 
|  | For_statement* | 
|  | for_statement() | 
|  | { return this->convert<For_statement, STATEMENT_FOR>(); } | 
|  |  | 
|  | // If this is a for statement over a range clause, return it. | 
|  | // Otherwise return NULL. | 
|  | For_range_statement* | 
|  | for_range_statement() | 
|  | { return this->convert<For_range_statement, STATEMENT_FOR_RANGE>(); } | 
|  |  | 
|  | // If this is a switch statement, return it.  Otherwise return NULL. | 
|  | Switch_statement* | 
|  | switch_statement() | 
|  | { return this->convert<Switch_statement, STATEMENT_SWITCH>(); } | 
|  |  | 
|  | // If this is a type switch statement, return it.  Otherwise return | 
|  | // NULL. | 
|  | Type_switch_statement* | 
|  | type_switch_statement() | 
|  | { return this->convert<Type_switch_statement, STATEMENT_TYPE_SWITCH>(); } | 
|  |  | 
|  | // If this is a send statement, return it.  Otherwise return NULL. | 
|  | Send_statement* | 
|  | send_statement() | 
|  | { return this->convert<Send_statement, STATEMENT_SEND>(); } | 
|  |  | 
|  | // If this is a select statement, return it.  Otherwise return NULL. | 
|  | Select_statement* | 
|  | select_statement() | 
|  | { return this->convert<Select_statement, STATEMENT_SELECT>(); } | 
|  |  | 
|  | // Return true if this statement may fall through--if after | 
|  | // executing this statement we may go on to execute the following | 
|  | // statement, if any. | 
|  | bool | 
|  | may_fall_through() const | 
|  | { return this->do_may_fall_through(); } | 
|  |  | 
|  | // Convert the statement to the backend representation. | 
|  | Bstatement* | 
|  | get_backend(Translate_context*); | 
|  |  | 
|  | // Dump AST representation of a statement to a dump context. | 
|  | void | 
|  | dump_statement(Ast_dump_context*) const; | 
|  |  | 
|  | protected: | 
|  | // Implemented by child class: traverse the tree. | 
|  | virtual int | 
|  | do_traverse(Traverse*) = 0; | 
|  |  | 
|  | // Implemented by child class: traverse assignments.  Any statement | 
|  | // which includes an assignment should implement this. | 
|  | virtual bool | 
|  | do_traverse_assignments(Traverse_assignments*) | 
|  | { return false; } | 
|  |  | 
|  | // Implemented by the child class: lower this statement to a simpler | 
|  | // one. | 
|  | virtual Statement* | 
|  | do_lower(Gogo*, Named_object*, Block*, Statement_inserter*) | 
|  | { return this; } | 
|  |  | 
|  | // Implemented by the child class: lower this statement to a simpler | 
|  | // one. | 
|  | virtual Statement* | 
|  | do_flatten(Gogo*, Named_object*, Block*, Statement_inserter*) | 
|  | { return this; } | 
|  |  | 
|  | // Implemented by child class: set type information for unnamed | 
|  | // constants.  Any statement which includes an expression needs to | 
|  | // implement this. | 
|  | virtual void | 
|  | do_determine_types() | 
|  | { } | 
|  |  | 
|  | // Implemented by child class: check types of expressions used in a | 
|  | // statement. | 
|  | virtual void | 
|  | do_check_types(Gogo*) | 
|  | { } | 
|  |  | 
|  | // Implemented by child class: return the cost of this statement for | 
|  | // inlining.  The default cost is high, so we only need to define | 
|  | // this method for statements that can be inlined. | 
|  | virtual int | 
|  | do_inlining_cost() | 
|  | { return 0x100000; } | 
|  |  | 
|  | // Implemented by child class: write export data for this statement | 
|  | // to the string.  This need only be implemented by classes that | 
|  | // implement do_inlining_cost with a reasonable value. | 
|  | virtual void | 
|  | do_export_statement(Export_function_body*) | 
|  | { go_unreachable(); } | 
|  |  | 
|  | // Implemented by child class: return true if this statement may | 
|  | // fall through. | 
|  | virtual bool | 
|  | do_may_fall_through() const | 
|  | { return true; } | 
|  |  | 
|  | // Implemented by child class: convert to backend representation. | 
|  | virtual Bstatement* | 
|  | do_get_backend(Translate_context*) = 0; | 
|  |  | 
|  | // Implemented by child class: dump ast representation. | 
|  | virtual void | 
|  | do_dump_statement(Ast_dump_context*) const = 0; | 
|  |  | 
|  | // Implemented by child class: make implicit conversions explicit. | 
|  | virtual void | 
|  | do_add_conversions() | 
|  | { } | 
|  |  | 
|  | // Traverse an expression in a statement. | 
|  | int | 
|  | traverse_expression(Traverse*, Expression**); | 
|  |  | 
|  | // Traverse an expression list in a statement.  The Expression_list | 
|  | // may be NULL. | 
|  | int | 
|  | traverse_expression_list(Traverse*, Expression_list*); | 
|  |  | 
|  | // Traverse a type in a statement. | 
|  | int | 
|  | traverse_type(Traverse*, Type*); | 
|  |  | 
|  | // For children to call when they detect that they are in error. | 
|  | void | 
|  | set_is_error(); | 
|  |  | 
|  | // For children to call to report an error conveniently. | 
|  | void | 
|  | report_error(const char*); | 
|  |  | 
|  | // For children to return an error statement from lower(). | 
|  | static Statement* | 
|  | make_error_statement(Location); | 
|  |  | 
|  | private: | 
|  | // Convert to the desired statement classification, or return NULL. | 
|  | // This is a controlled dynamic cast. | 
|  | template<typename Statement_class, Statement_classification sc> | 
|  | Statement_class* | 
|  | convert() | 
|  | { | 
|  | return (this->classification_ == sc | 
|  | ? static_cast<Statement_class*>(this) | 
|  | : NULL); | 
|  | } | 
|  |  | 
|  | template<typename Statement_class, Statement_classification sc> | 
|  | const Statement_class* | 
|  | convert() const | 
|  | { | 
|  | return (this->classification_ == sc | 
|  | ? static_cast<const Statement_class*>(this) | 
|  | : NULL); | 
|  | } | 
|  |  | 
|  | // The statement classification. | 
|  | Statement_classification classification_; | 
|  | // The location in the input file of the start of this statement. | 
|  | Location location_; | 
|  | }; | 
|  |  | 
|  | // An assignment statement. | 
|  |  | 
|  | class Assignment_statement : public Statement | 
|  | { | 
|  | public: | 
|  | Assignment_statement(Expression* lhs, Expression* rhs, | 
|  | Location location) | 
|  | : Statement(STATEMENT_ASSIGNMENT, location), | 
|  | lhs_(lhs), rhs_(rhs), omit_write_barrier_(false) | 
|  | { } | 
|  |  | 
|  | Expression* | 
|  | lhs() const | 
|  | { return this->lhs_; } | 
|  |  | 
|  | Expression* | 
|  | rhs() const | 
|  | { return this->rhs_; } | 
|  |  | 
|  | bool | 
|  | omit_write_barrier() const | 
|  | { return this->omit_write_barrier_; } | 
|  |  | 
|  | void | 
|  | set_omit_write_barrier() | 
|  | { this->omit_write_barrier_ = true; } | 
|  |  | 
|  | protected: | 
|  | int | 
|  | do_traverse(Traverse* traverse); | 
|  |  | 
|  | bool | 
|  | do_traverse_assignments(Traverse_assignments*); | 
|  |  | 
|  | virtual Statement* | 
|  | do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); | 
|  |  | 
|  | void | 
|  | do_determine_types(); | 
|  |  | 
|  | void | 
|  | do_check_types(Gogo*); | 
|  |  | 
|  | int | 
|  | do_inlining_cost() | 
|  | { return 1; } | 
|  |  | 
|  | void | 
|  | do_export_statement(Export_function_body*); | 
|  |  | 
|  | Statement* | 
|  | do_flatten(Gogo*, Named_object*, Block*, Statement_inserter*); | 
|  |  | 
|  | Bstatement* | 
|  | do_get_backend(Translate_context*); | 
|  |  | 
|  | void | 
|  | do_dump_statement(Ast_dump_context*) const; | 
|  |  | 
|  | void | 
|  | do_add_conversions(); | 
|  |  | 
|  | private: | 
|  | // Left hand side--the lvalue. | 
|  | Expression* lhs_; | 
|  | // Right hand side--the rvalue. | 
|  | Expression* rhs_; | 
|  | // True if we can omit a write barrier from this assignment. | 
|  | bool omit_write_barrier_; | 
|  | }; | 
|  |  | 
|  | // A statement which creates and initializes a temporary variable. | 
|  |  | 
|  | class Temporary_statement : public Statement | 
|  | { | 
|  | public: | 
|  | Temporary_statement(Type* type, Expression* init, Location location) | 
|  | : Statement(STATEMENT_TEMPORARY, location), | 
|  | type_(type), init_(init), bvariable_(NULL), is_address_taken_(false), | 
|  | value_escapes_(false), assigned_(false), uses_(0) | 
|  | { } | 
|  |  | 
|  | // Return the type of the temporary variable. | 
|  | Type* | 
|  | type() const; | 
|  |  | 
|  | // Return the initializer if there is one. | 
|  | Expression* | 
|  | init() const | 
|  | { return this->init_; } | 
|  |  | 
|  | // Set the initializer. | 
|  | void | 
|  | set_init(Expression* expr) | 
|  | { this->init_ = expr; } | 
|  |  | 
|  | // Whether something takes the address of this temporary | 
|  | // variable. | 
|  | bool | 
|  | is_address_taken() | 
|  | { return this->is_address_taken_; } | 
|  |  | 
|  | // Record that something takes the address of this temporary | 
|  | // variable. | 
|  | void | 
|  | set_is_address_taken() | 
|  | { this->is_address_taken_ = true; } | 
|  |  | 
|  | // Whether the value escapes. | 
|  | bool | 
|  | value_escapes() const | 
|  | { return this->value_escapes_; } | 
|  |  | 
|  | // Record that the value escapes. | 
|  | void | 
|  | set_value_escapes() | 
|  | { this->value_escapes_ = true; } | 
|  |  | 
|  | // Whether this temporary variable is assigned (after initialization). | 
|  | bool | 
|  | assigned() | 
|  | { return this->assigned_; } | 
|  |  | 
|  | // Record that this temporary variable is assigned. | 
|  | void | 
|  | set_assigned() | 
|  | { this->assigned_ = true; } | 
|  |  | 
|  | // Number of uses of this temporary variable. | 
|  | int | 
|  | uses() | 
|  | { return this->uses_; } | 
|  |  | 
|  | // Add one use of this temporary variable. | 
|  | void | 
|  | add_use() | 
|  | { this->uses_++; } | 
|  |  | 
|  | // Return the temporary variable.  This should not be called until | 
|  | // after the statement itself has been converted. | 
|  | Bvariable* | 
|  | get_backend_variable(Translate_context*) const; | 
|  |  | 
|  | // Import the declaration of a temporary. | 
|  | static Statement* | 
|  | do_import(Import_function_body*, Location); | 
|  |  | 
|  | protected: | 
|  | int | 
|  | do_traverse(Traverse*); | 
|  |  | 
|  | bool | 
|  | do_traverse_assignments(Traverse_assignments*); | 
|  |  | 
|  | void | 
|  | do_determine_types(); | 
|  |  | 
|  | void | 
|  | do_check_types(Gogo*); | 
|  |  | 
|  | int | 
|  | do_inlining_cost() | 
|  | { return 1; } | 
|  |  | 
|  | void | 
|  | do_export_statement(Export_function_body*); | 
|  |  | 
|  | Statement* | 
|  | do_flatten(Gogo*, Named_object*, Block*, Statement_inserter*); | 
|  |  | 
|  | Bstatement* | 
|  | do_get_backend(Translate_context*); | 
|  |  | 
|  | void | 
|  | do_dump_statement(Ast_dump_context*) const; | 
|  |  | 
|  | void | 
|  | do_add_conversions(); | 
|  |  | 
|  | private: | 
|  | // The type of the temporary variable. | 
|  | Type* type_; | 
|  | // The initial value of the temporary variable.  This may be NULL. | 
|  | Expression* init_; | 
|  | // The backend representation of the temporary variable. | 
|  | Bvariable* bvariable_; | 
|  | // True if something takes the address of this temporary variable. | 
|  | bool is_address_taken_; | 
|  | // True if the value assigned to this temporary variable escapes. | 
|  | // This is used for select statements. | 
|  | bool value_escapes_; | 
|  | // True if this temporary variable is assigned (after initialization). | 
|  | bool assigned_; | 
|  | // Number of uses of this temporary variable. | 
|  | int uses_; | 
|  | }; | 
|  |  | 
|  | // A variable declaration.  This marks the point in the code where a | 
|  | // variable is declared.  The Variable is also attached to a Block. | 
|  |  | 
|  | class Variable_declaration_statement : public Statement | 
|  | { | 
|  | public: | 
|  | Variable_declaration_statement(Named_object* var); | 
|  |  | 
|  | // The variable being declared. | 
|  | Named_object* | 
|  | var() | 
|  | { return this->var_; } | 
|  |  | 
|  | // Import a variable declaration. | 
|  | static Statement* | 
|  | do_import(Import_function_body*, Location); | 
|  |  | 
|  | protected: | 
|  | int | 
|  | do_traverse(Traverse*); | 
|  |  | 
|  | bool | 
|  | do_traverse_assignments(Traverse_assignments*); | 
|  |  | 
|  | Statement* | 
|  | do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); | 
|  |  | 
|  | int | 
|  | do_inlining_cost() | 
|  | { return 1; } | 
|  |  | 
|  | void | 
|  | do_export_statement(Export_function_body*); | 
|  |  | 
|  | Statement* | 
|  | do_flatten(Gogo*, Named_object*, Block*, Statement_inserter*); | 
|  |  | 
|  | Bstatement* | 
|  | do_get_backend(Translate_context*); | 
|  |  | 
|  | void | 
|  | do_dump_statement(Ast_dump_context*) const; | 
|  |  | 
|  | void | 
|  | do_add_conversions(); | 
|  |  | 
|  | private: | 
|  | Named_object* var_; | 
|  | }; | 
|  |  | 
|  | // A return statement. | 
|  |  | 
|  | class Return_statement : public Statement | 
|  | { | 
|  | public: | 
|  | Return_statement(Expression_list* vals, Location location) | 
|  | : Statement(STATEMENT_RETURN, location), | 
|  | vals_(vals), is_lowered_(false) | 
|  | { } | 
|  |  | 
|  | // The list of values being returned.  This may be NULL. | 
|  | const Expression_list* | 
|  | vals() const | 
|  | { return this->vals_; } | 
|  |  | 
|  | protected: | 
|  | int | 
|  | do_traverse(Traverse* traverse) | 
|  | { return this->traverse_expression_list(traverse, this->vals_); } | 
|  |  | 
|  | bool | 
|  | do_traverse_assignments(Traverse_assignments*); | 
|  |  | 
|  | Statement* | 
|  | do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); | 
|  |  | 
|  | bool | 
|  | do_may_fall_through() const | 
|  | { return false; } | 
|  |  | 
|  | int | 
|  | do_inlining_cost() | 
|  | { return 1; } | 
|  |  | 
|  | void | 
|  | do_export_statement(Export_function_body*); | 
|  |  | 
|  | Bstatement* | 
|  | do_get_backend(Translate_context*); | 
|  |  | 
|  | void | 
|  | do_dump_statement(Ast_dump_context*) const; | 
|  |  | 
|  | private: | 
|  | // Return values.  This may be NULL. | 
|  | Expression_list* vals_; | 
|  | // True if this statement has been lowered. | 
|  | bool is_lowered_; | 
|  | }; | 
|  |  | 
|  | // An expression statement. | 
|  |  | 
|  | class Expression_statement : public Statement | 
|  | { | 
|  | public: | 
|  | Expression_statement(Expression* expr, bool is_ignored); | 
|  |  | 
|  | Expression* | 
|  | expr() | 
|  | { return this->expr_; } | 
|  |  | 
|  | protected: | 
|  | int | 
|  | do_traverse(Traverse* traverse) | 
|  | { return this->traverse_expression(traverse, &this->expr_); } | 
|  |  | 
|  | void | 
|  | do_determine_types(); | 
|  |  | 
|  | void | 
|  | do_check_types(Gogo*); | 
|  |  | 
|  | bool | 
|  | do_may_fall_through() const; | 
|  |  | 
|  | int | 
|  | do_inlining_cost() | 
|  | { return 0; } | 
|  |  | 
|  | void | 
|  | do_export_statement(Export_function_body*); | 
|  |  | 
|  | Bstatement* | 
|  | do_get_backend(Translate_context* context); | 
|  |  | 
|  | void | 
|  | do_dump_statement(Ast_dump_context*) const; | 
|  |  | 
|  | private: | 
|  | Expression* expr_; | 
|  | // Whether the value of this expression is being explicitly ignored. | 
|  | bool is_ignored_; | 
|  | }; | 
|  |  | 
|  | // A block statement--a list of statements which may include variable | 
|  | // definitions. | 
|  |  | 
|  | class Block_statement : public Statement | 
|  | { | 
|  | public: | 
|  | Block_statement(Block* block, Location location) | 
|  | : Statement(STATEMENT_BLOCK, location), | 
|  | block_(block), is_lowered_for_statement_(false) | 
|  | { } | 
|  |  | 
|  | // Return the actual block. | 
|  | Block* | 
|  | block() const | 
|  | { return this->block_; } | 
|  |  | 
|  | void | 
|  | set_is_lowered_for_statement() | 
|  | { this->is_lowered_for_statement_ = true; } | 
|  |  | 
|  | bool | 
|  | is_lowered_for_statement() | 
|  | { return this->is_lowered_for_statement_; } | 
|  |  | 
|  | // Export a block for a block statement. | 
|  | static void | 
|  | export_block(Export_function_body*, Block*, bool is_lowered_for_statement); | 
|  |  | 
|  | // Import a block statement, returning the block. | 
|  | // *IS_LOWERED_FOR_STATEMENT reports whether this block statement | 
|  | // was lowered from a for statement. | 
|  | static Block* | 
|  | do_import(Import_function_body*, Location, bool* is_lowered_for_statement); | 
|  |  | 
|  | protected: | 
|  | int | 
|  | do_traverse(Traverse* traverse) | 
|  | { return this->block_->traverse(traverse); } | 
|  |  | 
|  | void | 
|  | do_determine_types() | 
|  | { this->block_->determine_types(); } | 
|  |  | 
|  | int | 
|  | do_inlining_cost() | 
|  | { return 0; } | 
|  |  | 
|  | void | 
|  | do_export_statement(Export_function_body*); | 
|  |  | 
|  | bool | 
|  | do_may_fall_through() const | 
|  | { return this->block_->may_fall_through(); } | 
|  |  | 
|  | Bstatement* | 
|  | do_get_backend(Translate_context* context); | 
|  |  | 
|  | void | 
|  | do_dump_statement(Ast_dump_context*) const; | 
|  |  | 
|  | private: | 
|  | Block* block_; | 
|  | // True if this block statement represents a lowered for statement. | 
|  | bool is_lowered_for_statement_; | 
|  | }; | 
|  |  | 
|  | // A send statement. | 
|  |  | 
|  | class Send_statement : public Statement | 
|  | { | 
|  | public: | 
|  | Send_statement(Expression* channel, Expression* val, | 
|  | Location location) | 
|  | : Statement(STATEMENT_SEND, location), | 
|  | channel_(channel), val_(val) | 
|  | { } | 
|  |  | 
|  | Expression* | 
|  | channel() | 
|  | { return this->channel_; } | 
|  |  | 
|  | Expression* | 
|  | val() | 
|  | { return this->val_; } | 
|  |  | 
|  | protected: | 
|  | int | 
|  | do_traverse(Traverse* traverse); | 
|  |  | 
|  | void | 
|  | do_determine_types(); | 
|  |  | 
|  | void | 
|  | do_check_types(Gogo*); | 
|  |  | 
|  | Statement* | 
|  | do_flatten(Gogo*, Named_object*, Block*, Statement_inserter*); | 
|  |  | 
|  | Bstatement* | 
|  | do_get_backend(Translate_context*); | 
|  |  | 
|  | void | 
|  | do_dump_statement(Ast_dump_context*) const; | 
|  |  | 
|  | void | 
|  | do_add_conversions(); | 
|  |  | 
|  | private: | 
|  | // The channel on which to send the value. | 
|  | Expression* channel_; | 
|  | // The value to send. | 
|  | Expression* val_; | 
|  | }; | 
|  |  | 
|  | // Select_clauses holds the clauses of a select statement.  This is | 
|  | // built by the parser. | 
|  |  | 
|  | class Select_clauses | 
|  | { | 
|  | public: | 
|  | Select_clauses() | 
|  | : clauses_() | 
|  | { } | 
|  |  | 
|  | // Add a new clause.  IS_SEND is true if this is a send clause, | 
|  | // false for a receive clause.  For a send clause CHANNEL is the | 
|  | // channel and VAL is the value to send.  For a receive clause | 
|  | // CHANNEL is the channel, VAL is either NULL or a Var_expression | 
|  | // for the variable to set, and CLOSED is either NULL or a | 
|  | // Var_expression to set to whether the channel is closed.  If VAL | 
|  | // is NULL, VAR may be a variable to be initialized with the | 
|  | // received value, and CLOSEDVAR may be a variable to be initialized | 
|  | // with whether the channel is closed.  IS_DEFAULT is true if this | 
|  | // is the default clause.  STATEMENTS is the list of statements to | 
|  | // execute. | 
|  | void | 
|  | add(bool is_send, Expression* channel, Expression* val, Expression* closed, | 
|  | Named_object* var, Named_object* closedvar, bool is_default, | 
|  | Block* statements, Location location) | 
|  | { | 
|  | this->clauses_.push_back(Select_clause(is_send, channel, val, closed, var, | 
|  | closedvar, is_default, statements, | 
|  | location)); | 
|  | } | 
|  |  | 
|  | size_t | 
|  | size() const | 
|  | { return this->clauses_.size(); } | 
|  |  | 
|  | // Traverse the select clauses. | 
|  | int | 
|  | traverse(Traverse*); | 
|  |  | 
|  | // Lower statements. | 
|  | void | 
|  | lower(Gogo*, Named_object*, Block*, Temporary_statement*, | 
|  | Temporary_statement*); | 
|  |  | 
|  | // Determine types. | 
|  | void | 
|  | determine_types(); | 
|  |  | 
|  | // Check types. | 
|  | void | 
|  | check_types(); | 
|  |  | 
|  | // Whether the select clauses may fall through to the statement | 
|  | // which follows the overall select statement. | 
|  | bool | 
|  | may_fall_through() const; | 
|  |  | 
|  | // Convert to the backend representation. | 
|  | Bstatement* | 
|  | get_backend(Translate_context*, Temporary_statement* index, | 
|  | Unnamed_label* break_label, Location); | 
|  |  | 
|  | // Dump AST representation. | 
|  | void | 
|  | dump_clauses(Ast_dump_context*) const; | 
|  |  | 
|  | // A single clause. | 
|  | class Select_clause | 
|  | { | 
|  | public: | 
|  | Select_clause() | 
|  | : channel_(NULL), val_(NULL), closed_(NULL), var_(NULL), | 
|  | closedvar_(NULL), statements_(NULL), is_send_(false), | 
|  | is_default_(false) | 
|  | { } | 
|  |  | 
|  | Select_clause(bool is_send, Expression* channel, Expression* val, | 
|  | Expression* closed, Named_object* var, | 
|  | Named_object* closedvar, bool is_default, Block* statements, | 
|  | Location location) | 
|  | : channel_(channel), val_(val), closed_(closed), var_(var), | 
|  | closedvar_(closedvar), statements_(statements), location_(location), | 
|  | is_send_(is_send), is_default_(is_default), is_lowered_(false) | 
|  | { go_assert(is_default ? channel == NULL : channel != NULL); } | 
|  |  | 
|  | // Traverse the select clause. | 
|  | int | 
|  | traverse(Traverse*); | 
|  |  | 
|  | // Lower statements. | 
|  | void | 
|  | lower(Gogo*, Named_object*, Block*, Temporary_statement*, size_t, | 
|  | Temporary_statement*); | 
|  |  | 
|  | // Determine types. | 
|  | void | 
|  | determine_types(); | 
|  |  | 
|  | // Check types. | 
|  | void | 
|  | check_types(); | 
|  |  | 
|  | // Return true if this is the default clause. | 
|  | bool | 
|  | is_default() const | 
|  | { return this->is_default_; } | 
|  |  | 
|  | // Return the channel.  This will return NULL for the default | 
|  | // clause. | 
|  | Expression* | 
|  | channel() const | 
|  | { return this->channel_; } | 
|  |  | 
|  | // Return true for a send, false for a receive. | 
|  | bool | 
|  | is_send() const | 
|  | { | 
|  | go_assert(!this->is_default_); | 
|  | return this->is_send_; | 
|  | } | 
|  |  | 
|  | // Return the value to send or the lvalue to receive into. | 
|  | Expression* | 
|  | val() const | 
|  | { return this->val_; } | 
|  |  | 
|  | // Return the lvalue to set to whether the channel is closed | 
|  | // on a receive. | 
|  | Expression* | 
|  | closed() const | 
|  | { return this->closed_; } | 
|  |  | 
|  | // Return the variable to initialize, for "case a := <-ch". | 
|  | Named_object* | 
|  | var() const | 
|  | { return this->var_; } | 
|  |  | 
|  | // Return the variable to initialize to whether the channel | 
|  | // is closed, for "case a, c := <-ch". | 
|  | Named_object* | 
|  | closedvar() const | 
|  | { return this->closedvar_; } | 
|  |  | 
|  | // Return the statements. | 
|  | Block* | 
|  | statements() const | 
|  | { return this->statements_; } | 
|  |  | 
|  | // Return the location. | 
|  | Location | 
|  | location() const | 
|  | { return this->location_; } | 
|  |  | 
|  | // Whether this clause may fall through to the statement which | 
|  | // follows the overall select statement. | 
|  | bool | 
|  | may_fall_through() const; | 
|  |  | 
|  | // Convert the statements to the backend representation. | 
|  | Bstatement* | 
|  | get_statements_backend(Translate_context*); | 
|  |  | 
|  | // Dump AST representation. | 
|  | void | 
|  | dump_clause(Ast_dump_context*) const; | 
|  |  | 
|  | private: | 
|  | // These values must match the values in libgo/go/runtime/select.go. | 
|  | enum | 
|  | { | 
|  | caseRecv = 1, | 
|  | caseSend = 2, | 
|  | caseDefault = 3, | 
|  | }; | 
|  |  | 
|  | void | 
|  | lower_default(Block*, Expression*); | 
|  |  | 
|  | void | 
|  | lower_send(Block*, Expression*, Expression*); | 
|  |  | 
|  | void | 
|  | lower_recv(Gogo*, Named_object*, Block*, Expression*, Expression*, | 
|  | Temporary_statement*); | 
|  |  | 
|  | void | 
|  | set_case(Block*, Expression*, Expression*, Expression*, int); | 
|  |  | 
|  | // The channel. | 
|  | Expression* channel_; | 
|  | // The value to send or the lvalue to receive into. | 
|  | Expression* val_; | 
|  | // The lvalue to set to whether the channel is closed on a | 
|  | // receive. | 
|  | Expression* closed_; | 
|  | // The variable to initialize, for "case a := <-ch". | 
|  | Named_object* var_; | 
|  | // The variable to initialize to whether the channel is closed, | 
|  | // for "case a, c := <-ch". | 
|  | Named_object* closedvar_; | 
|  | // The statements to execute. | 
|  | Block* statements_; | 
|  | // The location of this clause. | 
|  | Location location_; | 
|  | // Whether this is a send or a receive. | 
|  | bool is_send_; | 
|  | // Whether this is the default. | 
|  | bool is_default_; | 
|  | // Whether this has been lowered. | 
|  | bool is_lowered_; | 
|  | }; | 
|  |  | 
|  | Select_clause& | 
|  | at(size_t i) | 
|  | { return this->clauses_.at(i); } | 
|  |  | 
|  | private: | 
|  | typedef std::vector<Select_clause> Clauses; | 
|  |  | 
|  | Clauses clauses_; | 
|  | }; | 
|  |  | 
|  | // A select statement. | 
|  |  | 
|  | class Select_statement : public Statement | 
|  | { | 
|  | public: | 
|  | Select_statement(Location location) | 
|  | : Statement(STATEMENT_SELECT, location), | 
|  | clauses_(NULL), index_(NULL), break_label_(NULL), is_lowered_(false) | 
|  | { } | 
|  |  | 
|  | // Add the clauses. | 
|  | void | 
|  | add_clauses(Select_clauses* clauses) | 
|  | { | 
|  | go_assert(this->clauses_ == NULL); | 
|  | this->clauses_ = clauses; | 
|  | } | 
|  |  | 
|  | // Return the break label for this select statement. | 
|  | Unnamed_label* | 
|  | break_label(); | 
|  |  | 
|  | protected: | 
|  | int | 
|  | do_traverse(Traverse* traverse) | 
|  | { return this->clauses_->traverse(traverse); } | 
|  |  | 
|  | Statement* | 
|  | do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); | 
|  |  | 
|  | void | 
|  | do_determine_types() | 
|  | { this->clauses_->determine_types(); } | 
|  |  | 
|  | void | 
|  | do_check_types(Gogo*) | 
|  | { this->clauses_->check_types(); } | 
|  |  | 
|  | bool | 
|  | do_may_fall_through() const; | 
|  |  | 
|  | Bstatement* | 
|  | do_get_backend(Translate_context*); | 
|  |  | 
|  | void | 
|  | do_dump_statement(Ast_dump_context*) const; | 
|  |  | 
|  | private: | 
|  | // Lower a one-case select statement. | 
|  | Statement* | 
|  | lower_one_case(Block*); | 
|  |  | 
|  | // Lower a two-case select statement with one defualt case. | 
|  | Statement* | 
|  | lower_two_case(Block*); | 
|  |  | 
|  | // The select clauses. | 
|  | Select_clauses* clauses_; | 
|  | // A temporary that holds the index value returned by selectgo. | 
|  | Temporary_statement* index_; | 
|  | // The break label. | 
|  | Unnamed_label* break_label_; | 
|  | // Whether this statement has been lowered. | 
|  | bool is_lowered_; | 
|  | }; | 
|  |  | 
|  | // A statement which requires a thunk: go or defer. | 
|  |  | 
|  | class Thunk_statement : public Statement | 
|  | { | 
|  | public: | 
|  | Thunk_statement(Statement_classification, Call_expression*, | 
|  | Location); | 
|  |  | 
|  | // Return the call expression. | 
|  | Expression* | 
|  | call() const | 
|  | { return this->call_; } | 
|  |  | 
|  | // Simplify a go or defer statement so that it only uses a single | 
|  | // parameter. | 
|  | bool | 
|  | simplify_statement(Gogo*, Named_object*, Block*); | 
|  |  | 
|  | protected: | 
|  | int | 
|  | do_traverse(Traverse* traverse); | 
|  |  | 
|  | bool | 
|  | do_traverse_assignments(Traverse_assignments*); | 
|  |  | 
|  | void | 
|  | do_determine_types(); | 
|  |  | 
|  | void | 
|  | do_check_types(Gogo*); | 
|  |  | 
|  | // Return the function and argument for the call. | 
|  | bool | 
|  | get_fn_and_arg(Expression** pfn, Expression** parg); | 
|  |  | 
|  | private: | 
|  | // Return whether this is a simple go statement. | 
|  | bool | 
|  | is_simple(Function_type*) const; | 
|  |  | 
|  | // Return whether the thunk function is a constant. | 
|  | bool | 
|  | is_constant_function() const; | 
|  |  | 
|  | // Build the struct to use for a complex case. | 
|  | Struct_type* | 
|  | build_struct(Function_type* fntype); | 
|  |  | 
|  | // Build the thunk. | 
|  | void | 
|  | build_thunk(Gogo*, const std::string&); | 
|  |  | 
|  | // Set the name to use for thunk field N. | 
|  | void | 
|  | thunk_field_param(int n, char* buf, size_t buflen); | 
|  |  | 
|  | // The function call to be executed in a separate thread (go) or | 
|  | // later (defer). | 
|  | Expression* call_; | 
|  | // The type used for a struct to pass to a thunk, if this is not a | 
|  | // simple call. | 
|  | Struct_type* struct_type_; | 
|  | }; | 
|  |  | 
|  | // A go statement. | 
|  |  | 
|  | class Go_statement : public Thunk_statement | 
|  | { | 
|  | public: | 
|  | Go_statement(Call_expression* call, Location location) | 
|  | : Thunk_statement(STATEMENT_GO, call, location) | 
|  | { } | 
|  |  | 
|  | protected: | 
|  | Bstatement* | 
|  | do_get_backend(Translate_context*); | 
|  |  | 
|  | void | 
|  | do_dump_statement(Ast_dump_context*) const; | 
|  | }; | 
|  |  | 
|  | // A defer statement. | 
|  |  | 
|  | class Defer_statement : public Thunk_statement | 
|  | { | 
|  | public: | 
|  | Defer_statement(Call_expression* call, Location location) | 
|  | : Thunk_statement(STATEMENT_DEFER, call, location), | 
|  | on_stack_(false) | 
|  | { } | 
|  |  | 
|  | void | 
|  | set_on_stack() | 
|  | { this->on_stack_ = true; } | 
|  |  | 
|  | protected: | 
|  | Bstatement* | 
|  | do_get_backend(Translate_context*); | 
|  |  | 
|  | void | 
|  | do_dump_statement(Ast_dump_context*) const; | 
|  |  | 
|  | private: | 
|  | static Type* | 
|  | defer_struct_type(); | 
|  |  | 
|  | bool on_stack_; | 
|  | }; | 
|  |  | 
|  | // A goto statement. | 
|  |  | 
|  | class Goto_statement : public Statement | 
|  | { | 
|  | public: | 
|  | Goto_statement(Label* label, Location location) | 
|  | : Statement(STATEMENT_GOTO, location), | 
|  | label_(label) | 
|  | { } | 
|  |  | 
|  | // Return the label being jumped to. | 
|  | Label* | 
|  | label() const | 
|  | { return this->label_; } | 
|  |  | 
|  | // Import a goto statement. | 
|  | static Statement* | 
|  | do_import(Import_function_body*, Location); | 
|  |  | 
|  | protected: | 
|  | int | 
|  | do_traverse(Traverse*); | 
|  |  | 
|  | void | 
|  | do_check_types(Gogo*); | 
|  |  | 
|  | bool | 
|  | do_may_fall_through() const | 
|  | { return false; } | 
|  |  | 
|  | Bstatement* | 
|  | do_get_backend(Translate_context*); | 
|  |  | 
|  | int | 
|  | do_inlining_cost() | 
|  | { return 5; } | 
|  |  | 
|  | void | 
|  | do_export_statement(Export_function_body*); | 
|  |  | 
|  | void | 
|  | do_dump_statement(Ast_dump_context*) const; | 
|  |  | 
|  | private: | 
|  | Label* label_; | 
|  | }; | 
|  |  | 
|  | // A goto statement to an unnamed label. | 
|  |  | 
|  | class Goto_unnamed_statement : public Statement | 
|  | { | 
|  | public: | 
|  | Goto_unnamed_statement(Unnamed_label* label, Location location) | 
|  | : Statement(STATEMENT_GOTO_UNNAMED, location), | 
|  | label_(label) | 
|  | { } | 
|  |  | 
|  | Unnamed_label* | 
|  | unnamed_label() const | 
|  | { return this->label_; } | 
|  |  | 
|  | protected: | 
|  | int | 
|  | do_traverse(Traverse*); | 
|  |  | 
|  | bool | 
|  | do_may_fall_through() const | 
|  | { return false; } | 
|  |  | 
|  | Bstatement* | 
|  | do_get_backend(Translate_context* context); | 
|  |  | 
|  | int | 
|  | do_inlining_cost() | 
|  | { return 5; } | 
|  |  | 
|  | void | 
|  | do_export_statement(Export_function_body*); | 
|  |  | 
|  | void | 
|  | do_dump_statement(Ast_dump_context*) const; | 
|  |  | 
|  | private: | 
|  | Unnamed_label* label_; | 
|  | }; | 
|  |  | 
|  | // A label statement. | 
|  |  | 
|  | class Label_statement : public Statement | 
|  | { | 
|  | public: | 
|  | Label_statement(Label* label, Location location) | 
|  | : Statement(STATEMENT_LABEL, location), | 
|  | label_(label) | 
|  | { } | 
|  |  | 
|  | // Return the label itself. | 
|  | Label* | 
|  | label() const | 
|  | { return this->label_; } | 
|  |  | 
|  | // Import a label or unnamed label. | 
|  | static Statement* | 
|  | do_import(Import_function_body*, Location); | 
|  |  | 
|  | protected: | 
|  | int | 
|  | do_traverse(Traverse*); | 
|  |  | 
|  | Bstatement* | 
|  | do_get_backend(Translate_context*); | 
|  |  | 
|  | int | 
|  | do_inlining_cost() | 
|  | { return 1; } | 
|  |  | 
|  | void | 
|  | do_export_statement(Export_function_body*); | 
|  |  | 
|  | void | 
|  | do_dump_statement(Ast_dump_context*) const; | 
|  |  | 
|  | private: | 
|  | // The label. | 
|  | Label* label_; | 
|  | }; | 
|  |  | 
|  | // An unnamed label statement. | 
|  |  | 
|  | class Unnamed_label_statement : public Statement | 
|  | { | 
|  | public: | 
|  | Unnamed_label_statement(Unnamed_label* label); | 
|  |  | 
|  | protected: | 
|  | int | 
|  | do_traverse(Traverse*); | 
|  |  | 
|  | Bstatement* | 
|  | do_get_backend(Translate_context* context); | 
|  |  | 
|  | int | 
|  | do_inlining_cost() | 
|  | { return 1; } | 
|  |  | 
|  | void | 
|  | do_export_statement(Export_function_body*); | 
|  |  | 
|  | void | 
|  | do_dump_statement(Ast_dump_context*) const; | 
|  |  | 
|  | private: | 
|  | // The label. | 
|  | Unnamed_label* label_; | 
|  | }; | 
|  |  | 
|  | // An if statement. | 
|  |  | 
|  | class If_statement : public Statement | 
|  | { | 
|  | public: | 
|  | If_statement(Expression* cond, Block* then_block, Block* else_block, | 
|  | Location location) | 
|  | : Statement(STATEMENT_IF, location), | 
|  | cond_(cond), then_block_(then_block), else_block_(else_block) | 
|  | { } | 
|  |  | 
|  | Expression* | 
|  | condition() const | 
|  | { return this->cond_; } | 
|  |  | 
|  | Block* | 
|  | then_block() const | 
|  | { return this->then_block_; } | 
|  |  | 
|  | Block* | 
|  | else_block() const | 
|  | { return this->else_block_; } | 
|  |  | 
|  | // Import an if statement. | 
|  | static Statement* | 
|  | do_import(Import_function_body*, Location); | 
|  |  | 
|  | protected: | 
|  | int | 
|  | do_traverse(Traverse*); | 
|  |  | 
|  | void | 
|  | do_determine_types(); | 
|  |  | 
|  | void | 
|  | do_check_types(Gogo*); | 
|  |  | 
|  | int | 
|  | do_inlining_cost() | 
|  | { return 5; } | 
|  |  | 
|  | void | 
|  | do_export_statement(Export_function_body*); | 
|  |  | 
|  | bool | 
|  | do_may_fall_through() const; | 
|  |  | 
|  | Bstatement* | 
|  | do_get_backend(Translate_context*); | 
|  |  | 
|  | void | 
|  | do_dump_statement(Ast_dump_context*) const; | 
|  |  | 
|  | private: | 
|  | Expression* cond_; | 
|  | Block* then_block_; | 
|  | Block* else_block_; | 
|  | }; | 
|  |  | 
|  | // A for statement. | 
|  |  | 
|  | class For_statement : public Statement | 
|  | { | 
|  | public: | 
|  | For_statement(Block* init, Expression* cond, Block* post, | 
|  | Location location) | 
|  | : Statement(STATEMENT_FOR, location), | 
|  | init_(init), cond_(cond), post_(post), statements_(NULL), | 
|  | break_label_(NULL), continue_label_(NULL) | 
|  | { } | 
|  |  | 
|  | // Add the statements. | 
|  | void | 
|  | add_statements(Block* statements) | 
|  | { | 
|  | go_assert(this->statements_ == NULL); | 
|  | this->statements_ = statements; | 
|  | } | 
|  |  | 
|  | // Return the break label for this for statement. | 
|  | Unnamed_label* | 
|  | break_label(); | 
|  |  | 
|  | // Return the continue label for this for statement. | 
|  | Unnamed_label* | 
|  | continue_label(); | 
|  |  | 
|  | // Set the break and continue labels for this statement. | 
|  | void | 
|  | set_break_continue_labels(Unnamed_label* break_label, | 
|  | Unnamed_label* continue_label); | 
|  |  | 
|  | protected: | 
|  | int | 
|  | do_traverse(Traverse*); | 
|  |  | 
|  | bool | 
|  | do_traverse_assignments(Traverse_assignments*) | 
|  | { go_unreachable(); } | 
|  |  | 
|  | Statement* | 
|  | do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); | 
|  |  | 
|  | bool | 
|  | do_may_fall_through() const; | 
|  |  | 
|  | Bstatement* | 
|  | do_get_backend(Translate_context*) | 
|  | { go_unreachable(); } | 
|  |  | 
|  | void | 
|  | do_dump_statement(Ast_dump_context*) const; | 
|  |  | 
|  | private: | 
|  | // The initialization statements.  This may be NULL. | 
|  | Block* init_; | 
|  | // The condition.  This may be NULL. | 
|  | Expression* cond_; | 
|  | // The statements to run after each iteration.  This may be NULL. | 
|  | Block* post_; | 
|  | // The statements in the loop itself. | 
|  | Block* statements_; | 
|  | // The break label, if needed. | 
|  | Unnamed_label* break_label_; | 
|  | // The continue label, if needed. | 
|  | Unnamed_label* continue_label_; | 
|  | }; | 
|  |  | 
|  | // A for statement over a range clause. | 
|  |  | 
|  | class For_range_statement : public Statement | 
|  | { | 
|  | public: | 
|  | For_range_statement(Expression* index_var, Expression* value_var, | 
|  | Expression* range, Location location) | 
|  | : Statement(STATEMENT_FOR_RANGE, location), | 
|  | index_var_(index_var), value_var_(value_var), range_(range), | 
|  | statements_(NULL), break_label_(NULL), continue_label_(NULL) | 
|  | { } | 
|  |  | 
|  | // Add the statements. | 
|  | void | 
|  | add_statements(Block* statements) | 
|  | { | 
|  | go_assert(this->statements_ == NULL); | 
|  | this->statements_ = statements; | 
|  | } | 
|  |  | 
|  | // Return the break label for this for statement. | 
|  | Unnamed_label* | 
|  | break_label(); | 
|  |  | 
|  | // Return the continue label for this for statement. | 
|  | Unnamed_label* | 
|  | continue_label(); | 
|  |  | 
|  | protected: | 
|  | int | 
|  | do_traverse(Traverse*); | 
|  |  | 
|  | bool | 
|  | do_traverse_assignments(Traverse_assignments*) | 
|  | { go_unreachable(); } | 
|  |  | 
|  | Statement* | 
|  | do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); | 
|  |  | 
|  | Bstatement* | 
|  | do_get_backend(Translate_context*) | 
|  | { go_unreachable(); } | 
|  |  | 
|  | void | 
|  | do_dump_statement(Ast_dump_context*) const; | 
|  |  | 
|  | private: | 
|  | Expression* | 
|  | make_range_ref(Named_object*, Temporary_statement*, Location); | 
|  |  | 
|  | Call_expression* | 
|  | call_builtin(Gogo*, const char* funcname, Expression* arg, Location); | 
|  |  | 
|  | void | 
|  | lower_range_array(Gogo*, Block*, Block*, Named_object*, Temporary_statement*, | 
|  | Temporary_statement*, Temporary_statement*, | 
|  | Block**, Expression**, Block**, Block**); | 
|  |  | 
|  | void | 
|  | lower_range_slice(Gogo*, Block*, Block*, Named_object*, Temporary_statement*, | 
|  | Temporary_statement*, Temporary_statement*, | 
|  | Block**, Expression**, Block**, Block**); | 
|  |  | 
|  | void | 
|  | lower_range_string(Gogo*, Block*, Block*, Named_object*, Temporary_statement*, | 
|  | Temporary_statement*, Temporary_statement*, | 
|  | Block**, Expression**, Block**, Block**); | 
|  |  | 
|  | void | 
|  | lower_range_map(Gogo*, Map_type*, Block*, Block*, Named_object*, | 
|  | Temporary_statement*, Temporary_statement*, | 
|  | Temporary_statement*, Block**, Expression**, Block**, | 
|  | Block**); | 
|  |  | 
|  | void | 
|  | lower_range_channel(Gogo*, Block*, Block*, Named_object*, | 
|  | Temporary_statement*, Temporary_statement*, | 
|  | Temporary_statement*, Block**, Expression**, Block**, | 
|  | Block**); | 
|  |  | 
|  | Statement* | 
|  | lower_map_range_clear(Type*, Block*, Expression*, Named_object*, | 
|  | Temporary_statement*, Location); | 
|  |  | 
|  | Statement* | 
|  | lower_array_range_clear(Gogo*, Type*, Expression*, Block*, | 
|  | Named_object*, Temporary_statement*, | 
|  | Location); | 
|  |  | 
|  | // The variable which is set to the index value. | 
|  | Expression* index_var_; | 
|  | // The variable which is set to the element value.  This may be | 
|  | // NULL. | 
|  | Expression* value_var_; | 
|  | // The expression we are ranging over. | 
|  | Expression* range_; | 
|  | // The statements in the block. | 
|  | Block* statements_; | 
|  | // The break label, if needed. | 
|  | Unnamed_label* break_label_; | 
|  | // The continue label, if needed. | 
|  | Unnamed_label* continue_label_; | 
|  | }; | 
|  |  | 
|  | // Class Case_clauses holds the clauses of a switch statement.  This | 
|  | // is built by the parser. | 
|  |  | 
|  | class Case_clauses | 
|  | { | 
|  | public: | 
|  | Case_clauses() | 
|  | : clauses_() | 
|  | { } | 
|  |  | 
|  | // Add a new clause.  CASES is a list of case expressions; it may be | 
|  | // NULL.  IS_DEFAULT is true if this is the default case. | 
|  | // STATEMENTS is a block of statements.  IS_FALLTHROUGH is true if | 
|  | // after the statements the case clause should fall through to the | 
|  | // next clause. | 
|  | void | 
|  | add(Expression_list* cases, bool is_default, Block* statements, | 
|  | bool is_fallthrough, Location location) | 
|  | { | 
|  | this->clauses_.push_back(Case_clause(cases, is_default, statements, | 
|  | is_fallthrough, location)); | 
|  | } | 
|  |  | 
|  | // Return whether there are no clauses. | 
|  | bool | 
|  | empty() const | 
|  | { return this->clauses_.empty(); } | 
|  |  | 
|  | // Traverse the case clauses. | 
|  | int | 
|  | traverse(Traverse*); | 
|  |  | 
|  | // Lower for a nonconstant switch. | 
|  | void | 
|  | lower(Block*, Temporary_statement*, Unnamed_label*) const; | 
|  |  | 
|  | // Determine types of expressions.  The Type parameter is the type | 
|  | // of the switch value. | 
|  | void | 
|  | determine_types(Type*); | 
|  |  | 
|  | // Check types.  The Type parameter is the type of the switch value. | 
|  | bool | 
|  | check_types(Type*); | 
|  |  | 
|  | // Return true if all the clauses are constant values. | 
|  | bool | 
|  | is_constant() const; | 
|  |  | 
|  | // Return true if these clauses may fall through to the statements | 
|  | // following the switch statement. | 
|  | bool | 
|  | may_fall_through() const; | 
|  |  | 
|  | // Return the body of a SWITCH_EXPR when all the clauses are | 
|  | // constants. | 
|  | void | 
|  | get_backend(Translate_context*, Unnamed_label* break_label, | 
|  | std::vector<std::vector<Bexpression*> >* all_cases, | 
|  | std::vector<Bstatement*>* all_statements) const; | 
|  |  | 
|  | // Dump the AST representation to a dump context. | 
|  | void | 
|  | dump_clauses(Ast_dump_context*) const; | 
|  |  | 
|  | private: | 
|  | // For a constant switch we need to keep a record of constants we | 
|  | // have already seen. | 
|  | class Hash_integer_value; | 
|  | class Eq_integer_value; | 
|  | typedef Unordered_set_hash(Expression*, Hash_integer_value, | 
|  | Eq_integer_value) Case_constants; | 
|  |  | 
|  | // One case clause. | 
|  | class Case_clause | 
|  | { | 
|  | public: | 
|  | Case_clause() | 
|  | : cases_(NULL), statements_(NULL), is_default_(false), | 
|  | is_fallthrough_(false), location_(Linemap::unknown_location()) | 
|  | { } | 
|  |  | 
|  | Case_clause(Expression_list* cases, bool is_default, Block* statements, | 
|  | bool is_fallthrough, Location location) | 
|  | : cases_(cases), statements_(statements), is_default_(is_default), | 
|  | is_fallthrough_(is_fallthrough), location_(location) | 
|  | { } | 
|  |  | 
|  | // Whether this clause falls through to the next clause. | 
|  | bool | 
|  | is_fallthrough() const | 
|  | { return this->is_fallthrough_; } | 
|  |  | 
|  | // Whether this is the default. | 
|  | bool | 
|  | is_default() const | 
|  | { return this->is_default_; } | 
|  |  | 
|  | // The location of this clause. | 
|  | Location | 
|  | location() const | 
|  | { return this->location_; } | 
|  |  | 
|  | // Traversal. | 
|  | int | 
|  | traverse(Traverse*); | 
|  |  | 
|  | // Lower for a nonconstant switch. | 
|  | void | 
|  | lower(Block*, Temporary_statement*, Unnamed_label*, Unnamed_label*) const; | 
|  |  | 
|  | // Determine types. | 
|  | void | 
|  | determine_types(Type*); | 
|  |  | 
|  | // Check types. | 
|  | bool | 
|  | check_types(Type*); | 
|  |  | 
|  | // Return true if all the case expressions are constant. | 
|  | bool | 
|  | is_constant() const; | 
|  |  | 
|  | // Return true if this clause may fall through to execute the | 
|  | // statements following the switch statement.  This is not the | 
|  | // same as whether this clause falls through to the next clause. | 
|  | bool | 
|  | may_fall_through() const; | 
|  |  | 
|  | // Convert the case values and statements to the backend | 
|  | // representation. | 
|  | Bstatement* | 
|  | get_backend(Translate_context*, Unnamed_label* break_label, | 
|  | Case_constants*, std::vector<Bexpression*>* cases) const; | 
|  |  | 
|  | // Dump the AST representation to a dump context. | 
|  | void | 
|  | dump_clause(Ast_dump_context*) const; | 
|  |  | 
|  | private: | 
|  | // The list of case expressions. | 
|  | Expression_list* cases_; | 
|  | // The statements to execute. | 
|  | Block* statements_; | 
|  | // Whether this is the default case. | 
|  | bool is_default_; | 
|  | // Whether this falls through after the statements. | 
|  | bool is_fallthrough_; | 
|  | // The location of this case clause. | 
|  | Location location_; | 
|  | }; | 
|  |  | 
|  | friend class Case_clause; | 
|  |  | 
|  | // The type of the list of clauses. | 
|  | typedef std::vector<Case_clause> Clauses; | 
|  |  | 
|  | // All the case clauses. | 
|  | Clauses clauses_; | 
|  | }; | 
|  |  | 
|  | // A switch statement. | 
|  |  | 
|  | class Switch_statement : public Statement | 
|  | { | 
|  | public: | 
|  | Switch_statement(Expression* val, Location location) | 
|  | : Statement(STATEMENT_SWITCH, location), | 
|  | val_(val), clauses_(NULL), break_label_(NULL) | 
|  | { } | 
|  |  | 
|  | // Add the clauses. | 
|  | void | 
|  | add_clauses(Case_clauses* clauses) | 
|  | { | 
|  | go_assert(this->clauses_ == NULL); | 
|  | this->clauses_ = clauses; | 
|  | } | 
|  |  | 
|  | // Return the break label for this switch statement. | 
|  | Unnamed_label* | 
|  | break_label(); | 
|  |  | 
|  | protected: | 
|  | int | 
|  | do_traverse(Traverse*); | 
|  |  | 
|  | Statement* | 
|  | do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); | 
|  |  | 
|  | Bstatement* | 
|  | do_get_backend(Translate_context*) | 
|  | { go_unreachable(); } | 
|  |  | 
|  | void | 
|  | do_dump_statement(Ast_dump_context*) const; | 
|  |  | 
|  | bool | 
|  | do_may_fall_through() const; | 
|  |  | 
|  | private: | 
|  | // The value to switch on.  This may be NULL. | 
|  | Expression* val_; | 
|  | // The case clauses. | 
|  | Case_clauses* clauses_; | 
|  | // The break label, if needed. | 
|  | Unnamed_label* break_label_; | 
|  | }; | 
|  |  | 
|  | // Class Type_case_clauses holds the clauses of a type switch | 
|  | // statement.  This is built by the parser. | 
|  |  | 
|  | class Type_case_clauses | 
|  | { | 
|  | public: | 
|  | Type_case_clauses() | 
|  | : clauses_() | 
|  | { } | 
|  |  | 
|  | // Add a new clause.  TYPE is the type for this clause; it may be | 
|  | // NULL.  IS_FALLTHROUGH is true if this falls through to the next | 
|  | // clause; in this case STATEMENTS will be NULL.  IS_DEFAULT is true | 
|  | // if this is the default case.  STATEMENTS is a block of | 
|  | // statements; it may be NULL. | 
|  | void | 
|  | add(Type* type, bool is_fallthrough, bool is_default, Block* statements, | 
|  | Location location) | 
|  | { | 
|  | this->clauses_.push_back(Type_case_clause(type, is_fallthrough, is_default, | 
|  | statements, location)); | 
|  | } | 
|  |  | 
|  | // Return whether there are no clauses. | 
|  | bool | 
|  | empty() const | 
|  | { return this->clauses_.empty(); } | 
|  |  | 
|  | // Traverse the type case clauses. | 
|  | int | 
|  | traverse(Traverse*); | 
|  |  | 
|  | // Check for duplicates. | 
|  | void | 
|  | check_duplicates() const; | 
|  |  | 
|  | // Lower to if and goto statements. | 
|  | void | 
|  | lower(Type*, Block*, Temporary_statement* descriptor_temp, | 
|  | Unnamed_label* break_label) const; | 
|  |  | 
|  | // Return true if these clauses may fall through to the statements | 
|  | // following the switch statement. | 
|  | bool | 
|  | may_fall_through() const; | 
|  |  | 
|  | // Dump the AST representation to a dump context. | 
|  | void | 
|  | dump_clauses(Ast_dump_context*) const; | 
|  |  | 
|  | private: | 
|  | // One type case clause. | 
|  | class Type_case_clause | 
|  | { | 
|  | public: | 
|  | Type_case_clause() | 
|  | : type_(NULL), statements_(NULL), is_default_(false), | 
|  | location_(Linemap::unknown_location()) | 
|  | { } | 
|  |  | 
|  | Type_case_clause(Type* type, bool is_fallthrough, bool is_default, | 
|  | Block* statements, Location location) | 
|  | : type_(type), statements_(statements), is_fallthrough_(is_fallthrough), | 
|  | is_default_(is_default), location_(location) | 
|  | { } | 
|  |  | 
|  | // The type. | 
|  | Type* | 
|  | type() const | 
|  | { return this->type_; } | 
|  |  | 
|  | // Whether this is the default. | 
|  | bool | 
|  | is_default() const | 
|  | { return this->is_default_; } | 
|  |  | 
|  | // The location of this type clause. | 
|  | Location | 
|  | location() const | 
|  | { return this->location_; } | 
|  |  | 
|  | // Traversal. | 
|  | int | 
|  | traverse(Traverse*); | 
|  |  | 
|  | // Lower to if and goto statements. | 
|  | void | 
|  | lower(Type*, Block*, Temporary_statement* descriptor_temp, | 
|  | Unnamed_label* break_label, Unnamed_label** stmts_label) const; | 
|  |  | 
|  | // Return true if this clause may fall through to execute the | 
|  | // statements following the switch statement.  This is not the | 
|  | // same as whether this clause falls through to the next clause. | 
|  | bool | 
|  | may_fall_through() const; | 
|  |  | 
|  | // Dump the AST representation to a dump context. | 
|  | void | 
|  | dump_clause(Ast_dump_context*) const; | 
|  |  | 
|  | private: | 
|  | // The type for this type clause. | 
|  | Type* type_; | 
|  | // The statements to execute. | 
|  | Block* statements_; | 
|  | // Whether this falls through--this is true for "case T1, T2". | 
|  | bool is_fallthrough_; | 
|  | // Whether this is the default case. | 
|  | bool is_default_; | 
|  | // The location of this type case clause. | 
|  | Location location_; | 
|  | }; | 
|  |  | 
|  | friend class Type_case_clause; | 
|  |  | 
|  | // The type of the list of type clauses. | 
|  | typedef std::vector<Type_case_clause> Type_clauses; | 
|  |  | 
|  | // All the type case clauses. | 
|  | Type_clauses clauses_; | 
|  | }; | 
|  |  | 
|  | // A type switch statement. | 
|  |  | 
|  | class Type_switch_statement : public Statement | 
|  | { | 
|  | public: | 
|  | Type_switch_statement(const std::string& name, Expression* expr, | 
|  | Location location) | 
|  | : Statement(STATEMENT_TYPE_SWITCH, location), | 
|  | name_(name), expr_(expr), clauses_(NULL), break_label_(NULL) | 
|  | { } | 
|  |  | 
|  | // Add the clauses. | 
|  | void | 
|  | add_clauses(Type_case_clauses* clauses) | 
|  | { | 
|  | go_assert(this->clauses_ == NULL); | 
|  | this->clauses_ = clauses; | 
|  | } | 
|  |  | 
|  | // Return the break label for this type switch statement. | 
|  | Unnamed_label* | 
|  | break_label(); | 
|  |  | 
|  | protected: | 
|  | int | 
|  | do_traverse(Traverse*); | 
|  |  | 
|  | Statement* | 
|  | do_lower(Gogo*, Named_object*, Block*, Statement_inserter*); | 
|  |  | 
|  | Bstatement* | 
|  | do_get_backend(Translate_context*) | 
|  | { go_unreachable(); } | 
|  |  | 
|  | void | 
|  | do_dump_statement(Ast_dump_context*) const; | 
|  |  | 
|  | bool | 
|  | do_may_fall_through() const; | 
|  |  | 
|  | private: | 
|  | // The name of the variable declared in the type switch guard.  Empty if there | 
|  | // is no variable declared. | 
|  | std::string name_; | 
|  | // The expression we are switching on if there is no variable. | 
|  | Expression* expr_; | 
|  | // The type case clauses. | 
|  | Type_case_clauses* clauses_; | 
|  | // The break label, if needed. | 
|  | Unnamed_label* break_label_; | 
|  | }; | 
|  |  | 
|  | #endif // !defined(GO_STATEMENTS_H) |