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