diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2014-01-10 06:42:08 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2014-01-10 06:42:08 +0000 |
commit | 6ddb62884fb6c35cabf02f665cd29376746e983e (patch) | |
tree | c6fc9b9ae5c829e02e94e913745e0def82608611 /gcc/go | |
parent | 7f003d8337f2901787e9b3e77347fab06c315a6c (diff) | |
download | gcc-6ddb62884fb6c35cabf02f665cd29376746e983e.zip gcc-6ddb62884fb6c35cabf02f665cd29376746e983e.tar.gz gcc-6ddb62884fb6c35cabf02f665cd29376746e983e.tar.bz2 |
compiler: Use backend interface for slice info.
From-SVN: r206509
Diffstat (limited to 'gcc/go')
-rw-r--r-- | gcc/go/gofrontend/expressions.cc | 326 | ||||
-rw-r--r-- | gcc/go/gofrontend/expressions.h | 21 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo.cc | 9 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo.h | 4 | ||||
-rw-r--r-- | gcc/go/gofrontend/statements.cc | 1 | ||||
-rw-r--r-- | gcc/go/gofrontend/types.cc | 81 | ||||
-rw-r--r-- | gcc/go/gofrontend/types.h | 18 |
7 files changed, 322 insertions, 138 deletions
diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index 2f1c026..6f2f243 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -3060,6 +3060,9 @@ class Type_conversion_expression : public Expression Expression* do_lower(Gogo*, Named_object*, Statement_inserter*, int); + Expression* + do_flatten(Gogo*, Named_object*, Statement_inserter*); + bool do_is_constant() const; @@ -3203,6 +3206,25 @@ Type_conversion_expression::do_lower(Gogo*, Named_object*, 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 @@ -3361,47 +3383,24 @@ Type_conversion_expression::do_get_tree(Translate_context* context) } 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()) { @@ -6595,6 +6594,7 @@ Expression::comparison_tree(Translate_context* context, Type* result_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()) @@ -6603,7 +6603,8 @@ Expression::comparison_tree(Translate_context* context, Type* result_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) @@ -7037,6 +7038,9 @@ class Builtin_call_expression : public Call_expression Expression* do_lower(Gogo*, Named_object*, Statement_inserter*, int); + Expression* + do_flatten(Gogo*, Named_object*, Statement_inserter*); + bool do_is_constant() const; @@ -7367,6 +7371,36 @@ Builtin_call_expression::do_lower(Gogo* gogo, Named_object* function, 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* @@ -8503,7 +8537,8 @@ Builtin_call_expression::do_get_tree(Translate_context* context) 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) @@ -8543,8 +8578,9 @@ Builtin_call_expression::do_get_tree(Translate_context* context) 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) @@ -8848,9 +8884,11 @@ Builtin_call_expression::do_get_tree(Translate_context* context) 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; @@ -8860,9 +8898,11 @@ Builtin_call_expression::do_get_tree(Translate_context* context) 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 { @@ -8950,23 +8990,15 @@ Builtin_call_expression::do_get_tree(Translate_context* context) } 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, @@ -10371,6 +10403,9 @@ class Array_index_expression : public Expression do_check_types(Gogo*); Expression* + do_flatten(Gogo*, Named_object*, Statement_inserter*); + + Expression* do_copy() { return Expression::make_array_index(this->array_->copy(), @@ -10611,6 +10646,22 @@ Array_index_expression::do_check_types(Gogo*) } } +// 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 @@ -10643,22 +10694,17 @@ Array_index_expression::do_get_tree(Translate_context* context) 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); @@ -10667,7 +10713,8 @@ Array_index_expression::do_get_tree(Translate_context* context) 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); @@ -10732,13 +10779,18 @@ Array_index_expression::do_get_tree(Translate_context* context) 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); @@ -10820,7 +10872,8 @@ Array_index_expression::do_get_tree(Translate_context* context) 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; @@ -14133,6 +14186,22 @@ Expression::is_nonconstant_composite_literal() const } } +// 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 @@ -14574,6 +14643,117 @@ Expression::make_type_info(Type* type, Type_info type_info) 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. diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h index 66fbac3..abebbd1 100644 --- a/gcc/go/gofrontend/expressions.h +++ b/gcc/go/gofrontend/expressions.h @@ -102,6 +102,7 @@ class Expression EXPRESSION_RECEIVE, EXPRESSION_TYPE_DESCRIPTOR, EXPRESSION_TYPE_INFO, + EXPRESSION_SLICE_INFO, EXPRESSION_STRUCT_FIELD_OFFSET, EXPRESSION_MAP_DESCRIPTOR, EXPRESSION_LABEL_ADDR @@ -339,6 +340,22 @@ class Expression static Expression* make_type_info(Type* type, Type_info); + // Make an expression that evaluates to some characteristic of a + // slice. For simplicity, the enum values must match the field indexes + // in the underlying struct. + enum Slice_info + { + // The underlying data of the slice. + SLICE_INFO_VALUE_POINTER, + // The length of the slice. + SLICE_INFO_LENGTH, + // The capacity of the slice. + SLICE_INFO_CAPACITY + }; + + static Expression* + make_slice_info(Expression* slice, Slice_info, Location); + // Make an expression which evaluates to the offset of a field in a // struct. This is only used for type descriptors, so there is no // location parameter. @@ -544,6 +561,10 @@ class Expression bool is_nonconstant_composite_literal() const; + // Return true if this is a variable or temporary variable. + bool + is_variable() const; + // Return true if this is a reference to a local variable. bool is_local_variable() const; diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc index 9e822e6..6ecc6cd 100644 --- a/gcc/go/gofrontend/gogo.cc +++ b/gcc/go/gofrontend/gogo.cc @@ -2846,6 +2846,15 @@ Flatten::expression(Expression** pexpr) return TRAVERSE_SKIP_COMPONENTS; } +// Flatten a block. + +void +Gogo::flatten_block(Named_object* function, Block* block) +{ + Flatten flatten(this, function); + block->traverse(&flatten); +} + // Flatten an expression. INSERTER may be NULL, in which case the // expression had better not need to create any temporaries. diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h index 285e060..3f28087 100644 --- a/gcc/go/gofrontend/gogo.h +++ b/gcc/go/gofrontend/gogo.h @@ -487,6 +487,10 @@ class Gogo void lower_constant(Named_object*); + // Flatten all the statements in a block. + void + flatten_block(Named_object* function, Block*); + // Flatten an expression. void flatten_expression(Named_object* function, Statement_inserter*, Expression**); diff --git a/gcc/go/gofrontend/statements.cc b/gcc/go/gofrontend/statements.cc index a28aa3d..d195ab9 100644 --- a/gcc/go/gofrontend/statements.cc +++ b/gcc/go/gofrontend/statements.cc @@ -2471,6 +2471,7 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name) gogo->add_block(b, location); gogo->lower_block(function, b); + gogo->flatten_block(function, b); // We already ran the determine_types pass, so we need to run it // just for the call statement now. The other types are known. diff --git a/gcc/go/gofrontend/types.cc b/gcc/go/gofrontend/types.cc index d079565..2935523 100644 --- a/gcc/go/gofrontend/types.cc +++ b/gcc/go/gofrontend/types.cc @@ -6000,84 +6000,53 @@ Array_type::finish_backend_element(Gogo* gogo) } } -// 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. diff --git a/gcc/go/gofrontend/types.h b/gcc/go/gofrontend/types.h index 9f96591..980436a 100644 --- a/gcc/go/gofrontend/types.h +++ b/gcc/go/gofrontend/types.h @@ -2312,17 +2312,17 @@ class Array_type : public Type array_has_hidden_fields(const Named_type* within, std::string* reason) const { return this->element_type_->has_hidden_fields(within, reason); } - // Return a tree for the pointer to the values in an array. - tree - value_pointer_tree(Gogo*, tree array) const; + // Return an expression for the pointer to the values in an array. + Expression* + get_value_pointer(Gogo*, Expression* array) const; - // Return a tree for the length of an array with this type. - tree - length_tree(Gogo*, tree array); + // Return an expression for the length of an array with this type. + Expression* + get_length(Gogo*, Expression* array) const; - // Return a tree for the capacity of an array with this type. - tree - capacity_tree(Gogo*, tree array); + // Return an expression for the capacity of an array with this type. + Expression* + get_capacity(Gogo*, Expression* array) const; // Import an array type. static Array_type* |