diff options
Diffstat (limited to 'gcc/go/gofrontend/types.cc')
-rw-r--r-- | gcc/go/gofrontend/types.cc | 1989 |
1 files changed, 1640 insertions, 349 deletions
diff --git a/gcc/go/gofrontend/types.cc b/gcc/go/gofrontend/types.cc index 0443281dd8..f65dbd73c3 100644 --- a/gcc/go/gofrontend/types.cc +++ b/gcc/go/gofrontend/types.cc @@ -6,8 +6,12 @@ #include "go-system.h" +#include <ostream> + #include "go-c.h" #include "gogo.h" +#include "go-diagnostics.h" +#include "go-encode-id.h" #include "operator.h" #include "expressions.h" #include "statements.h" @@ -324,10 +328,10 @@ Type::are_identical(const Type* t1, const Type* t2, bool errors_are_identical, t2 = t2->forwarded(); // Ignore aliases for purposes of type identity. - if (t1->named_type() != NULL && t1->named_type()->is_alias()) - t1 = t1->named_type()->real_type(); - if (t2->named_type() != NULL && t2->named_type()->is_alias()) - t2 = t2->named_type()->real_type(); + while (t1->named_type() != NULL && t1->named_type()->is_alias()) + t1 = t1->named_type()->real_type()->forwarded(); + while (t2->named_type() != NULL && t2->named_type()->is_alias()) + t2 = t2->named_type()->real_type()->forwarded(); if (t1 == t2) return true; @@ -565,6 +569,12 @@ Type::are_compatible_for_comparison(bool is_equality_op, const Type *t1, return t2->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(); @@ -580,6 +590,12 @@ Type::are_compatible_for_comparison(bool is_equality_op, const Type *t1, } 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()) { @@ -806,6 +822,8 @@ Type::are_convertible(const Type* lhs, const Type* rhs, std::string* reason) unsigned int Type::hash_for_method(Gogo* gogo) const { + if (this->named_type() != NULL && this->named_type()->is_alias()) + return this->named_type()->real_type()->hash_for_method(gogo); unsigned int ret = 0; if (this->classification_ != TYPE_FORWARD) ret += this->classification_; @@ -1149,15 +1167,16 @@ Bexpression* Type::type_descriptor_pointer(Gogo* gogo, Location location) { Type* t = this->forwarded(); - if (t->named_type() != NULL && t->named_type()->is_alias()) - t = t->named_type()->real_type(); + while (t->named_type() != NULL && t->named_type()->is_alias()) + t = t->named_type()->real_type()->forwarded(); 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); + gogo->backend()->var_expression(t->type_descriptor_var_, + VE_rvalue, location); return gogo->backend()->address_expression(var_expr, location); } @@ -1202,10 +1221,12 @@ Type::make_type_descriptor_var(Gogo* gogo) Type* td_type = Type::make_type_descriptor_type(); Btype* td_btype = td_type->get_backend(gogo); + const char *name = "__go_tdn_unsafe.Pointer"; + std::string asm_name(go_selectively_encode_id(name)); this->type_descriptor_var_ = - gogo->backend()->immutable_struct_reference("__go_tdn_unsafe.Pointer", - td_btype, - bloc); + gogo->backend()->immutable_struct_reference(name, asm_name, + td_btype, + bloc); if (phash != NULL) *phash = this->type_descriptor_var_; @@ -1224,10 +1245,11 @@ Type::make_type_descriptor_var(Gogo* gogo) 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, - initializer_btype, - loc); + gogo->backend()->immutable_struct_reference(var_name, asm_name, + initializer_btype, + loc); if (phash != NULL) *phash = this->type_descriptor_var_; return; @@ -1256,8 +1278,9 @@ Type::make_type_descriptor_var(Gogo* gogo) // 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, false, is_common, + gogo->backend()->immutable_struct(var_name, asm_name, false, is_common, initializer_btype, loc); if (phash != NULL) *phash = this->type_descriptor_var_; @@ -1316,18 +1339,8 @@ Type::type_descriptor_var_name(Gogo* gogo, Named_type* nt) } } - // FIXME: This adds in pkgpath twice for hidden symbols, which is - // pointless. - const std::string& name(no->name()); - if (!Gogo::is_hidden_name(name)) - ret.append(name); - else - { - ret.append(1, '.'); - ret.append(Gogo::pkgpath_for_symbol(Gogo::hidden_name_pkgpath(name))); - ret.append(1, '.'); - ret.append(Gogo::unpack_hidden_name(name)); - } + std::string mname(Gogo::mangle_possibly_hidden_name(no->name())); + ret.append(mname); return ret; } @@ -1414,7 +1427,9 @@ Type::make_builtin_struct_type(int nfields, ...) va_end(ap); - return Type::make_struct_type(sfl, bloc); + Struct_type* ret = Type::make_struct_type(sfl, bloc); + ret->set_is_struct_incomparable(); + return ret; } // A list of builtin named types. @@ -1450,8 +1465,8 @@ Type::convert_builtin_named_types(Gogo* gogo) } // Return the type of a type descriptor. We should really tie this to -// runtime.Type rather than copying it. This must match commonType in -// libgo/go/runtime/type.go. +// 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() @@ -1476,7 +1491,7 @@ Type::make_type_descriptor_type() Typed_identifier_list *params = new Typed_identifier_list(); params->push_back(Typed_identifier("key", unsafe_pointer_type, bloc)); - params->push_back(Typed_identifier("key_size", uintptr_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)); @@ -1487,7 +1502,6 @@ Type::make_type_descriptor_type() 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("key_size", uintptr_type, bloc)); results = new Typed_identifier_list(); results->push_back(Typed_identifier("", Type::lookup_bool_type(), bloc)); @@ -1497,7 +1511,7 @@ Type::make_type_descriptor_type() // Forward declaration for the type descriptor type. Named_object* named_type_descriptor_type = - Named_object::make_type_declaration("commonType", NULL, bloc); + 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); @@ -1543,7 +1557,7 @@ Type::make_type_descriptor_type() "ptrToThis", pointer_type_descriptor_type); - Named_type* named = Type::make_builtin_named_type("commonType", + Named_type* named = Type::make_builtin_named_type("_type", type_descriptor_type); named_type_descriptor_type->set_type_value(named); @@ -1566,6 +1580,88 @@ Type::make_type_descriptor_ptr_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::type_functions. + 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; + } +} + // Set *HASH_FN and *EQUAL_FN to the runtime functions which compute a // hash code for this type and which compare whether two values of // this type are equal. If NAME is not NULL it is the name of this @@ -1577,6 +1673,18 @@ Type::type_functions(Gogo* gogo, Named_type* name, Function_type* hash_fntype, Function_type* equal_fntype, Named_object** hash_fn, Named_object** equal_fn) { + // If this loop leaves NAME as NULL, then the type does not have a + // name after all. + while (name != NULL && name->is_alias()) + name = name->real_type()->named_type(); + + if (!this->is_comparable()) + { + *hash_fn = NULL; + *equal_fn = NULL; + return; + } + if (hash_fntype == NULL || equal_fntype == NULL) { Location bloc = Linemap::predeclared_location(); @@ -1590,7 +1698,7 @@ Type::type_functions(Gogo* gogo, Named_type* name, Function_type* hash_fntype, Typed_identifier_list* params = new Typed_identifier_list(); params->push_back(Typed_identifier("key", unsafe_pointer_type, bloc)); - params->push_back(Typed_identifier("key_size", uintptr_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)); @@ -1604,7 +1712,6 @@ Type::type_functions(Gogo* gogo, Named_type* name, Function_type* hash_fntype, bloc)); params->push_back(Typed_identifier("key2", unsafe_pointer_type, bloc)); - params->push_back(Typed_identifier("key_size", uintptr_type, bloc)); Typed_identifier_list* results = new Typed_identifier_list(); results->push_back(Typed_identifier("", Type::lookup_bool_type(), @@ -1618,15 +1725,77 @@ Type::type_functions(Gogo* gogo, Named_type* name, Function_type* hash_fntype, const char* equal_fnname; if (this->compare_is_identity(gogo)) { - hash_fnname = "__go_type_hash_identity"; - equal_fnname = "__go_type_equal_identity"; - } - else if (!this->is_comparable() || - (this->struct_type() != NULL - && Thunk_statement::is_thunk_struct(this->struct_type()))) - { - hash_fnname = "__go_type_hash_error"; - equal_fnname = "__go_type_equal_error"; + int64_t size, align; + if (!this->backend_type_size(gogo, &size) + || !this->backend_type_align(gogo, &align)) + { + go_assert(saw_errors()); + return; + } + bool build_functions = 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: + hash_fnname = "runtime.memhash0"; + equal_fnname = "runtime.memequal0"; + break; + case 1: + hash_fnname = "runtime.memhash8"; + equal_fnname = "runtime.memequal8"; + break; + case 2: + if (align < Type::memequal_align(gogo, 16)) + build_functions = true; + else + { + hash_fnname = "runtime.memhash16"; + equal_fnname = "runtime.memequal16"; + } + break; + case 4: + if (align < Type::memequal_align(gogo, 32)) + build_functions = true; + else + { + hash_fnname = "runtime.memhash32"; + equal_fnname = "runtime.memequal32"; + } + break; + case 8: + if (align < Type::memequal_align(gogo, 64)) + build_functions = true; + else + { + hash_fnname = "runtime.memhash64"; + equal_fnname = "runtime.memequal64"; + } + break; + case 16: + if (align < Type::memequal_align(gogo, 128)) + build_functions = true; + else + { + hash_fnname = "runtime.memhash128"; + equal_fnname = "runtime.memequal128"; + } + break; + default: + build_functions = true; + break; + } + if (build_functions) + { + // 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 hash/equality functions for identity, passing the size. + this->specific_type_functions(gogo, name, size, hash_fntype, + equal_fntype, hash_fn, equal_fn); + return; + } } else { @@ -1648,18 +1817,40 @@ Type::type_functions(Gogo* gogo, Named_type* name, Function_type* hash_fntype, go_unreachable(); case Type::TYPE_FLOAT: - hash_fnname = "__go_type_hash_float"; - equal_fnname = "__go_type_equal_float"; + switch (this->float_type()->bits()) + { + case 32: + hash_fnname = "runtime.f32hash"; + equal_fnname = "runtime.f32equal"; + break; + case 64: + hash_fnname = "runtime.f64hash"; + equal_fnname = "runtime.f64equal"; + break; + default: + go_unreachable(); + } break; case Type::TYPE_COMPLEX: - hash_fnname = "__go_type_hash_complex"; - equal_fnname = "__go_type_equal_complex"; + switch (this->complex_type()->bits()) + { + case 64: + hash_fnname = "runtime.c64hash"; + equal_fnname = "runtime.c64equal"; + break; + case 128: + hash_fnname = "runtime.c128hash"; + equal_fnname = "runtime.c128equal"; + break; + default: + go_unreachable(); + } break; case Type::TYPE_STRING: - hash_fnname = "__go_type_hash_string"; - equal_fnname = "__go_type_equal_string"; + hash_fnname = "runtime.strhash"; + equal_fnname = "runtime.strequal"; break; case Type::TYPE_STRUCT: @@ -1667,7 +1858,7 @@ Type::type_functions(Gogo* gogo, Named_type* name, Function_type* hash_fntype, // This is a struct which can not be compared using a // simple identity function. We need to build a function // for comparison. - this->specific_type_functions(gogo, name, hash_fntype, + this->specific_type_functions(gogo, name, -1, hash_fntype, equal_fntype, hash_fn, equal_fn); return; } @@ -1684,7 +1875,7 @@ Type::type_functions(Gogo* gogo, Named_type* name, Function_type* hash_fntype, // This is an array which can not be compared using a // simple identity function. We need to build a // function for comparison. - this->specific_type_functions(gogo, name, hash_fntype, + this->specific_type_functions(gogo, name, -1, hash_fntype, equal_fntype, hash_fn, equal_fn); return; } @@ -1693,13 +1884,13 @@ Type::type_functions(Gogo* gogo, Named_type* name, Function_type* hash_fntype, case Type::TYPE_INTERFACE: if (this->interface_type()->is_empty()) { - hash_fnname = "__go_type_hash_empty_interface"; - equal_fnname = "__go_type_equal_empty_interface"; + hash_fnname = "runtime.nilinterhash"; + equal_fnname = "runtime.nilinterequal"; } else { - hash_fnname = "__go_type_hash_interface"; - equal_fnname = "__go_type_equal_interface"; + hash_fnname = "runtime.interhash"; + equal_fnname = "runtime.interequal"; } break; @@ -1726,11 +1917,13 @@ Type::type_functions(Gogo* gogo, Named_type* name, Function_type* hash_fntype, Type::Type_functions Type::type_functions_table; -// Handle a type function which is specific to a type: a struct or -// array which can not use an identity comparison. +// Handle a type function which is specific to a type: if SIZE == -1, +// this is a struct or array that can not use an identity comparison. +// Otherwise, it is a type that uses an identity comparison but is not +// one of the standard supported sizes. void -Type::specific_type_functions(Gogo* gogo, Named_type* name, +Type::specific_type_functions(Gogo* gogo, Named_type* name, int64_t size, Function_type* hash_fntype, Function_type* equal_fntype, Named_object** hash_fn, @@ -1816,11 +2009,13 @@ Type::specific_type_functions(Gogo* gogo, Named_type* name, if (!is_defined_elsewhere) { if (gogo->in_global_scope()) - this->write_specific_type_functions(gogo, name, hash_name, hash_fntype, - equal_name, equal_fntype); + this->write_specific_type_functions(gogo, name, size, hash_name, + hash_fntype, equal_name, + equal_fntype); else - gogo->queue_specific_type_function(this, name, hash_name, hash_fntype, - equal_name, equal_fntype); + gogo->queue_specific_type_function(this, name, size, hash_name, + hash_fntype, equal_name, + equal_fntype); } } @@ -1828,7 +2023,7 @@ Type::specific_type_functions(Gogo* gogo, Named_type* name, // written specially. void -Type::write_specific_type_functions(Gogo* gogo, Named_type* name, +Type::write_specific_type_functions(Gogo* gogo, Named_type* name, int64_t size, const std::string& hash_name, Function_type* hash_fntype, const std::string& equal_name, @@ -1842,12 +2037,16 @@ Type::write_specific_type_functions(Gogo* gogo, Named_type* name, 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 (name != NULL && name->real_type()->named_type() != NULL) + if (size != -1) + this->write_identity_hash(gogo, size); + else if (name != NULL && name->real_type()->named_type() != NULL) this->write_named_hash(gogo, name, hash_fntype, equal_fntype); else if (this->struct_type() != NULL) this->struct_type()->write_hash_function(gogo, name, hash_fntype, @@ -1868,7 +2067,9 @@ Type::write_specific_type_functions(Gogo* gogo, Named_type* name, equal_fn->func_value()->set_is_type_specific_function(); gogo->start_block(bloc); - if (name != NULL && name->real_type()->named_type() != NULL) + 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); @@ -1887,6 +2088,112 @@ Type::write_specific_type_functions(Gogo* gogo, Named_type* name, equal_fn->func_value()->descriptor(gogo, equal_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); +} + +// 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 a hash function that simply calls the hash 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 @@ -1900,6 +2207,11 @@ Type::write_named_hash(Gogo* gogo, Named_type* name, Location bloc = Linemap::predeclared_location(); Named_type* base_type = name->real_type()->named_type(); + while (base_type->is_alias()) + { + base_type = base_type->real_type()->named_type(); + go_assert(base_type != NULL); + } go_assert(base_type != NULL); // The pointer to the type we are going to hash. This is an @@ -1907,9 +2219,9 @@ Type::write_named_hash(Gogo* gogo, Named_type* name, Named_object* key_arg = gogo->lookup("key", NULL); go_assert(key_arg != NULL); - // The size of the type we are going to hash. - Named_object* keysz_arg = gogo->lookup("key_size", NULL); - go_assert(keysz_arg != NULL); + // The seed argument to the hash function. + Named_object* seed_arg = gogo->lookup("seed", NULL); + go_assert(seed_arg != NULL); Named_object* hash_fn; Named_object* equal_fn; @@ -1918,10 +2230,10 @@ Type::write_named_hash(Gogo* gogo, Named_type* name, // Call the hash function for the base type. Expression* key_ref = Expression::make_var_reference(key_arg, bloc); - Expression* keysz_ref = Expression::make_var_reference(keysz_arg, bloc); + Expression* seed_ref = Expression::make_var_reference(seed_arg, bloc); Expression_list* args = new Expression_list(); args->push_back(key_ref); - args->push_back(keysz_ref); + args->push_back(seed_ref); Expression* func = Expression::make_func_reference(hash_fn, NULL, bloc); Expression* call = Expression::make_call(func, args, false, bloc); @@ -2042,8 +2354,18 @@ Type::type_descriptor_constructor(Gogo* gogo, int runtime_type_kind, Named_object* equal_fn; this->type_functions(gogo, name, hash_fntype, equal_fntype, &hash_fn, &equal_fn); - vals->push_back(Expression::make_func_reference(hash_fn, NULL, bloc)); - vals->push_back(Expression::make_func_reference(equal_fn, NULL, bloc)); + if (hash_fn == NULL) + vals->push_back(Expression::make_cast(hash_fntype, + Expression::make_nil(bloc), + bloc)); + else + vals->push_back(Expression::make_func_reference(hash_fn, NULL, bloc)); + 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("gc")); @@ -2097,8 +2419,8 @@ Bexpression* Type::gc_symbol_pointer(Gogo* gogo) { Type* t = this->forwarded(); - if (t->named_type() != NULL && t->named_type()->is_alias()) - t = t->named_type()->real_type(); + while (t->named_type() != NULL && t->named_type()->is_alias()) + t = t->named_type()->real_type()->forwarded(); if (t->gc_symbol_var_ == NULL) { t->make_gc_symbol_var(gogo); @@ -2106,8 +2428,11 @@ Type::gc_symbol_pointer(Gogo* gogo) } Location bloc = Linemap::predeclared_location(); Bexpression* var_expr = - gogo->backend()->var_expression(t->gc_symbol_var_, bloc); - return gogo->backend()->address_expression(var_expr, bloc); + gogo->backend()->var_expression(t->gc_symbol_var_, VE_rvalue, bloc); + Bexpression* addr_expr = + gogo->backend()->address_expression(var_expr, bloc); + Btype* ubtype = Type::lookup_integer_type("uintptr")->get_backend(gogo); + return gogo->backend()->convert_expression(ubtype, addr_expr, bloc); } // A mapping from unnamed types to GC symbol variables. @@ -2152,8 +2477,10 @@ Type::make_gc_symbol_var(Gogo* gogo) 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, sym_btype); + gogo->backend()->implicit_variable_reference(sym_name, asm_name, + sym_btype); if (phash != NULL) *phash = this->gc_symbol_var_; return; @@ -2175,11 +2502,28 @@ Type::make_gc_symbol_var(Gogo* gogo) is_common = true; } + // The current garbage collector requires that the GC symbol be + // aligned to at least a four byte boundary. See the use of PRECISE + // and LOOP in libgo/runtime/mgc0.c. + int64_t align; + if (!sym_init->type()->backend_type_align(gogo, &align)) + go_assert(saw_errors()); + if (align < 4) + align = 4; + else + { + // Use default alignment. + align = 0; + } + // 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, sym_btype, false, true, is_common, 0); + gogo->backend()->implicit_variable(sym_name, asm_name, + sym_btype, false, true, is_common, + align); if (phash != NULL) *phash = this->gc_symbol_var_; @@ -2216,9 +2560,10 @@ Type::gc_symbol_constructor(Gogo* gogo) vals->push_back(Expression::make_integer_ul(GC_END, uintptr_t, bloc)); - Expression* len = Expression::make_integer_ul(vals->size() + 1, NULL, + Expression* len = Expression::make_integer_ul(vals->size(), NULL, bloc); Array_type* gc_symbol_type = Type::make_array_type(uintptr_t, len); + gc_symbol_type->set_is_array_incomparable(); return Expression::make_array_composite_literal(gc_symbol_type, vals, bloc); } @@ -2560,12 +2905,13 @@ Type::backend_type_size(Gogo* gogo, int64_t *psize) if (*psize == -1) { if (this->named_type() != NULL) - error_at(this->named_type()->location(), - "type %s larger than address space", - Gogo::message_name(this->named_type()->name()).c_str()); + go_error_at(this->named_type()->location(), + "type %s larger than address space", + Gogo::message_name(this->named_type()->name()).c_str()); else - error("type %s larger than address space", - this->reflection(gogo).c_str()); + go_error_at(Linemap::unknown_location(), + "type %s larger than address space", + this->reflection(gogo).c_str()); // Make this an error type to avoid knock-on errors. this->classification_ = TYPE_ERROR; @@ -2629,7 +2975,7 @@ Type::import_type(Import* imp) return Interface_type::do_import(imp); else { - error_at(imp->location(), "import error: expected type"); + go_error_at(imp->location(), "import error: expected type"); return Type::make_error_type(); } } @@ -3755,6 +4101,7 @@ Function_type::get_backend_fntype(Gogo* gogo) } Struct_type* st = Type::make_struct_type(sfl, this->location()); + st->set_is_struct_incomparable(); ins.first->second = st->get_backend(gogo); } bresult_struct = ins.first->second; @@ -3836,7 +4183,7 @@ Function_type::do_type_descriptor(Gogo* gogo, Named_type* name) vals->reserve(4); Struct_field_list::const_iterator p = fields->begin(); - go_assert(p->is_field_name("commonType")); + go_assert(p->is_field_name("_type")); vals->push_back(this->type_descriptor_constructor(gogo, RUNTIME_TYPE_KIND_FUNC, name, NULL, true)); @@ -4349,7 +4696,7 @@ Pointer_type::do_type_descriptor(Gogo* gogo, Named_type* name) vals->reserve(2); Struct_field_list::const_iterator p = fields->begin(); - go_assert(p->is_field_name("commonType")); + go_assert(p->is_field_name("_type")); vals->push_back(this->type_descriptor_constructor(gogo, RUNTIME_TYPE_KIND_PTR, name, methods, false)); @@ -4502,10 +4849,7 @@ class Call_multiple_result_type : public Type protected: bool do_has_pointer() const - { - go_assert(saw_errors()); - return false; - } + { return false; } bool do_compare_is_identity(Gogo*) @@ -4576,7 +4920,10 @@ Struct_field::field_name() const if (dt->forward_declaration_type() != NULL) return dt->forward_declaration_type()->name(); else if (dt->named_type() != NULL) - return dt->named_type()->name(); + { + // Note that this can be an alias name. + return dt->named_type()->name(); + } else if (t->is_error_type() || dt->is_error_type()) { static const std::string error_string = "*error*"; @@ -4720,13 +5067,13 @@ Struct_type::do_verify() { if (t->named_type() != NULL && t->points_to() != NULL) { - error_at(p->location(), "embedded type may not be a pointer"); + go_error_at(p->location(), "embedded type may not be a pointer"); p->set_type(Type::make_error_type()); } else if (t->points_to() != NULL && t->points_to()->interface_type() != NULL) { - error_at(p->location(), + go_error_at(p->location(), "embedded type may not be pointer to interface"); p->set_type(Type::make_error_type()); } @@ -4759,6 +5106,8 @@ bool Struct_type::is_identical(const Struct_type* t, bool errors_are_identical) const { + if (this->is_struct_incomparable_ != t->is_struct_incomparable_) + return false; const Struct_field_list* fields1 = this->fields(); const Struct_field_list* fields2 = t->fields(); if (fields1 == NULL || fields2 == NULL) @@ -4843,6 +5192,44 @@ Struct_type::do_compare_is_identity(Gogo* gogo) return true; } +// Return whether this struct type is reflexive--whether a value of +// this type is always equal to itself. + +bool +Struct_type::do_is_reflexive() +{ + const Struct_field_list* fields = this->fields_; + if (fields == NULL) + return true; + for (Struct_field_list::const_iterator pf = fields->begin(); + pf != fields->end(); + ++pf) + { + if (!pf->type()->is_reflexive()) + return false; + } + return true; +} + +// Return whether this struct type needs a key update when used as a +// map key. + +bool +Struct_type::do_needs_key_update() +{ + const Struct_field_list* fields = this->fields_; + if (fields == NULL) + return false; + for (Struct_field_list::const_iterator pf = fields->begin(); + pf != fields->end(); + ++pf) + { + if (pf->type()->needs_key_update()) + return true; + } + return false; +} + // Build identity and hash functions for this struct. // Hash code. @@ -4858,7 +5245,10 @@ Struct_type::do_hash_for_method(Gogo* gogo) const ++pf) ret = (ret << 1) + pf->type()->hash_for_method(gogo); } - return ret <<= 2; + ret <<= 2; + if (this->is_struct_incomparable_) + ret <<= 1; + return ret; } // Find the local field NAME. @@ -5219,7 +5609,7 @@ Struct_type::do_type_descriptor(Gogo* gogo, Named_type* name) go_assert(methods == NULL || name == NULL); Struct_field_list::const_iterator ps = fields->begin(); - go_assert(ps->is_field_name("commonType")); + go_assert(ps->is_field_name("_type")); vals->push_back(this->type_descriptor_constructor(gogo, RUNTIME_TYPE_KIND_STRUCT, name, methods, true)); @@ -5311,25 +5701,26 @@ Struct_type::write_hash_function(Gogo* gogo, Named_type*, go_assert(key_arg != NULL); Type* key_arg_type = key_arg->var_value()->type(); - Type* uintptr_type = Type::lookup_integer_type("uintptr"); + // The seed argument to the hash function. + Named_object* seed_arg = gogo->lookup("seed", NULL); + go_assert(seed_arg != NULL); - // Get a 0. - Expression* zero = Expression::make_integer_ul(0, uintptr_type, bloc); + Type* uintptr_type = Type::lookup_integer_type("uintptr"); - // Make a temporary to hold the return value, initialized to 0. - Temporary_statement* retval = Statement::make_temporary(uintptr_type, zero, + // Make a temporary to hold the return value, initialized to the seed. + Expression* ref = Expression::make_var_reference(seed_arg, bloc); + Temporary_statement* retval = Statement::make_temporary(uintptr_type, ref, bloc); gogo->add_statement(retval); // Make a temporary to hold the key as a uintptr. - Expression* ref = Expression::make_var_reference(key_arg, bloc); + ref = Expression::make_var_reference(key_arg, bloc); ref = Expression::make_cast(uintptr_type, ref, bloc); Temporary_statement* key = Statement::make_temporary(uintptr_type, ref, bloc); gogo->add_statement(key); // Loop over the struct fields. - bool first = true; const Struct_field_list* fields = this->fields_; for (Struct_field_list::const_iterator pf = fields->begin(); pf != fields->end(); @@ -5338,19 +5729,6 @@ Struct_type::write_hash_function(Gogo* gogo, Named_type*, if (Gogo::is_sink_name(pf->field_name())) continue; - if (first) - first = false; - else - { - // Multiply retval by 33. - Expression* i33 = Expression::make_integer_ul(33, uintptr_type, - bloc); - ref = Expression::make_temporary_reference(retval, bloc); - Statement* s = Statement::make_assignment_operation(OPERATOR_MULTEQ, - ref, i33, bloc); - gogo->add_statement(s); - } - // Get a pointer to the value of this field. Expression* offset = Expression::make_struct_field_offset(this, &*pf); ref = Expression::make_temporary_reference(key, bloc); @@ -5358,29 +5736,25 @@ Struct_type::write_hash_function(Gogo* gogo, Named_type*, bloc); subkey = Expression::make_cast(key_arg_type, subkey, bloc); - // Get the size of this field. - Expression* size = Expression::make_type_info(pf->type(), - Expression::TYPE_INFO_SIZE); - // Get the hash function to use for the type of this field. Named_object* hash_fn; Named_object* equal_fn; pf->type()->type_functions(gogo, pf->type()->named_type(), hash_fntype, equal_fntype, &hash_fn, &equal_fn); - // Call the hash function for the field. + // Call the hash function for the field, passing retval as the seed. + ref = Expression::make_temporary_reference(retval, bloc); Expression_list* args = new Expression_list(); args->push_back(subkey); - args->push_back(size); + args->push_back(ref); Expression* func = Expression::make_func_reference(hash_fn, NULL, bloc); Expression* call = Expression::make_call(func, args, false, bloc); - // Add the field's hash value to retval. + // Set retval to the result. Temporary_reference_expression* tref = Expression::make_temporary_reference(retval, bloc); tref->set_is_lvalue(); - Statement* s = Statement::make_assignment_operation(OPERATOR_PLUSEQ, - tref, call, bloc); + Statement* s = Statement::make_assignment(tref, call, bloc); gogo->add_statement(s); } @@ -5478,7 +5852,12 @@ Struct_type::do_reflection(Gogo* gogo, std::string* ret) const else ret->append(Gogo::unpack_hidden_name(p->field_name())); ret->push_back(' '); - this->append_reflection(p->type(), gogo, ret); + if (p->is_anonymous() + && p->type()->named_type() != NULL + && p->type()->named_type()->is_alias()) + p->type()->named_type()->append_reflection_type_name(gogo, true, ret); + else + this->append_reflection(p->type(), gogo, ret); if (p->has_tag()) { @@ -5549,15 +5928,24 @@ Struct_type::do_mangled_name(Gogo* gogo, std::string* ret) const if (p->is_anonymous()) ret->append("0_"); else - { - std::string n = Gogo::unpack_hidden_name(p->field_name()); + { + + std::string n(Gogo::mangle_possibly_hidden_name(p->field_name())); char buf[20]; snprintf(buf, sizeof buf, "%u_", static_cast<unsigned int>(n.length())); ret->append(buf); ret->append(n); } - this->append_mangled_name(p->type(), gogo, ret); + + // For an anonymous field with an alias type, the field name + // is the alias name. + if (p->is_anonymous() + && p->type()->named_type() != NULL + && p->type()->named_type()->is_alias()) + p->type()->named_type()->append_mangled_type_name(gogo, true, ret); + else + this->append_mangled_name(p->type(), gogo, ret); if (p->has_tag()) { const std::string& tag(p->tag()); @@ -5585,6 +5973,9 @@ Struct_type::do_mangled_name(Gogo* gogo, std::string* ret) const } } + if (this->is_struct_incomparable_) + ret->push_back('x'); + ret->push_back('e'); } @@ -5683,6 +6074,281 @@ Struct_type::do_import(Import* imp) return Type::make_struct_type(fields, imp->location()); } +// Whether we can write this struct type to a C header file. +// We can't if any of the fields are structs defined in a different package. + +bool +Struct_type::can_write_to_c_header( + std::vector<const Named_object*>* requires, + std::vector<const Named_object*>* declare) const +{ + const Struct_field_list* fields = this->fields_; + if (fields == NULL || fields->empty()) + return false; + for (Struct_field_list::const_iterator p = fields->begin(); + p != fields->end(); + ++p) + { + if (p->is_anonymous()) + return false; + if (!this->can_write_type_to_c_header(p->type(), requires, declare)) + return false; + } + return true; +} + +// Whether we can write the type T to a C header file. + +bool +Struct_type::can_write_type_to_c_header( + const Type* t, + std::vector<const Named_object*>* requires, + std::vector<const Named_object*>* declare) const +{ + t = t->forwarded(); + switch (t->classification()) + { + case TYPE_ERROR: + case TYPE_FORWARD: + return false; + + case TYPE_VOID: + case TYPE_BOOLEAN: + case TYPE_INTEGER: + case TYPE_FLOAT: + case TYPE_COMPLEX: + case TYPE_STRING: + case TYPE_FUNCTION: + case TYPE_MAP: + case TYPE_CHANNEL: + case TYPE_INTERFACE: + return true; + + case TYPE_POINTER: + // Don't try to handle a pointer to an array. + if (t->points_to()->array_type() != NULL + && !t->points_to()->is_slice_type()) + return false; + + if (t->points_to()->named_type() != NULL + && t->points_to()->struct_type() != NULL) + declare->push_back(t->points_to()->named_type()->named_object()); + return true; + + case TYPE_STRUCT: + return t->struct_type()->can_write_to_c_header(requires, declare); + + case TYPE_ARRAY: + if (t->is_slice_type()) + return true; + return this->can_write_type_to_c_header(t->array_type()->element_type(), + requires, declare); + + case TYPE_NAMED: + { + const Named_object* no = t->named_type()->named_object(); + if (no->package() != NULL) + { + if (t->is_unsafe_pointer_type()) + return true; + return false; + } + if (t->struct_type() != NULL) + { + requires->push_back(no); + return t->struct_type()->can_write_to_c_header(requires, declare); + } + return this->can_write_type_to_c_header(t->base(), requires, declare); + } + + case TYPE_CALL_MULTIPLE_RESULT: + case TYPE_NIL: + case TYPE_SINK: + default: + go_unreachable(); + } +} + +// Write this struct to a C header file. + +void +Struct_type::write_to_c_header(std::ostream& os) const +{ + const Struct_field_list* fields = this->fields_; + for (Struct_field_list::const_iterator p = fields->begin(); + p != fields->end(); + ++p) + { + os << '\t'; + this->write_field_to_c_header(os, p->field_name(), p->type()); + os << ';' << std::endl; + } +} + +// Write the type of a struct field to a C header file. + +void +Struct_type::write_field_to_c_header(std::ostream& os, const std::string& name, + const Type *t) const +{ + bool print_name = true; + t = t->forwarded(); + switch (t->classification()) + { + case TYPE_VOID: + os << "void"; + break; + + case TYPE_BOOLEAN: + os << "_Bool"; + break; + + case TYPE_INTEGER: + { + const Integer_type* it = t->integer_type(); + if (it->is_unsigned()) + os << 'u'; + os << "int" << it->bits() << "_t"; + } + break; + + case TYPE_FLOAT: + switch (t->float_type()->bits()) + { + case 32: + os << "float"; + break; + case 64: + os << "double"; + break; + default: + go_unreachable(); + } + break; + + case TYPE_COMPLEX: + switch (t->complex_type()->bits()) + { + case 64: + os << "float _Complex"; + break; + case 128: + os << "double _Complex"; + break; + default: + go_unreachable(); + } + break; + + case TYPE_STRING: + os << "String"; + break; + + case TYPE_FUNCTION: + os << "FuncVal*"; + break; + + case TYPE_POINTER: + { + std::vector<const Named_object*> requires; + std::vector<const Named_object*> declare; + if (!this->can_write_type_to_c_header(t->points_to(), &requires, + &declare)) + os << "void*"; + else + { + this->write_field_to_c_header(os, "", t->points_to()); + os << '*'; + } + } + break; + + case TYPE_MAP: + os << "Map*"; + break; + + case TYPE_CHANNEL: + os << "Chan*"; + break; + + case TYPE_INTERFACE: + if (t->interface_type()->is_empty()) + os << "Eface"; + else + os << "Iface"; + break; + + case TYPE_STRUCT: + os << "struct {" << std::endl; + t->struct_type()->write_to_c_header(os); + os << "\t}"; + break; + + case TYPE_ARRAY: + if (t->is_slice_type()) + os << "Slice"; + else + { + const Type *ele = t; + std::vector<const Type*> array_types; + while (ele->array_type() != NULL && !ele->is_slice_type()) + { + array_types.push_back(ele); + ele = ele->array_type()->element_type(); + } + this->write_field_to_c_header(os, "", ele); + os << ' ' << Gogo::message_name(name); + print_name = false; + while (!array_types.empty()) + { + ele = array_types.back(); + array_types.pop_back(); + os << '['; + Numeric_constant nc; + if (!ele->array_type()->length()->numeric_constant_value(&nc)) + go_unreachable(); + mpz_t val; + if (!nc.to_int(&val)) + go_unreachable(); + char* s = mpz_get_str(NULL, 10, val); + os << s; + free(s); + mpz_clear(val); + os << ']'; + } + } + break; + + case TYPE_NAMED: + { + const Named_object* no = t->named_type()->named_object(); + if (t->struct_type() != NULL) + os << "struct " << no->message_name(); + else if (t->is_unsafe_pointer_type()) + os << "void*"; + else if (t == Type::lookup_integer_type("uintptr")) + os << "uintptr_t"; + else + { + this->write_field_to_c_header(os, name, t->base()); + print_name = false; + } + } + break; + + case TYPE_ERROR: + case TYPE_FORWARD: + case TYPE_CALL_MULTIPLE_RESULT: + case TYPE_NIL: + case TYPE_SINK: + default: + go_unreachable(); + } + + if (print_name && !name.empty()) + os << ' ' << Gogo::message_name(name); +} + // Make a struct type. Struct_type* @@ -5703,6 +6369,9 @@ Array_type::is_identical(const Array_type* t, bool errors_are_identical) const errors_are_identical, NULL)) return false; + if (this->is_array_incomparable_ != t->is_array_incomparable_) + return false; + Expression* l1 = this->length(); Expression* l2 = t->length(); @@ -5769,7 +6438,7 @@ Array_type::verify_length() if (!this->length_->is_constant()) { - error_at(this->length_->location(), "array bound is not constant"); + go_error_at(this->length_->location(), "array bound is not constant"); return false; } @@ -5778,9 +6447,9 @@ Array_type::verify_length() { if (this->length_->type()->integer_type() != NULL || this->length_->type()->float_type() != NULL) - error_at(this->length_->location(), "array bound is not constant"); + go_error_at(this->length_->location(), "array bound is not constant"); else - error_at(this->length_->location(), "array bound is not numeric"); + go_error_at(this->length_->location(), "array bound is not numeric"); return false; } @@ -5792,15 +6461,15 @@ Array_type::verify_length() case Numeric_constant::NC_UL_VALID: if (sizeof(val) >= tbits / 8 && val >> (tbits - 1) != 0) { - error_at(this->length_->location(), "array bound overflows"); + go_error_at(this->length_->location(), "array bound overflows"); return false; } break; case Numeric_constant::NC_UL_NOTINT: - error_at(this->length_->location(), "array bound truncated to integer"); + go_error_at(this->length_->location(), "array bound truncated to integer"); return false; case Numeric_constant::NC_UL_NEGATIVE: - error_at(this->length_->location(), "negative array bound"); + go_error_at(this->length_->location(), "negative array bound"); return false; case Numeric_constant::NC_UL_BIG: { @@ -5811,7 +6480,7 @@ Array_type::verify_length() mpz_clear(val); if (bits >= tbits) { - error_at(this->length_->location(), "array bound overflows"); + go_error_at(this->length_->location(), "array bound overflows"); return false; } } @@ -5867,9 +6536,14 @@ Array_type::do_compare_is_identity(Gogo* gogo) unsigned int Array_type::do_hash_for_method(Gogo* gogo) const { + unsigned int ret; + // There is no very convenient way to get a hash code for the // length. - return this->element_type_->hash_for_method(gogo) + 1; + ret = this->element_type_->hash_for_method(gogo) + 1; + if (this->is_array_incomparable_) + ret <<= 1; + return ret; } // Write the hash function for an array which can not use the identify @@ -5888,18 +6562,20 @@ Array_type::write_hash_function(Gogo* gogo, Named_type* name, go_assert(key_arg != NULL); Type* key_arg_type = key_arg->var_value()->type(); - Type* uintptr_type = Type::lookup_integer_type("uintptr"); + // The seed argument to the hash function. + Named_object* seed_arg = gogo->lookup("seed", NULL); + go_assert(seed_arg != NULL); - // Get a 0. - Expression* zero = Expression::make_integer_ul(0, uintptr_type, bloc); + Type* uintptr_type = Type::lookup_integer_type("uintptr"); - // Make a temporary to hold the return value, initialized to 0. - Temporary_statement* retval = Statement::make_temporary(uintptr_type, zero, + // Make a temporary to hold the return value, initialized to the seed. + Expression* ref = Expression::make_var_reference(seed_arg, bloc); + Temporary_statement* retval = Statement::make_temporary(uintptr_type, ref, bloc); gogo->add_statement(retval); // Make a temporary to hold the key as a uintptr. - Expression* ref = Expression::make_var_reference(key_arg, bloc); + ref = Expression::make_var_reference(key_arg, bloc); ref = Expression::make_cast(uintptr_type, ref, bloc); Temporary_statement* key = Statement::make_temporary(uintptr_type, ref, bloc); @@ -5924,14 +6600,6 @@ Array_type::write_hash_function(Gogo* gogo, Named_type* name, gogo->start_block(bloc); - // Multiply retval by 33. - Expression* i33 = Expression::make_integer_ul(33, uintptr_type, bloc); - - ref = Expression::make_temporary_reference(retval, bloc); - Statement* s = Statement::make_assignment_operation(OPERATOR_MULTEQ, ref, - i33, bloc); - gogo->add_statement(s); - // Get the hash function for the element type. Named_object* hash_fn; Named_object* equal_fn; @@ -5947,18 +6615,19 @@ Array_type::write_hash_function(Gogo* gogo, Named_type* name, Expression* ele_size = Expression::make_type_info(this->element_type_, Expression::TYPE_INFO_SIZE); - // Get the hash of this element. + // Get the hash of this element, passing retval as the seed. + ref = Expression::make_temporary_reference(retval, bloc); Expression_list* args = new Expression_list(); args->push_back(subkey); - args->push_back(ele_size); + args->push_back(ref); Expression* func = Expression::make_func_reference(hash_fn, NULL, bloc); Expression* call = Expression::make_call(func, args, false, bloc); - // Add the element's hash value to retval. + // Set retval to the result. Temporary_reference_expression* tref = Expression::make_temporary_reference(retval, bloc); tref->set_is_lvalue(); - s = Statement::make_assignment_operation(OPERATOR_PLUSEQ, tref, call, bloc); + Statement* s = Statement::make_assignment(tref, call, bloc); gogo->add_statement(s); // Increase the element pointer. @@ -6340,7 +7009,7 @@ Array_type::array_type_descriptor(Gogo* gogo, Named_type* name) vals->reserve(3); Struct_field_list::const_iterator p = fields->begin(); - go_assert(p->is_field_name("commonType")); + go_assert(p->is_field_name("_type")); vals->push_back(this->type_descriptor_constructor(gogo, RUNTIME_TYPE_KIND_ARRAY, name, NULL, true)); @@ -6379,7 +7048,7 @@ Array_type::slice_type_descriptor(Gogo* gogo, Named_type* name) vals->reserve(2); Struct_field_list::const_iterator p = fields->begin(); - go_assert(p->is_field_name("commonType")); + go_assert(p->is_field_name("_type")); vals->push_back(this->type_descriptor_constructor(gogo, RUNTIME_TYPE_KIND_SLICE, name, NULL, true)); @@ -6563,6 +7232,8 @@ Array_type::do_mangled_name(Gogo* gogo, std::string* ret) const ret->append(s); free(s); mpz_clear(val); + if (this->is_array_incomparable_) + ret->push_back('x'); } ret->push_back('e'); } @@ -6577,6 +7248,103 @@ Type::make_array_type(Type* element_type, Expression* length) // Class Map_type. +Named_object* Map_type::zero_value; +int64_t Map_type::zero_value_size; +int64_t Map_type::zero_value_align; + +// If this map requires the "fat" functions, return the pointer to +// pass as the zero value to those functions. Otherwise, in the +// normal case, return NULL. The map requires the "fat" functions if +// the value size is larger than max_zero_size bytes. max_zero_size +// must match maxZero in libgo/go/runtime/hashmap.go. + +Expression* +Map_type::fat_zero_value(Gogo* gogo) +{ + int64_t valsize; + if (!this->val_type_->backend_type_size(gogo, &valsize)) + { + go_assert(saw_errors()); + return NULL; + } + if (valsize <= Map_type::max_zero_size) + return NULL; + + if (Map_type::zero_value_size < valsize) + Map_type::zero_value_size = valsize; + + int64_t valalign; + if (!this->val_type_->backend_type_align(gogo, &valalign)) + { + go_assert(saw_errors()); + return NULL; + } + + if (Map_type::zero_value_align < valalign) + Map_type::zero_value_align = valalign; + + Location bloc = Linemap::predeclared_location(); + + if (Map_type::zero_value == NULL) + { + // The final type will be set in backend_zero_value. + Type* uint8_type = Type::lookup_integer_type("uint8"); + Expression* size = Expression::make_integer_ul(0, NULL, bloc); + Array_type* array_type = Type::make_array_type(uint8_type, size); + array_type->set_is_array_incomparable(); + Variable* var = new Variable(array_type, NULL, true, false, false, bloc); + Map_type::zero_value = Named_object::make_variable("go$zerovalue", NULL, + var); + } + + Expression* z = Expression::make_var_reference(Map_type::zero_value, bloc); + z = Expression::make_unary(OPERATOR_AND, z, bloc); + Type* unsafe_ptr_type = Type::make_pointer_type(Type::make_void_type()); + z = Expression::make_cast(unsafe_ptr_type, z, bloc); + return z; +} + +// Return whether VAR is the map zero value. + +bool +Map_type::is_zero_value(Variable* var) +{ + return (Map_type::zero_value != NULL + && Map_type::zero_value->var_value() == var); +} + +// Return the backend representation for the zero value. + +Bvariable* +Map_type::backend_zero_value(Gogo* gogo) +{ + Location bloc = Linemap::predeclared_location(); + + go_assert(Map_type::zero_value != NULL); + + Type* uint8_type = Type::lookup_integer_type("uint8"); + Btype* buint8_type = uint8_type->get_backend(gogo); + + Type* int_type = Type::lookup_integer_type("int"); + + Expression* e = Expression::make_integer_int64(Map_type::zero_value_size, + int_type, bloc); + Translate_context context(gogo, NULL, NULL, NULL); + Bexpression* blength = e->get_backend(&context); + + Btype* barray_type = gogo->backend()->array_type(buint8_type, blength); + + std::string zname = Map_type::zero_value->name(); + std::string asm_name(go_selectively_encode_id(zname)); + Bvariable* zvar = + gogo->backend()->implicit_variable(zname, asm_name, + barray_type, false, true, true, + Map_type::zero_value_align); + gogo->backend()->implicit_variable_set_init(zvar, zname, barray_type, + false, true, true, NULL); + return zvar; +} + // Traversal. int @@ -6595,7 +7363,7 @@ Map_type::do_verify() { // The runtime support uses "map[void]void". if (!this->key_type_->is_comparable() && !this->key_type_->is_void_type()) - error_at(this->location_, "invalid map key type"); + go_error_at(this->location_, "invalid map key type"); return true; } @@ -6621,8 +7389,8 @@ Map_type::do_hash_for_method(Gogo* gogo) const } // Get the backend representation for a map type. A map type is -// represented as a pointer to a struct. The struct is __go_map in -// libgo/map.h. +// represented as a pointer to a struct. The struct is hmap in +// runtime/hashmap.go. Btype* Map_type::do_get_backend(Gogo* gogo) @@ -6630,33 +7398,55 @@ Map_type::do_get_backend(Gogo* gogo) static Btype* backend_map_type; if (backend_map_type == NULL) { - std::vector<Backend::Btyped_identifier> bfields(4); + std::vector<Backend::Btyped_identifier> bfields(9); Location bloc = Linemap::predeclared_location(); - Type* pdt = Type::make_type_descriptor_ptr_type(); - bfields[0].name = "__descriptor"; - bfields[0].btype = pdt->get_backend(gogo); + Type* int_type = Type::lookup_integer_type("int"); + bfields[0].name = "count"; + bfields[0].btype = int_type->get_backend(gogo); bfields[0].location = bloc; - Type* uintptr_type = Type::lookup_integer_type("uintptr"); - bfields[1].name = "__element_count"; - bfields[1].btype = uintptr_type->get_backend(gogo); + Type* uint8_type = Type::lookup_integer_type("uint8"); + bfields[1].name = "flags"; + bfields[1].btype = uint8_type->get_backend(gogo); bfields[1].location = bloc; - bfields[2].name = "__bucket_count"; + bfields[2].name = "B"; bfields[2].btype = bfields[1].btype; bfields[2].location = bloc; + Type* uint16_type = Type::lookup_integer_type("uint16"); + bfields[3].name = "noverflow"; + bfields[3].btype = uint16_type->get_backend(gogo); + bfields[3].location = bloc; + + Type* uint32_type = Type::lookup_integer_type("uint32"); + bfields[4].name = "hash0"; + bfields[4].btype = uint32_type->get_backend(gogo); + bfields[4].location = bloc; + Btype* bvt = gogo->backend()->void_type(); Btype* bpvt = gogo->backend()->pointer_type(bvt); - Btype* bppvt = gogo->backend()->pointer_type(bpvt); - bfields[3].name = "__buckets"; - bfields[3].btype = bppvt; - bfields[3].location = bloc; + bfields[5].name = "buckets"; + bfields[5].btype = bpvt; + bfields[5].location = bloc; + + bfields[6].name = "oldbuckets"; + bfields[6].btype = bpvt; + bfields[6].location = bloc; + + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + bfields[7].name = "nevacuate"; + bfields[7].btype = uintptr_type->get_backend(gogo); + bfields[7].location = bloc; + + bfields[8].name = "overflow"; + bfields[8].btype = bpvt; + bfields[8].location = bloc; Btype *bt = gogo->backend()->struct_type(bfields); - bt = gogo->backend()->named_type("__go_map", bt, bloc); + bt = gogo->backend()->named_type("runtime.hmap", bt, bloc); backend_map_type = gogo->backend()->pointer_type(bt); } return backend_map_type; @@ -6672,12 +7462,24 @@ Map_type::make_map_type_descriptor_type() { Type* tdt = Type::make_type_descriptor_type(); Type* ptdt = Type::make_type_descriptor_ptr_type(); + Type* uint8_type = Type::lookup_integer_type("uint8"); + Type* uint16_type = Type::lookup_integer_type("uint16"); + Type* bool_type = Type::lookup_bool_type(); Struct_type* sf = - Type::make_builtin_struct_type(3, + Type::make_builtin_struct_type(12, "", tdt, "key", ptdt, - "elem", ptdt); + "elem", ptdt, + "bucket", ptdt, + "hmap", ptdt, + "keysize", uint8_type, + "indirectkey", bool_type, + "valuesize", uint8_type, + "indirectvalue", bool_type, + "bucketsize", uint16_type, + "reflexivekey", bool_type, + "needkeyupdate", bool_type); ret = Type::make_builtin_named_type("MapType", sf); } @@ -6693,14 +7495,51 @@ Map_type::do_type_descriptor(Gogo* gogo, Named_type* name) Location bloc = Linemap::predeclared_location(); Type* mtdt = Map_type::make_map_type_descriptor_type(); + Type* uint8_type = Type::lookup_integer_type("uint8"); + Type* uint16_type = Type::lookup_integer_type("uint16"); + + int64_t keysize; + if (!this->key_type_->backend_type_size(gogo, &keysize)) + { + go_error_at(this->location_, "error determining map key type size"); + return Expression::make_error(this->location_); + } + + int64_t valsize; + if (!this->val_type_->backend_type_size(gogo, &valsize)) + { + go_error_at(this->location_, "error determining map value type size"); + return Expression::make_error(this->location_); + } + + int64_t ptrsize; + if (!Type::make_pointer_type(uint8_type)->backend_type_size(gogo, &ptrsize)) + { + go_assert(saw_errors()); + return Expression::make_error(this->location_); + } + + Type* bucket_type = this->bucket_type(gogo, keysize, valsize); + if (bucket_type == NULL) + { + go_assert(saw_errors()); + return Expression::make_error(this->location_); + } + + int64_t bucketsize; + if (!bucket_type->backend_type_size(gogo, &bucketsize)) + { + go_assert(saw_errors()); + return Expression::make_error(this->location_); + } const Struct_field_list* fields = mtdt->struct_type()->fields(); Expression_list* vals = new Expression_list(); - vals->reserve(3); + vals->reserve(12); Struct_field_list::const_iterator p = fields->begin(); - go_assert(p->is_field_name("commonType")); + go_assert(p->is_field_name("_type")); vals->push_back(this->type_descriptor_constructor(gogo, RUNTIME_TYPE_KIND_MAP, name, NULL, true)); @@ -6714,130 +7553,271 @@ Map_type::do_type_descriptor(Gogo* gogo, Named_type* name) vals->push_back(Expression::make_type_descriptor(this->val_type_, bloc)); ++p; - go_assert(p == fields->end()); - - return Expression::make_struct_composite_literal(mtdt, vals, bloc); -} - -// A mapping from map types to map descriptors. - -Map_type::Map_descriptors Map_type::map_descriptors; + go_assert(p->is_field_name("bucket")); + vals->push_back(Expression::make_type_descriptor(bucket_type, bloc)); -// Build a map descriptor for this type. Return a pointer to it. - -Bexpression* -Map_type::map_descriptor_pointer(Gogo* gogo, Location location) -{ - Bvariable* bvar = this->map_descriptor(gogo); - Bexpression* var_expr = gogo->backend()->var_expression(bvar, location); - return gogo->backend()->address_expression(var_expr, location); -} + ++p; + go_assert(p->is_field_name("hmap")); + Type* hmap_type = this->hmap_type(bucket_type); + vals->push_back(Expression::make_type_descriptor(hmap_type, bloc)); -// Build a map descriptor for this type. + ++p; + go_assert(p->is_field_name("keysize")); + if (keysize > Map_type::max_key_size) + vals->push_back(Expression::make_integer_int64(ptrsize, uint8_type, bloc)); + else + vals->push_back(Expression::make_integer_int64(keysize, uint8_type, bloc)); -Bvariable* -Map_type::map_descriptor(Gogo* gogo) -{ - std::pair<Map_type*, Bvariable*> val(this, NULL); - std::pair<Map_type::Map_descriptors::iterator, bool> ins = - Map_type::map_descriptors.insert(val); - if (!ins.second) - return ins.first->second; + ++p; + go_assert(p->is_field_name("indirectkey")); + vals->push_back(Expression::make_boolean(keysize > Map_type::max_key_size, + bloc)); - Type* key_type = this->key_type_; - Type* val_type = this->val_type_; + ++p; + go_assert(p->is_field_name("valuesize")); + if (valsize > Map_type::max_val_size) + vals->push_back(Expression::make_integer_int64(ptrsize, uint8_type, bloc)); + else + vals->push_back(Expression::make_integer_int64(valsize, uint8_type, bloc)); - // The map entry type is a struct with three fields. Build that - // struct so that we can get the offsets of the key and value within - // a map entry. The first field should technically be a pointer to - // this type itself, but since we only care about field offsets we - // just use pointer to bool. - Type* pbool = Type::make_pointer_type(Type::make_boolean_type()); - Struct_type* map_entry_type = - Type::make_builtin_struct_type(3, - "__next", pbool, - "__key", key_type, - "__val", val_type); + ++p; + go_assert(p->is_field_name("indirectvalue")); + vals->push_back(Expression::make_boolean(valsize > Map_type::max_val_size, + bloc)); - Type* map_descriptor_type = Map_type::make_map_descriptor_type(); + ++p; + go_assert(p->is_field_name("bucketsize")); + vals->push_back(Expression::make_integer_int64(bucketsize, uint16_type, + bloc)); - const Struct_field_list* fields = - map_descriptor_type->struct_type()->fields(); + ++p; + go_assert(p->is_field_name("reflexivekey")); + vals->push_back(Expression::make_boolean(this->key_type_->is_reflexive(), + bloc)); - Expression_list* vals = new Expression_list(); - vals->reserve(4); + ++p; + go_assert(p->is_field_name("needkeyupdate")); + vals->push_back(Expression::make_boolean(this->key_type_->needs_key_update(), + bloc)); - Location bloc = Linemap::predeclared_location(); + ++p; + go_assert(p == fields->end()); - Struct_field_list::const_iterator p = fields->begin(); + return Expression::make_struct_composite_literal(mtdt, vals, bloc); +} - go_assert(p->is_field_name("__map_descriptor")); - vals->push_back(Expression::make_type_descriptor(this, bloc)); +// Return the bucket type to use for a map type. This must correspond +// to libgo/go/runtime/hashmap.go. - ++p; - go_assert(p->is_field_name("__entry_size")); - Expression::Type_info type_info = Expression::TYPE_INFO_SIZE; - vals->push_back(Expression::make_type_info(map_entry_type, type_info)); +Type* +Map_type::bucket_type(Gogo* gogo, int64_t keysize, int64_t valsize) +{ + if (this->bucket_type_ != NULL) + return this->bucket_type_; - Struct_field_list::const_iterator pf = map_entry_type->fields()->begin(); - ++pf; - go_assert(pf->is_field_name("__key")); + Type* key_type = this->key_type_; + if (keysize > Map_type::max_key_size) + key_type = Type::make_pointer_type(key_type); - ++p; - go_assert(p->is_field_name("__key_offset")); - vals->push_back(Expression::make_struct_field_offset(map_entry_type, &*pf)); + Type* val_type = this->val_type_; + if (valsize > Map_type::max_val_size) + val_type = Type::make_pointer_type(val_type); + + Expression* bucket_size = Expression::make_integer_ul(Map_type::bucket_size, + NULL, this->location_); + + Type* uint8_type = Type::lookup_integer_type("uint8"); + Array_type* topbits_type = Type::make_array_type(uint8_type, bucket_size); + topbits_type->set_is_array_incomparable(); + Array_type* keys_type = Type::make_array_type(key_type, bucket_size); + keys_type->set_is_array_incomparable(); + Array_type* values_type = Type::make_array_type(val_type, bucket_size); + values_type->set_is_array_incomparable(); + + // If keys and values have no pointers, the map implementation can + // keep a list of overflow pointers on the side so that buckets can + // be marked as having no pointers. Arrange for the bucket to have + // no pointers by changing the type of the overflow field to uintptr + // in this case. See comment on the hmap.overflow field in + // libgo/go/runtime/hashmap.go. + Type* overflow_type; + if (!key_type->has_pointer() && !val_type->has_pointer()) + overflow_type = Type::lookup_integer_type("uintptr"); + else + { + // This should really be a pointer to the bucket type itself, + // but that would require us to construct a Named_type for it to + // give it a way to refer to itself. Since nothing really cares + // (except perhaps for someone using a debugger) just use an + // unsafe pointer. + overflow_type = Type::make_pointer_type(Type::make_void_type()); + } + + // Make sure the overflow pointer is the last memory in the struct, + // because the runtime assumes it can use size-ptrSize as the offset + // of the overflow pointer. We double-check that property below + // once the offsets and size are computed. + + int64_t topbits_field_size, topbits_field_align; + int64_t keys_field_size, keys_field_align; + int64_t values_field_size, values_field_align; + int64_t overflow_field_size, overflow_field_align; + if (!topbits_type->backend_type_size(gogo, &topbits_field_size) + || !topbits_type->backend_type_field_align(gogo, &topbits_field_align) + || !keys_type->backend_type_size(gogo, &keys_field_size) + || !keys_type->backend_type_field_align(gogo, &keys_field_align) + || !values_type->backend_type_size(gogo, &values_field_size) + || !values_type->backend_type_field_align(gogo, &values_field_align) + || !overflow_type->backend_type_size(gogo, &overflow_field_size) + || !overflow_type->backend_type_field_align(gogo, &overflow_field_align)) + { + go_assert(saw_errors()); + return NULL; + } - ++pf; - go_assert(pf->is_field_name("__val")); + Struct_type* ret; + int64_t max_align = std::max(std::max(topbits_field_align, keys_field_align), + values_field_align); + if (max_align <= overflow_field_align) + ret = make_builtin_struct_type(4, + "topbits", topbits_type, + "keys", keys_type, + "values", values_type, + "overflow", overflow_type); + else + { + size_t off = topbits_field_size; + off = ((off + keys_field_align - 1) + &~ static_cast<size_t>(keys_field_align - 1)); + off += keys_field_size; + off = ((off + values_field_align - 1) + &~ static_cast<size_t>(values_field_align - 1)); + off += values_field_size; + + int64_t padded_overflow_field_size = + ((overflow_field_size + max_align - 1) + &~ static_cast<size_t>(max_align - 1)); + + size_t ovoff = off; + ovoff = ((ovoff + max_align - 1) + &~ static_cast<size_t>(max_align - 1)); + size_t pad = (ovoff - off + + padded_overflow_field_size - overflow_field_size); + + Expression* pad_expr = Expression::make_integer_ul(pad, NULL, + this->location_); + Array_type* pad_type = Type::make_array_type(uint8_type, pad_expr); + pad_type->set_is_array_incomparable(); + + ret = make_builtin_struct_type(5, + "topbits", topbits_type, + "keys", keys_type, + "values", values_type, + "pad", pad_type, + "overflow", overflow_type); + } + + // Verify that the overflow field is just before the end of the + // bucket type. + + Btype* btype = ret->get_backend(gogo); + int64_t offset = gogo->backend()->type_field_offset(btype, + ret->field_count() - 1); + int64_t size; + if (!ret->backend_type_size(gogo, &size)) + { + go_assert(saw_errors()); + return NULL; + } - ++p; - go_assert(p->is_field_name("__val_offset")); - vals->push_back(Expression::make_struct_field_offset(map_entry_type, &*pf)); + int64_t ptr_size; + if (!Type::make_pointer_type(uint8_type)->backend_type_size(gogo, &ptr_size)) + { + go_assert(saw_errors()); + return NULL; + } - ++p; - go_assert(p == fields->end()); + go_assert(offset + ptr_size == size); - Expression* initializer = - Expression::make_struct_composite_literal(map_descriptor_type, vals, bloc); + ret->set_is_struct_incomparable(); - std::string mangled_name = "__go_map_" + this->mangled_name(gogo); - Btype* map_descriptor_btype = map_descriptor_type->get_backend(gogo); - Bvariable* bvar = gogo->backend()->immutable_struct(mangled_name, false, - true, - map_descriptor_btype, - bloc); + this->bucket_type_ = ret; + return ret; +} - Translate_context context(gogo, NULL, NULL, NULL); - context.set_is_const(); - Bexpression* binitializer = initializer->get_backend(&context); +// Return the hashmap type for a map type. - gogo->backend()->immutable_struct_set_init(bvar, mangled_name, false, true, - map_descriptor_btype, bloc, - binitializer); +Type* +Map_type::hmap_type(Type* bucket_type) +{ + if (this->hmap_type_ != NULL) + return this->hmap_type_; - ins.first->second = bvar; - return bvar; + Type* int_type = Type::lookup_integer_type("int"); + Type* uint8_type = Type::lookup_integer_type("uint8"); + Type* uint32_type = Type::lookup_integer_type("uint32"); + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + Type* void_ptr_type = Type::make_pointer_type(Type::make_void_type()); + + Type* ptr_bucket_type = Type::make_pointer_type(bucket_type); + + Struct_type* ret = make_builtin_struct_type(8, + "count", int_type, + "flags", uint8_type, + "B", uint8_type, + "hash0", uint32_type, + "buckets", ptr_bucket_type, + "oldbuckets", ptr_bucket_type, + "nevacuate", uintptr_type, + "overflow", void_ptr_type); + ret->set_is_struct_incomparable(); + this->hmap_type_ = ret; + return ret; } -// Build the type of a map descriptor. This must match the struct -// __go_map_descriptor in libgo/runtime/map.h. +// Return the iterator type for a map type. This is the type of the +// value used when doing a range over a map. Type* -Map_type::make_map_descriptor_type() +Map_type::hiter_type(Gogo* gogo) { - static Type* ret; - if (ret == NULL) + if (this->hiter_type_ != NULL) + return this->hiter_type_; + + int64_t keysize, valsize; + if (!this->key_type_->backend_type_size(gogo, &keysize) + || !this->val_type_->backend_type_size(gogo, &valsize)) { - Type* ptdt = Type::make_type_descriptor_ptr_type(); - Type* uintptr_type = Type::lookup_integer_type("uintptr"); - Struct_type* sf = - Type::make_builtin_struct_type(4, - "__map_descriptor", ptdt, - "__entry_size", uintptr_type, - "__key_offset", uintptr_type, - "__val_offset", uintptr_type); - ret = Type::make_builtin_named_type("__go_map_descriptor", sf); + go_assert(saw_errors()); + return NULL; } + + Type* key_ptr_type = Type::make_pointer_type(this->key_type_); + Type* val_ptr_type = Type::make_pointer_type(this->val_type_); + Type* uint8_type = Type::lookup_integer_type("uint8"); + Type* uint8_ptr_type = Type::make_pointer_type(uint8_type); + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + Type* bucket_type = this->bucket_type(gogo, keysize, valsize); + Type* bucket_ptr_type = Type::make_pointer_type(bucket_type); + Type* hmap_type = this->hmap_type(bucket_type); + Type* hmap_ptr_type = Type::make_pointer_type(hmap_type); + Type* void_ptr_type = Type::make_pointer_type(Type::make_void_type()); + + Struct_type* ret = make_builtin_struct_type(12, + "key", key_ptr_type, + "val", val_ptr_type, + "t", uint8_ptr_type, + "h", hmap_ptr_type, + "buckets", bucket_ptr_type, + "bptr", bucket_ptr_type, + "overflow0", void_ptr_type, + "overflow1", void_ptr_type, + "startBucket", uintptr_type, + "stuff", uintptr_type, + "bucket", uintptr_type, + "checkBucket", uintptr_type); + ret->set_is_struct_incomparable(); + this->hiter_type_ = ret; return ret; } @@ -6998,7 +7978,7 @@ Channel_type::do_type_descriptor(Gogo* gogo, Named_type* name) vals->reserve(3); Struct_field_list::const_iterator p = fields->begin(); - go_assert(p->is_field_name("commonType")); + go_assert(p->is_field_name("_type")); vals->push_back(this->type_descriptor_constructor(gogo, RUNTIME_TYPE_KIND_CHAN, name, NULL, true)); @@ -7118,6 +8098,53 @@ Channel_type::do_import(Import* imp) return Type::make_channel_type(may_send, may_receive, element_type); } +// Return the type to manage a select statement with ncases case +// statements. A value of this type is allocated on the stack. This +// must match the type hselect in libgo/go/runtime/select.go. + +Type* +Channel_type::select_type(int ncases) +{ + Type* unsafe_pointer_type = Type::make_pointer_type(Type::make_void_type()); + Type* uint16_type = Type::lookup_integer_type("uint16"); + + static Struct_type* scase_type; + if (scase_type == NULL) + { + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + Type* uint64_type = Type::lookup_integer_type("uint64"); + scase_type = + Type::make_builtin_struct_type(7, + "elem", unsafe_pointer_type, + "chan", unsafe_pointer_type, + "pc", uintptr_type, + "kind", uint16_type, + "index", uint16_type, + "receivedp", unsafe_pointer_type, + "releasetime", uint64_type); + scase_type->set_is_struct_incomparable(); + } + + Expression* ncases_expr = + Expression::make_integer_ul(ncases, NULL, Linemap::predeclared_location()); + Array_type* scases = Type::make_array_type(scase_type, ncases_expr); + scases->set_is_array_incomparable(); + Array_type* order = Type::make_array_type(uint16_type, ncases_expr); + order->set_is_array_incomparable(); + + Struct_type* ret = + Type::make_builtin_struct_type(7, + "tcase", uint16_type, + "ncase", uint16_type, + "pollorder", unsafe_pointer_type, + "lockorder", unsafe_pointer_type, + "scase", scases, + "lockorderarr", order, + "pollorderarr", order); + ret->set_is_struct_incomparable(); + return ret; +} + // Make a new channel type. Channel_type* @@ -7184,7 +8211,7 @@ Interface_type::finalize_methods() else if (this->find_method(p->name()) == NULL) this->all_methods_->push_back(*p); else - error_at(p->location(), "duplicate method %qs", + go_error_at(p->location(), "duplicate method %qs", Gogo::message_name(p->name()).c_str()); } @@ -7201,14 +8228,14 @@ Interface_type::finalize_methods() if (it == NULL) { if (!t->is_error()) - error_at(tl, "interface contains embedded non-interface"); + go_error_at(tl, "interface contains embedded non-interface"); continue; } if (it == this) { if (!issued_recursive_error) { - error_at(tl, "invalid recursive interface"); + go_error_at(tl, "invalid recursive interface"); issued_recursive_error = true; } continue; @@ -7222,7 +8249,7 @@ Interface_type::finalize_methods() { if (*q == nt) { - error_at(tl, "inherited interface loop"); + go_error_at(tl, "inherited interface loop"); break; } } @@ -7244,7 +8271,7 @@ Interface_type::finalize_methods() this->all_methods_->push_back(Typed_identifier(q->name(), q->type(), tl)); else - error_at(tl, "inherited method %qs is ambiguous", + go_error_at(tl, "inherited method %qs is ambiguous", Gogo::message_name(q->name()).c_str()); } } @@ -7394,8 +8421,8 @@ Interface_type::is_compatible_for_assign(const Interface_type* t, char buf[200]; snprintf(buf, sizeof buf, _("need explicit conversion; missing method %s%s%s"), - open_quote, Gogo::message_name(p->name()).c_str(), - close_quote); + go_open_quote(), Gogo::message_name(p->name()).c_str(), + go_close_quote()); reason->assign(buf); } return false; @@ -7411,11 +8438,11 @@ Interface_type::is_compatible_for_assign(const Interface_type* t, char* buf = new char[len]; if (subreason.empty()) snprintf(buf, len, _("incompatible type for method %s%s%s"), - open_quote, n.c_str(), close_quote); + go_open_quote(), n.c_str(), go_close_quote()); else snprintf(buf, len, _("incompatible type for method %s%s%s (%s)"), - open_quote, n.c_str(), close_quote, + go_open_quote(), n.c_str(), go_close_quote(), subreason.c_str()); reason->assign(buf); delete[] buf; @@ -7530,10 +8557,10 @@ Interface_type::implements_interface(const Type* t, std::string* reason) const char* buf = new char[len]; if (is_ambiguous) snprintf(buf, len, _("ambiguous method %s%s%s"), - open_quote, n.c_str(), close_quote); + go_open_quote(), n.c_str(), go_close_quote()); else snprintf(buf, len, _("missing method %s%s%s"), - open_quote, n.c_str(), close_quote); + go_open_quote(), n.c_str(), go_close_quote()); reason->assign(buf); delete[] buf; } @@ -7553,11 +8580,11 @@ Interface_type::implements_interface(const Type* t, std::string* reason) const char* buf = new char[len]; if (subreason.empty()) snprintf(buf, len, _("incompatible type for method %s%s%s"), - open_quote, n.c_str(), close_quote); + go_open_quote(), n.c_str(), go_close_quote()); else snprintf(buf, len, _("incompatible type for method %s%s%s (%s)"), - open_quote, n.c_str(), close_quote, + go_open_quote(), n.c_str(), go_close_quote(), subreason.c_str()); reason->assign(buf); delete[] buf; @@ -7574,7 +8601,7 @@ Interface_type::implements_interface(const Type* t, std::string* reason) const char* buf = new char[len]; snprintf(buf, len, _("method %s%s%s requires a pointer receiver"), - open_quote, n.c_str(), close_quote); + go_open_quote(), n.c_str(), go_close_quote()); reason->assign(buf); delete[] buf; } @@ -7592,7 +8619,7 @@ Interface_type::implements_interface(const Type* t, std::string* reason) const char* buf = new char[len]; snprintf(buf, len, _("method %s%s%s is marked go:nointerface"), - open_quote, n.c_str(), close_quote); + go_open_quote(), n.c_str(), go_close_quote()); reason->assign(buf); delete[] buf; } @@ -7840,7 +8867,7 @@ Interface_type::do_type_descriptor(Gogo* gogo, Named_type* name) ivals->reserve(2); Struct_field_list::const_iterator pif = ifields->begin(); - go_assert(pif->is_field_name("commonType")); + go_assert(pif->is_field_name("_type")); const int rt = RUNTIME_TYPE_KIND_INTERFACE; ivals->push_back(this->type_descriptor_constructor(gogo, rt, name, NULL, true)); @@ -7982,17 +9009,7 @@ Interface_type::do_mangled_name(Gogo* gogo, std::string* ret) const { if (!p->name().empty()) { - std::string n; - if (!Gogo::is_hidden_name(p->name())) - n = p->name(); - else - { - n = "."; - std::string pkgpath = Gogo::hidden_name_pkgpath(p->name()); - n.append(Gogo::pkgpath_for_symbol(pkgpath)); - n.append(1, '.'); - n.append(Gogo::unpack_hidden_name(p->name())); - } + std::string n(Gogo::mangle_possibly_hidden_name(p->name())); char buf[20]; snprintf(buf, sizeof buf, "%u_", static_cast<unsigned int>(n.length())); @@ -8375,18 +9392,6 @@ Named_type::message_name() const return this->named_object_->message_name(); } -// Whether this is an alias. There are currently only two aliases so -// we just recognize them by name. - -bool -Named_type::is_alias() const -{ - if (!this->is_builtin()) - return false; - const std::string& name(this->name()); - return name == "byte" || name == "rune"; -} - // Return the base type for this type. We have to be careful about // circular type definitions, which are invalid but may be seen here. @@ -8446,6 +9451,7 @@ Named_type::named_type_is_comparable(std::string* reason) const Named_object* Named_type::add_method(const std::string& name, Function* function) { + go_assert(!this->is_alias_); if (this->local_methods_ == NULL) this->local_methods_ = new Bindings(NULL); return this->local_methods_->add_function(name, NULL, function); @@ -8458,6 +9464,7 @@ Named_type::add_method_declaration(const std::string& name, Package* package, Function_type* type, Location location) { + go_assert(!this->is_alias_); if (this->local_methods_ == NULL) this->local_methods_ = new Bindings(NULL); return this->local_methods_->add_function_declaration(name, package, type, @@ -8469,6 +9476,7 @@ Named_type::add_method_declaration(const std::string& name, Package* package, void Named_type::add_existing_method(Named_object* no) { + go_assert(!this->is_alias_); if (this->local_methods_ == NULL) this->local_methods_ = new Bindings(NULL); this->local_methods_->add_named_object(no); @@ -8480,11 +9488,51 @@ Named_type::add_existing_method(Named_object* no) Named_object* Named_type::find_local_method(const std::string& name) const { + if (this->is_error_) + return NULL; + if (this->is_alias_) + { + Named_type* nt = this->type_->named_type(); + if (nt != NULL) + { + if (this->seen_alias_) + return NULL; + this->seen_alias_ = true; + Named_object* ret = nt->find_local_method(name); + this->seen_alias_ = false; + return ret; + } + return NULL; + } if (this->local_methods_ == NULL) return NULL; return this->local_methods_->lookup(name); } +// Return the list of local methods. + +const Bindings* +Named_type::local_methods() const +{ + if (this->is_error_) + return NULL; + if (this->is_alias_) + { + Named_type* nt = this->type_->named_type(); + if (nt != NULL) + { + if (this->seen_alias_) + return NULL; + this->seen_alias_ = true; + const Bindings* ret = nt->local_methods(); + this->seen_alias_ = false; + return ret; + } + return NULL; + } + return this->local_methods_; +} + // Return whether NAME is an unexported field or method, for better // error reporting. @@ -8492,6 +9540,22 @@ bool Named_type::is_unexported_local_method(Gogo* gogo, const std::string& name) const { + if (this->is_error_) + return false; + if (this->is_alias_) + { + Named_type* nt = this->type_->named_type(); + if (nt != NULL) + { + if (this->seen_alias_) + return false; + this->seen_alias_ = true; + bool ret = nt->is_unexported_local_method(gogo, name); + this->seen_alias_ = false; + return ret; + } + return false; + } Bindings* methods = this->local_methods_; if (methods != NULL) { @@ -8516,6 +9580,8 @@ Named_type::is_unexported_local_method(Gogo* gogo, void Named_type::finalize_methods(Gogo* gogo) { + if (this->is_alias_) + return; if (this->all_methods_ != NULL) return; @@ -8526,8 +9592,8 @@ Named_type::finalize_methods(Gogo* gogo) for (Bindings::const_declarations_iterator p = lm->begin_declarations(); p != lm->end_declarations(); ++p) - error_at(p->second->location(), - "invalid pointer or interface receiver type"); + go_error_at(p->second->location(), + "invalid pointer or interface receiver type"); delete this->local_methods_; this->local_methods_ = NULL; return; @@ -8536,6 +9602,56 @@ Named_type::finalize_methods(Gogo* gogo) Type::finalize_methods(gogo, this, this->location_, &this->all_methods_); } +// Return whether this type has any methods. + +bool +Named_type::has_any_methods() const +{ + if (this->is_error_) + return false; + if (this->is_alias_) + { + if (this->type_->named_type() != NULL) + { + if (this->seen_alias_) + return false; + this->seen_alias_ = true; + bool ret = this->type_->named_type()->has_any_methods(); + this->seen_alias_ = false; + return ret; + } + if (this->type_->struct_type() != NULL) + return this->type_->struct_type()->has_any_methods(); + return false; + } + return this->all_methods_ != NULL; +} + +// Return the methods for this type. + +const Methods* +Named_type::methods() const +{ + if (this->is_error_) + return NULL; + if (this->is_alias_) + { + if (this->type_->named_type() != NULL) + { + if (this->seen_alias_) + return NULL; + this->seen_alias_ = true; + const Methods* ret = this->type_->named_type()->methods(); + this->seen_alias_ = false; + return ret; + } + if (this->type_->struct_type() != NULL) + return this->type_->struct_type()->methods(); + return NULL; + } + return this->all_methods_; +} + // Return the method NAME, or NULL if there isn't one or if it is // ambiguous. Set *IS_AMBIGUOUS if the method exists but is // ambiguous. @@ -8543,6 +9659,26 @@ Named_type::finalize_methods(Gogo* gogo) Method* Named_type::method_function(const std::string& name, bool* is_ambiguous) const { + if (this->is_error_) + return NULL; + if (this->is_alias_) + { + if (is_ambiguous != NULL) + *is_ambiguous = false; + if (this->type_->named_type() != NULL) + { + if (this->seen_alias_) + return NULL; + this->seen_alias_ = true; + Named_type* nt = this->type_->named_type(); + Method* ret = nt->method_function(name, is_ambiguous); + this->seen_alias_ = false; + return ret; + } + if (this->type_->struct_type() != NULL) + return this->type_->struct_type()->method_function(name, is_ambiguous); + return NULL; + } return Type::method_function(this->all_methods_, name, is_ambiguous); } @@ -8553,6 +9689,25 @@ Named_type::method_function(const std::string& name, bool* is_ambiguous) const Expression* Named_type::interface_method_table(Interface_type* interface, bool is_pointer) { + if (this->is_error_) + return Expression::make_error(this->location_); + if (this->is_alias_) + { + if (this->type_->named_type() != NULL) + { + if (this->seen_alias_) + return Expression::make_error(this->location_); + this->seen_alias_ = true; + Named_type* nt = this->type_->named_type(); + Expression* ret = nt->interface_method_table(interface, is_pointer); + this->seen_alias_ = false; + return ret; + } + if (this->type_->struct_type() != NULL) + return this->type_->struct_type()->interface_method_table(interface, + is_pointer); + go_unreachable(); + } return Type::interface_method_table(this, interface, is_pointer, &this->interface_method_tables_, &this->pointer_interface_method_tables_); @@ -8671,6 +9826,55 @@ Find_type_use::type(Type* type) return TRAVERSE_CONTINUE; } +// Look for a circular reference of an alias. + +class Find_alias : public Traverse +{ + public: + Find_alias(Named_type* find_type) + : Traverse(traverse_types), + find_type_(find_type), found_(false) + { } + + // Whether we found the type. + bool + found() const + { return this->found_; } + + protected: + int + type(Type*); + + private: + // The type we are looking for. + Named_type* find_type_; + // Whether we found the type. + bool found_; +}; + +int +Find_alias::type(Type* type) +{ + Named_type* nt = type->named_type(); + if (nt != NULL) + { + if (nt == this->find_type_) + { + this->found_ = true; + return TRAVERSE_EXIT; + } + + // We started from `type T1 = T2`, where T1 is find_type_ and T2 + // is, perhaps indirectly, the parameter TYPE. If TYPE is not + // an alias itself, it's OK if whatever T2 is defined as refers + // to T1. + if (!nt->is_alias()) + return TRAVERSE_SKIP_COMPONENTS; + } + + return TRAVERSE_CONTINUE; +} + // Verify that a named type does not refer to itself. bool @@ -8680,12 +9884,28 @@ Named_type::do_verify() return true; this->is_verified_ = true; + if (this->is_error_) + return false; + + if (this->is_alias_) + { + Find_alias find(this); + Type::traverse(this->type_, &find); + if (find.found()) + { + go_error_at(this->location_, "invalid recursive alias %qs", + this->message_name().c_str()); + this->is_error_ = true; + return false; + } + } + Find_type_use find(this); Type::traverse(this->type_, &find); if (find.found()) { - error_at(this->location_, "invalid recursive type %qs", - this->message_name().c_str()); + go_error_at(this->location_, "invalid recursive type %qs", + this->message_name().c_str()); this->is_error_ = true; return false; } @@ -8707,9 +9927,9 @@ Named_type::do_verify() const std::string& name(p->first); if (st != NULL && st->find_local_field(name, NULL) != NULL) { - error_at(p->second->location(), - "method %qs redeclares struct field name", - Gogo::message_name(name).c_str()); + go_error_at(p->second->location(), + "method %qs redeclares struct field name", + Gogo::message_name(name).c_str()); } } } @@ -8747,14 +9967,44 @@ Named_type::do_compare_is_identity(Gogo* gogo) return ret; } +// Return whether this type is reflexive--whether it is always equal +// to itself. + +bool +Named_type::do_is_reflexive() +{ + if (this->seen_in_compare_is_identity_) + return false; + this->seen_in_compare_is_identity_ = true; + bool ret = this->type_->is_reflexive(); + this->seen_in_compare_is_identity_ = false; + return ret; +} + +// Return whether this type needs a key update when used as a map key. + +bool +Named_type::do_needs_key_update() +{ + if (this->seen_in_compare_is_identity_) + return true; + this->seen_in_compare_is_identity_ = true; + bool ret = this->type_->needs_key_update(); + this->seen_in_compare_is_identity_ = false; + return ret; +} + // Return a hash code. This is used for method lookup. We simply // hash on the name itself. unsigned int Named_type::do_hash_for_method(Gogo* gogo) const { - if (this->is_alias()) - return this->type_->named_type()->do_hash_for_method(gogo); + if (this->is_error_) + return 0; + + // Aliases are handled in Type::hash_for_method. + go_assert(!this->is_alias_); const std::string& name(this->named_object()->name()); unsigned int ret = Type::hash_string(name, 0); @@ -9124,8 +10374,17 @@ Named_type::do_get_backend(Gogo* gogo) Expression* Named_type::do_type_descriptor(Gogo* gogo, Named_type* name) { - if (name == NULL && this->is_alias()) - return this->type_->type_descriptor(gogo, this->type_); + if (this->is_error_) + return Expression::make_error(this->location_); + if (name == NULL && this->is_alias_) + { + if (this->seen_alias_) + return Expression::make_error(this->location_); + this->seen_alias_ = true; + Expression* ret = this->type_->type_descriptor(gogo, NULL); + this->seen_alias_ = false; + return ret; + } // If NAME is not NULL, then we don't really want the type // descriptor for this type; we want the descriptor for the @@ -9141,9 +10400,25 @@ Named_type::do_type_descriptor(Gogo* gogo, Named_type* name) void Named_type::do_reflection(Gogo* gogo, std::string* ret) const { - if (this->is_alias()) + this->append_reflection_type_name(gogo, false, ret); +} + +// Add to the reflection string. For an alias we normally use the +// real name, but if USE_ALIAS is true we use the alias name itself. + +void +Named_type::append_reflection_type_name(Gogo* gogo, bool use_alias, + std::string* ret) const +{ + if (this->is_error_) + return; + if (this->is_alias_ && !use_alias) { + if (this->seen_alias_) + return; + this->seen_alias_ = true; this->append_reflection(this->type_, gogo, ret); + this->seen_alias_ = false; return; } if (!this->is_builtin()) @@ -9208,9 +10483,25 @@ Named_type::do_gc_symbol(Gogo* gogo, Expression_list** vals, void Named_type::do_mangled_name(Gogo* gogo, std::string* ret) const { - if (this->is_alias()) + this->append_mangled_type_name(gogo, false, ret); +} + +// Get the mangled name. For an alias we normally get the real name, +// but if USE_ALIAS is true we use the alias name itself. + +void +Named_type::append_mangled_type_name(Gogo* gogo, bool use_alias, + std::string* ret) const +{ + if (this->is_error_) + return; + if (this->is_alias_ && !use_alias) { + if (this->seen_alias_) + return; + this->seen_alias_ = true; this->append_mangled_name(this->type_, gogo, ret); + this->seen_alias_ = false; return; } Named_object* no = this->named_object_; @@ -9869,9 +11160,9 @@ Type::bind_field_or_method(Gogo* gogo, const Type* type, Expression* expr, go_assert(m != NULL); if (dereferenced) { - error_at(location, - "calling method %qs requires explicit dereference", - Gogo::message_name(name).c_str()); + go_error_at(location, + "calling method %qs requires explicit dereference", + Gogo::message_name(name).c_str()); return Expression::make_error(location); } if (!m->is_value_method() && expr->type()->points_to() == NULL) @@ -9888,16 +11179,16 @@ Type::bind_field_or_method(Gogo* gogo, const Type* type, Expression* expr, // An error was already reported. } else if (!ambig1.empty()) - error_at(location, "%qs is ambiguous via %qs and %qs", - Gogo::message_name(name).c_str(), ambig1.c_str(), - ambig2.c_str()); + go_error_at(location, "%qs is ambiguous via %qs and %qs", + Gogo::message_name(name).c_str(), ambig1.c_str(), + ambig2.c_str()); else if (found_pointer_method) - error_at(location, "method requires a pointer receiver"); + go_error_at(location, "method requires a pointer receiver"); else if (nt == NULL && st == NULL && it == NULL) - error_at(location, - ("reference to field %qs in object which " - "has no fields or methods"), - Gogo::message_name(name).c_str()); + go_error_at(location, + ("reference to field %qs in object which " + "has no fields or methods"), + Gogo::message_name(name).c_str()); else { bool is_unexported; @@ -9914,11 +11205,11 @@ Type::bind_field_or_method(Gogo* gogo, const Type* type, Expression* expr, &seen); } if (is_unexported) - error_at(location, "reference to unexported field or method %qs", - Gogo::message_name(name).c_str()); + go_error_at(location, "reference to unexported field or method %qs", + Gogo::message_name(name).c_str()); else - error_at(location, "reference to undefined field or method %qs", - Gogo::message_name(name).c_str()); + go_error_at(location, "reference to undefined field or method %qs", + Gogo::message_name(name).c_str()); } return Expression::make_error(location); } @@ -10267,9 +11558,9 @@ Forward_declaration_type::warn() const // The name was not defined anywhere. if (!this->warned_) { - error_at(this->named_object_->location(), - "use of undefined type %qs", - no->message_name().c_str()); + go_error_at(this->named_object_->location(), + "use of undefined type %qs", + no->message_name().c_str()); this->warned_ = true; } } @@ -10278,9 +11569,9 @@ Forward_declaration_type::warn() const // The name was seen as a type, but the type was never defined. if (no->type_declaration_value()->using_type()) { - error_at(this->named_object_->location(), - "use of undefined type %qs", - no->message_name().c_str()); + go_error_at(this->named_object_->location(), + "use of undefined type %qs", + no->message_name().c_str()); this->warned_ = true; } } @@ -10289,7 +11580,7 @@ Forward_declaration_type::warn() const // The name was defined, but not as a type. if (!this->warned_) { - error_at(this->named_object_->location(), "expected type"); + go_error_at(this->named_object_->location(), "expected type"); this->warned_ = true; } } @@ -10427,7 +11718,7 @@ Forward_declaration_type::do_type_descriptor(Gogo* gogo, Named_type* name) if (name != NULL) return this->named_type_descriptor(gogo, t, name); else - return Expression::make_type_descriptor(t, ploc); + return Expression::make_error(this->named_object_->location()); } } |