| // types.cc -- Go frontend types. |
| |
| // 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 <ostream> |
| |
| #include "go-c.h" |
| #include "gogo.h" |
| #include "go-diagnostics.h" |
| #include "go-encode-id.h" |
| #include "go-sha1.h" |
| #include "operator.h" |
| #include "expressions.h" |
| #include "statements.h" |
| #include "export.h" |
| #include "import.h" |
| #include "backend.h" |
| #include "types.h" |
| |
| // Forward declarations so that we don't have to make types.h #include |
| // backend.h. |
| |
| static void |
| get_backend_struct_fields(Gogo* gogo, Struct_type* type, bool use_placeholder, |
| std::vector<Backend::Btyped_identifier>* bfields); |
| |
| static void |
| get_backend_slice_fields(Gogo* gogo, Array_type* type, bool use_placeholder, |
| std::vector<Backend::Btyped_identifier>* bfields); |
| |
| static void |
| get_backend_interface_fields(Gogo* gogo, Interface_type* type, |
| bool use_placeholder, |
| std::vector<Backend::Btyped_identifier>* bfields); |
| |
| // Class Type. |
| |
| Type::Type(Type_classification classification) |
| : classification_(classification), btype_(NULL), type_descriptor_var_(NULL), |
| gc_symbol_var_(NULL) |
| { |
| } |
| |
| Type::~Type() |
| { |
| } |
| |
| // Get the base type for a type--skip names and forward declarations. |
| |
| Type* |
| Type::base() |
| { |
| switch (this->classification_) |
| { |
| case TYPE_NAMED: |
| return this->named_type()->named_base(); |
| case TYPE_FORWARD: |
| return this->forward_declaration_type()->real_type()->base(); |
| default: |
| return this; |
| } |
| } |
| |
| const Type* |
| Type::base() const |
| { |
| switch (this->classification_) |
| { |
| case TYPE_NAMED: |
| return this->named_type()->named_base(); |
| case TYPE_FORWARD: |
| return this->forward_declaration_type()->real_type()->base(); |
| default: |
| return this; |
| } |
| } |
| |
| // Skip defined forward declarations. |
| |
| Type* |
| Type::forwarded() |
| { |
| Type* t = this; |
| Forward_declaration_type* ftype = t->forward_declaration_type(); |
| while (ftype != NULL && ftype->is_defined()) |
| { |
| t = ftype->real_type(); |
| ftype = t->forward_declaration_type(); |
| } |
| return t; |
| } |
| |
| const Type* |
| Type::forwarded() const |
| { |
| const Type* t = this; |
| const Forward_declaration_type* ftype = t->forward_declaration_type(); |
| while (ftype != NULL && ftype->is_defined()) |
| { |
| t = ftype->real_type(); |
| ftype = t->forward_declaration_type(); |
| } |
| return t; |
| } |
| |
| // Skip alias definitions. |
| |
| Type* |
| Type::unalias() |
| { |
| Type* t = this->forwarded(); |
| Named_type* nt = t->named_type(); |
| while (nt != NULL && nt->is_alias()) |
| { |
| t = nt->real_type()->forwarded(); |
| nt = t->named_type(); |
| } |
| return t; |
| } |
| |
| const Type* |
| Type::unalias() const |
| { |
| const Type* t = this->forwarded(); |
| const Named_type* nt = t->named_type(); |
| while (nt != NULL && nt->is_alias()) |
| { |
| t = nt->real_type()->forwarded(); |
| nt = t->named_type(); |
| } |
| return t; |
| } |
| |
| // If this is a named type, return it. Otherwise, return NULL. |
| |
| Named_type* |
| Type::named_type() |
| { |
| return this->forwarded()->convert_no_base<Named_type, TYPE_NAMED>(); |
| } |
| |
| const Named_type* |
| Type::named_type() const |
| { |
| return this->forwarded()->convert_no_base<const Named_type, TYPE_NAMED>(); |
| } |
| |
| // Return true if this type is not defined. |
| |
| bool |
| Type::is_undefined() const |
| { |
| return this->forwarded()->forward_declaration_type() != NULL; |
| } |
| |
| // Return true if this is a basic type: a type which is not composed |
| // of other types, and is not void. |
| |
| bool |
| Type::is_basic_type() const |
| { |
| switch (this->classification_) |
| { |
| case TYPE_INTEGER: |
| case TYPE_FLOAT: |
| case TYPE_COMPLEX: |
| case TYPE_BOOLEAN: |
| case TYPE_STRING: |
| case TYPE_NIL: |
| return true; |
| |
| case TYPE_ERROR: |
| case TYPE_VOID: |
| case TYPE_FUNCTION: |
| case TYPE_POINTER: |
| case TYPE_STRUCT: |
| case TYPE_ARRAY: |
| case TYPE_MAP: |
| case TYPE_CHANNEL: |
| case TYPE_INTERFACE: |
| return false; |
| |
| case TYPE_NAMED: |
| case TYPE_FORWARD: |
| return this->base()->is_basic_type(); |
| |
| default: |
| go_unreachable(); |
| } |
| } |
| |
| // Return true if this is an abstract type. |
| |
| bool |
| Type::is_abstract() const |
| { |
| switch (this->classification()) |
| { |
| case TYPE_INTEGER: |
| return this->integer_type()->is_abstract(); |
| case TYPE_FLOAT: |
| return this->float_type()->is_abstract(); |
| case TYPE_COMPLEX: |
| return this->complex_type()->is_abstract(); |
| case TYPE_STRING: |
| return this->is_abstract_string_type(); |
| case TYPE_BOOLEAN: |
| return this->is_abstract_boolean_type(); |
| default: |
| return false; |
| } |
| } |
| |
| // Return a non-abstract version of an abstract type. |
| |
| Type* |
| Type::make_non_abstract_type() |
| { |
| go_assert(this->is_abstract()); |
| switch (this->classification()) |
| { |
| case TYPE_INTEGER: |
| if (this->integer_type()->is_rune()) |
| return Type::lookup_integer_type("int32"); |
| else |
| return Type::lookup_integer_type("int"); |
| case TYPE_FLOAT: |
| return Type::lookup_float_type("float64"); |
| case TYPE_COMPLEX: |
| return Type::lookup_complex_type("complex128"); |
| case TYPE_STRING: |
| return Type::lookup_string_type(); |
| case TYPE_BOOLEAN: |
| return Type::lookup_bool_type(); |
| default: |
| go_unreachable(); |
| } |
| } |
| |
| // Return true if this is an error type. Don't give an error if we |
| // try to dereference an undefined forwarding type, as this is called |
| // in the parser when the type may legitimately be undefined. |
| |
| bool |
| Type::is_error_type() const |
| { |
| const Type* t = this->forwarded(); |
| // Note that we return false for an undefined forward type. |
| switch (t->classification_) |
| { |
| case TYPE_ERROR: |
| return true; |
| case TYPE_NAMED: |
| return t->named_type()->is_named_error_type(); |
| default: |
| return false; |
| } |
| } |
| |
| // If this is a pointer type, return the type to which it points. |
| // Otherwise, return NULL. |
| |
| Type* |
| Type::points_to() const |
| { |
| const Pointer_type* ptype = this->convert<const Pointer_type, |
| TYPE_POINTER>(); |
| return ptype == NULL ? NULL : ptype->points_to(); |
| } |
| |
| // Return whether this is a slice type. |
| |
| bool |
| Type::is_slice_type() const |
| { |
| return this->array_type() != NULL && this->array_type()->length() == NULL; |
| } |
| |
| // Return whether this is the predeclared constant nil being used as a |
| // type. |
| |
| bool |
| Type::is_nil_constant_as_type() const |
| { |
| const Type* t = this->forwarded(); |
| if (t->forward_declaration_type() != NULL) |
| { |
| const Named_object* no = t->forward_declaration_type()->named_object(); |
| if (no->is_unknown()) |
| no = no->unknown_value()->real_named_object(); |
| if (no != NULL |
| && no->is_const() |
| && no->const_value()->expr()->is_nil_expression()) |
| return true; |
| } |
| return false; |
| } |
| |
| // Traverse a type. |
| |
| int |
| Type::traverse(Type* type, Traverse* traverse) |
| { |
| go_assert((traverse->traverse_mask() & Traverse::traverse_types) != 0 |
| || (traverse->traverse_mask() |
| & Traverse::traverse_expressions) != 0); |
| if (traverse->remember_type(type)) |
| { |
| // We have already traversed this type. |
| return TRAVERSE_CONTINUE; |
| } |
| if ((traverse->traverse_mask() & Traverse::traverse_types) != 0) |
| { |
| int t = traverse->type(type); |
| if (t == TRAVERSE_EXIT) |
| return TRAVERSE_EXIT; |
| else if (t == TRAVERSE_SKIP_COMPONENTS) |
| return TRAVERSE_CONTINUE; |
| } |
| // An array type has an expression which we need to traverse if |
| // traverse_expressions is set. |
| if (type->do_traverse(traverse) == TRAVERSE_EXIT) |
| return TRAVERSE_EXIT; |
| return TRAVERSE_CONTINUE; |
| } |
| |
| // Default implementation for do_traverse for child class. |
| |
| int |
| Type::do_traverse(Traverse*) |
| { |
| return TRAVERSE_CONTINUE; |
| } |
| |
| // Return whether two types are identical. If REASON is not NULL, |
| // optionally set *REASON to the reason the types are not identical. |
| |
| bool |
| Type::are_identical(const Type* t1, const Type* t2, int flags, |
| std::string* reason) |
| { |
| if (t1 == NULL || t2 == NULL) |
| { |
| // Something is wrong. |
| return (flags & COMPARE_ERRORS) == 0 ? true : t1 == t2; |
| } |
| |
| // Skip defined forward declarations. |
| t1 = t1->forwarded(); |
| t2 = t2->forwarded(); |
| |
| if ((flags & COMPARE_ALIASES) == 0) |
| { |
| // Ignore aliases. |
| t1 = t1->unalias(); |
| t2 = t2->unalias(); |
| } |
| |
| if (t1 == t2) |
| return true; |
| |
| // An undefined forward declaration is an error. |
| if (t1->forward_declaration_type() != NULL |
| || t2->forward_declaration_type() != NULL) |
| return (flags & COMPARE_ERRORS) == 0; |
| |
| // Avoid cascading errors with error types. |
| if (t1->is_error_type() || t2->is_error_type()) |
| { |
| if ((flags & COMPARE_ERRORS) == 0) |
| return true; |
| return t1->is_error_type() && t2->is_error_type(); |
| } |
| |
| // Get a good reason for the sink type. Note that the sink type on |
| // the left hand side of an assignment is handled in are_assignable. |
| if (t1->is_sink_type() || t2->is_sink_type()) |
| { |
| if (reason != NULL) |
| *reason = "invalid use of _"; |
| return false; |
| } |
| |
| // A named type is only identical to itself. |
| if (t1->named_type() != NULL || t2->named_type() != NULL) |
| return false; |
| |
| // Check type shapes. |
| if (t1->classification() != t2->classification()) |
| return false; |
| |
| switch (t1->classification()) |
| { |
| case TYPE_VOID: |
| case TYPE_BOOLEAN: |
| case TYPE_STRING: |
| case TYPE_NIL: |
| // These types are always identical. |
| return true; |
| |
| case TYPE_INTEGER: |
| return t1->integer_type()->is_identical(t2->integer_type()); |
| |
| case TYPE_FLOAT: |
| return t1->float_type()->is_identical(t2->float_type()); |
| |
| case TYPE_COMPLEX: |
| return t1->complex_type()->is_identical(t2->complex_type()); |
| |
| case TYPE_FUNCTION: |
| return t1->function_type()->is_identical(t2->function_type(), |
| false, flags, reason); |
| |
| case TYPE_POINTER: |
| return Type::are_identical(t1->points_to(), t2->points_to(), flags, |
| reason); |
| |
| case TYPE_STRUCT: |
| return t1->struct_type()->is_identical(t2->struct_type(), flags); |
| |
| case TYPE_ARRAY: |
| return t1->array_type()->is_identical(t2->array_type(), flags); |
| |
| case TYPE_MAP: |
| return t1->map_type()->is_identical(t2->map_type(), flags); |
| |
| case TYPE_CHANNEL: |
| return t1->channel_type()->is_identical(t2->channel_type(), flags); |
| |
| case TYPE_INTERFACE: |
| return t1->interface_type()->is_identical(t2->interface_type(), flags); |
| |
| case TYPE_CALL_MULTIPLE_RESULT: |
| if (reason != NULL) |
| *reason = "invalid use of multiple-value function call"; |
| return false; |
| |
| default: |
| go_unreachable(); |
| } |
| } |
| |
| // Return true if it's OK to have a binary operation with types LHS |
| // and RHS. This is not used for shifts or comparisons. |
| |
| bool |
| Type::are_compatible_for_binop(const Type* lhs, const Type* rhs) |
| { |
| if (Type::are_identical(lhs, rhs, Type::COMPARE_TAGS, NULL)) |
| return true; |
| |
| // A constant of abstract bool type may be mixed with any bool type. |
| if ((rhs->is_abstract_boolean_type() && lhs->is_boolean_type()) |
| || (lhs->is_abstract_boolean_type() && rhs->is_boolean_type())) |
| return true; |
| |
| // A constant of abstract string type may be mixed with any string |
| // type. |
| if ((rhs->is_abstract_string_type() && lhs->is_string_type()) |
| || (lhs->is_abstract_string_type() && rhs->is_string_type())) |
| return true; |
| |
| lhs = lhs->base(); |
| rhs = rhs->base(); |
| |
| // A constant of abstract integer, float, or complex type may be |
| // mixed with an integer, float, or complex type. |
| if ((rhs->is_abstract() |
| && (rhs->integer_type() != NULL |
| || rhs->float_type() != NULL |
| || rhs->complex_type() != NULL) |
| && (lhs->integer_type() != NULL |
| || lhs->float_type() != NULL |
| || lhs->complex_type() != NULL)) |
| || (lhs->is_abstract() |
| && (lhs->integer_type() != NULL |
| || lhs->float_type() != NULL |
| || lhs->complex_type() != NULL) |
| && (rhs->integer_type() != NULL |
| || rhs->float_type() != NULL |
| || rhs->complex_type() != NULL))) |
| return true; |
| |
| // The nil type may be compared to a pointer, an interface type, a |
| // slice type, a channel type, a map type, or a function type. |
| if (lhs->is_nil_type() |
| && (rhs->points_to() != NULL |
| || rhs->interface_type() != NULL |
| || rhs->is_slice_type() |
| || rhs->map_type() != NULL |
| || rhs->channel_type() != NULL |
| || rhs->function_type() != NULL)) |
| return true; |
| if (rhs->is_nil_type() |
| && (lhs->points_to() != NULL |
| || lhs->interface_type() != NULL |
| || lhs->is_slice_type() |
| || lhs->map_type() != NULL |
| || lhs->channel_type() != NULL |
| || lhs->function_type() != NULL)) |
| return true; |
| |
| return false; |
| } |
| |
| // Return true if a value with type T1 may be compared with a value of |
| // type T2. IS_EQUALITY_OP is true for == or !=, false for <, etc. |
| |
| bool |
| Type::are_compatible_for_comparison(bool is_equality_op, const Type *t1, |
| const Type *t2, std::string *reason) |
| { |
| if (t1 != t2 |
| && !Type::are_assignable(t1, t2, NULL) |
| && !Type::are_assignable(t2, t1, NULL)) |
| { |
| if (reason != NULL) |
| *reason = "incompatible types in binary expression"; |
| return false; |
| } |
| |
| if (!is_equality_op) |
| { |
| if (t1->integer_type() == NULL |
| && t1->float_type() == NULL |
| && !t1->is_string_type()) |
| { |
| if (reason != NULL) |
| *reason = _("invalid comparison of non-ordered type"); |
| return false; |
| } |
| } |
| else if (t1->is_slice_type() |
| || t1->map_type() != NULL |
| || t1->function_type() != NULL |
| || t2->is_slice_type() |
| || t2->map_type() != NULL |
| || t2->function_type() != NULL) |
| { |
| if (!t1->is_nil_type() && !t2->is_nil_type()) |
| { |
| if (reason != NULL) |
| { |
| if (t1->is_slice_type() || t2->is_slice_type()) |
| *reason = _("slice can only be compared to nil"); |
| else if (t1->map_type() != NULL || t2->map_type() != NULL) |
| *reason = _("map can only be compared to nil"); |
| else |
| *reason = _("func can only be compared to nil"); |
| |
| // Match 6g error messages. |
| if (t1->interface_type() != NULL || t2->interface_type() != NULL) |
| { |
| char buf[200]; |
| snprintf(buf, sizeof buf, _("invalid operation (%s)"), |
| reason->c_str()); |
| *reason = buf; |
| } |
| } |
| return false; |
| } |
| } |
| else |
| { |
| if (!t1->is_boolean_type() |
| && t1->integer_type() == NULL |
| && t1->float_type() == NULL |
| && t1->complex_type() == NULL |
| && !t1->is_string_type() |
| && t1->points_to() == NULL |
| && t1->channel_type() == NULL |
| && t1->interface_type() == NULL |
| && t1->struct_type() == NULL |
| && t1->array_type() == NULL |
| && !t1->is_nil_type()) |
| { |
| if (reason != NULL) |
| *reason = _("invalid comparison of non-comparable type"); |
| return false; |
| } |
| |
| if (t1->unalias()->named_type() != NULL) |
| return t1->unalias()->named_type()->named_type_is_comparable(reason); |
| else if (t2->unalias()->named_type() != NULL) |
| return t2->unalias()->named_type()->named_type_is_comparable(reason); |
| else if (t1->struct_type() != NULL) |
| { |
| if (t1->struct_type()->is_struct_incomparable()) |
| { |
| if (reason != NULL) |
| *reason = _("invalid comparison of generated struct"); |
| return false; |
| } |
| const Struct_field_list* fields = t1->struct_type()->fields(); |
| for (Struct_field_list::const_iterator p = fields->begin(); |
| p != fields->end(); |
| ++p) |
| { |
| if (!p->type()->is_comparable()) |
| { |
| if (reason != NULL) |
| *reason = _("invalid comparison of non-comparable struct"); |
| return false; |
| } |
| } |
| } |
| else if (t1->array_type() != NULL) |
| { |
| if (t1->array_type()->is_array_incomparable()) |
| { |
| if (reason != NULL) |
| *reason = _("invalid comparison of generated array"); |
| return false; |
| } |
| if (t1->array_type()->length()->is_nil_expression() |
| || !t1->array_type()->element_type()->is_comparable()) |
| { |
| if (reason != NULL) |
| *reason = _("invalid comparison of non-comparable array"); |
| return false; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| // Return true if a value with type RHS may be assigned to a variable |
| // with type LHS. If REASON is not NULL, set *REASON to the reason |
| // the types are not assignable. |
| |
| bool |
| Type::are_assignable(const Type* lhs, const Type* rhs, std::string* reason) |
| { |
| // Do some checks first. Make sure the types are defined. |
| if (rhs != NULL && !rhs->is_undefined()) |
| { |
| if (rhs->is_void_type()) |
| { |
| if (reason != NULL) |
| *reason = "non-value used as value"; |
| return false; |
| } |
| if (rhs->is_call_multiple_result_type()) |
| { |
| if (reason != NULL) |
| reason->assign(_("multiple-value function call in " |
| "single-value context")); |
| return false; |
| } |
| } |
| |
| // Any value may be assigned to the blank identifier. |
| if (lhs != NULL |
| && !lhs->is_undefined() |
| && lhs->is_sink_type()) |
| return true; |
| |
| // Identical types are assignable. |
| if (Type::are_identical(lhs, rhs, Type::COMPARE_TAGS, reason)) |
| return true; |
| |
| // Ignore aliases, except for error messages. |
| const Type* lhs_orig = lhs; |
| const Type* rhs_orig = rhs; |
| lhs = lhs->unalias(); |
| rhs = rhs->unalias(); |
| |
| // The types are assignable if they have identical underlying types |
| // and either LHS or RHS is not a named type. |
| if (((lhs->named_type() != NULL && rhs->named_type() == NULL) |
| || (rhs->named_type() != NULL && lhs->named_type() == NULL)) |
| && Type::are_identical(lhs->base(), rhs->base(), Type::COMPARE_TAGS, |
| reason)) |
| return true; |
| |
| // The types are assignable if LHS is an interface type and RHS |
| // implements the required methods. |
| const Interface_type* lhs_interface_type = lhs->interface_type(); |
| if (lhs_interface_type != NULL) |
| { |
| if (lhs_interface_type->implements_interface(rhs, reason)) |
| return true; |
| const Interface_type* rhs_interface_type = rhs->interface_type(); |
| if (rhs_interface_type != NULL |
| && lhs_interface_type->is_compatible_for_assign(rhs_interface_type, |
| reason)) |
| return true; |
| } |
| |
| // The type are assignable if RHS is a bidirectional channel type, |
| // LHS is a channel type, they have identical element types, and |
| // either LHS or RHS is not a named type. |
| if (lhs->channel_type() != NULL |
| && rhs->channel_type() != NULL |
| && rhs->channel_type()->may_send() |
| && rhs->channel_type()->may_receive() |
| && (lhs->named_type() == NULL || rhs->named_type() == NULL) |
| && Type::are_identical(lhs->channel_type()->element_type(), |
| rhs->channel_type()->element_type(), |
| Type::COMPARE_TAGS, |
| reason)) |
| return true; |
| |
| // The nil type may be assigned to a pointer, function, slice, map, |
| // channel, or interface type. |
| if (rhs->is_nil_type() |
| && (lhs->points_to() != NULL |
| || lhs->function_type() != NULL |
| || lhs->is_slice_type() |
| || lhs->map_type() != NULL |
| || lhs->channel_type() != NULL |
| || lhs->interface_type() != NULL)) |
| return true; |
| |
| // An untyped numeric constant may be assigned to a numeric type if |
| // it is representable in that type. |
| if ((rhs->is_abstract() |
| && (rhs->integer_type() != NULL |
| || rhs->float_type() != NULL |
| || rhs->complex_type() != NULL)) |
| && (lhs->integer_type() != NULL |
| || lhs->float_type() != NULL |
| || lhs->complex_type() != NULL)) |
| return true; |
| |
| // Give some better error messages. |
| if (reason != NULL && reason->empty()) |
| { |
| if (rhs->interface_type() != NULL) |
| reason->assign(_("need explicit conversion")); |
| else if (lhs_orig->named_type() != NULL |
| && rhs_orig->named_type() != NULL) |
| { |
| size_t len = (lhs_orig->named_type()->name().length() |
| + rhs_orig->named_type()->name().length() |
| + 100); |
| char* buf = new char[len]; |
| snprintf(buf, len, _("cannot use type %s as type %s"), |
| rhs_orig->named_type()->message_name().c_str(), |
| lhs_orig->named_type()->message_name().c_str()); |
| reason->assign(buf); |
| delete[] buf; |
| } |
| } |
| |
| return false; |
| } |
| |
| // Return true if a value with type RHS may be converted to type LHS. |
| // If REASON is not NULL, set *REASON to the reason the types are not |
| // convertible. |
| |
| bool |
| Type::are_convertible(const Type* lhs, const Type* rhs, std::string* reason) |
| { |
| // The types are convertible if they are assignable. |
| if (Type::are_assignable(lhs, rhs, reason)) |
| return true; |
| |
| // Ignore aliases. |
| lhs = lhs->unalias(); |
| rhs = rhs->unalias(); |
| |
| // A pointer to a regular type may not be converted to a pointer to |
| // a type that may not live in the heap, except when converting from |
| // unsafe.Pointer. |
| if (lhs->points_to() != NULL |
| && rhs->points_to() != NULL |
| && !lhs->points_to()->in_heap() |
| && rhs->points_to()->in_heap() |
| && !rhs->is_unsafe_pointer_type()) |
| { |
| if (reason != NULL) |
| reason->assign(_("conversion from normal type to notinheap type")); |
| return false; |
| } |
| |
| // The types are convertible if they have identical underlying |
| // types, ignoring struct field tags. |
| if ((lhs->named_type() != NULL || rhs->named_type() != NULL) |
| && Type::are_identical(lhs->base(), rhs->base(), 0, reason)) |
| return true; |
| |
| // The types are convertible if they are both unnamed pointer types |
| // and their pointer base types have identical underlying types, |
| // ignoring struct field tags. |
| if (lhs->named_type() == NULL |
| && rhs->named_type() == NULL |
| && lhs->points_to() != NULL |
| && rhs->points_to() != NULL |
| && (lhs->points_to()->named_type() != NULL |
| || rhs->points_to()->named_type() != NULL) |
| && Type::are_identical(lhs->points_to()->base(), |
| rhs->points_to()->base(), |
| 0, reason)) |
| return true; |
| |
| // Integer and floating point types are convertible to each other. |
| if ((lhs->integer_type() != NULL || lhs->float_type() != NULL) |
| && (rhs->integer_type() != NULL || rhs->float_type() != NULL)) |
| return true; |
| |
| // Complex types are convertible to each other. |
| if (lhs->complex_type() != NULL && rhs->complex_type() != NULL) |
| return true; |
| |
| // An integer, or []byte, or []rune, may be converted to a string. |
| if (lhs->is_string_type()) |
| { |
| if (rhs->integer_type() != NULL) |
| return true; |
| if (rhs->is_slice_type()) |
| { |
| const Type* e = rhs->array_type()->element_type()->forwarded(); |
| if (e->integer_type() != NULL |
| && (e->integer_type()->is_byte() |
| || e->integer_type()->is_rune())) |
| return true; |
| } |
| } |
| |
| // A string may be converted to []byte or []rune. |
| if (rhs->is_string_type() && lhs->is_slice_type()) |
| { |
| const Type* e = lhs->array_type()->element_type()->forwarded(); |
| if (e->integer_type() != NULL |
| && (e->integer_type()->is_byte() || e->integer_type()->is_rune())) |
| return true; |
| } |
| |
| // An unsafe.Pointer type may be converted to any pointer type or to |
| // a type whose underlying type is uintptr, and vice-versa. |
| if (lhs->is_unsafe_pointer_type() |
| && (rhs->points_to() != NULL |
| || (rhs->integer_type() != NULL |
| && rhs->integer_type() == Type::lookup_integer_type("uintptr")->real_type()))) |
| return true; |
| if (rhs->is_unsafe_pointer_type() |
| && (lhs->points_to() != NULL |
| || (lhs->integer_type() != NULL |
| && lhs->integer_type() == Type::lookup_integer_type("uintptr")->real_type()))) |
| return true; |
| |
| // Give a better error message. |
| if (reason != NULL) |
| { |
| if (reason->empty()) |
| *reason = "invalid type conversion"; |
| else |
| { |
| std::string s = "invalid type conversion ("; |
| s += *reason; |
| s += ')'; |
| *reason = s; |
| } |
| } |
| |
| return false; |
| } |
| |
| // Copy expressions if it may change the size. |
| // |
| // The only type that has an expression is an array type. The only |
| // types whose size can be changed by the size of an array type are an |
| // array type itself, or a struct type with an array field. |
| Type* |
| Type::copy_expressions() |
| { |
| // This is run during parsing, so types may not be valid yet. |
| // We only have to worry about array type literals. |
| switch (this->classification_) |
| { |
| default: |
| return this; |
| |
| case TYPE_ARRAY: |
| { |
| Array_type* at = this->array_type(); |
| if (at->length() == NULL) |
| return this; |
| Expression* len = at->length()->copy(); |
| if (at->length() == len) |
| return this; |
| return Type::make_array_type(at->element_type(), len); |
| } |
| |
| case TYPE_STRUCT: |
| { |
| Struct_type* st = this->struct_type(); |
| const Struct_field_list* sfl = st->fields(); |
| if (sfl == NULL) |
| return this; |
| bool changed = false; |
| Struct_field_list *nsfl = new Struct_field_list(); |
| for (Struct_field_list::const_iterator pf = sfl->begin(); |
| pf != sfl->end(); |
| ++pf) |
| { |
| Type* ft = pf->type()->copy_expressions(); |
| Struct_field nf(Typed_identifier((pf->is_anonymous() |
| ? "" |
| : pf->field_name()), |
| ft, |
| pf->location())); |
| if (pf->has_tag()) |
| nf.set_tag(pf->tag()); |
| nsfl->push_back(nf); |
| if (ft != pf->type()) |
| changed = true; |
| } |
| if (!changed) |
| { |
| delete(nsfl); |
| return this; |
| } |
| return Type::make_struct_type(nsfl, st->location()); |
| } |
| } |
| |
| go_unreachable(); |
| } |
| |
| // Return a hash code for the type to be used for method lookup. |
| |
| unsigned int |
| Type::hash_for_method(Gogo* gogo, int flags) const |
| { |
| const Type* t = this->forwarded(); |
| if (t->named_type() != NULL && t->named_type()->is_alias()) |
| { |
| unsigned int r = |
| t->named_type()->real_type()->hash_for_method(gogo, flags); |
| if ((flags & Type::COMPARE_ALIASES) != 0) |
| r += TYPE_FORWARD; |
| return r; |
| } |
| unsigned int ret = t->classification_; |
| return ret + t->do_hash_for_method(gogo, flags); |
| } |
| |
| // Default implementation of do_hash_for_method. This is appropriate |
| // for types with no subfields. |
| |
| unsigned int |
| Type::do_hash_for_method(Gogo*, int) const |
| { |
| return 0; |
| } |
| |
| // A hash table mapping unnamed types to the backend representation of |
| // those types. |
| |
| Type::Type_btypes Type::type_btypes; |
| |
| // Return the backend representation for this type. |
| |
| Btype* |
| Type::get_backend(Gogo* gogo) |
| { |
| if (this->btype_ != NULL) |
| return this->btype_; |
| |
| if (this->named_type() != NULL && this->named_type()->is_alias()) |
| { |
| Btype* bt = this->unalias()->get_backend(gogo); |
| if (gogo != NULL && gogo->named_types_are_converted()) |
| this->btype_ = bt; |
| return bt; |
| } |
| |
| if (this->forward_declaration_type() != NULL |
| || this->named_type() != NULL) |
| return this->get_btype_without_hash(gogo); |
| |
| if (this->is_error_type()) |
| return gogo->backend()->error_type(); |
| |
| // To avoid confusing the backend, translate all identical Go types |
| // to the same backend representation. We use a hash table to do |
| // that. There is no need to use the hash table for named types, as |
| // named types are only identical to themselves. |
| |
| std::pair<Type*, Type_btype_entry> val; |
| val.first = this; |
| val.second.btype = NULL; |
| val.second.is_placeholder = false; |
| std::pair<Type_btypes::iterator, bool> ins = |
| Type::type_btypes.insert(val); |
| if (!ins.second && ins.first->second.btype != NULL) |
| { |
| // Note that GOGO can be NULL here, but only when the GCC |
| // middle-end is asking for a frontend type. That will only |
| // happen for simple types, which should never require |
| // placeholders. |
| if (!ins.first->second.is_placeholder) |
| this->btype_ = ins.first->second.btype; |
| else if (gogo->named_types_are_converted()) |
| { |
| this->finish_backend(gogo, ins.first->second.btype); |
| ins.first->second.is_placeholder = false; |
| } |
| |
| // We set the has_padding field of a Struct_type when we convert |
| // to the backend type, so if we have multiple Struct_type's |
| // mapping to the same backend type we need to copy the |
| // has_padding field. FIXME: This is awkward. We shouldn't |
| // really change the type when setting the backend type, but |
| // there isn't any other good time to add the padding field. |
| if (ins.first->first->struct_type() != NULL |
| && ins.first->first->struct_type()->has_padding()) |
| this->struct_type()->set_has_padding(); |
| |
| return ins.first->second.btype; |
| } |
| |
| Btype* bt = this->get_btype_without_hash(gogo); |
| |
| if (ins.first->second.btype == NULL) |
| { |
| ins.first->second.btype = bt; |
| ins.first->second.is_placeholder = false; |
| } |
| else |
| { |
| // We have already created a backend representation for this |
| // type. This can happen when an unnamed type is defined using |
| // a named type which in turns uses an identical unnamed type. |
| // Use the representation we created earlier and ignore the one we just |
| // built. |
| if (this->btype_ == bt) |
| this->btype_ = ins.first->second.btype; |
| bt = ins.first->second.btype; |
| } |
| |
| return bt; |
| } |
| |
| // Return the backend representation for a type without looking in the |
| // hash table for identical types. This is used for named types, |
| // since a named type is never identical to any other type. |
| |
| Btype* |
| Type::get_btype_without_hash(Gogo* gogo) |
| { |
| if (this->btype_ == NULL) |
| { |
| Btype* bt = this->do_get_backend(gogo); |
| |
| // For a recursive function or pointer type, we will temporarily |
| // return a circular pointer type during the recursion. We |
| // don't want to record that for a forwarding type, as it may |
| // confuse us later. |
| if (this->forward_declaration_type() != NULL |
| && gogo->backend()->is_circular_pointer_type(bt)) |
| return bt; |
| |
| if (gogo == NULL || !gogo->named_types_are_converted()) |
| return bt; |
| |
| this->btype_ = bt; |
| } |
| return this->btype_; |
| } |
| |
| // Get the backend representation of a type without forcing the |
| // creation of the backend representation of all supporting types. |
| // This will return a backend type that has the correct size but may |
| // be incomplete. E.g., a pointer will just be a placeholder pointer, |
| // and will not contain the final representation of the type to which |
| // it points. This is used while converting all named types to the |
| // backend representation, to avoid problems with indirect references |
| // to types which are not yet complete. When this is called, the |
| // sizes of all direct references (e.g., a struct field) should be |
| // known, but the sizes of indirect references (e.g., the type to |
| // which a pointer points) may not. |
| |
| Btype* |
| Type::get_backend_placeholder(Gogo* gogo) |
| { |
| if (gogo->named_types_are_converted()) |
| return this->get_backend(gogo); |
| if (this->btype_ != NULL) |
| return this->btype_; |
| |
| Btype* bt; |
| switch (this->classification_) |
| { |
| case TYPE_ERROR: |
| case TYPE_VOID: |
| case TYPE_BOOLEAN: |
| case TYPE_INTEGER: |
| case TYPE_FLOAT: |
| case TYPE_COMPLEX: |
| case TYPE_STRING: |
| case TYPE_NIL: |
| // These are simple types that can just be created directly. |
| return this->get_backend(gogo); |
| |
| case TYPE_MAP: |
| case TYPE_CHANNEL: |
| // All maps and channels have the same backend representation. |
| return this->get_backend(gogo); |
| |
| case TYPE_NAMED: |
| case TYPE_FORWARD: |
| // Named types keep track of their own dependencies and manage |
| // their own placeholders. |
| if (this->named_type() != NULL && this->named_type()->is_alias()) |
| return this->unalias()->get_backend_placeholder(gogo); |
| return this->get_backend(gogo); |
| |
| case TYPE_INTERFACE: |
| if (this->interface_type()->is_empty()) |
| return Interface_type::get_backend_empty_interface_type(gogo); |
| break; |
| |
| default: |
| break; |
| } |
| |
| std::pair<Type*, Type_btype_entry> val; |
| val.first = this; |
| val.second.btype = NULL; |
| val.second.is_placeholder = false; |
| std::pair<Type_btypes::iterator, bool> ins = |
| Type::type_btypes.insert(val); |
| if (!ins.second && ins.first->second.btype != NULL) |
| return ins.first->second.btype; |
| |
| switch (this->classification_) |
| { |
| case TYPE_FUNCTION: |
| { |
| // A Go function type is a pointer to a struct type. |
| Location loc = this->function_type()->location(); |
| bt = gogo->backend()->placeholder_pointer_type("", loc, false); |
| Type::placeholder_pointers.push_back(this); |
| } |
| break; |
| |
| case TYPE_POINTER: |
| { |
| Location loc = Linemap::unknown_location(); |
| bt = gogo->backend()->placeholder_pointer_type("", loc, false); |
| Type::placeholder_pointers.push_back(this); |
| } |
| break; |
| |
| case TYPE_STRUCT: |
| // We don't have to make the struct itself be a placeholder. We |
| // are promised that we know the sizes of the struct fields. |
| // But we may have to use a placeholder for any particular |
| // struct field. |
| { |
| std::vector<Backend::Btyped_identifier> bfields; |
| get_backend_struct_fields(gogo, this->struct_type(), true, &bfields); |
| bt = gogo->backend()->struct_type(bfields); |
| } |
| break; |
| |
| case TYPE_ARRAY: |
| if (this->is_slice_type()) |
| { |
| std::vector<Backend::Btyped_identifier> bfields; |
| get_backend_slice_fields(gogo, this->array_type(), true, &bfields); |
| bt = gogo->backend()->struct_type(bfields); |
| } |
| else |
| { |
| Btype* element = this->array_type()->get_backend_element(gogo, true); |
| Bexpression* len = this->array_type()->get_backend_length(gogo); |
| bt = gogo->backend()->array_type(element, len); |
| } |
| break; |
| |
| case TYPE_INTERFACE: |
| { |
| go_assert(!this->interface_type()->is_empty()); |
| std::vector<Backend::Btyped_identifier> bfields; |
| get_backend_interface_fields(gogo, this->interface_type(), true, |
| &bfields); |
| bt = gogo->backend()->struct_type(bfields); |
| } |
| break; |
| |
| case TYPE_SINK: |
| case TYPE_CALL_MULTIPLE_RESULT: |
| /* Note that various classifications were handled in the earlier |
| switch. */ |
| default: |
| go_unreachable(); |
| } |
| |
| if (ins.first->second.btype == NULL) |
| { |
| ins.first->second.btype = bt; |
| ins.first->second.is_placeholder = true; |
| } |
| else |
| { |
| // A placeholder for this type got created along the way. Use |
| // that one and ignore the one we just built. |
| bt = ins.first->second.btype; |
| } |
| |
| return bt; |
| } |
| |
| // Complete the backend representation. This is called for a type |
| // using a placeholder type. |
| |
| void |
| Type::finish_backend(Gogo* gogo, Btype *placeholder) |
| { |
| switch (this->classification_) |
| { |
| case TYPE_ERROR: |
| case TYPE_VOID: |
| case TYPE_BOOLEAN: |
| case TYPE_INTEGER: |
| case TYPE_FLOAT: |
| case TYPE_COMPLEX: |
| case TYPE_STRING: |
| case TYPE_NIL: |
| go_unreachable(); |
| |
| case TYPE_FUNCTION: |
| { |
| Btype* bt = this->do_get_backend(gogo); |
| if (!gogo->backend()->set_placeholder_pointer_type(placeholder, bt)) |
| go_assert(saw_errors()); |
| } |
| break; |
| |
| case TYPE_POINTER: |
| { |
| Btype* bt = this->do_get_backend(gogo); |
| if (!gogo->backend()->set_placeholder_pointer_type(placeholder, bt)) |
| go_assert(saw_errors()); |
| } |
| break; |
| |
| case TYPE_STRUCT: |
| // The struct type itself is done, but we have to make sure that |
| // all the field types are converted. |
| this->struct_type()->finish_backend_fields(gogo); |
| break; |
| |
| case TYPE_ARRAY: |
| // The array type itself is done, but make sure the element type |
| // is converted. |
| this->array_type()->finish_backend_element(gogo); |
| break; |
| |
| case TYPE_MAP: |
| case TYPE_CHANNEL: |
| go_unreachable(); |
| |
| case TYPE_INTERFACE: |
| // The interface type itself is done, but make sure the method |
| // types are converted. |
| this->interface_type()->finish_backend_methods(gogo); |
| break; |
| |
| case TYPE_NAMED: |
| case TYPE_FORWARD: |
| go_unreachable(); |
| |
| case TYPE_SINK: |
| case TYPE_CALL_MULTIPLE_RESULT: |
| default: |
| go_unreachable(); |
| } |
| |
| this->btype_ = placeholder; |
| } |
| |
| // Return a pointer to the type descriptor for this type. |
| |
| Bexpression* |
| Type::type_descriptor_pointer(Gogo* gogo, Location location) |
| { |
| Type* t = this->unalias(); |
| if (t->type_descriptor_var_ == NULL) |
| { |
| t->make_type_descriptor_var(gogo); |
| go_assert(t->type_descriptor_var_ != NULL); |
| } |
| Bexpression* var_expr = |
| gogo->backend()->var_expression(t->type_descriptor_var_, location); |
| Bexpression* var_addr = |
| gogo->backend()->address_expression(var_expr, location); |
| Type* td_type = Type::make_type_descriptor_type(); |
| Btype* td_btype = td_type->get_backend(gogo); |
| Btype* ptd_btype = gogo->backend()->pointer_type(td_btype); |
| return gogo->backend()->convert_expression(ptd_btype, var_addr, location); |
| } |
| |
| // A mapping from unnamed types to type descriptor variables. |
| |
| Type::Type_descriptor_vars Type::type_descriptor_vars; |
| |
| // Build the type descriptor for this type. |
| |
| void |
| Type::make_type_descriptor_var(Gogo* gogo) |
| { |
| go_assert(this->type_descriptor_var_ == NULL); |
| |
| Named_type* nt = this->named_type(); |
| |
| // We can have multiple instances of unnamed types, but we only want |
| // to emit the type descriptor once. We use a hash table. This is |
| // not necessary for named types, as they are unique, and we store |
| // the type descriptor in the type itself. |
| Bvariable** phash = NULL; |
| if (nt == NULL) |
| { |
| Bvariable* bvnull = NULL; |
| std::pair<Type_descriptor_vars::iterator, bool> ins = |
| Type::type_descriptor_vars.insert(std::make_pair(this, bvnull)); |
| if (!ins.second) |
| { |
| // We've already built a type descriptor for this type. |
| this->type_descriptor_var_ = ins.first->second; |
| return; |
| } |
| phash = &ins.first->second; |
| } |
| |
| // The type descriptor symbol for the unsafe.Pointer type is defined in |
| // libgo/go-unsafe-pointer.c, so we just return a reference to that |
| // symbol if necessary. |
| if (this->is_unsafe_pointer_type()) |
| { |
| Location bloc = Linemap::predeclared_location(); |
| |
| Type* td_type = Type::make_type_descriptor_type(); |
| Btype* td_btype = td_type->get_backend(gogo); |
| std::string name = gogo->type_descriptor_name(this, nt); |
| std::string asm_name(go_selectively_encode_id(name)); |
| this->type_descriptor_var_ = |
| gogo->backend()->immutable_struct_reference(name, asm_name, |
| td_btype, |
| bloc); |
| |
| if (phash != NULL) |
| *phash = this->type_descriptor_var_; |
| return; |
| } |
| |
| std::string var_name = gogo->type_descriptor_name(this, nt); |
| |
| // Build the contents of the type descriptor. |
| Expression* initializer = this->do_type_descriptor(gogo, NULL); |
| |
| Btype* initializer_btype = initializer->type()->get_backend(gogo); |
| |
| Location loc = nt == NULL ? Linemap::predeclared_location() : nt->location(); |
| |
| const Package* dummy; |
| if (this->type_descriptor_defined_elsewhere(nt, &dummy)) |
| { |
| std::string asm_name(go_selectively_encode_id(var_name)); |
| this->type_descriptor_var_ = |
| gogo->backend()->immutable_struct_reference(var_name, asm_name, |
| initializer_btype, |
| loc); |
| if (phash != NULL) |
| *phash = this->type_descriptor_var_; |
| return; |
| } |
| |
| // See if this type descriptor can appear in multiple packages. |
| bool is_common = false; |
| if (nt != NULL) |
| { |
| // We create the descriptor for a builtin type whenever we need |
| // it. |
| is_common = nt->is_builtin(); |
| } |
| else |
| { |
| // This is an unnamed type. The descriptor could be defined in |
| // any package where it is needed, and the linker will pick one |
| // descriptor to keep. |
| is_common = true; |
| } |
| |
| // We are going to build the type descriptor in this package. We |
| // must create the variable before we convert the initializer to the |
| // backend representation, because the initializer may refer to the |
| // type descriptor of this type. By setting type_descriptor_var_ we |
| // ensure that type_descriptor_pointer will work if called while |
| // converting INITIALIZER. |
| |
| std::string asm_name(go_selectively_encode_id(var_name)); |
| this->type_descriptor_var_ = |
| gogo->backend()->immutable_struct(var_name, asm_name, false, is_common, |
| initializer_btype, loc); |
| if (phash != NULL) |
| *phash = this->type_descriptor_var_; |
| |
| Translate_context context(gogo, NULL, NULL, NULL); |
| context.set_is_const(); |
| Bexpression* binitializer = initializer->get_backend(&context); |
| |
| gogo->backend()->immutable_struct_set_init(this->type_descriptor_var_, |
| var_name, false, is_common, |
| initializer_btype, loc, |
| binitializer); |
| |
| // For types that may be created by reflection, add it to the |
| // list of which we will register the type descriptor to the |
| // runtime. |
| // Do not add generated incomparable array/struct types, see |
| // issue #22605. |
| if (is_common |
| && (this->points_to() != NULL |
| || this->channel_type() != NULL |
| || this->map_type() != NULL |
| || this->function_type() != NULL |
| || this->is_slice_type() |
| || (this->struct_type() != NULL |
| && !this->struct_type()->is_struct_incomparable()) |
| || (this->array_type() != NULL |
| && !this->array_type()->is_array_incomparable()))) |
| gogo->add_type_descriptor(this); |
| } |
| |
| // Return true if this type descriptor is defined in a different |
| // package. If this returns true it sets *PACKAGE to the package. |
| |
| bool |
| Type::type_descriptor_defined_elsewhere(Named_type* nt, |
| const Package** package) |
| { |
| if (nt != NULL) |
| { |
| if (nt->named_object()->package() != NULL) |
| { |
| // This is a named type defined in a different package. The |
| // type descriptor should be defined in that package. |
| *package = nt->named_object()->package(); |
| return true; |
| } |
| } |
| else |
| { |
| if (this->points_to() != NULL |
| && this->points_to()->unalias()->named_type() != NULL |
| && this->points_to()->unalias()->named_type()->named_object()->package() != NULL) |
| { |
| // This is an unnamed pointer to a named type defined in a |
| // different package. The descriptor should be defined in |
| // that package. |
| *package = this->points_to()->unalias()->named_type()->named_object()->package(); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| // Return a composite literal for a type descriptor. |
| |
| Expression* |
| Type::type_descriptor(Gogo* gogo, Type* type) |
| { |
| return type->do_type_descriptor(gogo, NULL); |
| } |
| |
| // Return a composite literal for a type descriptor with a name. |
| |
| Expression* |
| Type::named_type_descriptor(Gogo* gogo, Type* type, Named_type* name) |
| { |
| go_assert(name != NULL && type->named_type() != name); |
| return type->do_type_descriptor(gogo, name); |
| } |
| |
| // Make a builtin struct type from a list of fields. The fields are |
| // pairs of a name and a type. |
| |
| Struct_type* |
| Type::make_builtin_struct_type(int nfields, ...) |
| { |
| va_list ap; |
| va_start(ap, nfields); |
| |
| Location bloc = Linemap::predeclared_location(); |
| Struct_field_list* sfl = new Struct_field_list(); |
| for (int i = 0; i < nfields; i++) |
| { |
| const char* field_name = va_arg(ap, const char *); |
| Type* type = va_arg(ap, Type*); |
| sfl->push_back(Struct_field(Typed_identifier(field_name, type, bloc))); |
| } |
| |
| va_end(ap); |
| |
| Struct_type* ret = Type::make_struct_type(sfl, bloc); |
| ret->set_is_struct_incomparable(); |
| return ret; |
| } |
| |
| // A list of builtin named types. |
| |
| std::vector<Named_type*> Type::named_builtin_types; |
| |
| // Make a builtin named type. |
| |
| Named_type* |
| Type::make_builtin_named_type(const char* name, Type* type) |
| { |
| Location bloc = Linemap::predeclared_location(); |
| Named_object* no = Named_object::make_type(name, NULL, type, bloc); |
| Named_type* ret = no->type_value(); |
| Type::named_builtin_types.push_back(ret); |
| return ret; |
| } |
| |
| // Convert the named builtin types. |
| |
| void |
| Type::convert_builtin_named_types(Gogo* gogo) |
| { |
| for (std::vector<Named_type*>::const_iterator p = |
| Type::named_builtin_types.begin(); |
| p != Type::named_builtin_types.end(); |
| ++p) |
| { |
| bool r = (*p)->verify(); |
| go_assert(r); |
| (*p)->convert(gogo); |
| } |
| } |
| |
| // Values to store in the tflag field of a type descriptor. This must |
| // match the definitions in libgo/go/runtime/type.go. |
| |
| const int TFLAG_REGULAR_MEMORY = 1 << 3; |
| |
| // Return the type of a type descriptor. We should really tie this to |
| // runtime.Type rather than copying it. This must match the struct "_type" |
| // declared in libgo/go/runtime/type.go. |
| |
| Type* |
| Type::make_type_descriptor_type() |
| { |
| static Type* ret; |
| if (ret == NULL) |
| { |
| Location bloc = Linemap::predeclared_location(); |
| |
| Type* uint8_type = Type::lookup_integer_type("uint8"); |
| Type* pointer_uint8_type = Type::make_pointer_type(uint8_type); |
| Type* uint32_type = Type::lookup_integer_type("uint32"); |
| Type* uintptr_type = Type::lookup_integer_type("uintptr"); |
| Type* string_type = Type::lookup_string_type(); |
| Type* pointer_string_type = Type::make_pointer_type(string_type); |
| |
| // This is an unnamed version of unsafe.Pointer. Perhaps we |
| // should use the named version instead, although that would |
| // require us to create the unsafe package if it has not been |
| // imported. It probably doesn't matter. |
| Type* void_type = Type::make_void_type(); |
| Type* unsafe_pointer_type = Type::make_pointer_type(void_type); |
| |
| Typed_identifier_list* params = new Typed_identifier_list(); |
| params->push_back(Typed_identifier("key1", unsafe_pointer_type, bloc)); |
| params->push_back(Typed_identifier("key2", unsafe_pointer_type, bloc)); |
| |
| Typed_identifier_list* results = new Typed_identifier_list(); |
| results->push_back(Typed_identifier("", Type::lookup_bool_type(), bloc)); |
| |
| Type* equal_fntype = Type::make_function_type(NULL, params, results, |
| bloc); |
| |
| // Forward declaration for the type descriptor type. |
| Named_object* named_type_descriptor_type = |
| Named_object::make_type_declaration("_type", NULL, bloc); |
| Type* ft = Type::make_forward_declaration(named_type_descriptor_type); |
| Type* pointer_type_descriptor_type = Type::make_pointer_type(ft); |
| |
| // The type of a method on a concrete type. |
| Struct_type* method_type = |
| Type::make_builtin_struct_type(5, |
| "name", pointer_string_type, |
| "pkgPath", pointer_string_type, |
| "mtyp", pointer_type_descriptor_type, |
| "typ", pointer_type_descriptor_type, |
| "tfn", unsafe_pointer_type); |
| Named_type* named_method_type = |
| Type::make_builtin_named_type("method", method_type); |
| |
| // Information for types with a name or methods. |
| Type* slice_named_method_type = |
| Type::make_array_type(named_method_type, NULL); |
| Struct_type* uncommon_type = |
| Type::make_builtin_struct_type(3, |
| "name", pointer_string_type, |
| "pkgPath", pointer_string_type, |
| "methods", slice_named_method_type); |
| Named_type* named_uncommon_type = |
| Type::make_builtin_named_type("uncommonType", uncommon_type); |
| |
| Type* pointer_uncommon_type = |
| Type::make_pointer_type(named_uncommon_type); |
| |
| // The type descriptor type. |
| |
| Struct_type* type_descriptor_type = |
| Type::make_builtin_struct_type(12, |
| "size", uintptr_type, |
| "ptrdata", uintptr_type, |
| "hash", uint32_type, |
| "tflag", uint8_type, |
| "align", uint8_type, |
| "fieldAlign", uint8_type, |
| "kind", uint8_type, |
| "equal", equal_fntype, |
| "gcdata", pointer_uint8_type, |
| "string", pointer_string_type, |
| "", pointer_uncommon_type, |
| "ptrToThis", |
| pointer_type_descriptor_type); |
| |
| Named_type* named = Type::make_builtin_named_type("_type", |
| type_descriptor_type); |
| |
| named_type_descriptor_type->set_type_value(named); |
| |
| ret = named; |
| } |
| |
| return ret; |
| } |
| |
| // Make the type of a pointer to a type descriptor as represented in |
| // Go. |
| |
| Type* |
| Type::make_type_descriptor_ptr_type() |
| { |
| static Type* ret; |
| if (ret == NULL) |
| ret = Type::make_pointer_type(Type::make_type_descriptor_type()); |
| return ret; |
| } |
| |
| // Return the alignment required by the memequalN function. N is a |
| // type size: 16, 32, 64, or 128. The memequalN functions are defined |
| // in libgo/go/runtime/alg.go. |
| |
| int64_t |
| Type::memequal_align(Gogo* gogo, int size) |
| { |
| const char* tn; |
| switch (size) |
| { |
| case 16: |
| tn = "int16"; |
| break; |
| case 32: |
| tn = "int32"; |
| break; |
| case 64: |
| tn = "int64"; |
| break; |
| case 128: |
| // The code uses [2]int64, which must have the same alignment as |
| // int64. |
| tn = "int64"; |
| break; |
| default: |
| go_unreachable(); |
| } |
| |
| Type* t = Type::lookup_integer_type(tn); |
| |
| int64_t ret; |
| if (!t->backend_type_align(gogo, &ret)) |
| go_unreachable(); |
| return ret; |
| } |
| |
| // Return whether this type needs specially built type functions. |
| // This returns true for types that are comparable and either can not |
| // use an identity comparison, or are a non-standard size. |
| |
| bool |
| Type::needs_specific_type_functions(Gogo* gogo) |
| { |
| Named_type* nt = this->named_type(); |
| if (nt != NULL && nt->is_alias()) |
| return false; |
| if (!this->is_comparable()) |
| return false; |
| if (!this->compare_is_identity(gogo)) |
| return true; |
| |
| // We create a few predeclared types for type descriptors; they are |
| // really just for the backend and don't need hash or equality |
| // functions. |
| if (nt != NULL && Linemap::is_predeclared_location(nt->location())) |
| return false; |
| |
| int64_t size, align; |
| if (!this->backend_type_size(gogo, &size) |
| || !this->backend_type_align(gogo, &align)) |
| { |
| go_assert(saw_errors()); |
| return false; |
| } |
| // This switch matches the one in Type::equal_function. |
| switch (size) |
| { |
| case 0: |
| case 1: |
| case 2: |
| return align < Type::memequal_align(gogo, 16); |
| case 4: |
| return align < Type::memequal_align(gogo, 32); |
| case 8: |
| return align < Type::memequal_align(gogo, 64); |
| case 16: |
| return align < Type::memequal_align(gogo, 128); |
| default: |
| return true; |
| } |
| } |
| |
| // Return the runtime function that computes the hash of this type. |
| // HASH_FNTYPE is the type of the hash function function, for |
| // convenience; it may be NULL. This returns NULL if the type is not |
| // comparable. |
| |
| Named_object* |
| Type::hash_function(Gogo* gogo, Function_type* hash_fntype) |
| { |
| if (!this->is_comparable()) |
| return NULL; |
| |
| if (hash_fntype == NULL) |
| { |
| Location bloc = Linemap::predeclared_location(); |
| Type* uintptr_type = Type::lookup_integer_type("uintptr"); |
| Type* void_type = Type::make_void_type(); |
| Type* unsafe_pointer_type = Type::make_pointer_type(void_type); |
| Typed_identifier_list* params = new Typed_identifier_list(); |
| params->push_back(Typed_identifier("key", unsafe_pointer_type, bloc)); |
| params->push_back(Typed_identifier("seed", uintptr_type, bloc)); |
| Typed_identifier_list* results = new Typed_identifier_list(); |
| results->push_back(Typed_identifier("", uintptr_type, bloc)); |
| hash_fntype = Type::make_function_type(NULL, params, results, bloc); |
| } |
| |
| const char* hash_fnname; |
| if (this->compare_is_identity(gogo)) |
| { |
| int64_t size; |
| if (!this->backend_type_size(gogo, &size)) |
| { |
| go_assert(saw_errors()); |
| return NULL; |
| } |
| switch (size) |
| { |
| case 0: |
| hash_fnname = "runtime.memhash0"; |
| break; |
| case 1: |
| hash_fnname = "runtime.memhash8"; |
| break; |
| case 2: |
| hash_fnname = "runtime.memhash16"; |
| break; |
| case 4: |
| hash_fnname = "runtime.memhash32"; |
| break; |
| case 8: |
| hash_fnname = "runtime.memhash64"; |
| break; |
| case 16: |
| hash_fnname = "runtime.memhash128"; |
| break; |
| default: |
| // We don't have a built-in function for a type of this |
| // size. Build a function to use that calls the generic |
| // hash functions for identity, passing the size. |
| return this->build_hash_function(gogo, size, hash_fntype); |
| } |
| } |
| else |
| { |
| switch (this->base()->classification()) |
| { |
| case Type::TYPE_ERROR: |
| case Type::TYPE_VOID: |
| case Type::TYPE_NIL: |
| case Type::TYPE_FUNCTION: |
| case Type::TYPE_MAP: |
| // For these types is_comparable should have returned false. |
| go_unreachable(); |
| |
| case Type::TYPE_BOOLEAN: |
| case Type::TYPE_INTEGER: |
| case Type::TYPE_POINTER: |
| case Type::TYPE_CHANNEL: |
| // For these types compare_is_identity should have returned true. |
| go_unreachable(); |
| |
| case Type::TYPE_FLOAT: |
| switch (this->float_type()->bits()) |
| { |
| case 32: |
| hash_fnname = "runtime.f32hash"; |
| break; |
| case 64: |
| hash_fnname = "runtime.f64hash"; |
| break; |
| default: |
| go_unreachable(); |
| } |
| break; |
| |
| case Type::TYPE_COMPLEX: |
| switch (this->complex_type()->bits()) |
| { |
| case 64: |
| hash_fnname = "runtime.c64hash"; |
| break; |
| case 128: |
| hash_fnname = "runtime.c128hash"; |
| break; |
| default: |
| go_unreachable(); |
| } |
| break; |
| |
| case Type::TYPE_STRING: |
| hash_fnname = "runtime.strhash"; |
| break; |
| |
| case Type::TYPE_STRUCT: |
| // This is a struct which can not be compared using a simple |
| // identity function. We need to build a function to |
| // compute the hash. |
| return this->build_hash_function(gogo, -1, hash_fntype); |
| |
| case Type::TYPE_ARRAY: |
| if (this->is_slice_type()) |
| { |
| // Type::is_compatible_for_comparison should have |
| // returned false. |
| go_unreachable(); |
| } |
| else |
| { |
| // This is an array which can not be compared using a |
| // simple identity function. We need to build a |
| // function to compute the hash. |
| return this->build_hash_function(gogo, -1, hash_fntype); |
| } |
| break; |
| |
| case Type::TYPE_INTERFACE: |
| if (this->interface_type()->is_empty()) |
| hash_fnname = "runtime.nilinterhash"; |
| else |
| hash_fnname = "runtime.interhash"; |
| break; |
| |
| case Type::TYPE_NAMED: |
| case Type::TYPE_FORWARD: |
| go_unreachable(); |
| |
| default: |
| go_unreachable(); |
| } |
| } |
| |
| Location bloc = Linemap::predeclared_location(); |
| Named_object *hash_fn = Named_object::make_function_declaration(hash_fnname, |
| NULL, |
| hash_fntype, |
| bloc); |
| hash_fn->func_declaration_value()->set_asm_name(hash_fnname); |
| return hash_fn; |
| } |
| |
| // A hash table mapping types to the specific hash functions. |
| |
| Type::Type_function Type::type_hash_functions_table; |
| |
| // Build a hash function that is specific to a type: if SIZE == -1, |
| // this is a struct or array type that cannot use an identity |
| // comparison. Otherwise, it is a type that uses an identity |
| // comparison but is not one of the standard supported sizes. |
| // |
| // Unlike an equality function, hash functions are not in type |
| // descriptors, so we can't assume that a named type has defined a |
| // hash function in the package that defines the type. So hash |
| // functions are always defined locally. FIXME: It would be better to |
| // define hash functions with comdat linkage so that duplicate hash |
| // functions can be coalesced at link time. |
| |
| Named_object* |
| Type::build_hash_function(Gogo* gogo, int64_t size, Function_type* hash_fntype) |
| { |
| Type* type = this->base(); |
| |
| std::pair<Type*, Named_object*> val(type, NULL); |
| std::pair<Type_function::iterator, bool> ins = |
| Type::type_hash_functions_table.insert(val); |
| if (!ins.second) |
| { |
| // We already have a function for this type. |
| return ins.first->second; |
| } |
| |
| std::string hash_name = gogo->hash_function_name(type); |
| |
| Location bloc = Linemap::predeclared_location(); |
| |
| Named_object* hash_fn = gogo->declare_package_function(hash_name, |
| hash_fntype, bloc); |
| |
| ins.first->second = hash_fn; |
| |
| if (gogo->in_global_scope()) |
| type->write_hash_function(gogo, size, hash_name, hash_fntype); |
| else |
| gogo->queue_hash_function(type, size, hash_name, hash_fntype); |
| |
| return hash_fn; |
| } |
| |
| // Write the hash function for a type that needs it written specially. |
| |
| void |
| Type::write_hash_function(Gogo* gogo, int64_t size, |
| const std::string& hash_name, |
| Function_type* hash_fntype) |
| { |
| Location bloc = Linemap::predeclared_location(); |
| |
| if (gogo->specific_type_functions_are_written()) |
| { |
| go_assert(saw_errors()); |
| return; |
| } |
| |
| go_assert(this->is_comparable()); |
| |
| Named_object* hash_fn = gogo->start_function(hash_name, hash_fntype, false, |
| bloc); |
| hash_fn->func_value()->set_is_type_specific_function(); |
| gogo->start_block(bloc); |
| |
| if (size != -1) |
| this->write_identity_hash(gogo, size); |
| else if (this->struct_type() != NULL) |
| this->struct_type()->write_hash_function(gogo, hash_fntype); |
| else if (this->array_type() != NULL) |
| this->array_type()->write_hash_function(gogo, hash_fntype); |
| else |
| go_unreachable(); |
| |
| Block* b = gogo->finish_block(bloc); |
| gogo->add_block(b, bloc); |
| gogo->lower_block(hash_fn, b); |
| gogo->order_block(b); |
| gogo->remove_shortcuts_in_block(b); |
| gogo->finish_function(bloc); |
| |
| // Build the function descriptor for the type descriptor to refer to. |
| hash_fn->func_value()->descriptor(gogo, hash_fn); |
| } |
| |
| // Write a hash function for a type that can use an identity hash but |
| // is not one of the standard supported sizes. For example, this |
| // would be used for the type [3]byte. This builds a return statement |
| // that returns a call to the memhash function, passing the key and |
| // seed from the function arguments (already constructed before this |
| // is called), and the constant size. |
| |
| void |
| Type::write_identity_hash(Gogo* gogo, int64_t size) |
| { |
| Location bloc = Linemap::predeclared_location(); |
| |
| Type* unsafe_pointer_type = Type::make_pointer_type(Type::make_void_type()); |
| Type* uintptr_type = Type::lookup_integer_type("uintptr"); |
| |
| Typed_identifier_list* params = new Typed_identifier_list(); |
| params->push_back(Typed_identifier("key", unsafe_pointer_type, bloc)); |
| params->push_back(Typed_identifier("seed", uintptr_type, bloc)); |
| params->push_back(Typed_identifier("size", uintptr_type, bloc)); |
| |
| Typed_identifier_list* results = new Typed_identifier_list(); |
| results->push_back(Typed_identifier("", uintptr_type, bloc)); |
| |
| Function_type* memhash_fntype = Type::make_function_type(NULL, params, |
| results, bloc); |
| |
| Named_object* memhash = |
| Named_object::make_function_declaration("runtime.memhash", NULL, |
| memhash_fntype, bloc); |
| memhash->func_declaration_value()->set_asm_name("runtime.memhash"); |
| |
| Named_object* key_arg = gogo->lookup("key", NULL); |
| go_assert(key_arg != NULL); |
| Named_object* seed_arg = gogo->lookup("seed", NULL); |
| go_assert(seed_arg != NULL); |
| |
| Expression* key_ref = Expression::make_var_reference(key_arg, bloc); |
| Expression* seed_ref = Expression::make_var_reference(seed_arg, bloc); |
| Expression* size_arg = Expression::make_integer_int64(size, uintptr_type, |
| bloc); |
| Expression_list* args = new Expression_list(); |
| args->push_back(key_ref); |
| args->push_back(seed_ref); |
| args->push_back(size_arg); |
| Expression* func = Expression::make_func_reference(memhash, NULL, bloc); |
| Expression* call = Expression::make_call(func, args, false, bloc); |
| |
| Expression_list* vals = new Expression_list(); |
| vals->push_back(call); |
| Statement* s = Statement::make_return_statement(vals, bloc); |
| gogo->add_statement(s); |
| } |
| |
| // Return the runtime function that compares whether two values of |
| // this type are equal. If NAME is not NULL it is the name of this |
| // type. EQUAL_FNTYPE is the type of the equality function, for |
| // convenience; it may be NULL. This returns NULL if the type is not |
| // comparable. |
| |
| Named_object* |
| Type::equal_function(Gogo* gogo, Named_type* name, Function_type* equal_fntype) |
| { |
| // If the unaliased type is not a named type, then the type does not |
| // have a name after all. |
| if (name != NULL) |
| name = name->unalias()->named_type(); |
| |
| if (!this->is_comparable()) |
| return NULL; |
| |
| if (equal_fntype == NULL) |
| { |
| Location bloc = Linemap::predeclared_location(); |
| Type* void_type = Type::make_void_type(); |
| Type* unsafe_pointer_type = Type::make_pointer_type(void_type); |
| Typed_identifier_list* params = new Typed_identifier_list(); |
| params->push_back(Typed_identifier("key1", unsafe_pointer_type, bloc)); |
| params->push_back(Typed_identifier("key2", unsafe_pointer_type, bloc)); |
| Typed_identifier_list* results = new Typed_identifier_list(); |
| results->push_back(Typed_identifier("", Type::lookup_bool_type(), bloc)); |
| equal_fntype = Type::make_function_type(NULL, params, results, bloc); |
| } |
| |
| const char* equal_fnname; |
| if (this->compare_is_identity(gogo)) |
| { |
| int64_t size, align; |
| if (!this->backend_type_size(gogo, &size) |
| || !this->backend_type_align(gogo, &align)) |
| { |
| go_assert(saw_errors()); |
| return NULL; |
| } |
| bool build_function = false; |
| // This switch matches the one in Type::needs_specific_type_functions. |
| // The alignment tests are because of the memequal functions, |
| // which assume that the values are aligned as required for an |
| // integer of that size. |
| switch (size) |
| { |
| case 0: |
| equal_fnname = "runtime.memequal0"; |
| break; |
| case 1: |
| equal_fnname = "runtime.memequal8"; |
| break; |
| case 2: |
| if (align < Type::memequal_align(gogo, 16)) |
| build_function = true; |
| else |
| equal_fnname = "runtime.memequal16"; |
| break; |
| case 4: |
| if (align < Type::memequal_align(gogo, 32)) |
| build_function = true; |
| else |
| equal_fnname = "runtime.memequal32"; |
| break; |
| case 8: |
| if (align < Type::memequal_align(gogo, 64)) |
| build_function = true; |
| else |
| equal_fnname = "runtime.memequal64"; |
| break; |
| case 16: |
| if (align < Type::memequal_align(gogo, 128)) |
| build_function = true; |
| else |
| equal_fnname = "runtime.memequal128"; |
| break; |
| default: |
| build_function = true; |
| break; |
| } |
| if (build_function) |
| { |
| // We don't have a built-in function for a type of this size |
| // and alignment. Build a function to use that calls the |
| // generic equality functions for identity, passing the size. |
| return this->build_equal_function(gogo, name, size, equal_fntype); |
| } |
| } |
| else |
| { |
| switch (this->base()->classification()) |
| { |
| case Type::TYPE_ERROR: |
| case Type::TYPE_VOID: |
| case Type::TYPE_NIL: |
| case Type::TYPE_FUNCTION: |
| case Type::TYPE_MAP: |
| // For these types is_comparable should have returned false. |
| go_unreachable(); |
| |
| case Type::TYPE_BOOLEAN: |
| case Type::TYPE_INTEGER: |
| case Type::TYPE_POINTER: |
| case Type::TYPE_CHANNEL: |
| // For these types compare_is_identity should have returned true. |
| go_unreachable(); |
| |
| case Type::TYPE_FLOAT: |
| switch (this->float_type()->bits()) |
| { |
| case 32: |
| equal_fnname = "runtime.f32equal"; |
| break; |
| case 64: |
| equal_fnname = "runtime.f64equal"; |
| break; |
| default: |
| go_unreachable(); |
| } |
| break; |
| |
| case Type::TYPE_COMPLEX: |
| switch (this->complex_type()->bits()) |
| { |
| case 64: |
| equal_fnname = "runtime.c64equal"; |
| break; |
| case 128: |
| equal_fnname = "runtime.c128equal"; |
| break; |
| default: |
| go_unreachable(); |
| } |
| break; |
| |
| case Type::TYPE_STRING: |
| equal_fnname = "runtime.strequal"; |
| break; |
| |
| case Type::TYPE_STRUCT: |
| // This is a struct which can not be compared using a simple |
| // identity function. We need to build a function for |
| // comparison. |
| return this->build_equal_function(gogo, name, -1, equal_fntype); |
| |
| case Type::TYPE_ARRAY: |
| if (this->is_slice_type()) |
| { |
| // Type::is_compatible_for_comparison should have |
| // returned false. |
| go_unreachable(); |
| } |
| else |
| { |
| // This is an array which can not be compared using a |
| // simple identity function. We need to build a |
| // function for comparison. |
| return this->build_equal_function(gogo, name, -1, equal_fntype); |
| } |
| break; |
| |
| case Type::TYPE_INTERFACE: |
| if (this->interface_type()->is_empty()) |
| equal_fnname = "runtime.nilinterequal"; |
| else |
| equal_fnname = "runtime.interequal"; |
| break; |
| |
| case Type::TYPE_NAMED: |
| case Type::TYPE_FORWARD: |
| go_unreachable(); |
| |
| default: |
| go_unreachable(); |
| } |
| } |
| |
| Location bloc = Linemap::predeclared_location(); |
| Named_object* equal_fn = |
| Named_object::make_function_declaration(equal_fnname, NULL, equal_fntype, |
| bloc); |
| equal_fn->func_declaration_value()->set_asm_name(equal_fnname); |
| return equal_fn; |
| } |
| |
| // A hash table mapping types to the specific equal functions. |
| |
| Type::Type_function Type::type_equal_functions_table; |
| |
| // Build an equality function that is specific to a type: if SIZE == |
| // -1, this is a struct or array type that cannot use an identity |
| // comparison. Otherwise, it is a type that uses an identity |
| // comparison but is not one of the standard supported sizes or it is |
| // not aligned as needed. |
| |
| Named_object* |
| Type::build_equal_function(Gogo* gogo, Named_type* name, int64_t size, |
| Function_type* equal_fntype) |
| { |
| std::pair<Type*, Named_object*> val(name != NULL ? name : this, NULL); |
| std::pair<Type_function::iterator, bool> ins = |
| Type::type_equal_functions_table.insert(val); |
| if (!ins.second) |
| { |
| // We already have a function for this type. |
| return ins.first->second; |
| } |
| |
| std::string equal_name = gogo->equal_function_name(this, name); |
| |
| Location bloc = Linemap::predeclared_location(); |
| |
| const Package* package = NULL; |
| bool is_defined_elsewhere = |
| this->type_descriptor_defined_elsewhere(name, &package); |
| |
| Named_object* equal_fn; |
| if (is_defined_elsewhere) |
| equal_fn = Named_object::make_function_declaration(equal_name, package, |
| equal_fntype, bloc); |
| else |
| equal_fn = gogo->declare_package_function(equal_name, equal_fntype, bloc); |
| |
| ins.first->second = equal_fn; |
| |
| if (!is_defined_elsewhere) |
| { |
| if (gogo->in_global_scope()) |
| this->write_equal_function(gogo, name, size, equal_name, equal_fntype); |
| else |
| gogo->queue_equal_function(this, name, size, equal_name, equal_fntype); |
| } |
| |
| return equal_fn; |
| } |
| |
| // Write the equal function for a type that needs it written |
| // specially. |
| |
| void |
| Type::write_equal_function(Gogo* gogo, Named_type* name, int64_t size, |
| const std::string& equal_name, |
| Function_type* equal_fntype) |
| { |
| Location bloc = Linemap::predeclared_location(); |
| |
| if (gogo->specific_type_functions_are_written()) |
| { |
| go_assert(saw_errors()); |
| return; |
| } |
| |
| go_assert(this->is_comparable()); |
| |
| Named_object* equal_fn = gogo->start_function(equal_name, equal_fntype, |
| false, bloc); |
| equal_fn->func_value()->set_is_type_specific_function(); |
| gogo->start_block(bloc); |
| |
| if (size != -1) |
| this->write_identity_equal(gogo, size); |
| else if (name != NULL && name->real_type()->named_type() != NULL) |
| this->write_named_equal(gogo, name); |
| else if (this->struct_type() != NULL) |
| this->struct_type()->write_equal_function(gogo, name); |
| else if (this->array_type() != NULL) |
| this->array_type()->write_equal_function(gogo, name); |
| else |
| go_unreachable(); |
| |
| Block* b = gogo->finish_block(bloc); |
| gogo->add_block(b, bloc); |
| gogo->lower_block(equal_fn, b); |
| gogo->order_block(b); |
| gogo->remove_shortcuts_in_block(b); |
| gogo->finish_function(bloc); |
| |
| // Build the function descriptor for the type descriptor to refer to. |
| equal_fn->func_value()->descriptor(gogo, equal_fn); |
| } |
| |
| // Write an equality function for a type that can use an identity |
| // equality comparison but is not one of the standard supported sizes. |
| // For example, this would be used for the type [3]byte. This builds |
| // a return statement that returns a call to the memequal function, |
| // passing the two keys from the function arguments (already |
| // constructed before this is called), and the constant size. |
| |
| void |
| Type::write_identity_equal(Gogo* gogo, int64_t size) |
| { |
| Location bloc = Linemap::predeclared_location(); |
| |
| Type* unsafe_pointer_type = Type::make_pointer_type(Type::make_void_type()); |
| Type* uintptr_type = Type::lookup_integer_type("uintptr"); |
| |
| Typed_identifier_list* params = new Typed_identifier_list(); |
| params->push_back(Typed_identifier("key1", unsafe_pointer_type, bloc)); |
| params->push_back(Typed_identifier("key2", unsafe_pointer_type, bloc)); |
| params->push_back(Typed_identifier("size", uintptr_type, bloc)); |
| |
| Typed_identifier_list* results = new Typed_identifier_list(); |
| results->push_back(Typed_identifier("", Type::lookup_bool_type(), bloc)); |
| |
| Function_type* memequal_fntype = Type::make_function_type(NULL, params, |
| results, bloc); |
| |
| Named_object* memequal = |
| Named_object::make_function_declaration("runtime.memequal", NULL, |
| memequal_fntype, bloc); |
| memequal->func_declaration_value()->set_asm_name("runtime.memequal"); |
| |
| Named_object* key1_arg = gogo->lookup("key1", NULL); |
| go_assert(key1_arg != NULL); |
| Named_object* key2_arg = gogo->lookup("key2", NULL); |
| go_assert(key2_arg != NULL); |
| |
| Expression* key1_ref = Expression::make_var_reference(key1_arg, bloc); |
| Expression* key2_ref = Expression::make_var_reference(key2_arg, bloc); |
| Expression* size_arg = Expression::make_integer_int64(size, uintptr_type, |
| bloc); |
| Expression_list* args = new Expression_list(); |
| args->push_back(key1_ref); |
| args->push_back(key2_ref); |
| args->push_back(size_arg); |
| Expression* func = Expression::make_func_reference(memequal, NULL, bloc); |
| Expression* call = Expression::make_call(func, args, false, bloc); |
| |
| Expression_list* vals = new Expression_list(); |
| vals->push_back(call); |
| Statement* s = Statement::make_return_statement(vals, bloc); |
| gogo->add_statement(s); |
| } |
| |
| // Write an equality function that simply calls the equality function |
| // for a named type. This is used when one named type is defined as |
| // another. This ensures that this case works when the other named |
| // type is defined in another package and relies on calling equality |
| // functions defined only in that package. |
| |
| void |
| Type::write_named_equal(Gogo* gogo, Named_type* name) |
| { |
| Location bloc = Linemap::predeclared_location(); |
| |
| // The pointers to the types we are going to compare. These have |
| // type unsafe.Pointer. |
| Named_object* key1_arg = gogo->lookup("key1", NULL); |
| Named_object* key2_arg = gogo->lookup("key2", NULL); |
| go_assert(key1_arg != NULL && key2_arg != NULL); |
| |
| Named_type* base_type = name->real_type()->named_type(); |
| go_assert(base_type != NULL); |
| |
| // Build temporaries with the base type. |
| Type* pt = Type::make_pointer_type(base_type); |
| |
| Expression* ref = Expression::make_var_reference(key1_arg, bloc); |
| ref = Expression::make_cast(pt, ref, bloc); |
| Temporary_statement* p1 = Statement::make_temporary(pt, ref, bloc); |
| gogo->add_statement(p1); |
| |
| ref = Expression::make_var_reference(key2_arg, bloc); |
| ref = Expression::make_cast(pt, ref, bloc); |
| Temporary_statement* p2 = Statement::make_temporary(pt, ref, bloc); |
| gogo->add_statement(p2); |
| |
| // Compare the values for equality. |
| Expression* t1 = Expression::make_temporary_reference(p1, bloc); |
| t1 = Expression::make_dereference(t1, Expression::NIL_CHECK_NOT_NEEDED, bloc); |
| |
| Expression* t2 = Expression::make_temporary_reference(p2, bloc); |
| t2 = Expression::make_dereference(t2, Expression::NIL_CHECK_NOT_NEEDED, bloc); |
| |
| Expression* cond = Expression::make_binary(OPERATOR_EQEQ, t1, t2, bloc); |
| |
| // Return the equality comparison. |
| Expression_list* vals = new Expression_list(); |
| vals->push_back(cond); |
| Statement* s = Statement::make_return_statement(vals, bloc); |
| gogo->add_statement(s); |
| } |
| |
| // Return whether this type is stored directly in an interface's |
| // data word. |
| // |
| // Since finalize_methods runs before type checking, we may see a |
| // malformed type like 'type T struct { x T }'. Use a visited map |
| // to avoid infinite recursion. |
| |
| bool |
| Type::is_direct_iface_type() const |
| { |
| Unordered_set(const Type*) visited; |
| return this->is_direct_iface_type_helper(&visited); |
| } |
| |
| bool |
| Type::is_direct_iface_type_helper(Unordered_set(const Type*)* visited) const |
| { |
| if (this->points_to() != NULL |
| || this->channel_type() != NULL |
| || this->function_type() != NULL |
| || this->map_type() != NULL) |
| return true; |
| |
| std::pair<Unordered_set(const Type*)::iterator, bool> ins |
| = visited->insert(this); |
| if (!ins.second) |
| // malformed circular type |
| return false; |
| |
| const Struct_type* st = this->struct_type(); |
| if (st != NULL) |
| return (st->field_count() == 1 |
| && st->field(0)->type()->is_direct_iface_type_helper(visited)); |
| const Array_type* at = this->array_type(); |
| if (at != NULL && !at->is_slice_type()) |
| { |
| int64_t len; |
| return (at->int_length(&len) && len == 1 |
| && at->element_type()->is_direct_iface_type_helper(visited)); |
| } |
| return false; |
| } |
| |
| // Return a composite literal for the type descriptor for a plain type |
| // of kind RUNTIME_TYPE_KIND named NAME. |
| |
| Expression* |
| Type::type_descriptor_constructor(Gogo* gogo, int runtime_type_kind, |
| Named_type* name, const Methods* methods, |
| bool only_value_methods) |
| { |
| Location bloc = Linemap::predeclared_location(); |
| |
| Type* td_type = Type::make_type_descriptor_type(); |
| const Struct_field_list* fields = td_type->struct_type()->fields(); |
| |
| Expression_list* vals = new Expression_list(); |
| vals->reserve(12); |
| |
| if (!this->has_pointer()) |
| runtime_type_kind |= RUNTIME_TYPE_KIND_NO_POINTERS; |
| if (this->is_direct_iface_type()) |
| runtime_type_kind |= RUNTIME_TYPE_KIND_DIRECT_IFACE; |
| int64_t ptrsize; |
| int64_t ptrdata; |
| if (this->needs_gcprog(gogo, &ptrsize, &ptrdata)) |
| runtime_type_kind |= RUNTIME_TYPE_KIND_GC_PROG; |
| |
| Struct_field_list::const_iterator p = fields->begin(); |
| go_assert(p->is_field_name("size")); |
| Expression::Type_info type_info = Expression::TYPE_INFO_SIZE; |
| vals->push_back(Expression::make_type_info(this, type_info)); |
| |
| ++p; |
| go_assert(p->is_field_name("ptrdata")); |
| type_info = Expression::TYPE_INFO_DESCRIPTOR_PTRDATA; |
| vals->push_back(Expression::make_type_info(this, type_info)); |
| |
| ++p; |
| go_assert(p->is_field_name("hash")); |
| unsigned int h; |
| if (name != NULL) |
| h = name->hash_for_method(gogo, Type::COMPARE_TAGS); |
| else |
| h = this->hash_for_method(gogo, Type::COMPARE_TAGS); |
| vals->push_back(Expression::make_integer_ul(h, p->type(), bloc)); |
| |
| ++p; |
| go_assert(p->is_field_name("tflag")); |
| unsigned long tflag = 0; |
| if (this->compare_is_identity(gogo)) |
| tflag |= TFLAG_REGULAR_MEMORY; |
| vals->push_back(Expression::make_integer_ul(tflag, p->type(), bloc)); |
| |
| ++p; |
| go_assert(p->is_field_name("align")); |
| type_info = Expression::TYPE_INFO_ALIGNMENT; |
| vals->push_back(Expression::make_type_info(this, type_info)); |
| |
| ++p; |
| go_assert(p->is_field_name("fieldAlign")); |
| type_info = Expression::TYPE_INFO_FIELD_ALIGNMENT; |
| vals->push_back(Expression::make_type_info(this, type_info)); |
| |
| ++p; |
| go_assert(p->is_field_name("kind")); |
| vals->push_back(Expression::make_integer_ul(runtime_type_kind, p->type(), |
| bloc)); |
| |
| ++p; |
| go_assert(p->is_field_name("equal")); |
| Function_type* equal_fntype = p->type()->function_type(); |
| Named_object* equal_fn = this->equal_function(gogo, name, equal_fntype); |
| if (equal_fn == NULL) |
| vals->push_back(Expression::make_cast(equal_fntype, |
| Expression::make_nil(bloc), |
| bloc)); |
| else |
| vals->push_back(Expression::make_func_reference(equal_fn, NULL, bloc)); |
| |
| ++p; |
| go_assert(p->is_field_name("gcdata")); |
| vals->push_back(Expression::make_gc_symbol(this)); |
| |
| ++p; |
| go_assert(p->is_field_name("string")); |
| Expression* s = Expression::make_string((name != NULL |
| ? name->reflection(gogo) |
| : this->reflection(gogo)), |
| bloc); |
| vals->push_back(Expression::make_unary(OPERATOR_AND, s, bloc)); |
| |
| ++p; |
| go_assert(p->is_field_name("uncommonType")); |
| if (name == NULL && methods == NULL) |
| vals->push_back(Expression::make_nil(bloc)); |
| else |
| { |
| if (methods == NULL) |
| methods = name->methods(); |
| vals->push_back(this->uncommon_type_constructor(gogo, |
| p->type()->deref(), |
| name, methods, |
| only_value_methods)); |
| } |
| |
| ++p; |
| go_assert(p->is_field_name("ptrToThis")); |
| if (name == NULL && methods == NULL) |
| vals->push_back(Expression::make_nil(bloc)); |
| else |
| { |
| Type* pt; |
| if (name != NULL) |
| pt = Type::make_pointer_type(name); |
| else |
| pt = Type::make_pointer_type(this); |
| vals->push_back(Expression::make_type_descriptor(pt, bloc)); |
| } |
| |
| ++p; |
| go_assert(p == fields->end()); |
| |
| return Expression::make_struct_composite_literal(td_type, vals, bloc); |
| } |
| |
| // The maximum length of a GC ptrmask bitmap. This corresponds to the |
| // length used by the gc toolchain, and also appears in |
| // libgo/go/reflect/type.go. |
| |
| static const int64_t max_ptrmask_bytes = 2048; |
| |
| // Return a pointer to the Garbage Collection information for this type. |
| |
| Bexpression* |
| Type::gc_symbol_pointer(Gogo* gogo) |
| { |
| Type* t = this->unalias(); |
| |
| if (!t->has_pointer()) |
| return gogo->backend()->nil_pointer_expression(); |
| |
| if (t->gc_symbol_var_ == NULL) |
| { |
| t->make_gc_symbol_var(gogo); |
| go_assert(t->gc_symbol_var_ != NULL); |
| } |
| Location bloc = Linemap::predeclared_location(); |
| Bexpression* var_expr = |
| gogo->backend()->var_expression(t->gc_symbol_var_, bloc); |
| Bexpression* addr_expr = |
| gogo->backend()->address_expression(var_expr, bloc); |
| |
| Type* uint8_type = Type::lookup_integer_type("uint8"); |
| Type* pointer_uint8_type = Type::make_pointer_type(uint8_type); |
| Btype* ubtype = pointer_uint8_type->get_backend(gogo); |
| return gogo->backend()->convert_expression(ubtype, addr_expr, bloc); |
| } |
| |
| // A mapping from unnamed types to GC symbol variables. |
| |
| Type::GC_symbol_vars Type::gc_symbol_vars; |
| |
| // Build the GC symbol for this type. |
| |
| void |
| Type::make_gc_symbol_var(Gogo* gogo) |
| { |
| go_assert(this->gc_symbol_var_ == NULL); |
| |
| Named_type* nt = this->named_type(); |
| |
| // We can have multiple instances of unnamed types and similar to type |
| // descriptors, we only want to the emit the GC data once, so we use a |
| // hash table. |
| Bvariable** phash = NULL; |
| if (nt == NULL) |
| { |
| Bvariable* bvnull = NULL; |
| std::pair<GC_symbol_vars::iterator, bool> ins = |
| Type::gc_symbol_vars.insert(std::make_pair(this, bvnull)); |
| if (!ins.second) |
| { |
| // We've already built a gc symbol for this type. |
| this->gc_symbol_var_ = ins.first->second; |
| return; |
| } |
| phash = &ins.first->second; |
| } |
| |
| int64_t ptrsize; |
| int64_t ptrdata; |
| if (!this->needs_gcprog(gogo, &ptrsize, &ptrdata)) |
| { |
| this->gc_symbol_var_ = this->gc_ptrmask_var(gogo, ptrsize, ptrdata); |
| if (phash != NULL) |
| *phash = this->gc_symbol_var_; |
| return; |
| } |
| |
| std::string sym_name = gogo->gc_symbol_name(this); |
| |
| // Build the contents of the gc symbol. |
| Expression* sym_init = this->gcprog_constructor(gogo, ptrsize, ptrdata); |
| Btype* sym_btype = sym_init->type()->get_backend(gogo); |
| |
| // If the type descriptor for this type is defined somewhere else, so is the |
| // GC symbol. |
| const Package* dummy; |
| if (this->type_descriptor_defined_elsewhere(nt, &dummy)) |
| { |
| std::string asm_name(go_selectively_encode_id(sym_name)); |
| this->gc_symbol_var_ = |
| gogo->backend()->implicit_variable_reference(sym_name, asm_name, |
| sym_btype); |
| if (phash != NULL) |
| *phash = this->gc_symbol_var_; |
| return; |
| } |
| |
| // See if this gc symbol can appear in multiple packages. |
| bool is_common = false; |
| if (nt != NULL) |
| { |
| // We create the symbol for a builtin type whenever we need |
| // it. |
| is_common = nt->is_builtin(); |
| } |
| else |
| { |
| // This is an unnamed type. The descriptor could be defined in |
| // any package where it is needed, and the linker will pick one |
| // descriptor to keep. |
| is_common = true; |
| } |
| |
| // Since we are building the GC symbol in this package, we must create the |
| // variable before converting the initializer to its backend representation |
| // because the initializer may refer to the GC symbol for this type. |
| std::string asm_name(go_selectively_encode_id(sym_name)); |
| this->gc_symbol_var_ = |
| gogo->backend()->implicit_variable(sym_name, asm_name, |
| sym_btype, false, true, is_common, 0); |
| if (phash != NULL) |
| *phash = this->gc_symbol_var_; |
| |
| Translate_context context(gogo, NULL, NULL, NULL); |
| context.set_is_const(); |
| Bexpression* sym_binit = sym_init->get_backend(&context); |
| gogo->backend()->implicit_variable_set_init(this->gc_symbol_var_, sym_name, |
| sym_btype, false, true, is_common, |
| sym_binit); |
| } |
| |
| // Return whether this type needs a GC program, and set *PTRDATA to |
| // the size of the pointer data in bytes and *PTRSIZE to the size of a |
| // pointer. |
| |
| bool |
| Type::needs_gcprog(Gogo* gogo, int64_t* ptrsize, int64_t* ptrdata) |
| { |
| Type* voidptr = Type::make_pointer_type(Type::make_void_type()); |
| if (!voidptr->backend_type_size(gogo, ptrsize)) |
| go_unreachable(); |
| |
| if (!this->backend_type_ptrdata(gogo, ptrdata)) |
| { |
| go_assert(saw_errors()); |
| return false; |
| } |
| |
| return *ptrdata / *ptrsize > max_ptrmask_bytes; |
| } |
| |
| // A simple class used to build a GC ptrmask for a type. |
| |
| class Ptrmask |
| { |
| public: |
| Ptrmask(size_t count) |
| : bits_((count + 7) / 8, 0) |
| {} |
| |
| void |
| set_from(Gogo*, Type*, int64_t ptrsize, int64_t offset); |
| |
| std::string |
| symname() const; |
| |
| Expression* |
| constructor(Gogo* gogo) const; |
| |
| private: |
| void |
| set(size_t index) |
| { this->bits_.at(index / 8) |= 1 << (index % 8); } |
| |
| // The actual bits. |
| std::vector<unsigned char> bits_; |
| }; |
| |
| // Set bits in ptrmask starting from OFFSET based on TYPE. OFFSET |
| // counts in bytes. PTRSIZE is the size of a pointer on the target |
| // system. |
| |
| void |
| Ptrmask::set_from(Gogo* gogo, Type* type, int64_t ptrsize, int64_t offset) |
| { |
| switch (type->base()->classification()) |
| { |
| default: |
| case Type::TYPE_NIL: |
| case Type::TYPE_CALL_MULTIPLE_RESULT: |
| case Type::TYPE_NAMED: |
| case Type::TYPE_FORWARD: |
| go_unreachable(); |
| |
| case Type::TYPE_ERROR: |
| case Type::TYPE_VOID: |
| case Type::TYPE_BOOLEAN: |
| case Type::TYPE_INTEGER: |
| case Type::TYPE_FLOAT: |
| case Type::TYPE_COMPLEX: |
| case Type::TYPE_SINK: |
| break; |
| |
| case Type::TYPE_FUNCTION: |
| case Type::TYPE_POINTER: |
| case Type::TYPE_MAP: |
| case Type::TYPE_CHANNEL: |
| // These types are all a single pointer. |
| go_assert((offset % ptrsize) == 0); |
| this->set(offset / ptrsize); |
| break; |
| |
| case Type::TYPE_STRING: |
| // A string starts with a single pointer. |
| go_assert((offset % ptrsize) == 0); |
| this->set(offset / ptrsize); |
| break; |
| |
| case Type::TYPE_INTERFACE: |
| // An interface is two pointers. |
| go_assert((offset % ptrsize) == 0); |
| this->set(offset / ptrsize); |
| this->set((offset / ptrsize) + 1); |
| break; |
| |
| case Type::TYPE_STRUCT: |
| { |
| if (!type->has_pointer()) |
| return; |
| |
| const Struct_field_list* fields = type->struct_type()->fields(); |
| int64_t soffset = 0; |
| for (Struct_field_list::const_iterator pf = fields->begin(); |
| pf != fields->end(); |
| ++pf) |
| { |
| int64_t field_align; |
| if (!pf->type()->backend_type_field_align(gogo, &field_align)) |
| { |
| go_assert(saw_errors()); |
| return; |
| } |
| soffset = (soffset + (field_align - 1)) &~ (field_align - 1); |
| |
| this->set_from(gogo, pf->type(), ptrsize, offset + soffset); |
| |
| int64_t field_size; |
| if (!pf->type()->backend_type_size(gogo, &field_size)) |
| { |
| go_assert(saw_errors()); |
| return; |
| } |
| soffset += field_size; |
| } |
| } |
| break; |
| |
| case Type::TYPE_ARRAY: |
| if (type->is_slice_type()) |
| { |
| // A slice starts with a single pointer. |
| go_assert((offset % ptrsize) == 0); |
| this->set(offset / ptrsize); |
| break; |
| } |
| else |
| { |
| if (!type->has_pointer()) |
| return; |
| |
| int64_t len; |
| if (!type->array_type()->int_length(&len)) |
| { |
| go_assert(saw_errors()); |
| return; |
| } |
| |
| Type* element_type = type->array_type()->element_type(); |
| int64_t ele_size; |
| if (!element_type->backend_type_size(gogo, &ele_size)) |
| { |
| go_assert(saw_errors()); |
| return; |
| } |
| |
| int64_t eoffset = 0; |
| for (int64_t i = 0; i < len; i++, eoffset += ele_size) |
| this->set_from(gogo, element_type, ptrsize, offset + eoffset); |
| break; |
| } |
| } |
| } |
| |
| // Return a symbol name for this ptrmask. This is used to coalesce identical |
| // ptrmasks, which are common. The symbol name must use only characters that are |
| // valid in symbols. It's nice if it's short. For smaller ptrmasks, we convert |
| // it to a string that uses only 32 characters, avoiding digits and u and U. For |
| // longer pointer masks, apply the same process to the SHA1 digest of the bits, |
| // so as to avoid pathologically long symbol names (see related Go issues #32083 |
| // and #11583 for more on this). To avoid collisions between the two encoding |
| // schemes, use a prefix ("X") for the SHA form to disambiguate. |
| std::string |
| Ptrmask::symname() const |
| { |
| const std::vector<unsigned char>* bits(&this->bits_); |
| std::vector<unsigned char> shabits; |
| std::string prefix; |
| |
| if (this->bits_.size() > 128) |
| { |
| // Produce a SHA1 digest of the data. |
| Go_sha1_helper* sha1_helper = go_create_sha1_helper(); |
| sha1_helper->process_bytes(&this->bits_[0], this->bits_.size()); |
| std::string digest = sha1_helper->finish(); |
| delete sha1_helper; |
| |
| // Redirect the bits vector to the digest, and update the prefix. |
| prefix = "X"; |
| for (std::string::const_iterator p = digest.begin(); |
| p != digest.end(); |
| ++p) |
| { |
| unsigned char c = *p; |
| shabits.push_back(c); |
| } |
| bits = &shabits; |
| } |
| |
| const char chars[33] = "abcdefghijklmnopqrstvwxyzABCDEFG"; |
| go_assert(chars[32] == '\0'); |
| std::string ret(prefix); |
| unsigned int b = 0; |
| int remaining = 0; |
| for (std::vector<unsigned char>::const_iterator p = bits->begin(); |
| p != bits->end(); |
| ++p) |
| { |
| b |= *p << remaining; |
| remaining += 8; |
| while (remaining >= 5) |
| { |
| ret += chars[b & 0x1f]; |
| b >>= 5; |
| remaining -= 5; |
| } |
| } |
| while (remaining > 0) |
| { |
| ret += chars[b & 0x1f]; |
| b >>= 5; |
| remaining -= 5; |
| } |
| return ret; |
| } |
| |
| // Return a constructor for this ptrmask. This will be used to |
| // initialize the runtime ptrmask value. |
| |
| Expression* |
| Ptrmask::constructor(Gogo* gogo) const |
| { |
| Location bloc = Linemap::predeclared_location(); |
| Type* byte_type = gogo->lookup_global("byte")->type_value(); |
| Expression* len = Expression::make_integer_ul(this->bits_.size(), NULL, |
| bloc); |
| Array_type* at = Type::make_array_type(byte_type, len); |
| Expression_list* vals = new Expression_list(); |
| vals->reserve(this->bits_.size()); |
| for (std::vector<unsigned char>::const_iterator p = this->bits_.begin(); |
| p != this->bits_.end(); |
| ++p) |
| vals->push_back(Expression::make_integer_ul(*p, byte_type, bloc)); |
| return Expression::make_array_composite_literal(at, vals, bloc); |
| } |
| |
| // The hash table mapping a ptrmask symbol name to the ptrmask variable. |
| Type::GC_gcbits_vars Type::gc_gcbits_vars; |
| |
| // Return a ptrmask variable for a type. For a type descriptor this |
| // is only used for variables that are small enough to not need a |
| // gcprog, but for a global variable this is used for a variable of |
| // any size. PTRDATA is the number of bytes of the type that contain |
| // pointer data. PTRSIZE is the size of a pointer on the target |
| // system. |
| |
| Bvariable* |
| Type::gc_ptrmask_var(Gogo* gogo, int64_t ptrsize, int64_t ptrdata) |
| { |
| Ptrmask ptrmask(ptrdata / ptrsize); |
| if (ptrdata >= ptrsize) |
| ptrmask.set_from(gogo, this, ptrsize, 0); |
| else |
| { |
| // This can happen in error cases. Just build an empty gcbits. |
| go_assert(saw_errors()); |
| } |
| |
| std::string sym_name = gogo->ptrmask_symbol_name(ptrmask.symname()); |
| Bvariable* bvnull = NULL; |
| std::pair<GC_gcbits_vars::iterator, bool> ins = |
| Type::gc_gcbits_vars.insert(std::make_pair(sym_name, bvnull)); |
| if (!ins.second) |
| { |
| // We've already built a GC symbol for this set of gcbits. |
| return ins.first->second; |
| } |
| |
| Expression* val = ptrmask.constructor(gogo); |
| Translate_context context(gogo, NULL, NULL, NULL); |
| context.set_is_const(); |
| Bexpression* bval = val->get_backend(&context); |
| |
| std::string asm_name(go_selectively_encode_id(sym_name)); |
| Btype *btype = val->type()->get_backend(gogo); |
| Bvariable* ret = gogo->backend()->implicit_variable(sym_name, asm_name, |
| btype, false, true, |
| true, 0); |
| gogo->backend()->implicit_variable_set_init(ret, sym_name, btype, false, |
| true, true, bval); |
| ins.first->second = ret; |
| return ret; |
| } |
| |
| // A GCProg is used to build a program for the garbage collector. |
| // This is used for types with a lot of pointer data, to reduce the |
| // size of the data in the compiled program. The program is expanded |
| // at runtime. For the format, see runGCProg in libgo/go/runtime/mbitmap.go. |
| |
| class GCProg |
| { |
| public: |
| GCProg() |
| : bytes_(), index_(0), nb_(0) |
| {} |
| |
|