blob: 7c464ce7f54dae73ece5169a9e46882faeafd1bc [file] [log] [blame]
// expressions.cc -- Go frontend expression handling.
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "go-system.h"
#include <algorithm>
#include "go-c.h"
#include "gogo.h"
#include "go-diagnostics.h"
#include "go-encode-id.h"
#include "types.h"
#include "export.h"
#include "import.h"
#include "statements.h"
#include "lex.h"
#include "runtime.h"
#include "backend.h"
#include "expressions.h"
#include "ast-dump.h"
// Class Expression.
Expression::Expression(Expression_classification classification,
Location location)
: classification_(classification), location_(location)
{
}
Expression::~Expression()
{
}
// Traverse the expressions.
int
Expression::traverse(Expression** pexpr, Traverse* traverse)
{
Expression* expr = *pexpr;
if ((traverse->traverse_mask() & Traverse::traverse_expressions) != 0)
{
int t = traverse->expression(pexpr);
if (t == TRAVERSE_EXIT)
return TRAVERSE_EXIT;
else if (t == TRAVERSE_SKIP_COMPONENTS)
return TRAVERSE_CONTINUE;
}
return expr->do_traverse(traverse);
}
// Traverse subexpressions of this expression.
int
Expression::traverse_subexpressions(Traverse* traverse)
{
return this->do_traverse(traverse);
}
// Default implementation for do_traverse for child classes.
int
Expression::do_traverse(Traverse*)
{
return TRAVERSE_CONTINUE;
}
// This virtual function is called by the parser if the value of this
// expression is being discarded. By default, we give an error.
// Expressions with side effects override.
bool
Expression::do_discarding_value()
{
this->unused_value_error();
return false;
}
// This virtual function is called to export expressions. This will
// only be used by expressions which may be constant.
void
Expression::do_export(Export_function_body*) const
{
go_unreachable();
}
// Give an error saying that the value of the expression is not used.
void
Expression::unused_value_error()
{
this->report_error(_("value computed is not used"));
}
// Note that this expression is an error. This is called by children
// when they discover an error.
void
Expression::set_is_error()
{
this->classification_ = EXPRESSION_ERROR;
}
// For children to call to report an error conveniently.
void
Expression::report_error(const char* msg)
{
go_error_at(this->location_, "%s", msg);
this->set_is_error();
}
// Set types of variables and constants. This is implemented by the
// child class.
void
Expression::determine_type(const Type_context* context)
{
this->do_determine_type(context);
}
// Set types when there is no context.
void
Expression::determine_type_no_context()
{
Type_context context;
this->do_determine_type(&context);
}
// Return true if two expressions refer to the same variable or struct
// field. This can only be true when there are no side effects.
bool
Expression::is_same_variable(Expression* a, Expression* b)
{
if (a->classification() != b->classification())
return false;
Var_expression* av = a->var_expression();
if (av != NULL)
return av->named_object() == b->var_expression()->named_object();
Field_reference_expression* af = a->field_reference_expression();
if (af != NULL)
{
Field_reference_expression* bf = b->field_reference_expression();
return (af->field_index() == bf->field_index()
&& Expression::is_same_variable(af->expr(), bf->expr()));
}
Unary_expression* au = a->unary_expression();
if (au != NULL)
{
Unary_expression* bu = b->unary_expression();
return (au->op() == OPERATOR_MULT
&& bu->op() == OPERATOR_MULT
&& Expression::is_same_variable(au->operand(),
bu->operand()));
}
return false;
}
// Return an expression handling any conversions which must be done during
// assignment.
Expression*
Expression::convert_for_assignment(Gogo*, Type* lhs_type,
Expression* rhs, Location location)
{
Type* rhs_type = rhs->type();
if (lhs_type->is_error()
|| rhs_type->is_error()
|| rhs->is_error_expression())
return Expression::make_error(location);
bool are_identical = Type::are_identical(lhs_type, rhs_type,
(Type::COMPARE_ERRORS
| Type::COMPARE_TAGS),
NULL);
if (!are_identical && lhs_type->interface_type() != NULL)
{
if (rhs_type->interface_type() == NULL)
return Expression::convert_type_to_interface(lhs_type, rhs, location);
else
return Expression::convert_interface_to_interface(lhs_type, rhs, false,
location);
}
else if (!are_identical && rhs_type->interface_type() != NULL)
return Expression::convert_interface_to_type(lhs_type, rhs, location);
else if (lhs_type->is_slice_type() && rhs_type->is_nil_type())
{
// Assigning nil to a slice.
Expression* nil = Expression::make_nil(location);
Expression* zero = Expression::make_integer_ul(0, NULL, location);
return Expression::make_slice_value(lhs_type, nil, zero, zero, location);
}
else if (rhs_type->is_nil_type())
return Expression::make_nil(location);
else if (are_identical)
{
if (lhs_type->forwarded() != rhs_type->forwarded())
{
// Different but identical types require an explicit
// conversion. This happens with type aliases.
return Expression::make_cast(lhs_type, rhs, location);
}
// No conversion is needed.
return rhs;
}
else if (lhs_type->points_to() != NULL)
return Expression::make_unsafe_cast(lhs_type, rhs, location);
else if (lhs_type->is_numeric_type())
return Expression::make_cast(lhs_type, rhs, location);
else if ((lhs_type->struct_type() != NULL
&& rhs_type->struct_type() != NULL)
|| (lhs_type->array_type() != NULL
&& rhs_type->array_type() != NULL))
{
// This conversion must be permitted by Go, or we wouldn't have
// gotten here.
return Expression::make_unsafe_cast(lhs_type, rhs, location);
}
else
return rhs;
}
// Return an expression for a conversion from a non-interface type to an
// interface type.
Expression*
Expression::convert_type_to_interface(Type* lhs_type, Expression* rhs,
Location location)
{
Interface_type* lhs_interface_type = lhs_type->interface_type();
bool lhs_is_empty = lhs_interface_type->is_empty();
// Since RHS_TYPE is a static type, we can create the interface
// method table at compile time.
// When setting an interface to nil, we just set both fields to
// NULL.
Type* rhs_type = rhs->type();
if (rhs_type->is_nil_type())
{
Expression* nil = Expression::make_nil(location);
return Expression::make_interface_value(lhs_type, nil, nil, location);
}
// This should have been checked already.
if (!lhs_interface_type->implements_interface(rhs_type, NULL))
{
go_assert(saw_errors());
return Expression::make_error(location);
}
// An interface is a tuple. If LHS_TYPE is an empty interface type,
// then the first field is the type descriptor for RHS_TYPE.
// Otherwise it is the interface method table for RHS_TYPE.
Expression* first_field;
if (lhs_is_empty)
first_field = Expression::make_type_descriptor(rhs_type, location);
else
{
// Build the interface method table for this interface and this
// object type: a list of function pointers for each interface
// method.
Named_type* rhs_named_type = rhs_type->named_type();
Struct_type* rhs_struct_type = rhs_type->struct_type();
bool is_pointer = false;
if (rhs_named_type == NULL && rhs_struct_type == NULL)
{
rhs_named_type = rhs_type->deref()->named_type();
rhs_struct_type = rhs_type->deref()->struct_type();
is_pointer = true;
}
if (rhs_named_type != NULL)
first_field =
rhs_named_type->interface_method_table(lhs_interface_type,
is_pointer);
else if (rhs_struct_type != NULL)
first_field =
rhs_struct_type->interface_method_table(lhs_interface_type,
is_pointer);
else
first_field = Expression::make_nil(location);
}
Expression* obj;
if (rhs_type->points_to() != NULL)
{
// We are assigning a pointer to the interface; the interface
// holds the pointer itself.
obj = rhs;
}
else
{
// We are assigning a non-pointer value to the interface; the
// interface gets a copy of the value in the heap if it escapes.
// TODO(cmang): Associate escape state state of RHS with newly
// created OBJ.
obj = Expression::make_heap_expression(rhs, location);
}
return Expression::make_interface_value(lhs_type, first_field, obj, location);
}
// Return an expression for the type descriptor of RHS.
Expression*
Expression::get_interface_type_descriptor(Expression* rhs)
{
go_assert(rhs->type()->interface_type() != NULL);
Location location = rhs->location();
// The type descriptor is the first field of an empty interface.
if (rhs->type()->interface_type()->is_empty())
return Expression::make_interface_info(rhs, INTERFACE_INFO_TYPE_DESCRIPTOR,
location);
Expression* mtable =
Expression::make_interface_info(rhs, INTERFACE_INFO_METHODS, location);
Expression* descriptor =
Expression::make_dereference(mtable, NIL_CHECK_NOT_NEEDED, location);
descriptor = Expression::make_field_reference(descriptor, 0, location);
Expression* nil = Expression::make_nil(location);
Expression* eq =
Expression::make_binary(OPERATOR_EQEQ, mtable, nil, location);
return Expression::make_conditional(eq, nil, descriptor, location);
}
// Return an expression for the conversion of an interface type to an
// interface type.
Expression*
Expression::convert_interface_to_interface(Type *lhs_type, Expression* rhs,
bool for_type_guard,
Location location)
{
if (Type::are_identical(lhs_type, rhs->type(),
Type::COMPARE_ERRORS | Type::COMPARE_TAGS,
NULL))
return rhs;
Interface_type* lhs_interface_type = lhs_type->interface_type();
bool lhs_is_empty = lhs_interface_type->is_empty();
// In the general case this requires runtime examination of the type
// method table to match it up with the interface methods.
// FIXME: If all of the methods in the right hand side interface
// also appear in the left hand side interface, then we don't need
// to do a runtime check, although we still need to build a new
// method table.
// We are going to evaluate RHS multiple times.
go_assert(rhs->is_variable());
// Get the type descriptor for the right hand side. This will be
// NULL for a nil interface.
Expression* rhs_type_expr = Expression::get_interface_type_descriptor(rhs);
Expression* lhs_type_expr =
Expression::make_type_descriptor(lhs_type, location);
Expression* first_field;
if (for_type_guard)
{
// A type assertion fails when converting a nil interface.
first_field = Runtime::make_call(Runtime::ASSERTITAB, location, 2,
lhs_type_expr, rhs_type_expr);
}
else if (lhs_is_empty)
{
// A conversion to an empty interface always succeeds, and the
// first field is just the type descriptor of the object.
first_field = rhs_type_expr;
}
else
{
// A conversion to a non-empty interface may fail, but unlike a
// type assertion converting nil will always succeed.
first_field = Runtime::make_call(Runtime::REQUIREITAB, location, 2,
lhs_type_expr, rhs_type_expr);
}
// The second field is simply the object pointer.
Expression* obj =
Expression::make_interface_info(rhs, INTERFACE_INFO_OBJECT, location);
return Expression::make_interface_value(lhs_type, first_field, obj, location);
}
// Return an expression for the conversion of an interface type to a
// non-interface type.
Expression*
Expression::convert_interface_to_type(Type *lhs_type, Expression* rhs,
Location location)
{
// We are going to evaluate RHS multiple times.
go_assert(rhs->is_variable());
// Call a function to check that the type is valid. The function
// will panic with an appropriate runtime type error if the type is
// not valid.
Expression* lhs_type_expr = Expression::make_type_descriptor(lhs_type,
location);
Expression* rhs_descriptor =
Expression::get_interface_type_descriptor(rhs);
Type* rhs_type = rhs->type();
Expression* rhs_inter_expr = Expression::make_type_descriptor(rhs_type,
location);
Expression* check_iface = Runtime::make_call(Runtime::ASSERTI2T,
location, 3, lhs_type_expr,
rhs_descriptor, rhs_inter_expr);
// If the call succeeds, pull out the value.
Expression* obj = Expression::make_interface_info(rhs, INTERFACE_INFO_OBJECT,
location);
// If the value is a pointer, then it is the value we want.
// Otherwise it points to the value.
if (lhs_type->points_to() == NULL)
{
obj = Expression::make_unsafe_cast(Type::make_pointer_type(lhs_type), obj,
location);
obj = Expression::make_dereference(obj, NIL_CHECK_NOT_NEEDED,
location);
}
return Expression::make_compound(check_iface, obj, location);
}
// Convert an expression to its backend representation. This is implemented by
// the child class. Not that it is not in general safe to call this multiple
// times for a single expression, but that we don't catch such errors.
Bexpression*
Expression::get_backend(Translate_context* context)
{
// The child may have marked this expression as having an error.
if (this->classification_ == EXPRESSION_ERROR)
return context->backend()->error_expression();
return this->do_get_backend(context);
}
// Return a backend expression for VAL.
Bexpression*
Expression::backend_numeric_constant_expression(Translate_context* context,
Numeric_constant* val)
{
Gogo* gogo = context->gogo();
Type* type = val->type();
if (type == NULL)
return gogo->backend()->error_expression();
Btype* btype = type->get_backend(gogo);
Bexpression* ret;
if (type->integer_type() != NULL)
{
mpz_t ival;
if (!val->to_int(&ival))
{
go_assert(saw_errors());
return gogo->backend()->error_expression();
}
ret = gogo->backend()->integer_constant_expression(btype, ival);
mpz_clear(ival);
}
else if (type->float_type() != NULL)
{
mpfr_t fval;
if (!val->to_float(&fval))
{
go_assert(saw_errors());
return gogo->backend()->error_expression();
}
ret = gogo->backend()->float_constant_expression(btype, fval);
mpfr_clear(fval);
}
else if (type->complex_type() != NULL)
{
mpc_t cval;
if (!val->to_complex(&cval))
{
go_assert(saw_errors());
return gogo->backend()->error_expression();
}
ret = gogo->backend()->complex_constant_expression(btype, cval);
mpc_clear(cval);
}
else
go_unreachable();
return ret;
}
// Return an expression which evaluates to true if VAL, of arbitrary integer
// type, is negative or is more than the maximum value of the Go type "int".
Expression*
Expression::check_bounds(Expression* val, Location loc)
{
Type* val_type = val->type();
Type* bound_type = Type::lookup_integer_type("int");
int val_type_size;
bool val_is_unsigned = false;
if (val_type->integer_type() != NULL)
{
val_type_size = val_type->integer_type()->bits();
val_is_unsigned = val_type->integer_type()->is_unsigned();
}
else
{
if (!val_type->is_numeric_type()
|| !Type::are_convertible(bound_type, val_type, NULL))
{
go_assert(saw_errors());
return Expression::make_boolean(true, loc);
}
if (val_type->complex_type() != NULL)
val_type_size = val_type->complex_type()->bits();
else
val_type_size = val_type->float_type()->bits();
}
Expression* negative_index = Expression::make_boolean(false, loc);
Expression* index_overflows = Expression::make_boolean(false, loc);
if (!val_is_unsigned)
{
Expression* zero = Expression::make_integer_ul(0, val_type, loc);
negative_index = Expression::make_binary(OPERATOR_LT, val, zero, loc);
}
int bound_type_size = bound_type->integer_type()->bits();
if (val_type_size > bound_type_size
|| (val_type_size == bound_type_size
&& val_is_unsigned))
{
mpz_t one;
mpz_init_set_ui(one, 1UL);
// maxval = 2^(bound_type_size - 1) - 1
mpz_t maxval;
mpz_init(maxval);
mpz_mul_2exp(maxval, one, bound_type_size - 1);
mpz_sub_ui(maxval, maxval, 1);
Expression* max = Expression::make_integer_z(&maxval, val_type, loc);
mpz_clear(one);
mpz_clear(maxval);
index_overflows = Expression::make_binary(OPERATOR_GT, val, max, loc);
}
return Expression::make_binary(OPERATOR_OROR, negative_index, index_overflows,
loc);
}
void
Expression::dump_expression(Ast_dump_context* ast_dump_context) const
{
this->do_dump_expression(ast_dump_context);
}
// Error expressions. This are used to avoid cascading errors.
class Error_expression : public Expression
{
public:
Error_expression(Location location)
: Expression(EXPRESSION_ERROR, location)
{ }
protected:
bool
do_is_constant() const
{ return true; }
bool
do_numeric_constant_value(Numeric_constant* nc) const
{
nc->set_unsigned_long(NULL, 0);
return true;
}
bool
do_discarding_value()
{ return true; }
Type*
do_type()
{ return Type::make_error_type(); }
void
do_determine_type(const Type_context*)
{ }
Expression*
do_copy()
{ return this; }
bool
do_is_addressable() const
{ return true; }
Bexpression*
do_get_backend(Translate_context* context)
{ return context->backend()->error_expression(); }
void
do_dump_expression(Ast_dump_context*) const;
};
// Dump the ast representation for an error expression to a dump context.
void
Error_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const
{
ast_dump_context->ostream() << "_Error_" ;
}
Expression*
Expression::make_error(Location location)
{
return new Error_expression(location);
}
// An expression which is really a type. This is used during parsing.
// It is an error if these survive after lowering.
class
Type_expression : public Expression
{
public:
Type_expression(Type* type, Location location)
: Expression(EXPRESSION_TYPE, location),
type_(type)
{ }
protected:
int
do_traverse(Traverse* traverse)
{ return Type::traverse(this->type_, traverse); }
Type*
do_type()
{ return this->type_; }
void
do_determine_type(const Type_context*)
{ }
void
do_check_types(Gogo*)
{ this->report_error(_("invalid use of type")); }
Expression*
do_copy()
{ return this; }
Bexpression*
do_get_backend(Translate_context*)
{ go_unreachable(); }
void do_dump_expression(Ast_dump_context*) const;
private:
// The type which we are representing as an expression.
Type* type_;
};
void
Type_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const
{
ast_dump_context->dump_type(this->type_);
}
Expression*
Expression::make_type(Type* type, Location location)
{
return new Type_expression(type, location);
}
// Class Parser_expression.
Type*
Parser_expression::do_type()
{
// We should never really ask for the type of a Parser_expression.
// However, it can happen, at least when we have an invalid const
// whose initializer refers to the const itself. In that case we
// may ask for the type when lowering the const itself.
go_assert(saw_errors());
return Type::make_error_type();
}
// Class Var_expression.
// Lower a variable expression. Here we just make sure that the
// initialization expression of the variable has been lowered. This
// ensures that we will be able to determine the type of the variable
// if necessary.
Expression*
Var_expression::do_lower(Gogo* gogo, Named_object* function,
Statement_inserter* inserter, int)
{
if (this->variable_->is_variable())
{
Variable* var = this->variable_->var_value();
// This is either a local variable or a global variable. A
// reference to a variable which is local to an enclosing
// function will be a reference to a field in a closure.
if (var->is_global())
{
function = NULL;
inserter = NULL;
}
var->lower_init_expression(gogo, function, inserter);
}
return this;
}
// Return the type of a reference to a variable.
Type*
Var_expression::do_type()
{
if (this->variable_->is_variable())
return this->variable_->var_value()->type();
else if (this->variable_->is_result_variable())
return this->variable_->result_var_value()->type();
else
go_unreachable();
}
// Determine the type of a reference to a variable.
void
Var_expression::do_determine_type(const Type_context*)
{
if (this->variable_->is_variable())
this->variable_->var_value()->determine_type();
}
// Something takes the address of this variable. This means that we
// may want to move the variable onto the heap.
void
Var_expression::do_address_taken(bool escapes)
{
if (!escapes)
{
if (this->variable_->is_variable())
this->variable_->var_value()->set_non_escaping_address_taken();
else if (this->variable_->is_result_variable())
this->variable_->result_var_value()->set_non_escaping_address_taken();
else
go_unreachable();
}
else
{
if (this->variable_->is_variable())
this->variable_->var_value()->set_address_taken();
else if (this->variable_->is_result_variable())
this->variable_->result_var_value()->set_address_taken();
else
go_unreachable();
}
if (this->variable_->is_variable()
&& this->variable_->var_value()->is_in_heap())
{
Node::make_node(this)->set_encoding(Node::ESCAPE_HEAP);
Node::make_node(this->variable_)->set_encoding(Node::ESCAPE_HEAP);
}
}
// The cost to inline a variable reference. We currently only support
// references to parameters.
int
Var_expression::do_inlining_cost() const
{
if (this->variable_->is_variable())
{
if (this->variable_->var_value()->is_parameter())
return 1;
}
else if (this->variable_->is_result_variable())
return 1;
return 0x100000;
}
// Export a reference to a variable.
void
Var_expression::do_export(Export_function_body* efb) const
{
efb->write_string(Gogo::unpack_hidden_name(this->variable_->name()));
}
// Get the backend representation for a reference to a variable.
Bexpression*
Var_expression::do_get_backend(Translate_context* context)
{
Bvariable* bvar = this->variable_->get_backend_variable(context->gogo(),
context->function());
bool is_in_heap;
Location loc = this->location();
Btype* btype;
Gogo* gogo = context->gogo();
if (this->variable_->is_variable())
{
is_in_heap = this->variable_->var_value()->is_in_heap();
btype = this->variable_->var_value()->type()->get_backend(gogo);
}
else if (this->variable_->is_result_variable())
{
is_in_heap = this->variable_->result_var_value()->is_in_heap();
btype = this->variable_->result_var_value()->type()->get_backend(gogo);
}
else
go_unreachable();
Bexpression* ret =
context->backend()->var_expression(bvar, loc);
if (is_in_heap)
ret = context->backend()->indirect_expression(btype, ret, true, loc);
return ret;
}
// Ast dump for variable expression.
void
Var_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const
{
ast_dump_context->ostream() << this->variable_->message_name() ;
}
// Make a reference to a variable in an expression.
Expression*
Expression::make_var_reference(Named_object* var, Location location)
{
if (var->is_sink())
return Expression::make_sink(location);
// FIXME: Creating a new object for each reference to a variable is
// wasteful.
return new Var_expression(var, location);
}
// Class Enclosed_var_expression.
int
Enclosed_var_expression::do_traverse(Traverse*)
{
return TRAVERSE_CONTINUE;
}
// Lower the reference to the enclosed variable.
Expression*
Enclosed_var_expression::do_lower(Gogo* gogo, Named_object* function,
Statement_inserter* inserter, int)
{
gogo->lower_expression(function, inserter, &this->reference_);
return this;
}
// Flatten the reference to the enclosed variable.
Expression*
Enclosed_var_expression::do_flatten(Gogo* gogo, Named_object* function,
Statement_inserter* inserter)
{
gogo->flatten_expression(function, inserter, &this->reference_);
return this;
}
void
Enclosed_var_expression::do_address_taken(bool escapes)
{
if (!escapes)
{
if (this->variable_->is_variable())
this->variable_->var_value()->set_non_escaping_address_taken();
else if (this->variable_->is_result_variable())
this->variable_->result_var_value()->set_non_escaping_address_taken();
else
go_unreachable();
}
else
{
if (this->variable_->is_variable())
this->variable_->var_value()->set_address_taken();
else if (this->variable_->is_result_variable())
this->variable_->result_var_value()->set_address_taken();
else
go_unreachable();
}
if (this->variable_->is_variable()
&& this->variable_->var_value()->is_in_heap())
Node::make_node(this->variable_)->set_encoding(Node::ESCAPE_HEAP);
}
// Ast dump for enclosed variable expression.
void
Enclosed_var_expression::do_dump_expression(Ast_dump_context* adc) const
{
adc->ostream() << this->variable_->message_name();
}
// Make a reference to a variable within an enclosing function.
Expression*
Expression::make_enclosing_var_reference(Expression* reference,
Named_object* var, Location location)
{
return new Enclosed_var_expression(reference, var, location);
}
// Class Temporary_reference_expression.
// The type.
Type*
Temporary_reference_expression::do_type()
{
return this->statement_->type();
}
// Called if something takes the address of this temporary variable.
// We never have to move temporary variables to the heap, but we do
// need to know that they must live in the stack rather than in a
// register.
void
Temporary_reference_expression::do_address_taken(bool)
{
this->statement_->set_is_address_taken();
}
// Get a backend expression referring to the variable.
Bexpression*
Temporary_reference_expression::do_get_backend(Translate_context* context)
{
Gogo* gogo = context->gogo();
Bvariable* bvar = this->statement_->get_backend_variable(context);
Bexpression* ret = gogo->backend()->var_expression(bvar, this->location());
// The backend can't always represent the same set of recursive types
// that the Go frontend can. In some cases this means that a
// temporary variable won't have the right backend type. Correct
// that here by adding a type cast. We need to use base() to push
// the circularity down one level.
Type* stype = this->statement_->type();
if (!this->is_lvalue_
&& stype->points_to() != NULL
&& stype->points_to()->is_void_type())
{
Btype* btype = this->type()->base()->get_backend(gogo);
ret = gogo->backend()->convert_expression(btype, ret, this->location());
}
return ret;
}
// Ast dump for temporary reference.
void
Temporary_reference_expression::do_dump_expression(
Ast_dump_context* ast_dump_context) const
{
ast_dump_context->dump_temp_variable_name(this->statement_);
}
// Make a reference to a temporary variable.
Temporary_reference_expression*
Expression::make_temporary_reference(Temporary_statement* statement,
Location location)
{
return new Temporary_reference_expression(statement, location);
}
// Class Set_and_use_temporary_expression.
// Return the type.
Type*
Set_and_use_temporary_expression::do_type()
{
return this->statement_->type();
}
// Determine the type of the expression.
void
Set_and_use_temporary_expression::do_determine_type(
const Type_context* context)
{
this->expr_->determine_type(context);
}
// Take the address.
void
Set_and_use_temporary_expression::do_address_taken(bool)
{
this->statement_->set_is_address_taken();
}
// Return the backend representation.
Bexpression*
Set_and_use_temporary_expression::do_get_backend(Translate_context* context)
{
Location loc = this->location();
Gogo* gogo = context->gogo();
Bvariable* bvar = this->statement_->get_backend_variable(context);
Bexpression* lvar_ref = gogo->backend()->var_expression(bvar, loc);
Named_object* fn = context->function();
go_assert(fn != NULL);
Bfunction* bfn = fn->func_value()->get_or_make_decl(gogo, fn);
Bexpression* bexpr = this->expr_->get_backend(context);
Bstatement* set = gogo->backend()->assignment_statement(bfn, lvar_ref,
bexpr, loc);
Bexpression* var_ref = gogo->backend()->var_expression(bvar, loc);
Bexpression* ret = gogo->backend()->compound_expression(set, var_ref, loc);
return ret;
}
// Dump.
void
Set_and_use_temporary_expression::do_dump_expression(
Ast_dump_context* ast_dump_context) const
{
ast_dump_context->ostream() << '(';
ast_dump_context->dump_temp_variable_name(this->statement_);
ast_dump_context->ostream() << " = ";
this->expr_->dump_expression(ast_dump_context);
ast_dump_context->ostream() << ')';
}
// Make a set-and-use temporary.
Set_and_use_temporary_expression*
Expression::make_set_and_use_temporary(Temporary_statement* statement,
Expression* expr, Location location)
{
return new Set_and_use_temporary_expression(statement, expr, location);
}
// A sink expression--a use of the blank identifier _.
class Sink_expression : public Expression
{
public:
Sink_expression(Location location)
: Expression(EXPRESSION_SINK, location),
type_(NULL), bvar_(NULL)
{ }
protected:
bool
do_discarding_value()
{ return true; }
Type*
do_type();
void
do_determine_type(const Type_context*);
Expression*
do_copy()
{ return new Sink_expression(this->location()); }
Bexpression*
do_get_backend(Translate_context*);
void
do_dump_expression(Ast_dump_context*) const;
private:
// The type of this sink variable.
Type* type_;
// The temporary variable we generate.
Bvariable* bvar_;
};
// Return the type of a sink expression.
Type*
Sink_expression::do_type()
{
if (this->type_ == NULL)
return Type::make_sink_type();
return this->type_;
}
// Determine the type of a sink expression.
void
Sink_expression::do_determine_type(const Type_context* context)
{
if (context->type != NULL)
this->type_ = context->type;
}
// Return a temporary variable for a sink expression. This will
// presumably be a write-only variable which the middle-end will drop.
Bexpression*
Sink_expression::do_get_backend(Translate_context* context)
{
Location loc = this->location();
Gogo* gogo = context->gogo();
if (this->bvar_ == NULL)
{
go_assert(this->type_ != NULL && !this->type_->is_sink_type());
Named_object* fn = context->function();
go_assert(fn != NULL);
Bfunction* fn_ctx = fn->func_value()->get_or_make_decl(gogo, fn);
Btype* bt = this->type_->get_backend(context->gogo());
Bstatement* decl;
this->bvar_ =
gogo->backend()->temporary_variable(fn_ctx, context->bblock(), bt, NULL,
false, loc, &decl);
Bexpression* var_ref =
gogo->backend()->var_expression(this->bvar_, loc);
var_ref = gogo->backend()->compound_expression(decl, var_ref, loc);
return var_ref;
}
return gogo->backend()->var_expression(this->bvar_, loc);
}
// Ast dump for sink expression.
void
Sink_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const
{
ast_dump_context->ostream() << "_" ;
}
// Make a sink expression.
Expression*
Expression::make_sink(Location location)
{
return new Sink_expression(location);
}
// Class Func_expression.
// FIXME: Can a function expression appear in a constant expression?
// The value is unchanging. Initializing a constant to the address of
// a function seems like it could work, though there might be little
// point to it.
// Traversal.
int
Func_expression::do_traverse(Traverse* traverse)
{
return (this->closure_ == NULL
? TRAVERSE_CONTINUE
: Expression::traverse(&this->closure_, traverse));
}
// Return the type of a function expression.
Type*
Func_expression::do_type()
{
if (this->function_->is_function())
return this->function_->func_value()->type();
else if (this->function_->is_function_declaration())
return this->function_->func_declaration_value()->type();
else
go_unreachable();
}
// Get the backend representation for the code of a function expression.
Bexpression*
Func_expression::get_code_pointer(Gogo* gogo, Named_object* no, Location loc)
{
Function_type* fntype;
if (no->is_function())
fntype = no->func_value()->type();
else if (no->is_function_declaration())
fntype = no->func_declaration_value()->type();
else
go_unreachable();
// Builtin functions are handled specially by Call_expression. We
// can't take their address.
if (fntype->is_builtin())
{
go_error_at(loc,
"invalid use of special builtin function %qs; must be called",
no->message_name().c_str());
return gogo->backend()->error_expression();
}
Bfunction* fndecl;
if (no->is_function())
fndecl = no->func_value()->get_or_make_decl(gogo, no);
else if (no->is_function_declaration())
fndecl = no->func_declaration_value()->get_or_make_decl(gogo, no);
else
go_unreachable();
return gogo->backend()->function_code_expression(fndecl, loc);
}
// Get the backend representation for a function expression. This is used when
// we take the address of a function rather than simply calling it. A func
// value is represented as a pointer to a block of memory. The first
// word of that memory is a pointer to the function code. The
// remaining parts of that memory are the addresses of variables that
// the function closes over.
Bexpression*
Func_expression::do_get_backend(Translate_context* context)
{
// If there is no closure, just use the function descriptor.
if (this->closure_ == NULL)
{
Gogo* gogo = context->gogo();
Named_object* no = this->function_;
Expression* descriptor;
if (no->is_function())
descriptor = no->func_value()->descriptor(gogo, no);
else if (no->is_function_declaration())
{
if (no->func_declaration_value()->type()->is_builtin())
{
go_error_at(this->location(),
("invalid use of special builtin function %qs; "
"must be called"),
no->message_name().c_str());
return gogo->backend()->error_expression();
}
descriptor = no->func_declaration_value()->descriptor(gogo, no);
}
else
go_unreachable();
Bexpression* bdesc = descriptor->get_backend(context);
return gogo->backend()->address_expression(bdesc, this->location());
}
go_assert(this->function_->func_value()->enclosing() != NULL);
// If there is a closure, then the closure is itself the function
// expression. It is a pointer to a struct whose first field points
// to the function code and whose remaining fields are the addresses
// of the closed-over variables.
Bexpression *bexpr = this->closure_->get_backend(context);
// Introduce a backend type conversion, to account for any differences
// between the argument type (function descriptor, struct with a
// single field) and the closure (struct with multiple fields).
Gogo* gogo = context->gogo();
Btype *btype = this->type()->get_backend(gogo);
return gogo->backend()->convert_expression(btype, bexpr, this->location());
}
// Ast dump for function.
void
Func_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const
{
ast_dump_context->ostream() << this->function_->name();
if (this->closure_ != NULL)
{
ast_dump_context->ostream() << " {closure = ";
this->closure_->dump_expression(ast_dump_context);
ast_dump_context->ostream() << "}";
}
}
// Make a reference to a function in an expression.
Expression*
Expression::make_func_reference(Named_object* function, Expression* closure,
Location location)
{
Func_expression* fe = new Func_expression(function, closure, location);
// Detect references to builtin functions and set the runtime code if
// appropriate.
if (function->is_function_declaration())
fe->set_runtime_code(Runtime::name_to_code(function->name()));
return fe;
}
// Class Func_descriptor_expression.
// Constructor.
Func_descriptor_expression::Func_descriptor_expression(Named_object* fn)
: Expression(EXPRESSION_FUNC_DESCRIPTOR, fn->location()),
fn_(fn), dvar_(NULL)
{
go_assert(!fn->is_function() || !fn->func_value()->needs_closure());
}
// Traversal.
int
Func_descriptor_expression::do_traverse(Traverse*)
{
return TRAVERSE_CONTINUE;
}
// All function descriptors have the same type.
Type* Func_descriptor_expression::descriptor_type;
void
Func_descriptor_expression::make_func_descriptor_type()
{
if (Func_descriptor_expression::descriptor_type != NULL)
return;
Type* uintptr_type = Type::lookup_integer_type("uintptr");
Type* struct_type = Type::make_builtin_struct_type(1, "code", uintptr_type);
Func_descriptor_expression::descriptor_type =
Type::make_builtin_named_type("functionDescriptor", struct_type);
}
Type*
Func_descriptor_expression::do_type()
{
Func_descriptor_expression::make_func_descriptor_type();
return Func_descriptor_expression::descriptor_type;
}
// The backend representation for a function descriptor.
Bexpression*
Func_descriptor_expression::do_get_backend(Translate_context* context)
{
Named_object* no = this->fn_;
Location loc = no->location();
if (this->dvar_ != NULL)
return context->backend()->var_expression(this->dvar_, loc);
Gogo* gogo = context->gogo();
std::string var_name(gogo->function_descriptor_name(no));
bool is_descriptor = false;
if (no->is_function_declaration()
&& !no->func_declaration_value()->asm_name().empty()
&& Linemap::is_predeclared_location(no->location()))
is_descriptor = true;
// The runtime package implements some functions defined in the
// syscall package. Let the syscall package define the descriptor
// in this case.
if (gogo->compiling_runtime()
&& gogo->package_name() == "runtime"
&& no->is_function()
&& !no->func_value()->asm_name().empty()
&& no->func_value()->asm_name().compare(0, 8, "syscall.") == 0)
is_descriptor = true;
Btype* btype = this->type()->get_backend(gogo);
Bvariable* bvar;
std::string asm_name(go_selectively_encode_id(var_name));
if (no->package() != NULL || is_descriptor)
bvar = context->backend()->immutable_struct_reference(var_name, asm_name,
btype, loc);
else
{
Location bloc = Linemap::predeclared_location();
// The runtime package has hash/equality functions that are
// referenced by type descriptors outside of the runtime, so the
// function descriptors must be visible even though they are not
// exported.
bool is_exported_runtime = false;
if (gogo->compiling_runtime()
&& gogo->package_name() == "runtime"
&& (no->name().find("hash") != std::string::npos
|| no->name().find("equal") != std::string::npos))
is_exported_runtime = true;
bool is_hidden = ((no->is_function()
&& no->func_value()->enclosing() != NULL)
|| (Gogo::is_hidden_name(no->name())
&& !is_exported_runtime)
|| Gogo::is_thunk(no));
bvar = context->backend()->immutable_struct(var_name, asm_name,
is_hidden, false,
btype, bloc);
Expression_list* vals = new Expression_list();
vals->push_back(Expression::make_func_code_reference(this->fn_, bloc));
Expression* init =
Expression::make_struct_composite_literal(this->type(), vals, bloc);
Translate_context bcontext(gogo, NULL, NULL, NULL);
bcontext.set_is_const();
Bexpression* binit = init->get_backend(&bcontext);
context->backend()->immutable_struct_set_init(bvar, var_name, is_hidden,
false, btype, bloc, binit);
}
this->dvar_ = bvar;
return gogo->backend()->var_expression(bvar, loc);
}
// Print a function descriptor expression.
void
Func_descriptor_expression::do_dump_expression(Ast_dump_context* context) const
{
context->ostream() << "[descriptor " << this->fn_->name() << "]";
}
// Make a function descriptor expression.
Func_descriptor_expression*
Expression::make_func_descriptor(Named_object* fn)
{
return new Func_descriptor_expression(fn);
}
// Make the function descriptor type, so that it can be converted.
void
Expression::make_func_descriptor_type()
{
Func_descriptor_expression::make_func_descriptor_type();
}
// A reference to just the code of a function.
class Func_code_reference_expression : public Expression
{
public:
Func_code_reference_expression(Named_object* function, Location location)
: Expression(EXPRESSION_FUNC_CODE_REFERENCE, location),
function_(function)
{ }
protected:
int
do_traverse(Traverse*)
{ return TRAVERSE_CONTINUE; }
bool
do_is_static_initializer() const
{ return true; }
Type*
do_type()
{ return Type::make_pointer_type(Type::make_void_type()); }
void
do_determine_type(const Type_context*)
{ }
Expression*
do_copy()
{
return Expression::make_func_code_reference(this->function_,
this->location());
}
Bexpression*
do_get_backend(Translate_context*);
void
do_dump_expression(Ast_dump_context* context) const
{ context->ostream() << "[raw " << this->function_->name() << "]" ; }
private:
// The function.
Named_object* function_;
};
// Get the backend representation for a reference to function code.
Bexpression*
Func_code_reference_expression::do_get_backend(Translate_context* context)
{
return Func_expression::get_code_pointer(context->gogo(), this->function_,
this->location());
}
// Make a reference to the code of a function.
Expression*
Expression::make_func_code_reference(Named_object* function, Location location)
{
return new Func_code_reference_expression(function, location);
}
// Class Unknown_expression.
// Return the name of an unknown expression.
const std::string&
Unknown_expression::name() const
{
return this->named_object_->name();
}
// Lower a reference to an unknown name.
Expression*
Unknown_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int)
{
Location location = this->location();
Named_object* no = this->named_object_;
Named_object* real;
if (!no->is_unknown())
real = no;
else
{
real = no->unknown_value()->real_named_object();
if (real == NULL)
{
if (this->is_composite_literal_key_)
return this;
if (!this->no_error_message_)
go_error_at(location, "reference to undefined name %qs",
this->named_object_->message_name().c_str());
return Expression::make_error(location);
}
}
switch (real->classification())
{
case Named_object::NAMED_OBJECT_CONST:
return Expression::make_const_reference(real, location);
case Named_object::NAMED_OBJECT_TYPE:
return Expression::make_type(real->type_value(), location);
case Named_object::NAMED_OBJECT_TYPE_DECLARATION:
if (this->is_composite_literal_key_)
return this;
if (!this->no_error_message_)
go_error_at(location, "reference to undefined type %qs",
real->message_name().c_str());
return Expression::make_error(location);
case Named_object::NAMED_OBJECT_VAR:
real->var_value()->set_is_used();
return Expression::make_var_reference(real, location);
case Named_object::NAMED_OBJECT_FUNC:
case Named_object::NAMED_OBJECT_FUNC_DECLARATION:
return Expression::make_func_reference(real, NULL, location);
case Named_object::NAMED_OBJECT_PACKAGE:
if (this->is_composite_literal_key_)
return this;
if (!this->no_error_message_)
go_error_at(location, "unexpected reference to package");
return Expression::make_error(location);
default:
go_unreachable();
}
}
// Dump the ast representation for an unknown expression to a dump context.
void
Unknown_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const
{
ast_dump_context->ostream() << "_Unknown_(" << this->named_object_->name()
<< ")";
}
// Make a reference to an unknown name.
Unknown_expression*
Expression::make_unknown_reference(Named_object* no, Location location)
{
return new Unknown_expression(no, location);
}
// A boolean expression.
class Boolean_expression : public Expression
{
public:
Boolean_expression(bool val, Location location)
: Expression(EXPRESSION_BOOLEAN, location),
val_(val), type_(NULL)
{ }
static Expression*
do_import(Import_expression*, Location);
protected:
bool
do_is_constant() const
{ return true; }
bool
do_is_static_initializer() const
{ return true; }
Type*
do_type();
void
do_determine_type(const Type_context*);
Expression*
do_copy()
{ return this; }
Bexpression*
do_get_backend(Translate_context* context)
{ return context->backend()->boolean_constant_expression(this->val_); }
int
do_inlining_cost() const
{ return 1; }
void
do_export(Export_function_body* efb) const
{ efb->write_c_string(this->val_ ? "$true" : "$false"); }
void
do_dump_expression(Ast_dump_context* ast_dump_context) const
{ ast_dump_context->ostream() << (this->val_ ? "true" : "false"); }
private:
// The constant.
bool val_;
// The type as determined by context.
Type* type_;
};
// Get the type.
Type*
Boolean_expression::do_type()
{
if (this->type_ == NULL)
this->type_ = Type::make_boolean_type();
return this->type_;
}
// Set the type from the context.
void
Boolean_expression::do_determine_type(const Type_context* context)
{
if (this->type_ != NULL && !this->type_->is_abstract())
;
else if (context->type != NULL && context->type->is_boolean_type())
this->type_ = context->type;
else if (!context->may_be_abstract)
this->type_ = Type::lookup_bool_type();
}
// Import a boolean constant.
Expression*
Boolean_expression::do_import(Import_expression* imp, Location loc)
{
if (imp->version() >= EXPORT_FORMAT_V3)
imp->require_c_string("$");
if (imp->peek_char() == 't')
{
imp->require_c_string("true");
return Expression::make_boolean(true, loc);
}
else
{
imp->require_c_string("false");
return Expression::make_boolean(false, loc);
}
}
// Make a boolean expression.
Expression*
Expression::make_boolean(bool val, Location location)
{
return new Boolean_expression(val, location);
}
// Class String_expression.
// Get the type.
Type*
String_expression::do_type()
{
if (this->type_ == NULL)
this->type_ = Type::make_string_type();
return this->type_;
}
// Set the type from the context.
void
String_expression::do_determine_type(const Type_context* context)
{
if (this->type_ != NULL && !this->type_->is_abstract())
;
else if (context->type != NULL && context->type->is_string_type())
this->type_ = context->type;
else if (!context->may_be_abstract)
this->type_ = Type::lookup_string_type();
}
// Build a string constant.
Bexpression*
String_expression::do_get_backend(Translate_context* context)
{
Gogo* gogo = context->gogo();
Btype* btype = Type::make_string_type()->get_backend(gogo);
Location loc = this->location();
std::vector<Bexpression*> init(2);
Bexpression* str_cst =
gogo->backend()->string_constant_expression(this->val_);
init[0] = gogo->backend()->address_expression(str_cst, loc);
Btype* int_btype = Type::lookup_integer_type("int")->get_backend(gogo);
mpz_t lenval;
mpz_init_set_ui(lenval, this->val_.length());
init[1] = gogo->backend()->integer_constant_expression(int_btype, lenval);
mpz_clear(lenval);
return gogo->backend()->constructor_expression(btype, init, loc);
}
// Write string literal to string dump.
void
String_expression::export_string(String_dump* exp,
const String_expression* str)
{
std::string s;
s.reserve(str->val_.length() * 4 + 2);
s += '"';
for (std::string::const_iterator p = str->val_.begin();
p != str->val_.end();
++p)
{
if (*p == '\\' || *p == '"')
{
s += '\\';
s += *p;
}
else if (*p >= 0x20 && *p < 0x7f)
s += *p;
else if (*p == '\n')
s += "\\n";
else if (*p == '\t')
s += "\\t";
else
{
s += "\\x";
unsigned char c = *p;
unsigned int dig = c >> 4;
s += dig < 10 ? '0' + dig : 'A' + dig - 10;
dig = c & 0xf;
s += dig < 10 ? '0' + dig : 'A' + dig - 10;
}
}
s += '"';
exp->write_string(s);
}
// Export a string expression.
void
String_expression::do_export(Export_function_body* efb) const
{
String_expression::export_string(efb, this);
}
// Import a string expression.
Expression*
String_expression::do_import(Import_expression* imp, Location loc)
{
imp->require_c_string("\"");
std::string val;
while (true)
{
int c = imp->get_char();
if (c == '"' || c == -1)
break;
if (c != '\\')
val += static_cast<char>(c);
else
{
c = imp->get_char();
if (c == '\\' || c == '"')
val += static_cast<char>(c);
else if (c == 'n')
val += '\n';
else if (c == 't')
val += '\t';
else if (c == 'x')
{
c = imp->get_char();
unsigned int vh = c >= '0' && c <= '9' ? c - '0' : c - 'A' + 10;
c = imp->get_char();
unsigned int vl = c >= '0' && c <= '9' ? c - '0' : c - 'A' + 10;
char v = (vh << 4) | vl;
val += v;
}
else
{
go_error_at(imp->location(), "bad string constant");
return Expression::make_error(loc);
}
}
}
return Expression::make_string(val, loc);
}
// Ast dump for string expression.
void
String_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const
{
String_expression::export_string(ast_dump_context, this);
}
// Make a string expression.
Expression*
Expression::make_string(const std::string& val, Location location)
{
return new String_expression(val, location);
}
// An expression that evaluates to some characteristic of a string.
// This is used when indexing, bound-checking, or nil checking a string.
class String_info_expression : public Expression
{
public:
String_info_expression(Expression* string, String_info string_info,
Location location)
: Expression(EXPRESSION_STRING_INFO, location),
string_(string), string_info_(string_info)
{ }
protected:
Type*
do_type();
void
do_determine_type(const Type_context*)
{ go_unreachable(); }
Expression*
do_copy()
{
return new String_info_expression(this->string_->copy(), this->string_info_,
this->location());
}
Bexpression*
do_get_backend(Translate_context* context);
void
do_dump_expression(Ast_dump_context*) const;
void
do_issue_nil_check()
{ this->string_->issue_nil_check(); }
private:
// The string for which we are getting information.
Expression* string_;
// What information we want.
String_info string_info_;
};
// Return the type of the string info.
Type*
String_info_expression::do_type()
{
switch (this->string_info_)
{
case STRING_INFO_DATA:
{
Type* byte_type = Type::lookup_integer_type("uint8");
return Type::make_pointer_type(byte_type);
}
case STRING_INFO_LENGTH:
return Type::lookup_integer_type("int");
default:
go_unreachable();
}
}
// Return string information in GENERIC.
Bexpression*
String_info_expression::do_get_backend(Translate_context* context)
{
Gogo* gogo = context->gogo();
Bexpression* bstring = this->string_->get_backend(context);
switch (this->string_info_)
{
case STRING_INFO_DATA:
case STRING_INFO_LENGTH:
return gogo->backend()->struct_field_expression(bstring,
this->string_info_,
this->location());
break;
default:
go_unreachable();
}
}
// Dump ast representation for a type info expression.
void
String_info_expression::do_dump_expression(
Ast_dump_context* ast_dump_context) const
{
ast_dump_context->ostream() << "stringinfo(";
this->string_->dump_expression(ast_dump_context);
ast_dump_context->ostream() << ",";
ast_dump_context->ostream() <<
(this->string_info_ == STRING_INFO_DATA ? "data"
: this->string_info_ == STRING_INFO_LENGTH ? "length"
: "unknown");
ast_dump_context->ostream() << ")";
}
// Make a string info expression.
Expression*
Expression::make_string_info(Expression* string, String_info string_info,
Location location)
{
return new String_info_expression(string, string_info, location);
}
// Make an integer expression.
class Integer_expression : public Expression
{
public:
Integer_expression(const mpz_t* val, Type* type, bool is_character_constant,
Location location)
: Expression(EXPRESSION_INTEGER, location),
type_(type), is_character_constant_(is_character_constant)
{ mpz_init_set(this->val_, *val); }
static Expression*
do_import(Import_expression*, Location);
// Write VAL to string dump.
static void
export_integer(String_dump* exp, const mpz_t val);
// Write VAL to dump context.
static void
dump_integer(Ast_dump_context* ast_dump_context, const mpz_t val);
protected:
bool
do_is_constant() const
{ return true; }
bool
do_is_static_initializer() const
{ return true; }
bool
do_numeric_constant_value(Numeric_constant* nc) const;
Type*
do_type();
void
do_determine_type(const Type_context* context);
void
do_check_types(Gogo*);
Bexpression*
do_get_backend(Translate_context*);
Expression*
do_copy()
{
if (this->is_character_constant_)
return Expression::make_character(&this->val_,
(this->type_ == NULL
? NULL
: this->type_->copy_expressions()),
this->location());
else
return Expression::make_integer_z(&this->val_,
(this->type_ == NULL
? NULL
: this->type_->copy_expressions()),
this->location());
}
int
do_inlining_cost() const
{ return 1; }
void
do_export(Export_function_body*) const;
void
do_dump_expression(Ast_dump_context*) const;
private:
// The integer value.
mpz_t val_;
// The type so far.
Type* type_;
// Whether this is a character constant.
bool is_character_constant_;
};
// Return a numeric constant for this expression. We have to mark
// this as a character when appropriate.
bool
Integer_expression::do_numeric_constant_value(Numeric_constant* nc) const
{
if (this->is_character_constant_)
nc->set_rune(this->type_, this->val_);
else
nc->set_int(this->type_, this->val_);
return true;
}
// Return the current type. If we haven't set the type yet, we return
// an abstract integer type.
Type*
Integer_expression::do_type()
{
if (this->type_ == NULL)
{
if (this->is_character_constant_)
this->type_ = Type::make_abstract_character_type();
else
this->type_ = Type::make_abstract_integer_type();
}
return this->type_;
}
// Set the type of the integer value. Here we may switch from an
// abstract type to a real type.
void
Integer_expression::do_determine_type(const Type_context* context)
{
if (this->type_ != NULL && !this->type_->is_abstract())
;
else if (context->type != NULL && context->type->is_numeric_type())
this->type_ = context->type;
else if (!context->may_be_abstract)
{
if (this->is_character_constant_)
this->type_ = Type::lookup_integer_type("int32");
else
this->type_ = Type::lookup_integer_type("int");
}
}
// Check the type of an integer constant.
void
Integer_expression::do_check_types(Gogo*)
{
Type* type = this->type_;
if (type == NULL)
return;
Numeric_constant nc;
if (this->is_character_constant_)
nc.set_rune(NULL, this->val_);
else
nc.set_int(NULL, this->val_);
if (!nc.set_type(type, true, this->location()))
this->set_is_error();
}
// Get the backend representation for an integer constant.
Bexpression*
Integer_expression::do_get_backend(Translate_context* context)
{
if (this->is_error_expression()
|| (this->type_ != NULL && this->type_->is_error_type()))
{
go_assert(saw_errors());
return context->gogo()->backend()->error_expression();
}
Type* resolved_type = NULL;
if (this->type_ != NULL && !this->type_->is_abstract())
resolved_type = this->type_;
else if (this->type_ != NULL && this->type_->float_type() != NULL)
{
// We are converting to an abstract floating point type.
resolved_type = Type::lookup_float_type("float64");
}
else if (this->type_ != NULL && this->type_->complex_type() != NULL)
{
// We are converting to an abstract complex type.
resolved_type = Type::lookup_complex_type("complex128");
}
else
{
// If we still have an abstract type here, then this is being
// used in a constant expression which didn't get reduced for
// some reason. Use a type which will fit the value. We use <,
// not <=, because we need an extra bit for the sign bit.
int bits = mpz_sizeinbase(this->val_, 2);
Type* int_type = Type::lookup_integer_type("int");
if (bits < int_type->integer_type()->bits())
resolved_type = int_type;
else if (bits < 64)
resolved_type = Type::lookup_integer_type("int64");
else
{
if (!saw_errors())
go_error_at(this->location(),
"unknown type for large integer constant");
return context->gogo()->backend()->error_expression();
}
}
Numeric_constant nc;
nc.set_int(resolved_type, this->val_);
return Expression::backend_numeric_constant_expression(context, &nc);
}
// Write VAL to export data.
void
Integer_expression::export_integer(String_dump* exp, const mpz_t val)
{
char* s = mpz_get_str(NULL, 10, val);
exp->write_c_string(s);
free(s);
}
// Export an integer in a constant expression.
void
Integer_expression::do_export(Export_function_body* efb) const
{
bool added_type = false;
if (this->type_ != NULL
&& !this->type_->is_abstract()
&& this->type_ != efb->type_context())
{
efb->write_c_string("$convert(");
efb->write_type(this->type_);
efb->write_c_string(", ");
added_type = true;
}
Integer_expression::export_integer(efb, this->val_);
if (this->is_character_constant_)
efb->write_c_string("'");
// A trailing space lets us reliably identify the end of the number.
efb->write_c_string(" ");
if (added_type)
efb->write_c_string(")");
}
// Import an integer, floating point, or complex value. This handles
// all these types because they all start with digits.
Expression*
Integer_expression::do_import(Import_expression* imp, Location loc)
{
std::string num = imp->read_identifier();
imp->require_c_string(" ");
if (!num.empty() && num[num.length() - 1] == 'i')
{
mpfr_t real;
size_t plus_pos = num.find('+', 1);
size_t minus_pos = num.find('-', 1);
size_t pos;
if (plus_pos == std::string::npos)
pos = minus_pos;
else if (minus_pos == std::string::npos)
pos = plus_pos;
else
{
go_error_at(imp->location(), "bad number in import data: %qs",
num.c_str());
return Expression::make_error(loc);
}
if (pos == std::string::npos)
mpfr_set_ui(real, 0, GMP_RNDN);
else
{
std::string real_str = num.substr(0, pos);
if (mpfr_init_set_str(real, real_str.c_str(), 10, GMP_RNDN) != 0)
{
go_error_at(imp->location(), "bad number in import data: %qs",
real_str.c_str());
return Expression::make_error(loc);
}
}
std::string imag_str;
if (pos == std::string::npos)
imag_str = num;
else
imag_str = num.substr(pos);
imag_str = imag_str.substr(0, imag_str.size() - 1);
mpfr_t imag;
if (mpfr_init_set_str(imag, imag_str.c_str(), 10, GMP_RNDN) != 0)
{
go_error_at(imp->location(), "bad number in import data: %qs",
imag_str.c_str());
return Expression::make_error(loc);
}
mpc_t cval;
mpc_init2(cval, mpc_precision);
mpc_set_fr_fr(cval, real, imag, MPC_RNDNN);
mpfr_clear(real);
mpfr_clear(imag);
Expression* ret = Expression::make_complex(&cval, NULL, loc);
mpc_clear(cval);
return ret;
}
else if (num.find('.') == std::string::npos
&& num.find('E') == std::string::npos)
{
bool is_character_constant = (!num.empty()
&& num[num.length() - 1] == '\'');
if (is_character_constant)
num = num.substr(0, num.length() - 1);
mpz_t val;
if (mpz_init_set_str(val, num.c_str(), 10) != 0)
{
go_error_at(imp->location(), "bad number in import data: %qs",
num.c_str());
return Expression::make_error(loc);
}
Expression* ret;
if (is_character_constant)
ret = Expression::make_character(&val, NULL, loc);
else
ret = Expression::make_integer_z(&val, NULL, loc);
mpz_clear(val);
return ret;
}
else
{
mpfr_t val;
if (mpfr_init_set_str(val, num.c_str(), 10, GMP_RNDN) != 0)
{
go_error_at(imp->location(), "bad number in import data: %qs",
num.c_str());
return Expression::make_error(loc);
}
Expression* ret = Expression::make_float(&val, NULL, loc);
mpfr_clear(val);
return ret;
}
}
// Ast dump for integer expression.
void
Integer_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const
{
if (this->is_character_constant_)
ast_dump_context->ostream() << '\'';
Integer_expression::export_integer(ast_dump_context, this->val_);
if (this->is_character_constant_)
ast_dump_context->ostream() << '\'';
}
// Build a new integer value from a multi-precision integer.
Expression*
Expression::make_integer_z(const mpz_t* val, Type* type, Location location)
{
return new Integer_expression(val, type, false, location);
}
// Build a new integer value from an unsigned long.
Expression*
Expression::make_integer_ul(unsigned long val, Type *type, Location location)
{
mpz_t zval;
mpz_init_set_ui(zval, val);
Expression* ret = Expression::make_integer_z(&zval, type, location);
mpz_clear(zval);
return ret;
}
// Build a new integer value from a signed long.
Expression*
Expression::make_integer_sl(long val, Type *type, Location location)
{
mpz_t zval;
mpz_init_set_si(zval, val);
Expression* ret = Expression::make_integer_z(&zval, type, location);
mpz_clear(zval);
return ret;
}
// Store an int64_t in an uninitialized mpz_t.
static void
set_mpz_from_int64(mpz_t* zval, int64_t val)
{
if (val >= 0)
{
unsigned long ul = static_cast<unsigned long>(val);
if (static_cast<int64_t>(ul) == val)
{
mpz_init_set_ui(*zval, ul);
return;
}
}
uint64_t uv;
if (val >= 0)
uv = static_cast<uint64_t>(val);
else
uv = static_cast<uint64_t>(- val);
unsigned long ul = uv & 0xffffffffUL;
mpz_init_set_ui(*zval, ul);
mpz_t hval;
mpz_init_set_ui(hval, static_cast<unsigned long>(uv >> 32));
mpz_mul_2exp(hval, hval, 32);
mpz_add(*zval, *zval, hval);
mpz_clear(hval);
if (val < 0)
mpz_neg(*zval, *zval);
}
// Build a new integer value from an int64_t.
Expression*
Expression::make_integer_int64(int64_t val, Type* type, Location location)
{
mpz_t zval;
set_mpz_from_int64(&zval, val);
Expression* ret = Expression::make_integer_z(&zval, type, location);
mpz_clear(zval);
return ret;
}
// Build a new character constant value.
Expression*
Expression::make_character(const mpz_t* val, Type* type, Location location)
{
return new Integer_expression(val, type, true, location);
}
// Floats.
class Float_expression : public Expression
{
public:
Float_expression(const mpfr_t* val, Type* type, Location location)
: Expression(EXPRESSION_FLOAT, location),
type_(type)
{
mpfr_init_set(this->val_, *val, GMP_RNDN);
}
// Write VAL to export data.
static void
export_float(String_dump* exp, const mpfr_t val);
// Write VAL to dump file.
static void
dump_float(Ast_dump_context* ast_dump_context, const mpfr_t val);
protected:
bool
do_is_constant() const
{ return true; }
bool
do_is_static_initializer() const
{ return true; }
bool
do_numeric_constant_value(Numeric_constant* nc) const
{
nc->set_float(this->type_, this->val_);
return true;
}
Type*
do_type();
void
do_determine_type(const Type_context*);
void
do_check_types(Gogo*);
Expression*
do_copy()
{ return Expression::make_float(&this->val_,
(this->type_ == NULL
? NULL
: this->type_->copy_expressions()),
this->location()); }
Bexpression*
do_get_backend(Translate_context*);
int
do_inlining_cost() const
{ return 1; }
void
do_export(Export_function_body*) const;
void
do_dump_expression(Ast_dump_context*) const;
private:
// The floating point value.
mpfr_t val_;
// The type so far.
Type* type_;
};
// Return the current type. If we haven't set the type yet, we return
// an abstract float type.
Type*
Float_expression::do_type()
{
if (this->type_ == NULL)
this->type_ = Type::make_abstract_float_type();
return this->type_;
}
// Set the type of the float value. Here we may switch from an
// abstract type to a real type.
void
Float_expression::do_determine_type(const Type_context* context)
{
if (this->type_ != NULL && !this->type_->is_abstract())
;
else if (context->type != NULL
&& (context->type->integer_type() != NULL
|| context->type->float_type() != NULL
|| context->type->complex_type() != NULL))
this->type_ = context->type;
else if (!context->may_be_abstract)
this->type_ = Type::lookup_float_type("float64");
}
// Check the type of a float value.
void
Float_expression::do_check_types(Gogo*)
{
Type* type = this->type_;
if (type == NULL)
return;
Numeric_constant nc;
nc.set_float(NULL, this->val_);
if (!nc.set_type(this->type_, true, this->location()))
this->set_is_error();
}
// Get the backend representation for a float constant.
Bexpression*
Float_expression::do_get_backend(Translate_context* context)
{
if (this->is_error_expression()
|| (this->type_ != NULL && this->type_->is_error_type()))
{
go_assert(saw_errors());
return context->gogo()->backend()->error_expression();
}
Type* resolved_type;
if (this->type_ != NULL && !this->type_->is_abstract())
resolved_type = this->type_;
else if (this->type_ != NULL && this->type_->integer_type() != NULL)
{
// We have an abstract integer type. We just hope for the best.
resolved_type = Type::lookup_integer_type("int");
}
else if (this->type_ != NULL && this->type_->complex_type() != NULL)
{
// We are converting to an abstract complex type.
resolved_type = Type::lookup_complex_type("complex128");
}
else
{
// If we still have an abstract type here, then this is being
// used in a constant expression which didn't get reduced. We
// just use float64 and hope for the best.
resolved_type = Type::lookup_float_type("float64");
}
Numeric_constant nc;
nc.set_float(resolved_type, this->val_);
return Expression::backend_numeric_constant_expression(context, &nc);
}
// Write a floating point number to a string dump.
void
Float_expression::export_float(String_dump *exp, const mpfr_t val)
{
mp_exp_t exponent;
char* s = mpfr_get_str(NULL, &exponent, 10, 0, val, GMP_RNDN);
if (*s == '-')
exp->write_c_string("-");
exp->write_c_string("0.");
exp->write_c_string(*s == '-' ? s + 1 : s);
mpfr_free_str(s);
char buf[30];
snprintf(buf, sizeof buf, "E%ld", exponent);
exp->write_c_string(buf);
}
// Export a floating point number in a constant expression.
void
Float_expression::do_export(Export_function_body* efb) const
{
bool added_type = false;
if (this->type_ != NULL
&& !this->type_->is_abstract()
&& this->type_ != efb->type_context())
{
efb->write_c_string("$convert(");
efb->write_type(this->type_);
efb->write_c_string(", ");
added_type = true;
}
Float_expression::export_float(efb, this->val_);
// A trailing space lets us reliably identify the end of the number.
efb->write_c_string(" ");
if (added_type)
efb->write_c_string(")");
}
// Dump a floating point number to the dump file.
void
Float_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const
{
Float_expression::export_float(ast_dump_context, this->val_);
}
// Make a float expression.
Expression*
Expression::make_float(const mpfr_t* val, Type* type, Location location)
{
return new Float_expression(val, type, location);
}
// Complex numbers.
class Complex_expression : public Expression
{
public:
Complex_expression(const mpc_t* val, Type* type, Location location)
: Expression(EXPRESSION_COMPLEX, location),
type_(type)
{
mpc_init2(this->val_, mpc_precision);
mpc_set(this->val_, *val, MPC_RNDNN);
}
// Write VAL to string dump.
static void
export_complex(String_dump* exp, const mpc_t val);
// Write REAL/IMAG to dump context.
static void
dump_complex(Ast_dump_context* ast_dump_context, const mpc_t val);
protected:
bool
do_is_constant() const
{ return true; }
bool
do_is_static_initializer() const
{ return true; }
bool
do_numeric_constant_value(Numeric_constant* nc) const
{
nc->set_complex(this->type_, this->val_);
return true;
}
Type*
do_type();
void
do_determine_type(const Type_context*);
void
do_check_types(Gogo*);
Expression*
do_copy()
{
return Expression::make_complex(&this->val_,
(this->type_ == NULL
? NULL
: this->type_->copy_expressions()),
this->location());
}
Bexpression*
do_get_backend(Translate_context*);
int
do_inlining_cost() const
{ return 2; }
void
do_export(Export_function_body*) const;
void
do_dump_expression(Ast_dump_context*) const;
private:
// The complex value.
mpc_t val_;
// The type if known.
Type* type_;
};
// Return the current type. If we haven't set the type yet, we return
// an abstract complex type.
Type*
Complex_expression::do_type()
{
if (this->type_ == NULL)
this->type_ = Type::make_abstract_complex_type();
return this->type_;
}
// Set the type of the complex value. Here we may switch from an
// abstract type to a real type.
void
Complex_expression::do_determine_type(const Type_context* context)
{
if (this->type_ != NULL && !this->type_->is_abstract())
;
else if (context->type != NULL && context->type->is_numeric_type())
this->type_ = context->type;
else if (!context->may_be_abstract)
this->type_ = Type::lookup_complex_type("complex128");
}
// Check the type of a complex value.
void
Complex_expression::do_check_types(Gogo*)
{
Type* type = this->type_;
if (type == NULL)
return;
Numeric_constant nc;
nc.set_complex(NULL, this->val_);
if (!nc.set_type(this->type_, true, this->location()))
this->set_is_error();
}
// Get the backend representation for a complex constant.
Bexpression*
Complex_expression::do_get_backend(Translate_context* context)
{
if (this->is_error_expression()
|| (this->type_ != NULL && this->type_->is_error_type()))
{
go_assert(saw_errors());
return context->gogo()->backend()->error_expression();
}
Type* resolved_type;
if (this->type_ != NULL && !this->type_->is_abstract())
resolved_type = this->type_;
else if (this->type_ != NULL && this->type_->integer_type() != NULL)
{
// We are converting to an abstract integer type.
resolved_type = Type::lookup_integer_type("int");
}
else if (this->type_ != NULL && this->type_->float_type() != NULL)
{
// We are converting to an abstract float type.
resolved_type = Type::lookup_float_type("float64");
}
else
{
// If we still have an abstract type here, this is being
// used in a constant expression which didn't get reduced. We
// just use complex128 and hope for the best.
resolved_type = Type::lookup_complex_type("complex128");
}
Numeric_constant nc;
nc.set_complex(resolved_type, this->val_);
return Expression::backend_numeric_constant_expression(context, &nc);
}
// Write REAL/IMAG to export data.
void
Complex_expression::export_complex(String_dump* exp, const mpc_t val)
{
if (!mpfr_zero_p(mpc_realref(val)))
{
Float_expression::export_float(exp, mpc_realref(val));
if (mpfr_sgn(mpc_imagref(val)) >= 0)
exp->write_c_string("+");
}
Float_expression::export_float(exp, mpc_imagref(val));
exp->write_c_string("i");
}
// Export a complex number in a constant expression.
void
Complex_expression::do_export(Export_function_body* efb) const
{
bool added_type = false;
if (this->type_ != NULL
&& !this->type_->is_abstract()
&& this->type_ != efb->type_context())
{
efb->write_c_string("$convert(");
efb->write_type(this->type_);
efb->write_c_string(", ");
added_type = true;
}
Complex_expression::export_complex(efb, this->val_);
// A trailing space lets us reliably identify the end of the number.
efb->write_c_string(" ");
if (added_type)
efb->write_c_string(")");
}
// Dump a complex expression to the dump file.
void
Complex_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const
{
Complex_expression::export_complex(ast_dump_context, this->val_);
}
// Make a complex expression.
Expression*
Expression::make_complex(const mpc_t* val, Type* type, Location location)
{
return new Complex_expression(val, type, location);
}
// Find a named object in an expression.
class Find_named_object : public Traverse
{
public:
Find_named_object(Named_object* no)
: Traverse(traverse_expressions),
no_(no), found_(false)
{ }
// Whether we found the object.
bool
found() const