| // 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(); |
| } |
| |
| // Write a name to the export data. |
| |
| void |
| Expression::export_name(Export_function_body* efb, const Named_object* no) |
| { |
| if (no->package() != NULL) |
| { |
| char buf[50]; |
| snprintf(buf, sizeof buf, "<p%d>", efb->package_index(no->package())); |
| efb->write_c_string(buf); |
| } |
| |
| if (!Gogo::is_hidden_name(no->name())) |
| efb->write_string(no->name()); |
| else |
| { |
| efb->write_c_string("."); |
| efb->write_string(Gogo::unpack_hidden_name(no->name())); |
| } |
| } |
| |
| // 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())); |
| } |
| |
| Array_index_expression* aie = a->array_index_expression(); |
| if (aie != NULL) |
| { |
| Array_index_expression* bie = b->array_index_expression(); |
| return (aie->end() == NULL |
| && bie->end() == NULL |
| && Expression::is_same_variable(aie->array(), bie->array()) |
| && Expression::is_same_variable(aie->start(), bie->start())); |
| } |
| |
| Numeric_constant aval; |
| if (a->numeric_constant_value(&aval)) |
| { |
| Numeric_constant bval; |
| if (b->numeric_constant_value(&bval)) |
| return aval.equals(bval); |
| } |
| |
| return false; |
| } |
| |
| // Return an expression handling any conversions which must be done during |
| // assignment. |
| |
| Expression* |
| Expression::convert_for_assignment(Gogo* 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) |
| { |
| // Type to interface conversions have been made explicit early. |
| go_assert(rhs_type->interface_type() != NULL); |
| 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(gogo, 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. If ON_STACK is true, it can allocate the storage on |
| // stack. |
| |
| Expression* |
| Expression::convert_type_to_interface(Type* lhs_type, Expression* rhs, |
| bool on_stack, 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->is_direct_iface_type()) |
| { |
| // We are assigning a pointer to the interface; the interface |
| // holds the pointer itself. |
| obj = unpack_direct_iface(rhs, location); |
| } |
| 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. |
| if (rhs->is_constant()) |
| obj = Expression::make_unary(OPERATOR_AND, rhs, location); |
| else |
| { |
| obj = Expression::make_heap_expression(rhs, location); |
| if (on_stack) |
| obj->heap_expression()->set_allocate_on_stack(); |
| } |
| } |
| |
| return Expression::make_interface_value(lhs_type, first_field, obj, location); |
| } |
| |
| // Return an expression for the pointer-typed value of a direct interface |
| // type. Specifically, for single field struct or array, get the single |
| // field, and do this recursively. The reason for this is that we don't |
| // want to assign a struct or an array to a pointer-typed field. The |
| // backend may not like that. |
| |
| Expression* |
| Expression::unpack_direct_iface(Expression* rhs, Location loc) |
| { |
| Struct_type* st = rhs->type()->struct_type(); |
| if (st != NULL) |
| { |
| go_assert(st->field_count() == 1); |
| Expression* field = Expression::make_field_reference(rhs, 0, loc); |
| return unpack_direct_iface(field, loc); |
| } |
| Array_type* at = rhs->type()->array_type(); |
| if (at != NULL) |
| { |
| int64_t len; |
| bool ok = at->int_length(&len); |
| go_assert(ok && len == 1); |
| Type* int_type = Type::lookup_integer_type("int"); |
| Expression* index = Expression::make_integer_ul(0, int_type, loc); |
| Expression* elem = Expression::make_array_index(rhs, index, NULL, NULL, loc); |
| return unpack_direct_iface(elem, loc); |
| } |
| return rhs; |
| } |
| |
| // The opposite of unpack_direct_iface. |
| |
| Expression* |
| Expression::pack_direct_iface(Type* t, Expression* rhs, Location loc) |
| { |
| if (rhs->type() == t) |
| return rhs; |
| Struct_type* st = t->struct_type(); |
| if (st != NULL) |
| { |
| Expression_list* vals = new Expression_list(); |
| vals->push_back(pack_direct_iface(st->field(0)->type(), rhs, loc)); |
| return Expression::make_struct_composite_literal(t, vals, loc); |
| } |
| Array_type* at = t->array_type(); |
| if (at != NULL) |
| { |
| Expression_list* vals = new Expression_list(); |
| vals->push_back(pack_direct_iface(at->element_type(), rhs, loc)); |
| return Expression::make_array_composite_literal(t, vals, loc); |
| } |
| return Expression::make_unsafe_cast(t, rhs, loc); |
| } |
| |
| // 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(Gogo* gogo, Type *lhs_type, Expression* rhs, |
| Location location) |
| { |
| // We are going to evaluate RHS multiple times. |
| go_assert(rhs->is_variable()); |
| |
| // Build an expression to check that the type is valid. It will |
| // panic with an appropriate runtime type error if the type is not |
| // valid. |
| // (lhs_type == rhs_type ? nil /*dummy*/ : |
| // panicdottype(lhs_type, rhs_type, inter_type)) |
| // For some Oses, we need to call runtime.eqtype instead of |
| // lhs_type == rhs_type, as we may have unmerged type descriptors |
| // from shared libraries. |
| 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* cond; |
| if (gogo->need_eqtype()) { |
| cond = Runtime::make_call(Runtime::EQTYPE, location, |
| 2, lhs_type_expr, |
| rhs_descriptor); |
| } else { |
| cond = Expression::make_binary(OPERATOR_EQEQ, lhs_type_expr, |
| rhs_descriptor, location); |
| } |
| |
| rhs_descriptor = Expression::get_interface_type_descriptor(rhs); |
| Expression* panic = Runtime::make_call(Runtime::PANICDOTTYPE, location, |
| 3, lhs_type_expr->copy(), |
| rhs_descriptor, |
| rhs_inter_expr); |
| Expression* nil = Expression::make_nil(location); |
| Expression* check = Expression::make_conditional(cond, nil, panic, |
| location); |
| |
| // If the conversion succeeds, pull out the value. |
| Expression* obj = Expression::make_interface_info(rhs, INTERFACE_INFO_OBJECT, |
| location); |
| |
| // If the value is a direct interface, then it is the value we want. |
| // Otherwise it points to the value. |
| if (lhs_type->is_direct_iface_type()) |
| obj = Expression::pack_direct_iface(lhs_type, obj, location); |
| else |
| { |
| 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, 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) |
| { |
| go_assert(saw_errors()); |
| 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; |
| } |
| |
| // Insert bounds checks for an index expression. Check that that VAL |
| // >= 0 and that it fits in an int. Then check that VAL OP BOUND is |
| // true. If any condition is false, call one of the CODE runtime |
| // functions, which will panic. |
| |
| void |
| Expression::check_bounds(Expression* val, Operator op, Expression* bound, |
| Runtime::Function code, |
| Runtime::Function code_u, |
| Runtime::Function code_extend, |
| Runtime::Function code_extend_u, |
| Statement_inserter* inserter, |
| Location loc) |
| { |
| go_assert(val->is_variable() || val->is_constant()); |
| go_assert(bound->is_variable() || bound->is_constant()); |
| |
| Type* int_type = Type::lookup_integer_type("int"); |
| int int_type_size = int_type->integer_type()->bits(); |
| |
| Type* val_type = val->type(); |
| if (val_type->integer_type() == NULL) |
| { |
| go_assert(saw_errors()); |
| return; |
| } |
| int val_type_size = val_type->integer_type()->bits(); |
| bool val_is_unsigned = val_type->integer_type()->is_unsigned(); |
| |
| // Check that VAL >= 0. |
| Expression* check = NULL; |
| if (!val_is_unsigned) |
| { |
| Expression* zero = Expression::make_integer_ul(0, val_type, loc); |
| check = Expression::make_binary(OPERATOR_GE, val->copy(), zero, loc); |
| } |
| |
| // If VAL's type is larger than int, check that VAL fits in an int. |
| if (val_type_size > int_type_size |
| || (val_type_size == int_type_size |
| && val_is_unsigned)) |
| { |
| mpz_t one; |
| mpz_init_set_ui(one, 1UL); |
| |
| // maxval = 2^(int_type_size - 1) - 1 |
| mpz_t maxval; |
| mpz_init(maxval); |
| mpz_mul_2exp(maxval, one, int_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); |
| |
| Expression* cmp = Expression::make_binary(OPERATOR_LE, val->copy(), |
| max, loc); |
| if (check == NULL) |
| check = cmp; |
| else |
| check = Expression::make_binary(OPERATOR_ANDAND, check, cmp, loc); |
| } |
| |
| // For the final check we can assume that VAL fits in an int. |
| Expression* ival; |
| if (val_type == int_type) |
| ival = val->copy(); |
| else |
| ival = Expression::make_cast(int_type, val->copy(), loc); |
| |
| // BOUND is assumed to fit in an int. Either it comes from len or |
| // cap, or it was checked by an earlier call. |
| Expression* ibound; |
| if (bound->type() == int_type) |
| ibound = bound->copy(); |
| else |
| ibound = Expression::make_cast(int_type, bound->copy(), loc); |
| |
| Expression* cmp = Expression::make_binary(op, ival, ibound, loc); |
| if (check == NULL) |
| check = cmp; |
| else |
| check = Expression::make_binary(OPERATOR_ANDAND, check, cmp, loc); |
| |
| Runtime::Function c; |
| if (val_type_size > int_type_size) |
| { |
| if (val_is_unsigned) |
| c = code_extend_u; |
| else |
| c = code_extend; |
| } |
| else |
| { |
| if (val_is_unsigned) |
| c = code_u; |
| else |
| c = code; |
| } |
| |
| Expression* ignore = Expression::make_boolean(true, loc); |
| Expression* crash = Runtime::make_call(c, loc, 2, |
| val->copy(), bound->copy()); |
| Expression* cond = Expression::make_conditional(check, ignore, crash, loc); |
| inserter->insert(Statement::make_statement(cond, true)); |
| } |
| |
| 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); |
| } |
| } |
| |
| // Export a reference to a variable. |
| |
| void |
| Var_expression::do_export(Export_function_body* efb) const |
| { |
| Named_object* no = this->variable_; |
| if (no->is_result_variable() || !no->var_value()->is_global()) |
| efb->write_string(Gogo::unpack_hidden_name(no->name())); |
| else |
| Expression::export_name(efb, no); |
| } |
| |
| // 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(); |
| } |
| |
| // Export a reference to a temporary. |
| |
| void |
| Temporary_reference_expression::do_export(Export_function_body* efb) const |
| { |
| unsigned int idx = efb->temporary_index(this->statement_); |
| char buf[50]; |
| snprintf(buf, sizeof buf, "$t%u", idx); |
| efb->write_c_string(buf); |
| } |
| |
| // Import a reference to a temporary. |
| |
| Expression* |
| Temporary_reference_expression::do_import(Import_function_body* ifb, |
| Location loc) |
| { |
| std::string id = ifb->read_identifier(); |
| go_assert(id[0] == '$' && id[1] == 't'); |
| const char *p = id.c_str(); |
| char *end; |
| long idx = strtol(p + 2, &end, 10); |
| if (*end != '\0' || idx > 0x7fffffff) |
| { |
| if (!ifb->saw_error()) |
| go_error_at(loc, |
| ("invalid export data for %qs: " |
| "invalid temporary reference index at %lu"), |
| ifb->name().c_str(), |
| static_cast<unsigned long>(ifb->off())); |
| ifb->set_saw_error(); |
| return Expression::make_error(loc); |
| } |
| |
| Temporary_statement* temp = |
| ifb->temporary_statement(static_cast<unsigned int>(idx)); |
| if (temp == NULL) |
| { |
| if (!ifb->saw_error()) |
| go_error_at(loc, |
| ("invalid export data for %qs: " |
| "undefined temporary reference index at %lu"), |
| ifb->name().c_str(), |
| static_cast<unsigned long>(ifb->off())); |
| ifb->set_saw_error(); |
| return Expression::make_error(loc); |
| } |
| |
| return Expression::make_temporary_reference(temp, loc); |
| } |
| |
| // 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) |
| { |
| statement->add_use(); |
| 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 built-in 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 built-in 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()); |
| } |
| |
| // The cost of inlining a function reference. |
| |
| int |
| Func_expression::do_inlining_cost() const |
| { |
| // FIXME: We don't inline references to nested functions. |
| if (this->closure_ != NULL) |
| return 0x100000; |
| if (this->function_->is_function() |
| && this->function_->func_value()->enclosing() != NULL) |
| return 0x100000; |
| |
| return 1; |
| } |
| |
| // Export a reference to a function. |
| |
| void |
| Func_expression::do_export(Export_function_body* efb) const |
| { |
| Expression::export_name(efb, this->function_); |
| } |
| |
| // 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, "fn", 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)); |
| |
| if (no->is_function() && no->func_value()->is_referenced_by_inline()) |
| is_hidden = false; |
| |
| 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->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->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->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: |
| int |
| do_traverse(Traverse*); |
| |
| bool |
| do_is_constant() const |
| { return true; } |
| |
| bool |
| do_is_zero_value() const |
| { return this->val_ == false; } |
| |
| bool |
| do_boolean_constant_value(bool* val) const |
| { |
| *val = this->val_; |
| 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_; |
| }; |
| |
| // Traverse a boolean expression. We just need to traverse the type |
| // if there is one. |
| |
| int |
| Boolean_expression::do_traverse(Traverse* traverse) |
| { |
| if (this->type_ != NULL) |
| return Type::traverse(this->type_, traverse); |
| return TRAVERSE_CONTINUE; |
| } |
| |
| // 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. |
| |
| // Traverse a string expression. We just need to traverse the type |
| // if there is one. |
| |
| int |
| String_expression::do_traverse(Traverse* traverse) |
| { |
| if (this->type_ != NULL) |
| return Type::traverse(this->type_, traverse); |
| return TRAVERSE_CONTINUE; |
| } |
| |
| // 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 with abstract string type (common case). |
| |
| Expression* |
| Expression::make_string(const std::string& val, Location location) |
| { |
| return new String_expression(val, NULL, location); |
| } |
| |
| // Make a string expression with a specific string type. |
| |
| Expression* |
| Expression::make_string_typed(const std::string& val, Type* type, Location location) |
| { |
| return new String_expression(val, type, 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); |
| } |
| |
| // An expression that represents an string value: a struct with value pointer |
| // and length fields. |
| |
| class String_value_expression : public Expression |
| { |
| public: |
| String_value_expression(Expression* valptr, Expression* len, Location location) |
| : Expression(EXPRESSION_STRING_VALUE, location), |
| valptr_(valptr), len_(len) |
| { } |
| |
| protected: |
| int |
| do_traverse(Traverse*); |
| |
| Type* |
| do_type() |
| { return Type::make_string_type(); } |
| |
| void |
| do_determine_type(const Type_context*) |
| { go_unreachable(); } |
| |
| Expression* |
| do_copy() |
| { |
| return new String_value_expression(this->valptr_->copy(), |
| this->len_->copy(), |
| this->location()); |
| } |
| |
| Bexpression* |
| do_get_backend(Translate_context* context); |
| |
| void |
| do_dump_expression(Ast_dump_context*) const; |
| |
| private: |
| // The value pointer. |
| Expression* valptr_; |
| // The length. |
| Expression* len_; |
| }; |
| |
| int |
| String_value_expression::do_traverse(Traverse* traverse) |
| { |
| if (Expression::traverse(&this->valptr_, traverse) == TRAVERSE_EXIT |
| || Expression::traverse(&this->len_, traverse) == TRAVERSE_EXIT) |
| return TRAVERSE_EXIT; |
| return TRAVERSE_CONTINUE; |
| } |
| |
| Bexpression* |
| String_value_expression::do_get_backend(Translate_context* context) |
| { |
| std::vector<Bexpression*> vals(2); |
| vals[0] = this->valptr_->get_backend(context); |
| vals[1] = this->len_->get_backend(context); |
| |
| Gogo* gogo = context->gogo(); |
| Btype* btype = Type::make_string_type()->get_backend(gogo); |
| return gogo->backend()->constructor_expression(btype, vals, this->location()); |
| } |
| |
| void |
| String_value_expression::do_dump_expression( |
| Ast_dump_context* ast_dump_context) const |
| { |
| ast_dump_context->ostream() << "stringvalue("; |
| ast_dump_context->ostream() << "value: "; |
| this->valptr_->dump_expression(ast_dump_context); |
| ast_dump_context->ostream() << ", length: "; |
| this->len_->dump_expression(ast_dump_context); |
| ast_dump_context->ostream() << ")"; |
| } |
| |
| Expression* |
| Expression::make_string_value(Expression* valptr, Expression* len, |
| Location location) |
| { |
| return new String_value_expression(valptr, len, 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: |
| int |
| do_traverse(Traverse*); |
| |
| bool |
| do_is_constant() const |
| { return true; } |
| |
| bool |
| do_is_zero_value() const |
| { return mpz_sgn(this->val_) == 0; } |
| |
| 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_; |
| }; |
| |
| // Traverse an integer expression. We just need to traverse the type |
| // if there is one. |
| |
| int |
| Integer_expression::do_traverse(Traverse* traverse) |
| { |
| if (this->type_ != NULL) |
| return Type::traverse(this->type_, traverse); |
| return TRAVERSE_CONTINUE; |
| } |
| |
| // 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, MPFR_RNDN); |
| else |
| { |
| std::string real_str = num.substr(0, pos); |
| if (mpfr_init_set_str(real, real_str.c_str(), 10, MPFR_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, MPFR_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, MPFR_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, MPFR_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: |
| int |
| do_traverse(Traverse*); |
| |
| bool |
| do_is_constant() const |
| { return true; } |
| |
| bool |
| do_is_zero_value() const |
| { |
| return mpfr_zero_p(this->val_) != 0 |
| && mpfr_signbit(this->val_) == 0; |
| } |
| |
| 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_; |
| }; |
| |
| // Traverse a float expression. We just need to traverse the type if |
| // there is one. |
| |
| int |
| Float_expression::do_traverse(Traverse* traverse) |
| { |
| if (this->type_ != NULL) |
| return Type::traverse(this->type_, traverse); |
| return TRAVERSE_CONTINUE; |
| } |
| |
| // 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) |
| { |
| mpfr_exp_t exponent; |
| char* s = mpfr_get_str(NULL, &exponent, 10, 0, val, MPFR_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: |
| int |
| do_traverse(Traverse*); |
| |
| bool |
| do_is_constant() const |
| { return true; } |
| |
| bool |
| do_is_zero_value() const |
| { |
| return mpfr_zero_p(mpc_realref(this->val_)) != 0 |
| && mpfr_signbit(mpc_realref(this->val_)) == 0 |
| && mpfr_zero_p(mpc_imagref(this->val_)) != 0 |
| && mpfr_signbit(mpc_imagref(this->val_)) == 0; |
| } |
| |
| 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_; |
| }; |
| |
| // Traverse a complex expression. We just need to traverse the type |
| // if there is one. |
| |
| int |
| Complex_expression::do_traverse(Traverse* traverse) |
| { |
| if (this->type_ != NULL) |
| return Type::traverse(this->type_, traverse); |
| return TRAVERSE_CONTINUE; |
| } |
| |
| // 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. |
|