Expression*
do_lower(Gogo*, Named_object*, Statement_inserter*, int);
+ Expression*
+ do_flatten(Gogo*, Named_object*, Statement_inserter*);
+
bool
do_is_constant() const;
return this;
}
+// Flatten a type conversion by using a temporary variable for the slice
+// in slice to string conversions.
+
+Expression*
+Type_conversion_expression::do_flatten(Gogo*, Named_object*,
+ Statement_inserter* inserter)
+{
+ if (this->type()->is_string_type()
+ && this->expr_->type()->is_slice_type()
+ && !this->expr_->is_variable())
+ {
+ Temporary_statement* temp =
+ Statement::make_temporary(NULL, this->expr_, this->location());
+ inserter->insert(temp);
+ this->expr_ = Expression::make_temporary_reference(temp, this->location());
+ }
+ return this;
+}
+
// Return whether a type conversion is a constant.
bool
}
else if (type->is_string_type() && expr_type->is_slice_type())
{
- if (!DECL_P(expr_tree))
- expr_tree = save_expr(expr_tree);
-
- Type* int_type = Type::lookup_integer_type("int");
- tree int_type_tree = type_to_tree(int_type->get_backend(gogo));
-
+ Location location = this->location();
Array_type* a = expr_type->array_type();
Type* e = a->element_type()->forwarded();
go_assert(e->integer_type() != NULL);
- tree valptr = fold_convert(const_ptr_type_node,
- a->value_pointer_tree(gogo, expr_tree));
- tree len = a->length_tree(gogo, expr_tree);
- len = fold_convert_loc(this->location().gcc_location(), int_type_tree,
- len);
+ go_assert(this->expr_->is_variable());
+
+ Runtime::Function code;
if (e->integer_type()->is_byte())
- {
- static tree byte_array_to_string_fndecl;
- ret = Gogo::call_builtin(&byte_array_to_string_fndecl,
- this->location(),
- "__go_byte_array_to_string",
- 2,
- type_tree,
- const_ptr_type_node,
- valptr,
- int_type_tree,
- len);
- }
+ code = Runtime::BYTE_ARRAY_TO_STRING;
else
- {
- go_assert(e->integer_type()->is_rune());
- static tree int_array_to_string_fndecl;
- ret = Gogo::call_builtin(&int_array_to_string_fndecl,
- this->location(),
- "__go_int_array_to_string",
- 2,
- type_tree,
- const_ptr_type_node,
- valptr,
- int_type_tree,
- len);
- }
+ {
+ go_assert(e->integer_type()->is_rune());
+ code = Runtime::INT_ARRAY_TO_STRING;
+ }
+ Expression* valptr = a->get_value_pointer(gogo, this->expr_);
+ Expression* len = a->get_length(gogo, this->expr_);
+ Expression* a2s_expr = Runtime::make_call(code, location, 2, valptr, len);
+ ret = a2s_expr->get_tree(context);
}
else if (type->is_slice_type() && expr_type->is_string_type())
{
{
std::swap(left_type, right_type);
std::swap(left_tree, right_tree);
+ std::swap(left_expr, right_expr);
}
if (right_type->is_nil_type())
&& left_type->array_type()->length() == NULL)
{
Array_type* at = left_type->array_type();
- left_tree = at->value_pointer_tree(context->gogo(), left_tree);
+ left_expr = at->get_value_pointer(context->gogo(), left_expr);
+ left_tree = left_expr->get_tree(context);
right_tree = fold_convert(TREE_TYPE(left_tree), null_pointer_node);
}
else if (left_type->interface_type() != NULL)
Expression*
do_lower(Gogo*, Named_object*, Statement_inserter*, int);
+ Expression*
+ do_flatten(Gogo*, Named_object*, Statement_inserter*);
+
bool
do_is_constant() const;
return this;
}
+// Flatten a builtin call expression. This turns the arguments of copy and
+// append into temporary expressions.
+
+Expression*
+Builtin_call_expression::do_flatten(Gogo*, Named_object*,
+ Statement_inserter* inserter)
+{
+ if (this->code_ == BUILTIN_APPEND
+ || this->code_ == BUILTIN_COPY)
+ {
+ Location loc = this->location();
+ Type* at = this->args()->front()->type();
+ for (Expression_list::iterator pa = this->args()->begin();
+ pa != this->args()->end();
+ ++pa)
+ {
+ if ((*pa)->is_nil_expression())
+ *pa = Expression::make_slice_composite_literal(at, NULL, loc);
+ if (!(*pa)->is_variable())
+ {
+ Temporary_statement* temp =
+ Statement::make_temporary(NULL, *pa, loc);
+ inserter->insert(temp);
+ *pa = Expression::make_temporary_reference(temp, loc);
+ }
+ }
+ }
+ return this;
+}
+
// Lower a make expression.
Expression*
return error_mark_node;
}
this->seen_ = true;
- val_tree = arg_type->array_type()->length_tree(gogo, arg_tree);
+ Expression* len = arg_type->array_type()->get_length(gogo, arg);
+ val_tree = len->get_tree(context);
this->seen_ = false;
}
else if (arg_type->map_type() != NULL)
return error_mark_node;
}
this->seen_ = true;
- val_tree = arg_type->array_type()->capacity_tree(gogo,
- arg_tree);
+ Expression* cap =
+ arg_type->array_type()->get_capacity(gogo, arg);
+ val_tree = cap->get_tree(context);
this->seen_ = false;
}
else if (arg_type->channel_type() != NULL)
Type* arg1_type = arg1->type();
Array_type* at = arg1_type->array_type();
- arg1_tree = save_expr(arg1_tree);
- tree arg1_val = at->value_pointer_tree(gogo, arg1_tree);
- tree arg1_len = at->length_tree(gogo, arg1_tree);
+ go_assert(arg1->is_variable());
+ Expression* arg1_valptr = at->get_value_pointer(gogo, arg1);
+ Expression* arg1_len_expr = at->get_length(gogo, arg1);
+ tree arg1_val = arg1_valptr->get_tree(context);
+ tree arg1_len = arg1_len_expr->get_tree(context);
if (arg1_val == error_mark_node || arg1_len == error_mark_node)
return error_mark_node;
if (arg2_type->is_slice_type())
{
at = arg2_type->array_type();
- arg2_tree = save_expr(arg2_tree);
- arg2_val = at->value_pointer_tree(gogo, arg2_tree);
- arg2_len = at->length_tree(gogo, arg2_tree);
+ go_assert(arg2->is_variable());
+ Expression* arg2_valptr = at->get_value_pointer(gogo, arg2);
+ Expression* arg2_len_expr = at->get_length(gogo, arg2);
+ arg2_val = arg2_valptr->get_tree(context);
+ arg2_len = arg2_len_expr->get_tree(context);
}
else
{
}
else
{
- arg2_tree = Expression::convert_for_assignment(context, at,
- arg2->type(),
- arg2_tree,
- location);
- if (arg2_tree == error_mark_node)
+ go_assert(arg2->is_variable());
+ arg2_val =
+ at->get_value_pointer(gogo, arg2)->get_tree(context);
+ arg2_len = at->get_length(gogo, arg2)->get_tree(context);
+ Btype* element_btype = element_type->get_backend(gogo);
+ tree element_type_tree = type_to_tree(element_btype);
+ if (element_type_tree == error_mark_node)
return error_mark_node;
-
- arg2_tree = save_expr(arg2_tree);
-
- arg2_val = at->value_pointer_tree(gogo, arg2_tree);
- arg2_len = at->length_tree(gogo, arg2_tree);
-
- Btype* element_btype = element_type->get_backend(gogo);
- tree element_type_tree = type_to_tree(element_btype);
- if (element_type_tree == error_mark_node)
- return error_mark_node;
- element_size = TYPE_SIZE_UNIT(element_type_tree);
+ element_size = TYPE_SIZE_UNIT(element_type_tree);
}
arg2_val = fold_convert_loc(location.gcc_location(), ptr_type_node,
do_check_types(Gogo*);
Expression*
+ do_flatten(Gogo*, Named_object*, Statement_inserter*);
+
+ Expression*
do_copy()
{
return Expression::make_array_index(this->array_->copy(),
}
}
+// Flatten array indexing by using a temporary variable for slices.
+
+Expression*
+Array_index_expression::do_flatten(Gogo*, Named_object*,
+ Statement_inserter* inserter)
+{
+ Location loc = this->location();
+ if (this->array_->type()->is_slice_type() && !this->array_->is_variable())
+ {
+ Temporary_statement* temp = Statement::make_temporary(NULL, this->array_, loc);
+ inserter->insert(temp);
+ this->array_ = Expression::make_temporary_reference(temp, loc);
+ }
+ return this;
+}
+
// Return whether this expression is addressable.
bool
go_assert(this->array_->type()->is_error());
return error_mark_node;
}
+ go_assert(!array_type->is_slice_type() || this->array_->is_variable());
tree type_tree = type_to_tree(array_type->get_backend(gogo));
if (type_tree == error_mark_node)
return error_mark_node;
- tree array_tree = this->array_->get_tree(context);
- if (array_tree == error_mark_node)
- return error_mark_node;
-
- if (array_type->length() == NULL && !DECL_P(array_tree))
- array_tree = save_expr(array_tree);
-
tree length_tree = NULL_TREE;
if (this->end_ == NULL || this->end_->is_nil_expression())
{
- length_tree = array_type->length_tree(gogo, array_tree);
+ Expression* len = array_type->get_length(gogo, this->array_);
+ length_tree = len->get_tree(context);
if (length_tree == error_mark_node)
return error_mark_node;
length_tree = save_expr(length_tree);
tree capacity_tree = NULL_TREE;
if (this->end_ != NULL)
{
- capacity_tree = array_type->capacity_tree(gogo, array_tree);
+ Expression* cap = array_type->get_capacity(gogo, this->array_);
+ capacity_tree = cap->get_tree(context);
if (capacity_tree == error_mark_node)
return error_mark_node;
capacity_tree = save_expr(capacity_tree);
if (array_type->length() != NULL)
{
// Fixed array.
+ tree array_tree = this->array_->get_tree(context);
+ if (array_tree == error_mark_node)
+ return error_mark_node;
return build4(ARRAY_REF, TREE_TYPE(type_tree), array_tree,
start_tree, NULL_TREE, NULL_TREE);
}
else
{
// Open array.
- tree values = array_type->value_pointer_tree(gogo, array_tree);
+ Expression* valptr =
+ array_type->get_value_pointer(gogo, this->array_);
+ tree values = valptr->get_tree(context);
Type* element_type = array_type->element_type();
Btype* belement_type = element_type->get_backend(gogo);
tree element_type_tree = type_to_tree(belement_type);
start_tree),
element_size);
- tree value_pointer = array_type->value_pointer_tree(gogo, array_tree);
+ Expression* valptr = array_type->get_value_pointer(gogo, this->array_);
+ tree value_pointer = valptr->get_tree(context);
if (value_pointer == error_mark_node)
return error_mark_node;
}
}
+// Return true if this is a variable or temporary_variable.
+
+bool
+Expression::is_variable() const
+{
+ switch (this->classification_)
+ {
+ case EXPRESSION_VAR_REFERENCE:
+ case EXPRESSION_TEMPORARY_REFERENCE:
+ case EXPRESSION_SET_AND_USE_TEMPORARY:
+ return true;
+ default:
+ return false;
+ }
+}
+
// Return true if this is a reference to a local variable.
bool
return new Type_info_expression(type, type_info);
}
+// An expression that evaluates to some characteristic of a slice.
+// This is used when indexing, bound-checking, or nil checking a slice.
+
+class Slice_info_expression : public Expression
+{
+ public:
+ Slice_info_expression(Expression* slice, Slice_info slice_info,
+ Location location)
+ : Expression(EXPRESSION_SLICE_INFO, location),
+ slice_(slice), slice_info_(slice_info)
+ { }
+
+ protected:
+ Type*
+ do_type();
+
+ void
+ do_determine_type(const Type_context*)
+ { }
+
+ Expression*
+ do_copy()
+ {
+ return new Slice_info_expression(this->slice_->copy(), this->slice_info_,
+ this->location());
+ }
+
+ tree
+ do_get_tree(Translate_context* context);
+
+ void
+ do_dump_expression(Ast_dump_context*) const;
+
+ void
+ do_issue_nil_check()
+ { this->slice_->issue_nil_check(); }
+
+ private:
+ // The slice for which we are getting information.
+ Expression* slice_;
+ // What information we want.
+ Slice_info slice_info_;
+};
+
+// Return the type of the slice info.
+
+Type*
+Slice_info_expression::do_type()
+{
+ switch (this->slice_info_)
+ {
+ case SLICE_INFO_VALUE_POINTER:
+ return Type::make_pointer_type(
+ this->slice_->type()->array_type()->element_type());
+ case SLICE_INFO_LENGTH:
+ case SLICE_INFO_CAPACITY:
+ return Type::lookup_integer_type("int");
+ default:
+ go_unreachable();
+ }
+}
+
+// Return slice information in GENERIC.
+
+tree
+Slice_info_expression::do_get_tree(Translate_context* context)
+{
+ Gogo* gogo = context->gogo();
+
+ Bexpression* bslice = tree_to_expr(this->slice_->get_tree(context));
+ Bexpression* ret;
+ switch (this->slice_info_)
+ {
+ case SLICE_INFO_VALUE_POINTER:
+ case SLICE_INFO_LENGTH:
+ case SLICE_INFO_CAPACITY:
+ ret = gogo->backend()->struct_field_expression(bslice, this->slice_info_,
+ this->location());
+ break;
+ default:
+ go_unreachable();
+ }
+ return expr_to_tree(ret);
+}
+
+// Dump ast representation for a type info expression.
+
+void
+Slice_info_expression::do_dump_expression(
+ Ast_dump_context* ast_dump_context) const
+{
+ ast_dump_context->ostream() << "sliceinfo(";
+ this->slice_->dump_expression(ast_dump_context);
+ ast_dump_context->ostream() << ",";
+ ast_dump_context->ostream() <<
+ (this->slice_info_ == SLICE_INFO_VALUE_POINTER ? "values"
+ : this->slice_info_ == SLICE_INFO_LENGTH ? "length"
+ : this->slice_info_ == SLICE_INFO_CAPACITY ? "capacity "
+ : "unknown");
+ ast_dump_context->ostream() << ")";
+}
+
+// Make a slice info expression.
+
+Expression*
+Expression::make_slice_info(Expression* slice, Slice_info slice_info,
+ Location location)
+{
+ return new Slice_info_expression(slice, slice_info, location);
+}
+
// An expression which evaluates to the offset of a field within a
// struct. This, like Type_info_expression, q.v., is only used to
// initialize fields of a type descriptor.
}
}
-// Return a tree for a pointer to the values in ARRAY.
+// Return an expression for a pointer to the values in ARRAY.
-tree
-Array_type::value_pointer_tree(Gogo*, tree array) const
+Expression*
+Array_type::get_value_pointer(Gogo*, Expression* array) const
{
- tree ret;
if (this->length() != NULL)
{
// Fixed array.
- ret = fold_convert(build_pointer_type(TREE_TYPE(TREE_TYPE(array))),
- build_fold_addr_expr(array));
+ go_assert(array->type()->array_type() != NULL);
+ Type* etype = array->type()->array_type()->element_type();
+ array = Expression::make_unary(OPERATOR_AND, array, array->location());
+ return Expression::make_cast(Type::make_pointer_type(etype), array,
+ array->location());
}
- else
- {
- // Open array.
- tree field = TYPE_FIELDS(TREE_TYPE(array));
- go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)),
- "__values") == 0);
- ret = fold_build3(COMPONENT_REF, TREE_TYPE(field), array, field,
- NULL_TREE);
- }
- if (TREE_CONSTANT(array))
- TREE_CONSTANT(ret) = 1;
- return ret;
+
+ // Open array.
+ return Expression::make_slice_info(array,
+ Expression::SLICE_INFO_VALUE_POINTER,
+ array->location());
}
-// Return a tree for the length of the array ARRAY which has this
+// Return an expression for the length of the array ARRAY which has this
// type.
-tree
-Array_type::length_tree(Gogo* gogo, tree array)
+Expression*
+Array_type::get_length(Gogo*, Expression* array) const
{
if (this->length_ != NULL)
- {
- if (TREE_CODE(array) == SAVE_EXPR)
- return this->get_length_tree(gogo);
- else
- {
- tree len = this->get_length_tree(gogo);
- return omit_one_operand(TREE_TYPE(len), len, array);
- }
- }
+ return this->length_;
// This is an open array. We need to read the length field.
-
- tree type = TREE_TYPE(array);
- go_assert(TREE_CODE(type) == RECORD_TYPE);
-
- tree field = DECL_CHAIN(TYPE_FIELDS(type));
- go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__count") == 0);
-
- tree ret = build3(COMPONENT_REF, TREE_TYPE(field), array, field, NULL_TREE);
- if (TREE_CONSTANT(array))
- TREE_CONSTANT(ret) = 1;
- return ret;
+ return Expression::make_slice_info(array, Expression::SLICE_INFO_LENGTH,
+ array->location());
}
-// Return a tree for the capacity of the array ARRAY which has this
+// Return an expression for the capacity of the array ARRAY which has this
// type.
-tree
-Array_type::capacity_tree(Gogo* gogo, tree array)
+Expression*
+Array_type::get_capacity(Gogo*, Expression* array) const
{
if (this->length_ != NULL)
- {
- tree len = this->get_length_tree(gogo);
- return omit_one_operand(TREE_TYPE(len), len, array);
- }
+ return this->length_;
// This is an open array. We need to read the capacity field.
-
- tree type = TREE_TYPE(array);
- go_assert(TREE_CODE(type) == RECORD_TYPE);
-
- tree field = DECL_CHAIN(DECL_CHAIN(TYPE_FIELDS(type)));
- go_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__capacity") == 0);
-
- return build3(COMPONENT_REF, TREE_TYPE(field), array, field, NULL_TREE);
+ return Expression::make_slice_info(array, Expression::SLICE_INFO_CAPACITY,
+ array->location());
}
// Export.