2245 lines
54 KiB
C++
2245 lines
54 KiB
C++
// 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(); }
|
|
|
|
bool
|
|
has_default() const;
|
|
|
|
// Traverse the select clauses.
|
|
int
|
|
traverse(Traverse*);
|
|
|
|
// Lower statements.
|
|
void
|
|
lower(Gogo*, Named_object*, Block*, Temporary_statement*,
|
|
Temporary_statement*, int* send_count, int* recv_count);
|
|
|
|
// 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), case_index_(0),
|
|
location_(location), is_send_(is_send), is_default_(is_default),
|
|
is_lowered_(false), is_case_index_set_(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*, int,
|
|
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_; }
|
|
|
|
// Return the case index for this clause.
|
|
int
|
|
case_index() const
|
|
{
|
|
go_assert(this->is_case_index_set_);
|
|
return this->case_index_;
|
|
}
|
|
|
|
// Set the case index.
|
|
void
|
|
set_case_index(int i)
|
|
{
|
|
go_assert(!this->is_case_index_set_);
|
|
this->case_index_ = i;
|
|
this->is_case_index_set_ = true;
|
|
}
|
|
|
|
// 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:
|
|
void
|
|
lower_send(Block*, Expression*, Expression*);
|
|
|
|
void
|
|
lower_recv(Gogo*, Named_object*, Block*, Expression*, Expression*,
|
|
Temporary_statement*);
|
|
|
|
void
|
|
set_case(Block*, Expression*, Expression*, Expression*);
|
|
|
|
// 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 index of this clause in the switch statement. If
|
|
// runtime.selectgo returns this index, this clause has been
|
|
// chosen.
|
|
int case_index_;
|
|
// 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_;
|
|
// Whether the case index has been set.
|
|
bool is_case_index_set_;
|
|
};
|
|
|
|
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(Gogo*, 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(Gogo*, 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)
|