blob: bf829b520ee9616f00fc9aae70a2a24066d75f86 [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 <gmp.h>
#ifndef ENABLE_BUILD_WITH_CXX
extern "C"
{
#endif
#include "toplev.h"
#include "intl.h"
#include "tree.h"
#include "gimple.h"
#include "tree-iterator.h"
#include "convert.h"
#include "real.h"
#include "realmpfr.h"
#ifndef ENABLE_BUILD_WITH_CXX
}
#endif
#include "go-c.h"
#include "gogo.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,
source_location location)
: classification_(classification), location_(location)
{
}
Expression::~Expression()
{
}
// If this expression has a constant integer value, return it.
bool
Expression::integer_constant_value(bool iota_is_constant, mpz_t val,
Type** ptype) const
{
*ptype = NULL;
return this->do_integer_constant_value(iota_is_constant, val, ptype);
}
// If this expression has a constant floating point value, return it.
bool
Expression::float_constant_value(mpfr_t val, Type** ptype) const
{
*ptype = NULL;
if (this->do_float_constant_value(val, ptype))
return true;
mpz_t ival;
mpz_init(ival);
Type* t;
bool ret;
if (!this->do_integer_constant_value(false, ival, &t))
ret = false;
else
{
mpfr_set_z(val, ival, GMP_RNDN);
ret = true;
}
mpz_clear(ival);
return ret;
}
// If this expression has a constant complex value, return it.
bool
Expression::complex_constant_value(mpfr_t real, mpfr_t imag,
Type** ptype) const
{
*ptype = NULL;
if (this->do_complex_constant_value(real, imag, ptype))
return true;
Type *t;
if (this->float_constant_value(real, &t))
{
mpfr_set_ui(imag, 0, GMP_RNDN);
return true;
}
return false;
}
// 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.
void
Expression::do_discarding_value()
{
this->unused_value_error();
}
// This virtual function is called to export expressions. This will
// only be used by expressions which may be constant.
void
Expression::do_export(Export*) const
{
go_unreachable();
}
// Give an error saying that the value of the expression is not used.
void
Expression::unused_value_error()
{
error_at(this->location(), "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)
{
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 a tree handling any conversions which must be done during
// assignment.
tree
Expression::convert_for_assignment(Translate_context* context, Type* lhs_type,
Type* rhs_type, tree rhs_tree,
source_location location)
{
if (lhs_type == rhs_type)
return rhs_tree;
if (lhs_type->is_error() || rhs_type->is_error())
return error_mark_node;
if (rhs_tree == error_mark_node || TREE_TYPE(rhs_tree) == error_mark_node)
return error_mark_node;
Gogo* gogo = context->gogo();
tree lhs_type_tree = type_to_tree(lhs_type->get_backend(gogo));
if (lhs_type_tree == error_mark_node)
return error_mark_node;
if (lhs_type->interface_type() != NULL)
{
if (rhs_type->interface_type() == NULL)
return Expression::convert_type_to_interface(context, lhs_type,
rhs_type, rhs_tree,
location);
else
return Expression::convert_interface_to_interface(context, lhs_type,
rhs_type, rhs_tree,
false, location);
}
else if (rhs_type->interface_type() != NULL)
return Expression::convert_interface_to_type(context, lhs_type, rhs_type,
rhs_tree, location);
else if (lhs_type->is_open_array_type()
&& rhs_type->is_nil_type())
{
// Assigning nil to an open array.
go_assert(TREE_CODE(lhs_type_tree) == RECORD_TYPE);
VEC(constructor_elt,gc)* init = VEC_alloc(constructor_elt, gc, 3);
constructor_elt* elt = VEC_quick_push(constructor_elt, init, NULL);
tree field = TYPE_FIELDS(lhs_type_tree);
go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)),
"__values") == 0);
elt->index = field;
elt->value = fold_convert(TREE_TYPE(field), null_pointer_node);
elt = VEC_quick_push(constructor_elt, init, NULL);
field = DECL_CHAIN(field);
go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)),
"__count") == 0);
elt->index = field;
elt->value = fold_convert(TREE_TYPE(field), integer_zero_node);
elt = VEC_quick_push(constructor_elt, init, NULL);
field = DECL_CHAIN(field);
go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)),
"__capacity") == 0);
elt->index = field;
elt->value = fold_convert(TREE_TYPE(field), integer_zero_node);
tree val = build_constructor(lhs_type_tree, init);
TREE_CONSTANT(val) = 1;
return val;
}
else if (rhs_type->is_nil_type())
{
// The left hand side should be a pointer type at the tree
// level.
go_assert(POINTER_TYPE_P(lhs_type_tree));
return fold_convert(lhs_type_tree, null_pointer_node);
}
else if (lhs_type_tree == TREE_TYPE(rhs_tree))
{
// No conversion is needed.
return rhs_tree;
}
else if (POINTER_TYPE_P(lhs_type_tree)
|| INTEGRAL_TYPE_P(lhs_type_tree)
|| SCALAR_FLOAT_TYPE_P(lhs_type_tree)
|| COMPLEX_FLOAT_TYPE_P(lhs_type_tree))
return fold_convert_loc(location, lhs_type_tree, rhs_tree);
else if (TREE_CODE(lhs_type_tree) == RECORD_TYPE
&& TREE_CODE(TREE_TYPE(rhs_tree)) == RECORD_TYPE)
{
// This conversion must be permitted by Go, or we wouldn't have
// gotten here.
go_assert(int_size_in_bytes(lhs_type_tree)
== int_size_in_bytes(TREE_TYPE(rhs_tree)));
return fold_build1_loc(location, VIEW_CONVERT_EXPR, lhs_type_tree,
rhs_tree);
}
else
{
go_assert(useless_type_conversion_p(lhs_type_tree, TREE_TYPE(rhs_tree)));
return rhs_tree;
}
}
// Return a tree for a conversion from a non-interface type to an
// interface type.
tree
Expression::convert_type_to_interface(Translate_context* context,
Type* lhs_type, Type* rhs_type,
tree rhs_tree, source_location location)
{
Gogo* gogo = context->gogo();
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.
if (rhs_type->is_nil_type())
{
Btype* lhs_btype = lhs_type->get_backend(gogo);
return expr_to_tree(gogo->backend()->zero_expression(lhs_btype));
}
// This should have been checked already.
go_assert(lhs_interface_type->implements_interface(rhs_type, NULL));
tree lhs_type_tree = type_to_tree(lhs_type->get_backend(gogo));
if (lhs_type_tree == error_mark_node)
return error_mark_node;
// 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.
tree first_field_value;
if (lhs_is_empty)
first_field_value = rhs_type->type_descriptor_pointer(gogo, 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();
bool is_pointer = false;
if (rhs_named_type == NULL)
{
rhs_named_type = rhs_type->deref()->named_type();
is_pointer = true;
}
tree method_table;
if (rhs_named_type == NULL)
method_table = null_pointer_node;
else
method_table =
rhs_named_type->interface_method_table(gogo, lhs_interface_type,
is_pointer);
first_field_value = fold_convert_loc(location, const_ptr_type_node,
method_table);
}
if (first_field_value == error_mark_node)
return error_mark_node;
// Start building a constructor for the value we will return.
VEC(constructor_elt,gc)* init = VEC_alloc(constructor_elt, gc, 2);
constructor_elt* elt = VEC_quick_push(constructor_elt, init, NULL);
tree field = TYPE_FIELDS(lhs_type_tree);
go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)),
(lhs_is_empty ? "__type_descriptor" : "__methods")) == 0);
elt->index = field;
elt->value = fold_convert_loc(location, TREE_TYPE(field), first_field_value);
elt = VEC_quick_push(constructor_elt, init, NULL);
field = DECL_CHAIN(field);
go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__object") == 0);
elt->index = field;
if (rhs_type->points_to() != NULL)
{
// We are assigning a pointer to the interface; the interface
// holds the pointer itself.
elt->value = rhs_tree;
return build_constructor(lhs_type_tree, init);
}
// We are assigning a non-pointer value to the interface; the
// interface gets a copy of the value in the heap.
tree object_size = TYPE_SIZE_UNIT(TREE_TYPE(rhs_tree));
tree space = gogo->allocate_memory(rhs_type, object_size, location);
space = fold_convert_loc(location, build_pointer_type(TREE_TYPE(rhs_tree)),
space);
space = save_expr(space);
tree ref = build_fold_indirect_ref_loc(location, space);
TREE_THIS_NOTRAP(ref) = 1;
tree set = fold_build2_loc(location, MODIFY_EXPR, void_type_node,
ref, rhs_tree);
elt->value = fold_convert_loc(location, TREE_TYPE(field), space);
return build2(COMPOUND_EXPR, lhs_type_tree, set,
build_constructor(lhs_type_tree, init));
}
// Return a tree for the type descriptor of RHS_TREE, which has
// interface type RHS_TYPE. If RHS_TREE is nil the result will be
// NULL.
tree
Expression::get_interface_type_descriptor(Translate_context*,
Type* rhs_type, tree rhs_tree,
source_location location)
{
tree rhs_type_tree = TREE_TYPE(rhs_tree);
go_assert(TREE_CODE(rhs_type_tree) == RECORD_TYPE);
tree rhs_field = TYPE_FIELDS(rhs_type_tree);
tree v = build3(COMPONENT_REF, TREE_TYPE(rhs_field), rhs_tree, rhs_field,
NULL_TREE);
if (rhs_type->interface_type()->is_empty())
{
go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(rhs_field)),
"__type_descriptor") == 0);
return v;
}
go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(rhs_field)), "__methods")
== 0);
go_assert(POINTER_TYPE_P(TREE_TYPE(v)));
v = save_expr(v);
tree v1 = build_fold_indirect_ref_loc(location, v);
go_assert(TREE_CODE(TREE_TYPE(v1)) == RECORD_TYPE);
tree f = TYPE_FIELDS(TREE_TYPE(v1));
go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(f)), "__type_descriptor")
== 0);
v1 = build3(COMPONENT_REF, TREE_TYPE(f), v1, f, NULL_TREE);
tree eq = fold_build2_loc(location, EQ_EXPR, boolean_type_node, v,
fold_convert_loc(location, TREE_TYPE(v),
null_pointer_node));
tree n = fold_convert_loc(location, TREE_TYPE(v1), null_pointer_node);
return fold_build3_loc(location, COND_EXPR, TREE_TYPE(v1),
eq, n, v1);
}
// Return a tree for the conversion of an interface type to an
// interface type.
tree
Expression::convert_interface_to_interface(Translate_context* context,
Type *lhs_type, Type *rhs_type,
tree rhs_tree, bool for_type_guard,
source_location location)
{
Gogo* gogo = context->gogo();
Interface_type* lhs_interface_type = lhs_type->interface_type();
bool lhs_is_empty = lhs_interface_type->is_empty();
tree lhs_type_tree = type_to_tree(lhs_type->get_backend(gogo));
if (lhs_type_tree == error_mark_node)
return error_mark_node;
// 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.
// Get the type descriptor for the right hand side. This will be
// NULL for a nil interface.
if (!DECL_P(rhs_tree))
rhs_tree = save_expr(rhs_tree);
tree rhs_type_descriptor =
Expression::get_interface_type_descriptor(context, rhs_type, rhs_tree,
location);
// The result is going to be a two element constructor.
VEC(constructor_elt,gc)* init = VEC_alloc(constructor_elt, gc, 2);
constructor_elt* elt = VEC_quick_push(constructor_elt, init, NULL);
tree field = TYPE_FIELDS(lhs_type_tree);
elt->index = field;
if (for_type_guard)
{
// A type assertion fails when converting a nil interface.
tree lhs_type_descriptor = lhs_type->type_descriptor_pointer(gogo,
location);
static tree assert_interface_decl;
tree call = Gogo::call_builtin(&assert_interface_decl,
location,
"__go_assert_interface",
2,
ptr_type_node,
TREE_TYPE(lhs_type_descriptor),
lhs_type_descriptor,
TREE_TYPE(rhs_type_descriptor),
rhs_type_descriptor);
if (call == error_mark_node)
return error_mark_node;
// This will panic if the interface conversion fails.
TREE_NOTHROW(assert_interface_decl) = 0;
elt->value = fold_convert_loc(location, TREE_TYPE(field), call);
}
else if (lhs_is_empty)
{
// A convertion to an empty interface always succeeds, and the
// first field is just the type descriptor of the object.
go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)),
"__type_descriptor") == 0);
go_assert(TREE_TYPE(field) == TREE_TYPE(rhs_type_descriptor));
elt->value = rhs_type_descriptor;
}
else
{
// A conversion to a non-empty interface may fail, but unlike a
// type assertion converting nil will always succeed.
go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__methods")
== 0);
tree lhs_type_descriptor = lhs_type->type_descriptor_pointer(gogo,
location);
static tree convert_interface_decl;
tree call = Gogo::call_builtin(&convert_interface_decl,
location,
"__go_convert_interface",
2,
ptr_type_node,
TREE_TYPE(lhs_type_descriptor),
lhs_type_descriptor,
TREE_TYPE(rhs_type_descriptor),
rhs_type_descriptor);
if (call == error_mark_node)
return error_mark_node;
// This will panic if the interface conversion fails.
TREE_NOTHROW(convert_interface_decl) = 0;
elt->value = fold_convert_loc(location, TREE_TYPE(field), call);
}
// The second field is simply the object pointer.
elt = VEC_quick_push(constructor_elt, init, NULL);
field = DECL_CHAIN(field);
go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__object") == 0);
elt->index = field;
tree rhs_type_tree = TREE_TYPE(rhs_tree);
go_assert(TREE_CODE(rhs_type_tree) == RECORD_TYPE);
tree rhs_field = DECL_CHAIN(TYPE_FIELDS(rhs_type_tree));
go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(rhs_field)), "__object") == 0);
elt->value = build3(COMPONENT_REF, TREE_TYPE(rhs_field), rhs_tree, rhs_field,
NULL_TREE);
return build_constructor(lhs_type_tree, init);
}
// Return a tree for the conversion of an interface type to a
// non-interface type.
tree
Expression::convert_interface_to_type(Translate_context* context,
Type *lhs_type, Type* rhs_type,
tree rhs_tree, source_location location)
{
Gogo* gogo = context->gogo();
tree rhs_type_tree = TREE_TYPE(rhs_tree);
tree lhs_type_tree = type_to_tree(lhs_type->get_backend(gogo));
if (lhs_type_tree == error_mark_node)
return error_mark_node;
// 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.
tree lhs_type_descriptor = lhs_type->type_descriptor_pointer(gogo, location);
if (!DECL_P(rhs_tree))
rhs_tree = save_expr(rhs_tree);
tree rhs_type_descriptor =
Expression::get_interface_type_descriptor(context, rhs_type, rhs_tree,
location);
tree rhs_inter_descriptor = rhs_type->type_descriptor_pointer(gogo,
location);
static tree check_interface_type_decl;
tree call = Gogo::call_builtin(&check_interface_type_decl,
location,
"__go_check_interface_type",
3,
void_type_node,
TREE_TYPE(lhs_type_descriptor),
lhs_type_descriptor,
TREE_TYPE(rhs_type_descriptor),
rhs_type_descriptor,
TREE_TYPE(rhs_inter_descriptor),
rhs_inter_descriptor);
if (call == error_mark_node)
return error_mark_node;
// This call will panic if the conversion is invalid.
TREE_NOTHROW(check_interface_type_decl) = 0;
// If the call succeeds, pull out the value.
go_assert(TREE_CODE(rhs_type_tree) == RECORD_TYPE);
tree rhs_field = DECL_CHAIN(TYPE_FIELDS(rhs_type_tree));
go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(rhs_field)), "__object") == 0);
tree val = build3(COMPONENT_REF, TREE_TYPE(rhs_field), rhs_tree, rhs_field,
NULL_TREE);
// 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)
{
val = fold_convert_loc(location, build_pointer_type(lhs_type_tree), val);
val = build_fold_indirect_ref_loc(location, val);
}
return build2(COMPOUND_EXPR, lhs_type_tree, call,
fold_convert_loc(location, lhs_type_tree, val));
}
// Convert an expression to a tree. 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.
tree
Expression::get_tree(Translate_context* context)
{
// The child may have marked this expression as having an error.
if (this->classification_ == EXPRESSION_ERROR)
return error_mark_node;
return this->do_get_tree(context);
}
// Return a tree for VAL in TYPE.
tree
Expression::integer_constant_tree(mpz_t val, tree type)
{
if (type == error_mark_node)
return error_mark_node;
else if (TREE_CODE(type) == INTEGER_TYPE)
return double_int_to_tree(type,
mpz_get_double_int(type, val, true));
else if (TREE_CODE(type) == REAL_TYPE)
{
mpfr_t fval;
mpfr_init_set_z(fval, val, GMP_RNDN);
tree ret = Expression::float_constant_tree(fval, type);
mpfr_clear(fval);
return ret;
}
else if (TREE_CODE(type) == COMPLEX_TYPE)
{
mpfr_t fval;
mpfr_init_set_z(fval, val, GMP_RNDN);
tree real = Expression::float_constant_tree(fval, TREE_TYPE(type));
mpfr_clear(fval);
tree imag = build_real_from_int_cst(TREE_TYPE(type),
integer_zero_node);
return build_complex(type, real, imag);
}
else
go_unreachable();
}
// Return a tree for VAL in TYPE.
tree
Expression::float_constant_tree(mpfr_t val, tree type)
{
if (type == error_mark_node)
return error_mark_node;
else if (TREE_CODE(type) == INTEGER_TYPE)
{
mpz_t ival;
mpz_init(ival);
mpfr_get_z(ival, val, GMP_RNDN);
tree ret = Expression::integer_constant_tree(ival, type);
mpz_clear(ival);
return ret;
}
else if (TREE_CODE(type) == REAL_TYPE)
{
REAL_VALUE_TYPE r1;
real_from_mpfr(&r1, val, type, GMP_RNDN);
REAL_VALUE_TYPE r2;
real_convert(&r2, TYPE_MODE(type), &r1);
return build_real(type, r2);
}
else if (TREE_CODE(type) == COMPLEX_TYPE)
{
REAL_VALUE_TYPE r1;
real_from_mpfr(&r1, val, TREE_TYPE(type), GMP_RNDN);
REAL_VALUE_TYPE r2;
real_convert(&r2, TYPE_MODE(TREE_TYPE(type)), &r1);
tree imag = build_real_from_int_cst(TREE_TYPE(type),
integer_zero_node);
return build_complex(type, build_real(TREE_TYPE(type), r2), imag);
}
else
go_unreachable();
}
// Return a tree for REAL/IMAG in TYPE.
tree
Expression::complex_constant_tree(mpfr_t real, mpfr_t imag, tree type)
{
if (type == error_mark_node)
return error_mark_node;
else if (TREE_CODE(type) == INTEGER_TYPE || TREE_CODE(type) == REAL_TYPE)
return Expression::float_constant_tree(real, type);
else if (TREE_CODE(type) == COMPLEX_TYPE)
{
REAL_VALUE_TYPE r1;
real_from_mpfr(&r1, real, TREE_TYPE(type), GMP_RNDN);
REAL_VALUE_TYPE r2;
real_convert(&r2, TYPE_MODE(TREE_TYPE(type)), &r1);
REAL_VALUE_TYPE r3;
real_from_mpfr(&r3, imag, TREE_TYPE(type), GMP_RNDN);
REAL_VALUE_TYPE r4;
real_convert(&r4, TYPE_MODE(TREE_TYPE(type)), &r3);
return build_complex(type, build_real(TREE_TYPE(type), r2),
build_real(TREE_TYPE(type), r4));
}
else
go_unreachable();
}
// Return a tree which evaluates to true if VAL, of arbitrary integer
// type, is negative or is more than the maximum value of BOUND_TYPE.
// If SOFAR is not NULL, it is or'red into the result. The return
// value may be NULL if SOFAR is NULL.
tree
Expression::check_bounds(tree val, tree bound_type, tree sofar,
source_location loc)
{
tree val_type = TREE_TYPE(val);
tree ret = NULL_TREE;
if (!TYPE_UNSIGNED(val_type))
{
ret = fold_build2_loc(loc, LT_EXPR, boolean_type_node, val,
build_int_cst(val_type, 0));
if (ret == boolean_false_node)
ret = NULL_TREE;
}
HOST_WIDE_INT val_type_size = int_size_in_bytes(val_type);
HOST_WIDE_INT bound_type_size = int_size_in_bytes(bound_type);
go_assert(val_type_size != -1 && bound_type_size != -1);
if (val_type_size > bound_type_size
|| (val_type_size == bound_type_size
&& TYPE_UNSIGNED(val_type)
&& !TYPE_UNSIGNED(bound_type)))
{
tree max = TYPE_MAX_VALUE(bound_type);
tree big = fold_build2_loc(loc, GT_EXPR, boolean_type_node, val,
fold_convert_loc(loc, val_type, max));
if (big == boolean_false_node)
;
else if (ret == NULL_TREE)
ret = big;
else
ret = fold_build2_loc(loc, TRUTH_OR_EXPR, boolean_type_node,
ret, big);
}
if (ret == NULL_TREE)
return sofar;
else if (sofar == NULL_TREE)
return ret;
else
return fold_build2_loc(loc, TRUTH_OR_EXPR, boolean_type_node,
sofar, ret);
}
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(source_location location)
: Expression(EXPRESSION_ERROR, location)
{ }
protected:
bool
do_is_constant() const
{ return true; }
bool
do_integer_constant_value(bool, mpz_t val, Type**) const
{
mpz_set_ui(val, 0);
return true;
}
bool
do_float_constant_value(mpfr_t val, Type**) const
{
mpfr_set_ui(val, 0, GMP_RNDN);
return true;
}
bool
do_complex_constant_value(mpfr_t real, mpfr_t imag, Type**) const
{
mpfr_set_ui(real, 0, GMP_RNDN);
mpfr_set_ui(imag, 0, GMP_RNDN);
return true;
}
void
do_discarding_value()
{ }
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; }
tree
do_get_tree(Translate_context*)
{ return error_mark_node; }
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(source_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, source_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; }
tree
do_get_tree(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, source_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();
}
}
// Get the tree for a reference to a variable.
tree
Var_expression::do_get_tree(Translate_context* context)
{
Bvariable* bvar = this->variable_->get_backend_variable(context->gogo(),
context->function());
tree ret = var_to_tree(bvar);
if (ret == error_mark_node)
return error_mark_node;
bool is_in_heap;
if (this->variable_->is_variable())
is_in_heap = this->variable_->var_value()->is_in_heap();
else if (this->variable_->is_result_variable())
is_in_heap = this->variable_->result_var_value()->is_in_heap();
else
go_unreachable();
if (is_in_heap)
{
ret = build_fold_indirect_ref_loc(this->location(), ret);
TREE_THIS_NOTRAP(ret) = 1;
}
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_->name() ;
}
// Make a reference to a variable in an expression.
Expression*
Expression::make_var_reference(Named_object* var, source_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 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 tree referring to the variable.
tree
Temporary_reference_expression::do_get_tree(Translate_context* context)
{
Bvariable* bvar = this->statement_->get_backend_variable(context);
// The gcc backend can't 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.
tree ret = var_to_tree(bvar);
if (!this->is_lvalue_
&& POINTER_TYPE_P(TREE_TYPE(ret))
&& VOID_TYPE_P(TREE_TYPE(TREE_TYPE(ret))))
{
Btype* type_btype = this->type()->base()->get_backend(context->gogo());
tree type_tree = type_to_tree(type_btype);
ret = fold_convert_loc(this->location(), type_tree, ret);
}
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,
source_location location)
{
return new Temporary_reference_expression(statement, location);
}
// A sink expression--a use of the blank identifier _.
class Sink_expression : public Expression
{
public:
Sink_expression(source_location location)
: Expression(EXPRESSION_SINK, location),
type_(NULL), var_(NULL_TREE)
{ }
protected:
void
do_discarding_value()
{ }
Type*
do_type();
void
do_determine_type(const Type_context*);
Expression*
do_copy()
{ return new Sink_expression(this->location()); }
tree
do_get_tree(Translate_context*);
void
do_dump_expression(Ast_dump_context*) const;
private:
// The type of this sink variable.
Type* type_;
// The temporary variable we generate.
tree var_;
};
// 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.
tree
Sink_expression::do_get_tree(Translate_context* context)
{
if (this->var_ == NULL_TREE)
{
go_assert(this->type_ != NULL && !this->type_->is_sink_type());
Btype* bt = this->type_->get_backend(context->gogo());
this->var_ = create_tmp_var(type_to_tree(bt), "blank");
}
return this->var_;
}
// 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(source_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 tree for a function expression without evaluating the
// closure.
tree
Func_expression::get_tree_without_closure(Gogo* gogo)
{
Function_type* fntype;
if (this->function_->is_function())
fntype = this->function_->func_value()->type();
else if (this->function_->is_function_declaration())
fntype = this->function_->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())
{
error_at(this->location(), "invalid use of special builtin function %qs",
this->function_->name().c_str());
return error_mark_node;
}
Named_object* no = this->function_;
tree id = no->get_id(gogo);
if (id == error_mark_node)
return error_mark_node;
tree fndecl;
if (no->is_function())
fndecl = no->func_value()->get_or_make_decl(gogo, no, id);
else if (no->is_function_declaration())
fndecl = no->func_declaration_value()->get_or_make_decl(gogo, no, id);
else
go_unreachable();
if (fndecl == error_mark_node)
return error_mark_node;
return build_fold_addr_expr_loc(this->location(), fndecl);
}
// Get the tree for a function expression. This is used when we take
// the address of a function rather than simply calling it. If the
// function has a closure, we must use a trampoline.
tree
Func_expression::do_get_tree(Translate_context* context)
{
Gogo* gogo = context->gogo();
tree fnaddr = this->get_tree_without_closure(gogo);
if (fnaddr == error_mark_node)
return error_mark_node;
go_assert(TREE_CODE(fnaddr) == ADDR_EXPR
&& TREE_CODE(TREE_OPERAND(fnaddr, 0)) == FUNCTION_DECL);
TREE_ADDRESSABLE(TREE_OPERAND(fnaddr, 0)) = 1;
// For a normal non-nested function call, that is all we have to do.
if (!this->function_->is_function()
|| this->function_->func_value()->enclosing() == NULL)
{
go_assert(this->closure_ == NULL);
return fnaddr;
}
// For a nested function call, we have to always allocate a
// trampoline. If we don't always allocate, then closures will not
// be reliably distinct.
Expression* closure = this->closure_;
tree closure_tree;
if (closure == NULL)
closure_tree = null_pointer_node;
else
{
// Get the value of the closure. This will be a pointer to
// space allocated on the heap.
closure_tree = closure->get_tree(context);
if (closure_tree == error_mark_node)
return error_mark_node;
go_assert(POINTER_TYPE_P(TREE_TYPE(closure_tree)));
}
// Now we need to build some code on the heap. This code will load
// the static chain pointer with the closure and then jump to the
// body of the function. The normal gcc approach is to build the
// code on the stack. Unfortunately we can not do that, as Go
// permits us to return the function pointer.
return gogo->make_trampoline(fnaddr, closure_tree, 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,
source_location location)
{
return new Func_expression(function, closure, 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)
{
source_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;
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;
error_at(location, "reference to undefined type %qs",
real->message_name().c_str());
return Expression::make_error(location);
case Named_object::NAMED_OBJECT_VAR:
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;
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.
Expression*
Expression::make_unknown_reference(Named_object* no, source_location location)
{
return new Unknown_expression(no, location);
}
// A boolean expression.
class Boolean_expression : public Expression
{
public:
Boolean_expression(bool val, source_location location)
: Expression(EXPRESSION_BOOLEAN, location),
val_(val), type_(NULL)
{ }
static Expression*
do_import(Import*);
protected:
bool
do_is_constant() const
{ return true; }
Type*
do_type();
void
do_determine_type(const Type_context*);
Expression*
do_copy()
{ return this; }
tree
do_get_tree(Translate_context*)
{ return this->val_ ? boolean_true_node : boolean_false_node; }
void
do_export(Export* exp) const
{ exp->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* imp)
{
if (imp->peek_char() == 't')
{
imp->require_c_string("true");
return Expression::make_boolean(true, imp->location());
}
else
{
imp->require_c_string("false");
return Expression::make_boolean(false, imp->location());
}
}
// Make a boolean expression.
Expression*
Expression::make_boolean(bool val, source_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.
tree
String_expression::do_get_tree(Translate_context* context)
{
return context->gogo()->go_string_constant_tree(this->val_);
}
// 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* exp) const
{
String_expression::export_string(exp, this);
}
// Import a string expression.
Expression*
String_expression::do_import(Import* imp)
{
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
{
error_at(imp->location(), "bad string constant");
return Expression::make_error(imp->location());
}
}
}
return Expression::make_string(val, imp->location());
}
// 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, source_location location)
{
return new String_expression(val, location);
}
// Make an integer expression.
class Integer_expression : public Expression
{
public:
Integer_expression(const mpz_t* val, Type* type, source_location location)
: Expression(EXPRESSION_INTEGER, location),
type_(type)
{ mpz_init_set(this->val_, *val); }
static Expression*
do_import(Import*);
// Return whether VAL fits in the type.
static bool
check_constant(mpz_t val, Type*, source_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_integer_constant_value(bool, mpz_t val, Type** ptype) const;
Type*
do_type();
void
do_determine_type(const Type_context* context);
void
do_check_types(Gogo*);
tree
do_get_tree(Translate_context*);
Expression*
do_copy()
{ return Expression::make_integer(&this->val_, this->type_,
this->location()); }
void
do_export(Export*) const;
void
do_dump_expression(Ast_dump_context*) const;
private:
// The integer value.
mpz_t val_;
// The type so far.
Type* type_;
};
// Return an integer constant value.
bool
Integer_expression::do_integer_constant_value(bool, mpz_t val,
Type** ptype) const
{
if (this->type_ != NULL)
*ptype = this->type_;
mpz_set(val, 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)
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->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_integer_type("int");
}
// Return true if the integer VAL fits in the range of the type TYPE.
// Otherwise give an error and return false. TYPE may be NULL.
bool
Integer_expression::check_constant(mpz_t val, Type* type,
source_location location)
{
if (type == NULL)
return true;
Integer_type* itype = type->integer_type();
if (itype == NULL || itype->is_abstract())
return true;
int bits = mpz_sizeinbase(val, 2);
if (itype->is_unsigned())
{
// For an unsigned type we can only accept a nonnegative number,
// and we must be able to represent at least BITS.
if (mpz_sgn(val) >= 0
&& bits <= itype->bits())
return true;
}
else
{
// For a signed type we need an extra bit to indicate the sign.
// We have to handle the most negative integer specially.
if (bits + 1 <= itype->bits()
|| (bits <= itype->bits()
&& mpz_sgn(val) < 0
&& (mpz_scan1(val, 0)
== static_cast<unsigned long>(itype->bits() - 1))
&& mpz_scan0(val, itype->bits()) == ULONG_MAX))
return true;
}
error_at(location, "integer constant overflow");
return false;
}
// Check the type of an integer constant.
void
Integer_expression::do_check_types(Gogo*)
{
if (this->type_ == NULL)
return;
if (!Integer_expression::check_constant(this->val_, this->type_,
this->location()))
this->set_is_error();
}
// Get a tree for an integer constant.
tree
Integer_expression::do_get_tree(Translate_context* context)
{
Gogo* gogo = context->gogo();
tree type;
if (this->type_ != NULL && !this->type_->is_abstract())
type = type_to_tree(this->type_->get_backend(gogo));
else if (this->type_ != NULL && this->type_->float_type() != NULL)
{
// We are converting to an abstract floating point type.
Type* ftype = Type::lookup_float_type("float64");
type = type_to_tree(ftype->get_backend(gogo));
}
else if (this->type_ != NULL && this->type_->complex_type() != NULL)
{
// We are converting to an abstract complex type.
Type* ctype = Type::lookup_complex_type("complex128");
type = type_to_tree(ctype->get_backend(gogo));
}
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);
if (bits < INT_TYPE_SIZE)
{
Type* t = Type::lookup_integer_type("int");
type = type_to_tree(t->get_backend(gogo));
}
else if (bits < 64)
{
Type* t = Type::lookup_integer_type("int64");
type = type_to_tree(t->get_backend(gogo));
}
else
type = long_long_integer_type_node;
}
return Expression::integer_constant_tree(this->val_, type);
}
// 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* exp) const
{
Integer_expression::export_integer(exp, this->val_);
// A trailing space lets us reliably identify the end of the number.
exp->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* imp)
{
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
{
error_at(imp->location(), "bad number in import data: %qs",
num.c_str());
return Expression::make_error(imp->location());
}
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)
{
error_at(imp->location(), "bad number in import data: %qs",
real_str.c_str());
return Expression::make_error(imp->location());
}
}
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)
{
error_at(imp->location(), "bad number in import data: %qs",
imag_str.c_str());
return Expression::make_error(imp->location());
}
Expression* ret = Expression::make_complex(&real, &imag, NULL,
imp->location());
mpfr_clear(real);
mpfr_clear(imag);
return ret;
}
else if (num.find('.') == std::string::npos
&& num.find('E') == std::string::npos)
{
mpz_t val;
if (mpz_init_set_str(val, num.c_str(), 10) != 0)
{
error_at(imp->location(), "bad number in import data: %qs",
num.c_str());
return Expression::make_error(imp->location());
}
Expression* ret = Expression::make_integer(&val, NULL, imp->location());
mpz_clear(val);
return ret;
}
else
{
mpfr_t val;
if (mpfr_init_set_str(val, num.c_str(), 10, GMP_RNDN) != 0)
{
error_at(imp->location(), "bad number in import data: %qs",
num.c_str());
return Expression::make_error(imp->location());
}
Expression* ret = Expression::make_float(&val, NULL, imp->location());
mpfr_clear(val);
return ret;
}
}
// Ast dump for integer expression.
void
Integer_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const
{
Integer_expression::export_integer(ast_dump_context, this->val_);
}
// Build a new integer value.
Expression*
Expression::make_integer(const mpz_t* val, Type* type,
source_location location)
{
return new Integer_expression(val, type, location);
}
// Floats.
class Float_expression : public Expression
{
public:
Float_expression(const mpfr_t* val, Type* type, source_location location)
: Expression(EXPRESSION_FLOAT, location),
type_(type)
{
mpfr_init_set(this->val_, *val, GMP_RNDN);
}
// Constrain VAL to fit into TYPE.
static void
constrain_float(mpfr_t val, Type* type);
// Return whether VAL fits in the type.
static bool
check_constant(mpfr_t val, Type*, source_location);
// 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_float_constant_value(mpfr_t val, Type**) const;
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_,
this->location()); }
tree
do_get_tree(Translate_context*);
void
do_export(Export*) const;
void
do_dump_expression(Ast_dump_context*) const;
private:
// The floating point value.
mpfr_t val_;
// The type so far.
Type* type_;
};
// Constrain VAL to fit into TYPE.
void
Float_expression::constrain_float(mpfr_t val, Type* type)
{
Float_type* ftype = type->float_type();
if (ftype != NULL && !ftype->is_abstract())
mpfr_prec_round(val, ftype->bits(), GMP_RNDN);
}
// Return a floating point constant value.
bool
Float_expression::do_float_constant_value(mpfr_t val, Type** ptype) const
{
if (this->type_ != NULL)
*ptype = this->type_;
mpfr_set(val, this->val_, GMP_RNDN);
return true;
}
// 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");
}
// Return true if the floating point value VAL fits in the range of
// the type TYPE. Otherwise give an error and return false. TYPE may
// be NULL.
bool
Float_expression::check_constant(mpfr_t val, Type* type,
source_location location)
{
if (type == NULL)
return true;
Float_type* ftype = type->float_type();
if (ftype == NULL || ftype->is_abstract())
return true;
// A NaN or Infinity always fits in the range of the type.
if (mpfr_nan_p(val) || mpfr_inf_p(val) || mpfr_zero_p(val))
return true;
mp_exp_t exp = mpfr_get_exp(val);
mp_exp_t max_exp;
switch (ftype->bits())
{
case 32:
max_exp = 128;
break;
case 64:
max_exp = 1024;
break;
default:
go_unreachable();
}
if (exp > max_exp)
{
error_at(location, "floating point constant overflow");
return false;
}
return true;
}
// Check the type of a float value.
void
Float_expression::do_check_types(Gogo*)
{
if (this->type_ == NULL)
return;
if (!Float_expression::check_constant(this->val_, this->type_,
this->location()))
this->set_is_error();
Integer_type* integer_type = this->type_->integer_type();
if (integer_type != NULL)
{
if (!mpfr_integer_p(this->val_))
this->report_error(_("floating point constant truncated to integer"));
else
{
go_assert(!integer_type->is_abstract());
mpz_t ival;
mpz_init(ival);
mpfr_get_z(ival, this->val_, GMP_RNDN);
Integer_expression::check_constant(ival, integer_type,
this->location());
mpz_clear(ival);
}
}
}
// Get a tree for a float constant.
tree
Float_expression::do_get_tree(Translate_context* context)
{
Gogo* gogo = context->gogo();
tree type;
if (this->type_ != NULL && !this->type_->is_abstract())
type = type_to_tree(this->type_->get_backend(gogo));
else if (this->type_ != NULL && this->type_->integer_type() != NULL)
{
// We have an abstract integer type. We just hope for the best.
type = type_to_tree(Type::lookup_integer_type("int")->get_backend(gogo));
}
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.
Type* ft = Type::lookup_float_type("float64");
type = type_to_tree(ft->get_backend(gogo));
}
return Expression::float_constant_tree(this->val_, type);
}
// 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* exp) const
{
Float_expression::export_float(exp, this->val_);
// A trailing space lets us reliably identify the end of the number.
exp->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, source_location location)
{
return new Float_expression(val, type, location);
}
// Complex numbers.
class Complex_expression : public Expression
{
public:
Complex_expression(const mpfr_t* real, const mpfr_t* imag, Type* type,
source_location location)
: Expression(EXPRESSION_COMPLEX, location),
type_(type)
{
mpfr_init_set(this->real_, *real, GMP_RNDN);
mpfr_init_set(this->imag_, *imag, GMP_RNDN);
}
// Constrain REAL/IMAG to fit into TYPE.
static void
constrain_complex(mpfr_t real, mpfr_t imag, Type* type);
// Return whether REAL/IMAG fits in the type.
static bool
check_constant(mpfr_t real, mpfr_t imag, Type*, source_location);
// Write REAL/IMAG to string dump.
static void
export_complex(String_dump* exp, const mpfr_t real, const mpfr_t val);
// Write REAL/IMAG to dump context.
static void
dump_complex(Ast_dump_context* ast_dump_context,
const mpfr_t real, const mpfr_t val);
protected:
bool
do_is_constant() const
{ return true; }
bool
do_complex_constant_value(mpfr_t real, mpfr_t imag, Type**) const;
Type*
do_type();
void
do_determine_type(const Type_context*);
void
do_check_types(Gogo*);
Expression*
do_copy()
{
return Expression::make_complex(&this->real_, &this->imag_, this->type_,
this->location());
}
tree
do_get_tree(Translate_context*);
void
do_export(Export*) const;
void
do_dump_expression(Ast_dump_context*) const;
private:
// The real part.
mpfr_t real_;
// The imaginary part;
mpfr_t imag_;
// The type if known.
Type* type_;
};
// Constrain REAL/IMAG to fit into TYPE.
void
Complex_expression::constrain_complex(mpfr_t real, mpfr_t imag, Type* type)
{
Complex_type* ctype = type->complex_type();
if (ctype != NULL && !ctype->is_abstract())
{
mpfr_prec_round(real, ctype->bits() / 2, GMP_RNDN);
mpfr_prec_round(imag, ctype->bits() / 2, GMP_RNDN);
}
}
// Return a complex constant value.
bool
Complex_expression::do_complex_constant_value(mpfr_t real, mpfr_t imag,
Type** ptype) const
{
if (this->type_ != NULL)
*ptype = this->type_;
mpfr_set(real, this->real_, GMP_RNDN);
mpfr_set(imag, this->imag_, GMP_RNDN);
return true;
}
// 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->complex_type() != NULL)
this->type_ = context->type;
else if (!context->may_be_abstract)
this->type_ = Type::lookup_complex_type("complex128");
}
// Return true if the complex value REAL/IMAG fits in the range of the
// type TYPE. Otherwise give an error and return false. TYPE may be
// NULL.
bool
Complex_expression::check_constant(mpfr_t real, mpfr_t imag, Type* type,
source_location location)
{
if (type == NULL)
return true;
Complex_type* ctype = type->complex_type();
if (ctype == NULL || ctype->is_abstract())
return true;
mp_exp_t max_exp;
switch (ctype->bits())
{
case 64:
max_exp = 128;
break;
case 128:
max_exp = 1024;
break;
default:
go_unreachable();
}
// A NaN or Infinity always fits in the range of the type.
if (!mpfr_nan_p(real) && !mpfr_inf_p(real) && !mpfr_zero_p(real))
{
if (mpfr_get_exp(real) > max_exp)
{
error_at(location, "complex real part constant overflow");
return false;
}
}
if (!mpfr_nan_p(imag) && !mpfr_inf_p(imag) && !mpfr_zero_p(imag))
{
if (mpfr_get_exp(imag) > max_exp)
{
error_at(location, "complex imaginary part constant overflow");
return false;
}
}
return true;
}
// Check the type of a complex value.
void
Complex_expression::do_check_types(Gogo*)
{
if (this->type_ == NULL)
return;
if (!Complex_expression::check_constant(this->real_, this->imag_,
this->type_, this->location()))
this->set_is_error();
}
// Get a tree for a complex constant.
tree
Complex_expression::do_get_tree(Translate_context* context)
{
Gogo* gogo = context->gogo();
tree type;
if (this->type_ != NULL && !this->type_->is_abstract())
type = type_to_tree(this->type_->get_backend(gogo));
else
{
// If we still have an abstract type here, this this is being
// used in a constant expression which didn't get reduced. We
// just use complex128 and hope for the best.
Type* ct = Type::lookup_complex_type("complex128");
type = type_to_tree(ct->get_backend(gogo));
}
return Expression::complex_constant_tree(this->real_, this->imag_, type);
}
// Write REAL/IMAG to export data.
void
Complex_expression::export_complex(String_dump* exp, const mpfr_t real,
const mpfr_t imag)
{
if (!mpfr_zero_p(real))
{
Float_expression::export_float(exp, real);
if (mpfr_sgn(imag) > 0)
exp->write_c_string("+");
}
Float_expression::export_float(exp, imag);
exp->write_c_string("i");
}
// Export a complex number in a constant expression.
void
Complex_expression::do_export(Export* exp) const
{
Complex_expression::export_complex(exp, this->real_, this->imag_);
// A trailing space lets us reliably identify the end of the number.
exp->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->real_,
this->imag_);
}
// Make a complex expression.
Expression*
Expression::make_complex(const mpfr_t* real, const mpfr_t* imag, Type* type,
source_location location)
{
return new Complex_expression(real, imag, 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
{ return this->found_; }
protected:
int
expression(Expression**);
private:
// The object we are looking for.
Named_object* no_;
// Whether we found it.
bool found_;
};
// A reference to a const in an expression.
class Const_expression : public Expression
{
public:
Const_expression(Named_object* constant, source_location location)
: Expression(EXPRESSION_CONST_REFERENCE, location),
constant_(constant), type_(NULL), seen_(false)
{ }
Named_object*
named_object()
{ return this->constant_; }
// Check that the initializer does not refer to the constant itself.
void
check_for_init_loop();
protected:
int
do_traverse(Traverse*);
Expression*
do_lower(Gogo*, Named_object*, Statement_inserter*, int);
bool
do_is_constant() const
{ return true; }
bool
do_integer_constant_value(bool, mpz_t val, Type**) const;
bool
do_float_constant_value(mpfr_t val, Type**) const;
bool
do_complex_constant_value(mpfr_t real, mpfr_t imag, Type**) const;
bool
do_string_constant_value(std::string* val) const
{ return this->constant_->const_value()->expr()->string_constant_value(val); }
Type*
do_type();
// The type of a const is set by the declaration, not the use.
void
do_determine_type(const Type_context*);
void
do_check_types(Gogo*);
Expression*
do_copy()
{ return this; }
tree
do_get_tree(Translate_context* context);
// When exporting a reference to a const as part of a const
// expression, we export the value. We ignore the fact that it has
// a name.
void
do_export(Export* exp) const
{ this->constant_->const_value()->expr()->export_expression(exp); }
void
do_dump_expression(Ast_dump_context*) const;
private:
// The constant.
Named_object* constant_;
// The type of this reference. This is used if the constant has an
// abstract type.
Type* type_;
// Used to prevent infinite recursion when a constant incorrectly
// refers to itself.
mutable bool seen_;
};
// Traversal.
int
Const_expression::do_traverse(Traverse* traverse)
{
if (this->type_ != NULL)
return Type::traverse(this->type_, traverse);
return TRAVERSE_CONTINUE;
}
// Lower a constant expression. This is where we convert the
// predeclared constant iota into an integer value.
Expression*
Const_expression::do_lower(Gogo* gogo, Named_object*,
Statement_inserter*, int iota_value)
{
if (this->constant_->const_value()->expr()->classification()
== EXPRESSION_IOTA)
{
if (iota_value == -1)
{
error_at(this->location(),
"iota is only defined in const declarations");
iota_value = 0;
}
mpz_t val;
mpz_init_set_ui(val, static_cast<unsigned long>(iota_value));
Expression* ret = Expression::make_integer(&val, NULL,
this->location());
mpz_clear(val);
return ret;
}
// Make sure that the constant itself has been lowered.
gogo->lower_constant(this->constant_);
return this;
}
// Return an integer constant value.
bool
Const_expression::do_integer_constant_value(bool iota_is_constant, mpz_t val,
Type** ptype) const
{
if (this->seen_)
return false;
Type* ctype;
if (this->type_ != NULL)
ctype = this->type_;
else
ctype = this->constant_->const_value()->type();
if (ctype != NULL && ctype->integer_type() == NULL)
return false;
Expression* e = this->constant_->const_value()->expr();
this->seen_ = true;
Type* t;
bool r = e->integer_constant_value(iota_is_constant, val, &t);
this->seen_ = false;
if (r
&& ctype != NULL
&& !Integer_expression::check_constant(val, ctype, this->location()))
return false;
*ptype = ctype != NULL ? ctype : t;
return r;
}
// Return a floating point constant value.
bool
Const_expression::do_float_constant_value(mpfr_t val, Type** ptype) const
{
if (this->seen_)
return false;
Type* ctype;
if (this->type_ != NULL)
ctype = this->type_;
else
ctype = this->constant_->const_value()->type();
if (ctype != NULL && ctype->float_type() == NULL)
return false;
this->seen_ = true;
Type* t;
bool r = this->constant_->const_value()->expr()->float_constant_value(val,
&t);
this->seen_ = false;
if (r && ctype != NULL)
{
if (!Float_expression::check_constant(val, ctype, this->location()))
return false;
Float_expression::constrain_float(val, ctype);
}
*ptype = ctype != NULL ? ctype : t;
return r;
}
// Return a complex constant value.
bool
Const_expression::do_complex_constant_value(mpfr_t real, mpfr_t imag,
Type **ptype) const
{
if (this->seen_)
return false;
Type* ctype;
if (this->type_ != NULL)
ctype = this->type_;
else
ctype = this->constant_->const_value()->type();
if (ctype != NULL && ctype->complex_type() == NULL)
return false;
this->seen_ = true;
Type *t;
bool r = this->constant_->const_value()->expr()->complex_constant_value(real,
imag,
&t);
this->seen_ = false;
if (r && ctype != NULL)
{
if (!Complex_expression::check_constant(real, imag, ctype,
this->location()))
return false;
Complex_expression::constrain_complex(real, imag, ctype);
}
*ptype = ctype != NULL ? ctype : t;
return r;
}
// Return the type of the const reference.
Type*
Const_expression::do_type()
{
if (this->type_ != NULL)
return this->type_;
Named_constant* nc = this->constant_->const_value();
if (this->seen_ || nc->lowering())
{
this->report_error(_("constant refers to itself"));
this->type_ = Type::make_error_type();
return this->type_;
}
this->seen_ = true;
Type* ret = nc->type();
if (ret != NULL)
{
this->seen_ = false;
return ret;
}
// During parsing, a named constant may have a NULL type, but we
// must not return a NULL type here.
ret = nc->expr()->type();
this->seen_ = false;
return ret;
}
// Set the type of the const reference.
void
Const_expression::do_determine_type(const Type_context* context)
{
Type* ctype = this->constant_->const_value()->type();
Type* cetype = (ctype != NULL
? ctype
: this->constant_->const_value()->expr()->type());
if (ctype != NULL && !ctype->is_abstract())
;
else if (context->type != NULL
&& (context->type->integer_type() != NULL
|| context->type->float_type() != NULL
|| context->type->complex_type() != NULL)
&& (cetype->integer_type() != NULL
|| cetype->float_type() != NULL
|| cetype->complex_type() != NULL))
this->type_ = context->type;
else if (context->type != NULL
&& context->type->is_string_type()
&& cetype->is_string_type())
this->type_ = context->type;
else if (context->type != NULL
&& context->type->is_boolean_type()
&& cetype->is_boolean_type())
this->type_ = context->type;
else if (!context->may_be_abstract)
{
if (cetype->is_abstract())
cetype = cetype->make_non_abstract_type();
this->type_ = cetype;
}
}
// Check for a loop in which the initializer of a constant refers to
// the constant itself.
void
Const_expression::check_for_init_loop()
{
if (this->type_ != NULL && this->type_->is_error())
return;
if (this->seen_)
{
this->report_error(_("constant refers to itself"));
this->type_ = Type::make_error_type();
return;
}
Expression* init = this->constant_->const_value()->expr();
Find_named_object find_named_object(this->constant_);
this->seen_ = true;
Expression::traverse(&init, &find_named_object);
this->seen_ = false;
if (find_named_object.found())
{
if (this->type_ == NULL || !this->type_->is_error())
{
this->report_error(_("constant refers to itself"));
this->type_ = Type::make_error_type();
}
return;
}
}
// Check types of a const reference.
void
Const_expression::do_check_types(Gogo*)
{
if (this->type_ != NULL && this->type_->is_error())
return;
this->check_for_init_loop();
if (this->type_ == NULL || this->type_->is_abstract())
return;
// Check for integer overflow.
if (this->type_->integer_type() != NULL)
{
mpz_t ival;
mpz_init(ival);
Type* dummy;
if (!this->integer_constant_value(true, ival, &dummy))
{
mpfr_t fval;
mpfr_init(fval);
Expression* cexpr = this->constant_->const_value()->expr();
if (cexpr->float_constant_value(fval, &dummy))
{
if (!mpfr_integer_p(fval))
this->report_error(_("floating point constant "
"truncated to integer"));
else
{
mpfr_get_z(ival, fval, GMP_RNDN);
Integer_expression::check_constant(ival, this->type_,
this->location());
}
}
mpfr_clear(fval);
}
mpz_clear(ival);
}
}
// Return a tree for the const reference.
tree
Const_expression::do_get_tree(Translate_context* context)
{
Gogo* gogo = context->gogo();
tree type_tree;
if (this->type_ == NULL)
type_tree = NULL_TREE;
else
{
type_tree = type_to_tree(this->type_->get_backend(gogo));
if (type_tree == error_mark_node)
return error_mark_node;
}
// If the type has been set for this expression, but the underlying
// object is an abstract int or float, we try to get the abstract
// value. Otherwise we may lose something in the conversion.
if (this->type_ != NULL
&& (this->constant_->const_value()->type() == NULL
|| this->constant_->const_value()->type()->is_abstract()))
{
Expression* expr = this->constant_->const_value()->expr();
mpz_t ival;
mpz_init(ival);
Type* t;
if (expr->integer_constant_value(true, ival, &t))
{
tree ret = Expression::integer_constant_tree(ival, type_tree);
mpz_clear(ival);
return ret;
}
mpz_clear(ival);
mpfr_t fval;
mpfr_init(fval);
if (expr->float_constant_value(fval, &t))
{
tree ret = Expression::float_constant_tree(fval, type_tree);
mpfr_clear(fval);
return ret;
}
mpfr_t imag;
mpfr_init(imag);
if (expr->complex_constant_value(fval, imag, &t))
{
tree ret = Expression::complex_constant_tree(fval, imag, type_tree);
mpfr_clear(fval);
mpfr_clear(imag);
return ret;
}
mpfr_clear(imag);
mpfr_clear(fval);
}
tree const_tree = this->constant_->get_tree(gogo, context->function());
if (this->type_ == NULL
|| const_tree == error_mark_node
|| TREE_TYPE(const_tree) == error_mark_node)
return const_tree;
tree ret;
if (TYPE_MAIN_VARIANT(type_tree) == TYPE_MAIN_VARIANT(TREE_TYPE(const_tree)))
ret = fold_convert(type_tree, const_tree);
else if (TREE_CODE(type_tree) == INTEGER_TYPE)
ret = fold(convert_to_integer(type_tree, const_tree));
else if (TREE_CODE(type_tree) == REAL_TYPE)
ret = fold(convert_to_real(type_tree, const_tree));
else if (TREE_CODE(type_tree) == COMPLEX_TYPE)
ret = fold(convert_to_complex(type_tree, const_tree));
else
go_unreachable();
return ret;
}
// Dump ast representation for constant expression.
void
Const_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const
{
ast_dump_context->ostream() << this->constant_->name();
}
// Make a reference to a constant in an expression.
Expression*
Expression::make_const_reference(Named_object* constant,
source_location location)
{
return new Const_expression(constant, location);
}
// Find a named object in an expression.
int
Find_named_object::expression(Expression** pexpr)
{
switch ((*pexpr)->classification())
{
case Expression::EXPRESSION_CONST_REFERENCE:
{
Const_expression* ce = static_cast<Const_expression*>(*pexpr);
if (ce->named_object() == this->no_)
break;
// We need to check a constant initializer explicitly, as
// loops here will not be caught by the loop checking for
// variable initializers.
ce->check_for_init_loop();
return TRAVERSE_CONTINUE;
}
case Expression::EXPRESSION_VAR_REFERENCE:
if ((*pexpr)->var_expression()->named_object() == this->no_)
break;
return TRAVERSE_CONTINUE;
case Expression::EXPRESSION_FUNC_REFERENCE:
if ((*pexpr)->func_expression()->named_object() == this->no_)
break;
return TRAVERSE_CONTINUE;
default:
return TRAVERSE_CONTINUE;
}
this->found_ = true;
return TRAVERSE_EXIT;
}
// The nil value.
class Nil_expression : public Expression
{
public:
Nil_expression(source_location location)
: Expression(EXPRESSION_NIL, location)
{ }
static Expression*
do_import(Import*);
protected:
bool
do_is_constant() const
{ return true; }
Type*
do_type()
{ return Type::make_nil_type(); }
void
do_determine_type(const Type_context*)
{ }
Expression*
do_copy()
{ return this; }
tree
do_get_tree(Translate_context*)
{ return null_pointer_node; }
void
do_export(Export* exp) const
{ exp->write_c_string("nil"); }
void
do_dump_expression(Ast_dump_context* ast_dump_context) const
{ ast_dump_context->ostream() << "nil"; }
};
// Import a nil expression.
Expression*
Nil_expression::do_import(Import* imp)
{
imp->require_c_string("nil");
return Expression::make_nil(imp->location());
}
// Make a nil expression.
Expression*
Expression::make_nil(source_location location)
{
return new Nil_expression(location);
}
// The value of the predeclared constant iota. This is little more
// than a marker. This will be lowered to an integer in
// Const_expression::do_lower, which is where we know the value that
// it should have.
class Iota_expression : public Parser_expression
{
public:
Iota_expression(source_location location)
: Parser_expression(EXPRESSION_IOTA, location)
{ }
protected:
Expression*
do_lower(Gogo*, Named_object*, Statement_inserter*, int)
{ go_unreachable(); }
// There should only ever be one of these.
Expression*
do_copy()
{ go_unreachable(); }
void
do_dump_expression(Ast_dump_context* ast_dump_context) const
{ ast_dump_context->ostream() << "iota"; }
};
// Make an iota expression. This is only called for one case: the
// value of the predeclared constant iota.
Expression*
Expression::make_iota()
{
static Iota_expression iota_expression(UNKNOWN_LOCATION);
return &iota_expression;
}
// A type conversion expression.
class Type_conversion_expression : public Expression
{
public:
Type_conversion_expression(Type* type, Expression* expr,
source_location location)
: Expression(EXPRESSION_CONVERSION, location),
type_(type), expr_(expr), may_convert_function_types_(false)
{ }
// Return the type to which we are converting.
Type*
type() const
{ return this->type_; }
// Return the expression which we are converting.
Expression*
expr() const
{ return this->expr_; }
// Permit converting from one function type to another. This is
// used internally for method expressions.
void
set_may_convert_function_types()
{
this->may_convert_function_types_ = true;
}
// Import a type conversion expression.
static Expression*
do_import(Import*);
protected:
int
do_traverse(Traverse* traverse);
Expression*
do_lower(Gogo*, Named_object*, Statement_inserter*, int);
bool
do_is_constant() const
{ return this->expr_->is_constant(); }
bool
do_integer_constant_value(bool, mpz_t, Type**) const;
bool
do_float_constant_value(mpfr_t, Type**) const;
bool
do_complex_constant_value(mpfr_t, mpfr_t, Type**) const;
bool
do_string_constant_value(std::string*) const;
Type*
do_type()
{ return this->type_; }
void
do_determine_type(const Type_context*)