Commit f9f96987 by Ian Lance Taylor

compiler, runtime: Implement struct and array comparisons.

From-SVN: r182971
parent 4b6aaa99
...@@ -379,9 +379,9 @@ class Backend ...@@ -379,9 +379,9 @@ class Backend
// must be a pointer to this struct type. // must be a pointer to this struct type.
// //
// We must create the named structure before we know its // We must create the named structure before we know its
// initializer, because the initializer refer to its own address. // initializer, because the initializer may refer to its own
// After calling this the frontend will call // address. After calling this the frontend will call
// set_immutable_struct_initializer. // immutable_struct_set_init.
virtual Bvariable* virtual Bvariable*
immutable_struct(const std::string& name, bool is_common, Btype* type, immutable_struct(const std::string& name, bool is_common, Btype* type,
Location) = 0; Location) = 0;
...@@ -400,8 +400,8 @@ class Backend ...@@ -400,8 +400,8 @@ class Backend
// Create a reference to a named immutable initialized data // Create a reference to a named immutable initialized data
// structure defined in some other package. This will be a // structure defined in some other package. This will be a
// structure created by a call to immutable_struct_expression with // structure created by a call to immutable_struct with the same
// the same NAME and TYPE and with IS_COMMON passed as false. This // NAME and TYPE and with IS_COMMON passed as false. This
// corresponds to an extern const global variable in C. // corresponds to an extern const global variable in C.
virtual Bvariable* virtual Bvariable*
immutable_struct_reference(const std::string& name, Btype* type, immutable_struct_reference(const std::string& name, Btype* type,
......
...@@ -1135,6 +1135,63 @@ Expression::make_temporary_reference(Temporary_statement* statement, ...@@ -1135,6 +1135,63 @@ Expression::make_temporary_reference(Temporary_statement* statement,
return new Temporary_reference_expression(statement, location); return new Temporary_reference_expression(statement, location);
} }
// Class Set_and_use_temporary_expression.
// Return the type.
Type*
Set_and_use_temporary_expression::do_type()
{
return this->statement_->type();
}
// Take the address.
void
Set_and_use_temporary_expression::do_address_taken(bool)
{
this->statement_->set_is_address_taken();
}
// Return the backend representation.
tree
Set_and_use_temporary_expression::do_get_tree(Translate_context* context)
{
Bvariable* bvar = this->statement_->get_backend_variable(context);
tree var_tree = var_to_tree(bvar);
tree expr_tree = this->expr_->get_tree(context);
if (var_tree == error_mark_node || expr_tree == error_mark_node)
return error_mark_node;
Location loc = this->location();
return build2_loc(loc.gcc_location(), COMPOUND_EXPR, TREE_TYPE(var_tree),
build2_loc(loc.gcc_location(), MODIFY_EXPR, void_type_node,
var_tree, expr_tree),
var_tree);
}
// Dump.
void
Set_and_use_temporary_expression::do_dump_expression(
Ast_dump_context* ast_dump_context) const
{
ast_dump_context->ostream() << '(';
ast_dump_context->dump_temp_variable_name(this->statement_);
ast_dump_context->ostream() << " = ";
this->expr_->dump_expression(ast_dump_context);
ast_dump_context->ostream() << ')';
}
// Make a set-and-use temporary.
Set_and_use_temporary_expression*
Expression::make_set_and_use_temporary(Temporary_statement* statement,
Expression* expr, Location location)
{
return new Set_and_use_temporary_expression(statement, expr, location);
}
// A sink expression--a use of the blank identifier _. // A sink expression--a use of the blank identifier _.
class Sink_expression : public Expression class Sink_expression : public Expression
...@@ -4468,11 +4525,38 @@ Unary_expression::do_check_types(Gogo*) ...@@ -4468,11 +4525,38 @@ Unary_expression::do_check_types(Gogo*)
tree tree
Unary_expression::do_get_tree(Translate_context* context) Unary_expression::do_get_tree(Translate_context* context)
{ {
Location loc = this->location();
// Taking the address of a set-and-use-temporary expression requires
// setting the temporary and then taking the address.
if (this->op_ == OPERATOR_AND)
{
Set_and_use_temporary_expression* sut =
this->expr_->set_and_use_temporary_expression();
if (sut != NULL)
{
Temporary_statement* temp = sut->temporary();
Bvariable* bvar = temp->get_backend_variable(context);
tree var_tree = var_to_tree(bvar);
Expression* val = sut->expression();
tree val_tree = val->get_tree(context);
if (var_tree == error_mark_node || val_tree == error_mark_node)
return error_mark_node;
tree addr_tree = build_fold_addr_expr_loc(loc.gcc_location(),
var_tree);
return build2_loc(loc.gcc_location(), COMPOUND_EXPR,
TREE_TYPE(addr_tree),
build2_loc(sut->location().gcc_location(),
MODIFY_EXPR, void_type_node,
var_tree, val_tree),
addr_tree);
}
}
tree expr = this->expr_->get_tree(context); tree expr = this->expr_->get_tree(context);
if (expr == error_mark_node) if (expr == error_mark_node)
return error_mark_node; return error_mark_node;
Location loc = this->location();
switch (this->op_) switch (this->op_)
{ {
case OPERATOR_PLUS: case OPERATOR_PLUS:
...@@ -5398,7 +5482,8 @@ Binary_expression::eval_complex(Operator op, Type* left_type, ...@@ -5398,7 +5482,8 @@ Binary_expression::eval_complex(Operator op, Type* left_type,
// constants. // constants.
Expression* Expression*
Binary_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int) Binary_expression::do_lower(Gogo* gogo, Named_object*,
Statement_inserter* inserter, int)
{ {
Location location = this->location(); Location location = this->location();
Operator op = this->op_; Operator op = this->op_;
...@@ -5727,9 +5812,183 @@ Binary_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int) ...@@ -5727,9 +5812,183 @@ Binary_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int)
mpz_clear(right_val); mpz_clear(right_val);
} }
// Lower struct and array comparisons.
if (op == OPERATOR_EQEQ || op == OPERATOR_NOTEQ)
{
if (left->type()->struct_type() != NULL)
return this->lower_struct_comparison(gogo, inserter);
else if (left->type()->array_type() != NULL
&& !left->type()->is_slice_type())
return this->lower_array_comparison(gogo, inserter);
}
return this; return this;
} }
// Lower a struct comparison.
Expression*
Binary_expression::lower_struct_comparison(Gogo* gogo,
Statement_inserter* inserter)
{
Struct_type* st = this->left_->type()->struct_type();
Struct_type* st2 = this->right_->type()->struct_type();
if (st2 == NULL)
return this;
if (st != st2 && !Type::are_identical(st, st2, false, NULL))
return this;
if (!Type::are_compatible_for_comparison(true, this->left_->type(),
this->right_->type(), NULL))
return this;
// See if we can compare using memcmp. As a heuristic, we use
// memcmp rather than field references and comparisons if there are
// more than two fields.
if (st->compare_is_identity() && st->total_field_count() > 2)
return this->lower_compare_to_memcmp(gogo, inserter);
Location loc = this->location();
Expression* left = this->left_;
Temporary_statement* left_temp = NULL;
if (left->var_expression() == NULL
&& left->temporary_reference_expression() == NULL)
{
left_temp = Statement::make_temporary(left->type(), NULL, loc);
inserter->insert(left_temp);
left = Expression::make_set_and_use_temporary(left_temp, left, loc);
}
Expression* right = this->right_;
Temporary_statement* right_temp = NULL;
if (right->var_expression() == NULL
&& right->temporary_reference_expression() == NULL)
{
right_temp = Statement::make_temporary(right->type(), NULL, loc);
inserter->insert(right_temp);
right = Expression::make_set_and_use_temporary(right_temp, right, loc);
}
Expression* ret = Expression::make_boolean(true, loc);
const Struct_field_list* fields = st->fields();
unsigned int field_index = 0;
for (Struct_field_list::const_iterator pf = fields->begin();
pf != fields->end();
++pf, ++field_index)
{
if (field_index > 0)
{
if (left_temp == NULL)
left = left->copy();
else
left = Expression::make_temporary_reference(left_temp, loc);
if (right_temp == NULL)
right = right->copy();
else
right = Expression::make_temporary_reference(right_temp, loc);
}
Expression* f1 = Expression::make_field_reference(left, field_index,
loc);
Expression* f2 = Expression::make_field_reference(right, field_index,
loc);
Expression* cond = Expression::make_binary(OPERATOR_EQEQ, f1, f2, loc);
ret = Expression::make_binary(OPERATOR_ANDAND, ret, cond, loc);
}
if (this->op_ == OPERATOR_NOTEQ)
ret = Expression::make_unary(OPERATOR_NOT, ret, loc);
return ret;
}
// Lower an array comparison.
Expression*
Binary_expression::lower_array_comparison(Gogo* gogo,
Statement_inserter* inserter)
{
Array_type* at = this->left_->type()->array_type();
Array_type* at2 = this->right_->type()->array_type();
if (at2 == NULL)
return this;
if (at != at2 && !Type::are_identical(at, at2, false, NULL))
return this;
if (!Type::are_compatible_for_comparison(true, this->left_->type(),
this->right_->type(), NULL))
return this;
// Call memcmp directly if possible. This may let the middle-end
// optimize the call.
if (at->compare_is_identity())
return this->lower_compare_to_memcmp(gogo, inserter);
// Call the array comparison function.
Named_object* hash_fn;
Named_object* equal_fn;
at->type_functions(gogo, this->left_->type()->named_type(), NULL, NULL,
&hash_fn, &equal_fn);
Location loc = this->location();
Expression* func = Expression::make_func_reference(equal_fn, NULL, loc);
Expression_list* args = new Expression_list();
args->push_back(this->operand_address(inserter, this->left_));
args->push_back(this->operand_address(inserter, this->right_));
args->push_back(Expression::make_type_info(at, TYPE_INFO_SIZE));
Expression* ret = Expression::make_call(func, args, false, loc);
if (this->op_ == OPERATOR_NOTEQ)
ret = Expression::make_unary(OPERATOR_NOT, ret, loc);
return ret;
}
// Lower a struct or array comparison to a call to memcmp.
Expression*
Binary_expression::lower_compare_to_memcmp(Gogo*, Statement_inserter* inserter)
{
Location loc = this->location();
Expression* a1 = this->operand_address(inserter, this->left_);
Expression* a2 = this->operand_address(inserter, this->right_);
Expression* len = Expression::make_type_info(this->left_->type(),
TYPE_INFO_SIZE);
Expression* call = Runtime::make_call(Runtime::MEMCMP, loc, 3, a1, a2, len);
mpz_t zval;
mpz_init_set_ui(zval, 0);
Expression* zero = Expression::make_integer(&zval, NULL, loc);
mpz_clear(zval);
return Expression::make_binary(this->op_, call, zero, loc);
}
// Return the address of EXPR, cast to unsafe.Pointer.
Expression*
Binary_expression::operand_address(Statement_inserter* inserter,
Expression* expr)
{
Location loc = this->location();
if (!expr->is_addressable())
{
Temporary_statement* temp = Statement::make_temporary(expr->type(), NULL,
loc);
inserter->insert(temp);
expr = Expression::make_set_and_use_temporary(temp, expr, loc);
}
expr = Expression::make_unary(OPERATOR_AND, expr, loc);
static_cast<Unary_expression*>(expr)->set_does_not_escape();
Type* void_type = Type::make_void_type();
Type* unsafe_pointer_type = Type::make_pointer_type(void_type);
return Expression::make_cast(unsafe_pointer_type, expr, loc);
}
// Return the integer constant value, if it has one. // Return the integer constant value, if it has one.
bool bool
...@@ -6072,49 +6331,28 @@ Binary_expression::check_operator_type(Operator op, Type* type, Type* otype, ...@@ -6072,49 +6331,28 @@ Binary_expression::check_operator_type(Operator op, Type* type, Type* otype,
case OPERATOR_EQEQ: case OPERATOR_EQEQ:
case OPERATOR_NOTEQ: case OPERATOR_NOTEQ:
if (type->integer_type() == NULL {
&& type->float_type() == NULL std::string reason;
&& type->complex_type() == NULL if (!Type::are_compatible_for_comparison(true, type, otype, &reason))
&& !type->is_string_type() {
&& type->points_to() == NULL error_at(location, "%s", reason.c_str());
&& !type->is_nil_type() return false;
&& !type->is_boolean_type() }
&& type->interface_type() == NULL }
&& (type->array_type() == NULL
|| type->array_type()->length() != NULL)
&& type->map_type() == NULL
&& type->channel_type() == NULL
&& type->function_type() == NULL)
{
error_at(location,
("expected integer, floating, complex, string, pointer, "
"boolean, interface, slice, map, channel, "
"or function type"));
return false;
}
if ((type->is_slice_type()
|| type->map_type() != NULL
|| type->function_type() != NULL)
&& !otype->is_nil_type())
{
error_at(location,
("slice, map, and function types may only "
"be compared to nil"));
return false;
}
break; break;
case OPERATOR_LT: case OPERATOR_LT:
case OPERATOR_LE: case OPERATOR_LE:
case OPERATOR_GT: case OPERATOR_GT:
case OPERATOR_GE: case OPERATOR_GE:
if (type->integer_type() == NULL {
&& type->float_type() == NULL std::string reason;
&& !type->is_string_type()) if (!Type::are_compatible_for_comparison(false, type, otype, &reason))
{ {
error_at(location, "expected integer, floating, or string type"); error_at(location, "%s", reason.c_str());
return false; return false;
} }
}
break; break;
case OPERATOR_PLUS: case OPERATOR_PLUS:
...@@ -12740,10 +12978,10 @@ class Composite_literal_expression : public Parser_expression ...@@ -12740,10 +12978,10 @@ class Composite_literal_expression : public Parser_expression
lower_struct(Gogo*, Type*); lower_struct(Gogo*, Type*);
Expression* Expression*
lower_array(Type*); lower_array(Gogo*, Type*);
Expression* Expression*
make_array(Type*, Expression_list*); make_array(Gogo*, Type*, Expression_list*);
Expression* Expression*
lower_map(Gogo*, Named_object*, Statement_inserter*, Type*); lower_map(Gogo*, Named_object*, Statement_inserter*, Type*);
...@@ -12810,7 +13048,7 @@ Composite_literal_expression::do_lower(Gogo* gogo, Named_object* function, ...@@ -12810,7 +13048,7 @@ Composite_literal_expression::do_lower(Gogo* gogo, Named_object* function,
else if (type->struct_type() != NULL) else if (type->struct_type() != NULL)
ret = this->lower_struct(gogo, type); ret = this->lower_struct(gogo, type);
else if (type->array_type() != NULL) else if (type->array_type() != NULL)
ret = this->lower_array(type); ret = this->lower_array(gogo, type);
else if (type->map_type() != NULL) else if (type->map_type() != NULL)
ret = this->lower_map(gogo, function, inserter, type); ret = this->lower_map(gogo, function, inserter, type);
else else
...@@ -13023,11 +13261,11 @@ Composite_literal_expression::lower_struct(Gogo* gogo, Type* type) ...@@ -13023,11 +13261,11 @@ Composite_literal_expression::lower_struct(Gogo* gogo, Type* type)
// Lower an array composite literal. // Lower an array composite literal.
Expression* Expression*
Composite_literal_expression::lower_array(Type* type) Composite_literal_expression::lower_array(Gogo* gogo, Type* type)
{ {
Location location = this->location(); Location location = this->location();
if (this->vals_ == NULL || !this->has_keys_) if (this->vals_ == NULL || !this->has_keys_)
return this->make_array(type, this->vals_); return this->make_array(gogo, type, this->vals_);
std::vector<Expression*> vals; std::vector<Expression*> vals;
vals.reserve(this->vals_->size()); vals.reserve(this->vals_->size());
...@@ -13127,14 +13365,15 @@ Composite_literal_expression::lower_array(Type* type) ...@@ -13127,14 +13365,15 @@ Composite_literal_expression::lower_array(Type* type)
for (size_t i = 0; i < size; ++i) for (size_t i = 0; i < size; ++i)
list->push_back(vals[i]); list->push_back(vals[i]);
return this->make_array(type, list); return this->make_array(gogo, type, list);
} }
// Actually build the array composite literal. This handles // Actually build the array composite literal. This handles
// [...]{...}. // [...]{...}.
Expression* Expression*
Composite_literal_expression::make_array(Type* type, Expression_list* vals) Composite_literal_expression::make_array(Gogo* gogo, Type* type,
Expression_list* vals)
{ {
Location location = this->location(); Location location = this->location();
Array_type* at = type->array_type(); Array_type* at = type->array_type();
...@@ -13146,6 +13385,10 @@ Composite_literal_expression::make_array(Type* type, Expression_list* vals) ...@@ -13146,6 +13385,10 @@ Composite_literal_expression::make_array(Type* type, Expression_list* vals)
Expression* elen = Expression::make_integer(&vlen, NULL, location); Expression* elen = Expression::make_integer(&vlen, NULL, location);
mpz_clear(vlen); mpz_clear(vlen);
at = Type::make_array_type(at->element_type(), elen); at = Type::make_array_type(at->element_type(), elen);
// This is after the finalize_methods pass, so run that now.
at->finalize_methods(gogo);
type = at; type = at;
} }
if (at->length() != NULL) if (at->length() != NULL)
......
...@@ -25,6 +25,7 @@ class Struct_field; ...@@ -25,6 +25,7 @@ class Struct_field;
class Expression_list; class Expression_list;
class Var_expression; class Var_expression;
class Temporary_reference_expression; class Temporary_reference_expression;
class Set_and_use_temporary_expression;
class String_expression; class String_expression;
class Binary_expression; class Binary_expression;
class Call_expression; class Call_expression;
...@@ -60,6 +61,7 @@ class Expression ...@@ -60,6 +61,7 @@ class Expression
EXPRESSION_CONST_REFERENCE, EXPRESSION_CONST_REFERENCE,
EXPRESSION_VAR_REFERENCE, EXPRESSION_VAR_REFERENCE,
EXPRESSION_TEMPORARY_REFERENCE, EXPRESSION_TEMPORARY_REFERENCE,
EXPRESSION_SET_AND_USE_TEMPORARY,
EXPRESSION_SINK, EXPRESSION_SINK,
EXPRESSION_FUNC_REFERENCE, EXPRESSION_FUNC_REFERENCE,
EXPRESSION_UNKNOWN_REFERENCE, EXPRESSION_UNKNOWN_REFERENCE,
...@@ -134,6 +136,13 @@ class Expression ...@@ -134,6 +136,13 @@ class Expression
static Temporary_reference_expression* static Temporary_reference_expression*
make_temporary_reference(Temporary_statement*, Location); make_temporary_reference(Temporary_statement*, Location);
// Make an expressions which sets a temporary variable and then
// evaluates to a reference to that temporary variable. This is
// used to set a temporary variable while retaining the order of
// evaluation.
static Set_and_use_temporary_expression*
make_set_and_use_temporary(Temporary_statement*, Expression*, Location);
// Make a sink expression--a reference to the blank identifier _. // Make a sink expression--a reference to the blank identifier _.
static Expression* static Expression*
make_sink(Location); make_sink(Location);
...@@ -396,6 +405,15 @@ class Expression ...@@ -396,6 +405,15 @@ class Expression
EXPRESSION_TEMPORARY_REFERENCE>(); EXPRESSION_TEMPORARY_REFERENCE>();
} }
// If this is a set-and-use-temporary, return the
// Set_and_use_temporary_expression. Otherwise, return NULL.
Set_and_use_temporary_expression*
set_and_use_temporary_expression()
{
return this->convert<Set_and_use_temporary_expression,
EXPRESSION_SET_AND_USE_TEMPORARY>();
}
// Return whether this is a sink expression. // Return whether this is a sink expression.
bool bool
is_sink_expression() const is_sink_expression() const
...@@ -1021,6 +1039,62 @@ class Temporary_reference_expression : public Expression ...@@ -1021,6 +1039,62 @@ class Temporary_reference_expression : public Expression
bool is_lvalue_; bool is_lvalue_;
}; };
// Set and use a temporary variable.
class Set_and_use_temporary_expression : public Expression
{
public:
Set_and_use_temporary_expression(Temporary_statement* statement,
Expression* expr, Location location)
: Expression(EXPRESSION_SET_AND_USE_TEMPORARY, location),
statement_(statement), expr_(expr)
{ }
// Return the temporary.
Temporary_statement*
temporary() const
{ return this->statement_; }
// Return the expression.
Expression*
expression() const
{ return this->expr_; }
protected:
Type*
do_type();
void
do_determine_type(const Type_context*)
{ }
Expression*
do_copy()
{
return make_set_and_use_temporary(this->statement_, this->expr_,
this->location());
}
bool
do_is_addressable() const
{ return true; }
void
do_address_taken(bool);
tree
do_get_tree(Translate_context*);
void
do_dump_expression(Ast_dump_context*) const;
private:
// The statement where the temporary variable is defined.
Temporary_statement* statement_;
// The expression to assign to the temporary.
Expression* expr_;
};
// A string expression. // A string expression.
class String_expression : public Expression class String_expression : public Expression
...@@ -1200,6 +1274,18 @@ class Binary_expression : public Expression ...@@ -1200,6 +1274,18 @@ class Binary_expression : public Expression
do_dump_expression(Ast_dump_context*) const; do_dump_expression(Ast_dump_context*) const;
private: private:
Expression*
lower_struct_comparison(Gogo*, Statement_inserter*);
Expression*
lower_array_comparison(Gogo*, Statement_inserter*);
Expression*
lower_compare_to_memcmp(Gogo*, Statement_inserter*);
Expression*
operand_address(Statement_inserter*, Expression*);
// The binary operator to apply. // The binary operator to apply.
Operator op_; Operator op_;
// The left hand side operand. // The left hand side operand.
......
...@@ -106,6 +106,9 @@ go_parse_input_files(const char** filenames, unsigned int filename_count, ...@@ -106,6 +106,9 @@ go_parse_input_files(const char** filenames, unsigned int filename_count,
// form which is easier to use. // form which is easier to use.
::gogo->lower_parse_tree(); ::gogo->lower_parse_tree();
// Write out queued up functions for hash and comparison of types.
::gogo->write_specific_type_functions();
// Now that we have seen all the names, verify that types are // Now that we have seen all the names, verify that types are
// correct. // correct.
::gogo->verify_types(); ::gogo->verify_types();
......
...@@ -116,10 +116,10 @@ Gogo::define_builtin_function_trees() ...@@ -116,10 +116,10 @@ Gogo::define_builtin_function_trees()
NULL_TREE), NULL_TREE),
true); true);
// We use __builtin_memmove for the predeclared copy function. // We use __builtin_memcmp for struct comparisons.
define_builtin(BUILT_IN_MEMMOVE, "__builtin_memmove", "memmove", define_builtin(BUILT_IN_MEMCMP, "__builtin_memcmp", "memcmp",
build_function_type_list(ptr_type_node, build_function_type_list(integer_type_node,
ptr_type_node, const_ptr_type_node,
const_ptr_type_node, const_ptr_type_node,
size_type_node, size_type_node,
NULL_TREE), NULL_TREE),
...@@ -647,7 +647,8 @@ Gogo::write_globals() ...@@ -647,7 +647,8 @@ Gogo::write_globals()
this->build_interface_method_tables(); this->build_interface_method_tables();
Bindings* bindings = this->current_bindings(); Bindings* bindings = this->current_bindings();
size_t count = bindings->size_definitions(); size_t count_definitions = bindings->size_definitions();
size_t count = count_definitions;
tree* vec = new tree[count]; tree* vec = new tree[count];
...@@ -822,6 +823,10 @@ Gogo::write_globals() ...@@ -822,6 +823,10 @@ Gogo::write_globals()
|| this->is_main_package()) || this->is_main_package())
this->write_initialization_function(init_fndecl, init_stmt_list); this->write_initialization_function(init_fndecl, init_stmt_list);
// We should not have seen any new bindings created during the
// conversion.
go_assert(count_definitions == this->current_bindings()->size_definitions());
// Pass everything back to the middle-end. // Pass everything back to the middle-end.
wrapup_global_declarations(vec, count); wrapup_global_declarations(vec, count);
......
...@@ -38,6 +38,8 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int int_type_size, ...@@ -38,6 +38,8 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int int_type_size,
unique_prefix_(), unique_prefix_(),
unique_prefix_specified_(false), unique_prefix_specified_(false),
interface_types_(), interface_types_(),
specific_type_functions_(),
specific_type_functions_are_written_(false),
named_types_are_converted_(false) named_types_are_converted_(false)
{ {
const Location loc = Linemap::predeclared_location(); const Location loc = Linemap::predeclared_location();
...@@ -978,6 +980,16 @@ Gogo::declare_package_type(const std::string& name, Location location) ...@@ -978,6 +980,16 @@ Gogo::declare_package_type(const std::string& name, Location location)
return this->package_->bindings()->add_type_declaration(name, NULL, location); return this->package_->bindings()->add_type_declaration(name, NULL, location);
} }
// Declare a function at the package level.
Named_object*
Gogo::declare_package_function(const std::string& name, Function_type* type,
Location location)
{
return this->package_->bindings()->add_function_declaration(name, NULL, type,
location);
}
// Define a type which was already declared. // Define a type which was already declared.
void void
...@@ -1116,6 +1128,46 @@ Gogo::clear_file_scope() ...@@ -1116,6 +1128,46 @@ Gogo::clear_file_scope()
} }
} }
// Queue up a type specific function for later writing. These are
// written out in write_specific_type_functions, called after the
// parse tree is lowered.
void
Gogo::queue_specific_type_function(Type* type, Named_type* name,
const std::string& hash_name,
Function_type* hash_fntype,
const std::string& equal_name,
Function_type* equal_fntype)
{
go_assert(!this->specific_type_functions_are_written_);
go_assert(!this->in_global_scope());
Specific_type_function* tsf = new Specific_type_function(type, name,
hash_name,
hash_fntype,
equal_name,
equal_fntype);
this->specific_type_functions_.push_back(tsf);
}
// Write out type specific functions.
void
Gogo::write_specific_type_functions()
{
while (!this->specific_type_functions_.empty())
{
Specific_type_function* tsf = this->specific_type_functions_.back();
this->specific_type_functions_.pop_back();
tsf->type->write_specific_type_functions(this, tsf->name,
tsf->hash_name,
tsf->hash_fntype,
tsf->equal_name,
tsf->equal_fntype);
delete tsf;
}
this->specific_type_functions_are_written_ = true;
}
// Traverse the tree. // Traverse the tree.
void void
...@@ -1468,6 +1520,10 @@ Finalize_methods::type(Type* t) ...@@ -1468,6 +1520,10 @@ Finalize_methods::type(Type* t)
t->struct_type()->finalize_methods(this->gogo_); t->struct_type()->finalize_methods(this->gogo_);
break; break;
case Type::TYPE_ARRAY:
t->array_type()->finalize_methods(this->gogo_);
break;
default: default:
break; break;
} }
......
...@@ -277,6 +277,11 @@ class Gogo ...@@ -277,6 +277,11 @@ class Gogo
Named_object* Named_object*
declare_function(const std::string&, Function_type*, Location); declare_function(const std::string&, Function_type*, Location);
// Declare a function at the package level. This is used for
// functions generated for a type.
Named_object*
declare_package_function(const std::string&, Function_type*, Location);
// Add a label. // Add a label.
Label* Label*
add_label_definition(const std::string&, Location); add_label_definition(const std::string&, Location);
...@@ -364,6 +369,20 @@ class Gogo ...@@ -364,6 +369,20 @@ class Gogo
void void
clear_file_scope(); clear_file_scope();
// Queue up a type-specific function to be written out. This is
// used when a type-specific function is needed when not at the top
// level.
void
queue_specific_type_function(Type* type, Named_type* name,
const std::string& hash_name,
Function_type* hash_fntype,
const std::string& equal_name,
Function_type* equal_fntype);
// Write out queued specific type functions.
void
write_specific_type_functions();
// Traverse the tree. See the Traverse class. // Traverse the tree. See the Traverse class.
void void
traverse(Traverse*); traverse(Traverse*);
...@@ -603,6 +622,27 @@ class Gogo ...@@ -603,6 +622,27 @@ class Gogo
// Type used to map special names in the sys package. // Type used to map special names in the sys package.
typedef std::map<std::string, std::string> Sys_names; typedef std::map<std::string, std::string> Sys_names;
// Type used to queue writing a type specific function.
struct Specific_type_function
{
Type* type;
Named_type* name;
std::string hash_name;
Function_type* hash_fntype;
std::string equal_name;
Function_type* equal_fntype;
Specific_type_function(Type* atype, Named_type* aname,
const std::string& ahash_name,
Function_type* ahash_fntype,
const std::string& aequal_name,
Function_type* aequal_fntype)
: type(atype), name(aname), hash_name(ahash_name),
hash_fntype(ahash_fntype), equal_name(aequal_name),
equal_fntype(aequal_fntype)
{ }
};
// The backend generator. // The backend generator.
Backend* backend_; Backend* backend_;
// The object used to keep track of file names and line numbers. // The object used to keep track of file names and line numbers.
...@@ -635,6 +675,10 @@ class Gogo ...@@ -635,6 +675,10 @@ class Gogo
bool unique_prefix_specified_; bool unique_prefix_specified_;
// A list of interface types defined while parsing. // A list of interface types defined while parsing.
std::vector<Interface_type*> interface_types_; std::vector<Interface_type*> interface_types_;
// Type specific functions to write out.
std::vector<Specific_type_function*> specific_type_functions_;
// Whether we are done writing out specific type functions.
bool specific_type_functions_are_written_;
// Whether named types have been converted. // Whether named types have been converted.
bool named_types_are_converted_; bool named_types_are_converted_;
}; };
......
...@@ -28,6 +28,9 @@ ...@@ -28,6 +28,9 @@
// the name. The third is the parameter types and the fourth is the // the name. The third is the parameter types and the fourth is the
// result types. // result types.
// The standard C memcmp function, used for struct comparisons.
DEF_GO_RUNTIME(MEMCMP, "memcmp", P3(POINTER, POINTER, UINTPTR), R1(INT))
// Range over a string, returning the next index. // Range over a string, returning the next index.
DEF_GO_RUNTIME(STRINGITER, "runtime.stringiter", P2(STRING, INT), R1(INT)) DEF_GO_RUNTIME(STRINGITER, "runtime.stringiter", P2(STRING, INT), R1(INT))
......
...@@ -474,6 +474,115 @@ Type::are_compatible_for_binop(const Type* lhs, const Type* rhs) ...@@ -474,6 +474,115 @@ Type::are_compatible_for_binop(const Type* lhs, const Type* rhs)
return false; 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->named_type() != NULL)
return t1->named_type()->named_type_is_comparable(reason);
else if (t2->named_type() != NULL)
return t2->named_type()->named_type_is_comparable(reason);
else if (t1->struct_type() != NULL)
{
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()->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 // Return true if a value with type RHS may be assigned to a variable
// with type LHS. If CHECK_HIDDEN_FIELDS is true, check whether any // with type LHS. If CHECK_HIDDEN_FIELDS is true, check whether any
// hidden fields are modified. If REASON is not NULL, set *REASON to // hidden fields are modified. If REASON is not NULL, set *REASON to
...@@ -897,44 +1006,17 @@ Type::make_type_descriptor_var(Gogo* gogo) ...@@ -897,44 +1006,17 @@ Type::make_type_descriptor_var(Gogo* gogo)
phash = &ins.first->second; phash = &ins.first->second;
} }
std::string var_name; std::string var_name = this->type_descriptor_var_name(gogo, nt);
if (nt == NULL)
var_name = this->unnamed_type_descriptor_var_name(gogo);
else
var_name = this->type_descriptor_var_name(gogo);
// Build the contents of the type descriptor. // Build the contents of the type descriptor.
Expression* initializer = this->do_type_descriptor(gogo, NULL); Expression* initializer = this->do_type_descriptor(gogo, NULL);
Btype* initializer_btype = initializer->type()->get_backend(gogo); Btype* initializer_btype = initializer->type()->get_backend(gogo);
// See if this type descriptor is defined in a different package.
bool is_defined_elsewhere = false;
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.
is_defined_elsewhere = true;
}
}
else
{
if (this->points_to() != NULL
&& this->points_to()->named_type() != NULL
&& this->points_to()->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.
is_defined_elsewhere = true;
}
}
Location loc = nt == NULL ? Linemap::predeclared_location() : nt->location(); Location loc = nt == NULL ? Linemap::predeclared_location() : nt->location();
if (is_defined_elsewhere) const Package* dummy;
if (this->type_descriptor_defined_elsewhere(nt, &dummy))
{ {
this->type_descriptor_var_ = this->type_descriptor_var_ =
gogo->backend()->immutable_struct_reference(var_name, gogo->backend()->immutable_struct_reference(var_name,
...@@ -984,21 +1066,15 @@ Type::make_type_descriptor_var(Gogo* gogo) ...@@ -984,21 +1066,15 @@ Type::make_type_descriptor_var(Gogo* gogo)
binitializer); binitializer);
} }
// Return the name of the type descriptor variable for an unnamed // Return the name of the type descriptor variable. If NT is not
// type. // NULL, use it to get the name. Otherwise this is an unnamed type.
std::string std::string
Type::unnamed_type_descriptor_var_name(Gogo* gogo) Type::type_descriptor_var_name(Gogo* gogo, Named_type* nt)
{ {
return "__go_td_" + this->mangled_name(gogo); if (nt == NULL)
} return "__go_td_" + this->mangled_name(gogo);
// Return the name of the type descriptor variable for a named type.
std::string
Type::type_descriptor_var_name(Gogo* gogo)
{
Named_type* nt = this->named_type();
Named_object* no = nt->named_object(); Named_object* no = nt->named_object();
const Named_object* in_function = nt->in_function(); const Named_object* in_function = nt->in_function();
std::string ret = "__go_tdn_"; std::string ret = "__go_tdn_";
...@@ -1026,6 +1102,39 @@ Type::type_descriptor_var_name(Gogo* gogo) ...@@ -1026,6 +1102,39 @@ Type::type_descriptor_var_name(Gogo* gogo)
return ret; return ret;
} }
// 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()->named_type() != NULL
&& this->points_to()->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()->named_type()->named_object()->package();
return true;
}
}
return false;
}
// Return a composite literal for a type descriptor. // Return a composite literal for a type descriptor.
Expression* Expression*
...@@ -1157,8 +1266,8 @@ Type::make_type_descriptor_type() ...@@ -1157,8 +1266,8 @@ Type::make_type_descriptor_type()
// The type descriptor type. // The type descriptor type.
Typed_identifier_list* params = new Typed_identifier_list(); Typed_identifier_list* params = new Typed_identifier_list();
params->push_back(Typed_identifier("", unsafe_pointer_type, bloc)); params->push_back(Typed_identifier("key", unsafe_pointer_type, bloc));
params->push_back(Typed_identifier("", uintptr_type, bloc)); params->push_back(Typed_identifier("key_size", uintptr_type, bloc));
Typed_identifier_list* results = new Typed_identifier_list(); Typed_identifier_list* results = new Typed_identifier_list();
results->push_back(Typed_identifier("", uintptr_type, bloc)); results->push_back(Typed_identifier("", uintptr_type, bloc));
...@@ -1166,9 +1275,9 @@ Type::make_type_descriptor_type() ...@@ -1166,9 +1275,9 @@ Type::make_type_descriptor_type()
Type* hashfn_type = Type::make_function_type(NULL, params, results, bloc); Type* hashfn_type = Type::make_function_type(NULL, params, results, bloc);
params = new Typed_identifier_list(); params = new Typed_identifier_list();
params->push_back(Typed_identifier("", unsafe_pointer_type, bloc)); params->push_back(Typed_identifier("key1", unsafe_pointer_type, bloc));
params->push_back(Typed_identifier("", unsafe_pointer_type, bloc)); params->push_back(Typed_identifier("key2", unsafe_pointer_type, bloc));
params->push_back(Typed_identifier("", uintptr_type, bloc)); params->push_back(Typed_identifier("key_size", uintptr_type, bloc));
results = new Typed_identifier_list(); results = new Typed_identifier_list();
results->push_back(Typed_identifier("", Type::lookup_bool_type(), bloc)); results->push_back(Typed_identifier("", Type::lookup_bool_type(), bloc));
...@@ -1213,67 +1322,278 @@ Type::make_type_descriptor_ptr_type() ...@@ -1213,67 +1322,278 @@ Type::make_type_descriptor_ptr_type()
return ret; return ret;
} }
// Return the names of runtime functions which compute a hash code for // Set *HASH_FN and *EQUAL_FN to the runtime functions which compute a
// this type and which compare whether two values of this type are // hash code for this type and which compare whether two values of
// equal. // this type are equal. If NAME is not NULL it is the name of this
// type. HASH_FNTYPE and EQUAL_FNTYPE are the types of these
// functions, for convenience; they may be NULL.
void void
Type::type_functions(const char** hash_fn, const char** equal_fn) const Type::type_functions(Gogo* gogo, Named_type* name, Function_type* hash_fntype,
Function_type* equal_fntype, Named_object** hash_fn,
Named_object** equal_fn)
{ {
switch (this->base()->classification()) if (hash_fntype == NULL || equal_fntype == NULL)
{ {
case Type::TYPE_ERROR: Location bloc = Linemap::predeclared_location();
case Type::TYPE_VOID:
case Type::TYPE_NIL:
// These types can not be hashed or compared.
*hash_fn = "__go_type_hash_error";
*equal_fn = "__go_type_equal_error";
break;
case Type::TYPE_BOOLEAN: Type* uintptr_type = Type::lookup_integer_type("uintptr");
case Type::TYPE_INTEGER: Type* void_type = Type::make_void_type();
case Type::TYPE_FLOAT: Type* unsafe_pointer_type = Type::make_pointer_type(void_type);
case Type::TYPE_COMPLEX:
case Type::TYPE_POINTER:
case Type::TYPE_CHANNEL:
*hash_fn = "__go_type_hash_identity";
*equal_fn = "__go_type_equal_identity";
break;
case Type::TYPE_STRING: if (hash_fntype == NULL)
*hash_fn = "__go_type_hash_string"; {
*equal_fn = "__go_type_equal_string"; Typed_identifier_list* params = new Typed_identifier_list();
break; params->push_back(Typed_identifier("key", unsafe_pointer_type,
bloc));
params->push_back(Typed_identifier("key_size", uintptr_type, bloc));
case Type::TYPE_STRUCT: Typed_identifier_list* results = new Typed_identifier_list();
case Type::TYPE_ARRAY: results->push_back(Typed_identifier("", uintptr_type, bloc));
case Type::TYPE_FUNCTION:
case Type::TYPE_MAP:
// These types can not be hashed or compared.
*hash_fn = "__go_type_hash_error";
*equal_fn = "__go_type_equal_error";
break;
case Type::TYPE_INTERFACE: hash_fntype = Type::make_function_type(NULL, params, results, bloc);
if (this->interface_type()->is_empty()) }
if (equal_fntype == NULL)
{ {
*hash_fn = "__go_type_hash_empty_interface"; Typed_identifier_list* params = new Typed_identifier_list();
*equal_fn = "__go_type_equal_empty_interface"; 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));
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);
} }
else }
const char* hash_fnname;
const char* equal_fnname;
if (this->compare_is_identity())
{
hash_fnname = "__go_type_hash_identity";
equal_fnname = "__go_type_equal_identity";
}
else if (!this->is_comparable())
{
hash_fnname = "__go_type_hash_error";
equal_fnname = "__go_type_equal_error";
}
else
{
switch (this->base()->classification())
{ {
*hash_fn = "__go_type_hash_interface"; case Type::TYPE_ERROR:
*equal_fn = "__go_type_equal_interface"; 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:
hash_fnname = "__go_type_hash_float";
equal_fnname = "__go_type_equal_float";
break;
case Type::TYPE_COMPLEX:
hash_fnname = "__go_type_hash_complex";
equal_fnname = "__go_type_equal_complex";
break;
case Type::TYPE_STRING:
hash_fnname = "__go_type_hash_string";
equal_fnname = "__go_type_equal_string";
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.
this->specific_type_functions(gogo, name, hash_fntype,
equal_fntype, hash_fn, equal_fn);
return;
}
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.
this->specific_type_functions(gogo, name, hash_fntype,
equal_fntype, hash_fn, equal_fn);
return;
}
break;
case Type::TYPE_INTERFACE:
if (this->interface_type()->is_empty())
{
hash_fnname = "__go_type_hash_empty_interface";
equal_fnname = "__go_type_equal_empty_interface";
}
else
{
hash_fnname = "__go_type_hash_interface";
equal_fnname = "__go_type_equal_interface";
}
break;
case Type::TYPE_NAMED:
case Type::TYPE_FORWARD:
go_unreachable();
default:
go_unreachable();
} }
break; }
case Type::TYPE_NAMED:
case Type::TYPE_FORWARD:
go_unreachable();
default: Location bloc = Linemap::predeclared_location();
go_unreachable(); *hash_fn = Named_object::make_function_declaration(hash_fnname, NULL,
hash_fntype, bloc);
(*hash_fn)->func_declaration_value()->set_asm_name(hash_fnname);
*equal_fn = Named_object::make_function_declaration(equal_fnname, NULL,
equal_fntype, bloc);
(*equal_fn)->func_declaration_value()->set_asm_name(equal_fnname);
}
// A hash table mapping types to the specific hash functions.
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.
void
Type::specific_type_functions(Gogo* gogo, Named_type* name,
Function_type* hash_fntype,
Function_type* equal_fntype,
Named_object** hash_fn,
Named_object** equal_fn)
{
Hash_equal_fn fnull(NULL, NULL);
std::pair<Type*, Hash_equal_fn> val(name != NULL ? name : this, fnull);
std::pair<Type_functions::iterator, bool> ins =
Type::type_functions_table.insert(val);
if (!ins.second)
{
// We already have functions for this type
*hash_fn = ins.first->second.first;
*equal_fn = ins.first->second.second;
return;
} }
std::string base_name;
if (name == NULL)
base_name = gogo->pack_hidden_name(this->mangled_name(gogo), false);
else
{
// This name is already hidden or not as appropriate.
base_name = name->name();
const Named_object* in_function = name->in_function();
if (in_function != NULL)
base_name += '$' + in_function->name();
}
std::string hash_name = base_name + "$hash";
std::string equal_name = base_name + "$equal";
Location bloc = Linemap::predeclared_location();
const Package* package = NULL;
bool is_defined_elsewhere =
this->type_descriptor_defined_elsewhere(name, &package);
if (is_defined_elsewhere)
{
*hash_fn = Named_object::make_function_declaration(hash_name, package,
hash_fntype, bloc);
*equal_fn = Named_object::make_function_declaration(equal_name, package,
equal_fntype, bloc);
}
else
{
*hash_fn = gogo->declare_package_function(hash_name, hash_fntype, bloc);
*equal_fn = gogo->declare_package_function(equal_name, equal_fntype,
bloc);
}
ins.first->second.first = *hash_fn;
ins.first->second.second = *equal_fn;
if (!is_defined_elsewhere)
{
if (gogo->in_global_scope())
this->write_specific_type_functions(gogo, name, hash_name, hash_fntype,
equal_name, equal_fntype);
else
gogo->queue_specific_type_function(this, name, hash_name, hash_fntype,
equal_name, equal_fntype);
}
}
// Write the hash and equality functions for a type which needs to be
// written specially.
void
Type::write_specific_type_functions(Gogo* gogo, Named_type* name,
const std::string& hash_name,
Function_type* hash_fntype,
const std::string& equal_name,
Function_type* equal_fntype)
{
Location bloc = Linemap::predeclared_location();
Named_object* hash_fn = gogo->start_function(hash_name, hash_fntype, false,
bloc);
gogo->start_block(bloc);
if (this->struct_type() != NULL)
this->struct_type()->write_hash_function(gogo, name, hash_fntype,
equal_fntype);
else if (this->array_type() != NULL)
this->array_type()->write_hash_function(gogo, name, hash_fntype,
equal_fntype);
else
go_unreachable();
Block* b = gogo->finish_block(bloc);
gogo->add_block(b, bloc);
gogo->lower_block(hash_fn, b);
gogo->finish_function(bloc);
Named_object *equal_fn = gogo->start_function(equal_name, equal_fntype,
false, bloc);
gogo->start_block(bloc);
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();
b = gogo->finish_block(bloc);
gogo->add_block(b, bloc);
gogo->lower_block(equal_fn, b);
gogo->finish_function(bloc);
} }
// Return a composite literal for the type descriptor for a plain type // Return a composite literal for the type descriptor for a plain type
...@@ -1320,25 +1640,20 @@ Type::type_descriptor_constructor(Gogo* gogo, int runtime_type_kind, ...@@ -1320,25 +1640,20 @@ Type::type_descriptor_constructor(Gogo* gogo, int runtime_type_kind,
mpz_set_ui(iv, this->hash_for_method(gogo)); mpz_set_ui(iv, this->hash_for_method(gogo));
vals->push_back(Expression::make_integer(&iv, p->type(), bloc)); vals->push_back(Expression::make_integer(&iv, p->type(), bloc));
const char* hash_fn;
const char* equal_fn;
this->type_functions(&hash_fn, &equal_fn);
++p; ++p;
go_assert(p->is_field_name("hashfn")); go_assert(p->is_field_name("hashfn"));
Function_type* fntype = p->type()->function_type(); Function_type* hash_fntype = p->type()->function_type();
Named_object* no = Named_object::make_function_declaration(hash_fn, NULL,
fntype,
bloc);
no->func_declaration_value()->set_asm_name(hash_fn);
vals->push_back(Expression::make_func_reference(no, NULL, bloc));
++p; ++p;
go_assert(p->is_field_name("equalfn")); go_assert(p->is_field_name("equalfn"));
fntype = p->type()->function_type(); Function_type* equal_fntype = p->type()->function_type();
no = Named_object::make_function_declaration(equal_fn, NULL, fntype, bloc);
no->func_declaration_value()->set_asm_name(equal_fn); Named_object* hash_fn;
vals->push_back(Expression::make_func_reference(no, NULL, bloc)); 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));
++p; ++p;
go_assert(p->is_field_name("string")); go_assert(p->is_field_name("string"));
...@@ -1680,6 +1995,10 @@ class Error_type : public Type ...@@ -1680,6 +1995,10 @@ class Error_type : public Type
{ } { }
protected: protected:
bool
do_compare_is_identity() const
{ return false; }
Btype* Btype*
do_get_backend(Gogo* gogo) do_get_backend(Gogo* gogo)
{ return gogo->backend()->error_type(); } { return gogo->backend()->error_type(); }
...@@ -1714,6 +2033,10 @@ class Void_type : public Type ...@@ -1714,6 +2033,10 @@ class Void_type : public Type
{ } { }
protected: protected:
bool
do_compare_is_identity() const
{ return false; }
Btype* Btype*
do_get_backend(Gogo* gogo) do_get_backend(Gogo* gogo)
{ return gogo->backend()->void_type(); } { return gogo->backend()->void_type(); }
...@@ -1748,6 +2071,10 @@ class Boolean_type : public Type ...@@ -1748,6 +2071,10 @@ class Boolean_type : public Type
{ } { }
protected: protected:
bool
do_compare_is_identity() const
{ return true; }
Btype* Btype*
do_get_backend(Gogo* gogo) do_get_backend(Gogo* gogo)
{ return gogo->backend()->bool_type(); } { return gogo->backend()->bool_type(); }
...@@ -2335,6 +2662,10 @@ class Sink_type : public Type ...@@ -2335,6 +2662,10 @@ class Sink_type : public Type
{ } { }
protected: protected:
bool
do_compare_is_identity() const
{ return false; }
Btype* Btype*
do_get_backend(Gogo*) do_get_backend(Gogo*)
{ go_unreachable(); } { go_unreachable(); }
...@@ -3243,6 +3574,10 @@ class Nil_type : public Type ...@@ -3243,6 +3574,10 @@ class Nil_type : public Type
{ } { }
protected: protected:
bool
do_compare_is_identity() const
{ return false; }
Btype* Btype*
do_get_backend(Gogo* gogo) do_get_backend(Gogo* gogo)
{ return gogo->backend()->pointer_type(gogo->backend()->void_type()); } { return gogo->backend()->pointer_type(gogo->backend()->void_type()); }
...@@ -3290,6 +3625,10 @@ class Call_multiple_result_type : public Type ...@@ -3290,6 +3625,10 @@ class Call_multiple_result_type : public Type
return false; return false;
} }
bool
do_compare_is_identity() const
{ return false; }
Btype* Btype*
do_get_backend(Gogo* gogo) do_get_backend(Gogo* gogo)
{ {
...@@ -3564,6 +3903,25 @@ Struct_type::struct_has_hidden_fields(const Named_type* within, ...@@ -3564,6 +3903,25 @@ Struct_type::struct_has_hidden_fields(const Named_type* within,
return false; return false;
} }
// Whether comparisons of this struct type are simple identity
// comparisons.
bool
Struct_type::do_compare_is_identity() const
{
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()->compare_is_identity())
return false;
return true;
}
// Build identity and hash functions for this struct.
// Hash code. // Hash code.
unsigned int unsigned int
...@@ -3779,6 +4137,17 @@ Struct_type::is_unexported_local_field(Gogo* gogo, ...@@ -3779,6 +4137,17 @@ Struct_type::is_unexported_local_field(Gogo* gogo,
void void
Struct_type::finalize_methods(Gogo* gogo) Struct_type::finalize_methods(Gogo* gogo)
{ {
// If this type needs explicit comparison and hash functions, create
// them now. It would be a bit better to do this only if the
// functions are needed, but they will be static so the backend can
// discard them if they are not used.
if (!this->compare_is_identity() && this->is_comparable())
{
Named_object* hash_fn;
Named_object* equal_fn;
this->type_functions(gogo, NULL, NULL, NULL, &hash_fn, &equal_fn);
}
if (this->all_methods_ != NULL) if (this->all_methods_ != NULL)
return; return;
Type::finalize_methods(gogo, this, this->location_, &this->all_methods_); Type::finalize_methods(gogo, this, this->location_, &this->all_methods_);
...@@ -3952,6 +4321,170 @@ Struct_type::do_type_descriptor(Gogo* gogo, Named_type* name) ...@@ -3952,6 +4321,170 @@ Struct_type::do_type_descriptor(Gogo* gogo, Named_type* name)
return Expression::make_struct_composite_literal(stdt, vals, bloc); return Expression::make_struct_composite_literal(stdt, vals, bloc);
} }
// Write the hash function for a struct which can not use the identity
// function.
void
Struct_type::write_hash_function(Gogo* gogo, Named_type*,
Function_type* hash_fntype,
Function_type* equal_fntype)
{
Location bloc = Linemap::predeclared_location();
// The pointer to the struct that we are going to hash. This is an
// argument to the hash function we are implementing here.
Named_object* key_arg = gogo->lookup("key", NULL);
go_assert(key_arg != NULL);
Type* key_arg_type = key_arg->var_value()->type();
Type* uintptr_type = Type::lookup_integer_type("uintptr");
// Get a 0.
mpz_t ival;
mpz_init_set_ui(ival, 0);
Expression* zero = Expression::make_integer(&ival, uintptr_type, bloc);
mpz_clear(ival);
// Make a temporary to hold the return value, initialized to 0.
Temporary_statement* retval = Statement::make_temporary(uintptr_type, zero,
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_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();
++pf)
{
if (first)
first = false;
else
{
// Multiply retval by 33.
mpz_init_set_ui(ival, 33);
Expression* i33 = Expression::make_integer(&ival, uintptr_type,
bloc);
mpz_clear(ival);
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);
Expression* subkey = Expression::make_binary(OPERATOR_PLUS, ref, offset,
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.
Expression_list* args = new Expression_list();
args->push_back(subkey);
args->push_back(size);
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.
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);
gogo->add_statement(s);
}
// Return retval to the caller of the hash function.
Expression_list* vals = new Expression_list();
ref = Expression::make_temporary_reference(retval, bloc);
vals->push_back(ref);
Statement* s = Statement::make_return_statement(vals, bloc);
gogo->add_statement(s);
}
// Write the equality function for a struct which can not use the
// identity function.
void
Struct_type::write_equal_function(Gogo* gogo, Named_type* name)
{
Location bloc = Linemap::predeclared_location();
// The pointers to the structs we are going to compare.
Named_object* key1_arg = gogo->lookup("key1", NULL);
Named_object* key2_arg = gogo->lookup("key2", NULL);
go_assert(key1_arg != NULL && key2_arg != NULL);
// Build temporaries with the right types.
Type* pt = Type::make_pointer_type(name != NULL
? static_cast<Type*>(name)
: static_cast<Type*>(this));
Expression* ref = Expression::make_var_reference(key1_arg, bloc);
ref = Expression::make_unsafe_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_unsafe_cast(pt, ref, bloc);
Temporary_statement* p2 = Statement::make_temporary(pt, ref, bloc);
gogo->add_statement(p2);
const Struct_field_list* fields = this->fields_;
unsigned int field_index = 0;
for (Struct_field_list::const_iterator pf = fields->begin();
pf != fields->end();
++pf, ++field_index)
{
// Compare one field in both P1 and P2.
Expression* f1 = Expression::make_temporary_reference(p1, bloc);
f1 = Expression::make_unary(OPERATOR_MULT, f1, bloc);
f1 = Expression::make_field_reference(f1, field_index, bloc);
Expression* f2 = Expression::make_temporary_reference(p2, bloc);
f2 = Expression::make_unary(OPERATOR_MULT, f2, bloc);
f2 = Expression::make_field_reference(f2, field_index, bloc);
Expression* cond = Expression::make_binary(OPERATOR_NOTEQ, f1, f2, bloc);
// If the values are not equal, return false.
gogo->start_block(bloc);
Expression_list* vals = new Expression_list();
vals->push_back(Expression::make_boolean(false, bloc));
Statement* s = Statement::make_return_statement(vals, bloc);
gogo->add_statement(s);
Block* then_block = gogo->finish_block(bloc);
s = Statement::make_if_statement(cond, then_block, NULL, bloc);
gogo->add_statement(s);
}
// All the fields are equal, so return true.
Expression_list* vals = new Expression_list();
vals->push_back(Expression::make_boolean(true, bloc));
Statement* s = Statement::make_return_statement(vals, bloc);
gogo->add_statement(s);
}
// Reflection string. // Reflection string.
void void
...@@ -4190,6 +4723,25 @@ Array_type::is_identical(const Array_type* t, bool errors_are_identical) const ...@@ -4190,6 +4723,25 @@ Array_type::is_identical(const Array_type* t, bool errors_are_identical) const
return false; return false;
} }
// If this type needs explicit comparison and hash functions, create
// them now. It would be a bit better to do this only if the
// functions are needed, but they will be static so the backend can
// discard them if they are not used.
void
Array_type::finalize_methods(Gogo* gogo)
{
if (this->length_ != NULL
&& !this->length_->is_nil_expression()
&& !this->compare_is_identity()
&& this->is_comparable())
{
Named_object* hash_fn;
Named_object* equal_fn;
this->type_functions(gogo, NULL, NULL, NULL, &hash_fn, &equal_fn);
}
}
// Traversal. // Traversal.
int int
...@@ -4298,6 +4850,198 @@ Array_type::do_hash_for_method(Gogo* gogo) const ...@@ -4298,6 +4850,198 @@ Array_type::do_hash_for_method(Gogo* gogo) const
return this->element_type_->hash_for_method(gogo) + 1; return this->element_type_->hash_for_method(gogo) + 1;
} }
// Write the hash function for an array which can not use the identify
// function.
void
Array_type::write_hash_function(Gogo* gogo, Named_type* name,
Function_type* hash_fntype,
Function_type* equal_fntype)
{
Location bloc = Linemap::predeclared_location();
// The pointer to the array that we are going to hash. This is an
// argument to the hash function we are implementing here.
Named_object* key_arg = gogo->lookup("key", NULL);
go_assert(key_arg != NULL);
Type* key_arg_type = key_arg->var_value()->type();
Type* uintptr_type = Type::lookup_integer_type("uintptr");
// Get a 0.
mpz_t ival;
mpz_init_set_ui(ival, 0);
Expression* zero = Expression::make_integer(&ival, uintptr_type, bloc);
mpz_clear(ival);
// Make a temporary to hold the return value, initialized to 0.
Temporary_statement* retval = Statement::make_temporary(uintptr_type, zero,
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_cast(uintptr_type, ref, bloc);
Temporary_statement* key = Statement::make_temporary(uintptr_type, ref,
bloc);
gogo->add_statement(key);
// Loop over the array elements.
// for i = range a
Type* int_type = Type::lookup_integer_type("int");
Temporary_statement* index = Statement::make_temporary(int_type, NULL, bloc);
gogo->add_statement(index);
Expression* iref = Expression::make_temporary_reference(index, bloc);
Expression* aref = Expression::make_var_reference(key_arg, bloc);
Type* pt = Type::make_pointer_type(name != NULL
? static_cast<Type*>(name)
: static_cast<Type*>(this));
aref = Expression::make_cast(pt, aref, bloc);
For_range_statement* for_range = Statement::make_for_range_statement(iref,
NULL,
aref,
bloc);
gogo->start_block(bloc);
// Multiply retval by 33.
mpz_init_set_ui(ival, 33);
Expression* i33 = Expression::make_integer(&ival, uintptr_type, bloc);
mpz_clear(ival);
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;
this->element_type_->type_functions(gogo, this->element_type_->named_type(),
hash_fntype, equal_fntype, &hash_fn,
&equal_fn);
// Get a pointer to this element in the loop.
Expression* subkey = Expression::make_temporary_reference(key, bloc);
subkey = Expression::make_cast(key_arg_type, subkey, bloc);
// Get the size of each element.
Expression* ele_size = Expression::make_type_info(this->element_type_,
Expression::TYPE_INFO_SIZE);
// Get the hash of this element.
Expression_list* args = new Expression_list();
args->push_back(subkey);
args->push_back(ele_size);
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.
Temporary_reference_expression* tref =
Expression::make_temporary_reference(retval, bloc);
tref->set_is_lvalue();
s = Statement::make_assignment_operation(OPERATOR_PLUSEQ, tref, call, bloc);
gogo->add_statement(s);
// Increase the element pointer.
tref = Expression::make_temporary_reference(key, bloc);
tref->set_is_lvalue();
s = Statement::make_assignment_operation(OPERATOR_PLUSEQ, tref, ele_size,
bloc);
Block* statements = gogo->finish_block(bloc);
for_range->add_statements(statements);
gogo->add_statement(for_range);
// Return retval to the caller of the hash function.
Expression_list* vals = new Expression_list();
ref = Expression::make_temporary_reference(retval, bloc);
vals->push_back(ref);
s = Statement::make_return_statement(vals, bloc);
gogo->add_statement(s);
}
// Write the equality function for an array which can not use the
// identity function.
void
Array_type::write_equal_function(Gogo* gogo, Named_type* name)
{
Location bloc = Linemap::predeclared_location();
// The pointers to the arrays we are going to compare.
Named_object* key1_arg = gogo->lookup("key1", NULL);
Named_object* key2_arg = gogo->lookup("key2", NULL);
go_assert(key1_arg != NULL && key2_arg != NULL);
// Build temporaries for the keys with the right types.
Type* pt = Type::make_pointer_type(name != NULL
? static_cast<Type*>(name)
: static_cast<Type*>(this));
Expression* ref = Expression::make_var_reference(key1_arg, bloc);
ref = Expression::make_unsafe_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_unsafe_cast(pt, ref, bloc);
Temporary_statement* p2 = Statement::make_temporary(pt, ref, bloc);
gogo->add_statement(p2);
// Loop over the array elements.
// for i = range a
Type* int_type = Type::lookup_integer_type("int");
Temporary_statement* index = Statement::make_temporary(int_type, NULL, bloc);
gogo->add_statement(index);
Expression* iref = Expression::make_temporary_reference(index, bloc);
Expression* aref = Expression::make_temporary_reference(p1, bloc);
For_range_statement* for_range = Statement::make_for_range_statement(iref,
NULL,
aref,
bloc);
gogo->start_block(bloc);
// Compare element in P1 and P2.
Expression* e1 = Expression::make_temporary_reference(p1, bloc);
e1 = Expression::make_unary(OPERATOR_MULT, e1, bloc);
ref = Expression::make_temporary_reference(index, bloc);
e1 = Expression::make_array_index(e1, ref, NULL, bloc);
Expression* e2 = Expression::make_temporary_reference(p2, bloc);
e2 = Expression::make_unary(OPERATOR_MULT, e2, bloc);
ref = Expression::make_temporary_reference(index, bloc);
e2 = Expression::make_array_index(e2, ref, NULL, bloc);
Expression* cond = Expression::make_binary(OPERATOR_NOTEQ, e1, e2, bloc);
// If the elements are not equal, return false.
gogo->start_block(bloc);
Expression_list* vals = new Expression_list();
vals->push_back(Expression::make_boolean(false, bloc));
Statement* s = Statement::make_return_statement(vals, bloc);
gogo->add_statement(s);
Block* then_block = gogo->finish_block(bloc);
s = Statement::make_if_statement(cond, then_block, NULL, bloc);
gogo->add_statement(s);
Block* statements = gogo->finish_block(bloc);
for_range->add_statements(statements);
gogo->add_statement(for_range);
// All the elements are equal, so return true.
vals = new Expression_list();
vals->push_back(Expression::make_boolean(true, bloc));
s = Statement::make_return_statement(vals, bloc);
gogo->add_statement(s);
}
// Get a tree for the length of a fixed array. The length may be // Get a tree for the length of a fixed array. The length may be
// computed using a function call, so we must only evaluate it once. // computed using a function call, so we must only evaluate it once.
...@@ -4730,10 +5474,8 @@ Map_type::do_traverse(Traverse* traverse) ...@@ -4730,10 +5474,8 @@ Map_type::do_traverse(Traverse* traverse)
bool bool
Map_type::do_verify() Map_type::do_verify()
{ {
if (this->key_type_->struct_type() != NULL // The runtime support uses "map[void]void".
|| this->key_type_->array_type() != NULL if (!this->key_type_->is_comparable() && !this->key_type_->is_void_type())
|| this->key_type_->function_type() != NULL
|| this->key_type_->map_type() != NULL)
{ {
error_at(this->location_, "invalid map key type"); error_at(this->location_, "invalid map key type");
return false; return false;
...@@ -6292,6 +7034,21 @@ Named_type::is_named_error_type() const ...@@ -6292,6 +7034,21 @@ Named_type::is_named_error_type() const
return ret; return ret;
} }
// Whether this type is comparable. We have to be careful about
// circular type definitions.
bool
Named_type::named_type_is_comparable(std::string* reason) const
{
if (this->seen_)
return false;
this->seen_ = true;
bool ret = Type::are_compatible_for_comparison(true, this->type_,
this->type_, reason);
this->seen_ = false;
return ret;
}
// Add a method to this type. // Add a method to this type.
Named_object* Named_object*
...@@ -6367,6 +7124,20 @@ Named_type::is_unexported_local_method(Gogo* gogo, ...@@ -6367,6 +7124,20 @@ Named_type::is_unexported_local_method(Gogo* gogo,
void void
Named_type::finalize_methods(Gogo* gogo) Named_type::finalize_methods(Gogo* gogo)
{ {
// If this type needs explicit comparison and hash functions, create
// them now. It would be a bit better to do this only if the
// functions are needed, but they will be static so the backend can
// discard them if they are not used.
if ((this->struct_type() != NULL
|| (this->array_type() != NULL && !this->is_slice_type()))
&& !this->compare_is_identity()
&& this->is_comparable())
{
Named_object* hash_fn;
Named_object* equal_fn;
this->type_functions(gogo, this, NULL, NULL, &hash_fn, &equal_fn);
}
if (this->all_methods_ != NULL) if (this->all_methods_ != NULL)
return; return;
...@@ -6616,6 +7387,20 @@ Named_type::do_has_pointer() const ...@@ -6616,6 +7387,20 @@ Named_type::do_has_pointer() const
return ret; return ret;
} }
// Return whether comparisons for this type can use the identity
// function.
bool
Named_type::do_compare_is_identity() const
{
if (this->seen_)
return false;
this->seen_ = true;
bool ret = this->type_->compare_is_identity();
this->seen_ = false;
return ret;
}
// Return a hash code. This is used for method lookup. We simply // Return a hash code. This is used for method lookup. We simply
// hash on the name itself. // hash on the name itself.
......
...@@ -522,6 +522,21 @@ class Type ...@@ -522,6 +522,21 @@ class Type
static bool static bool
are_compatible_for_binop(const Type* t1, const Type* t2); are_compatible_for_binop(const Type* t1, const Type* t2);
// Return true if two types are compatible for use with the
// comparison operator. IS_EQUALITY_OP is true if this is an
// equality comparison, false if it is an ordered comparison. This
// is an equivalence relation. If this returns false, and REASON is
// not NULL, it sets *REASON.
static bool
are_compatible_for_comparison(bool is_equality_op, const Type *t1,
const Type *t2, std::string* reason);
// Return true if a type is comparable with itself. This is true of
// most types, but false for, e.g., function types.
bool
is_comparable() const
{ return Type::are_compatible_for_comparison(true, this, this, NULL); }
// Return true if a value with type RHS is assignable to a variable // Return true if a value with type RHS is assignable to a variable
// with type LHS. This is not an equivalence relation. If this // with type LHS. This is not an equivalence relation. If this
// returns false, and REASON is not NULL, it sets *REASON. // returns false, and REASON is not NULL, it sets *REASON.
...@@ -549,6 +564,13 @@ class Type ...@@ -549,6 +564,13 @@ class Type
bool bool
has_hidden_fields(const Named_type* within, std::string* reason) const; has_hidden_fields(const Named_type* within, std::string* reason) const;
// Return true if values of this type can be compared using an
// identity function which gets nothing but a pointer to the value
// and a size.
bool
compare_is_identity() const
{ return this->do_compare_is_identity(); }
// Return a hash code for this type for the method hash table. // Return a hash code for this type for the method hash table.
// Types which are equivalent according to are_identical will have // Types which are equivalent according to are_identical will have
// the same hash code. // the same hash code.
...@@ -839,6 +861,20 @@ class Type ...@@ -839,6 +861,20 @@ class Type
std::string std::string
mangled_name(Gogo*) const; mangled_name(Gogo*) const;
// Get the hash and equality functions for a type.
void
type_functions(Gogo*, Named_type* name, Function_type* hash_fntype,
Function_type* equal_fntype, Named_object** hash_fn,
Named_object** equal_fn);
// Write the hash and equality type functions.
void
write_specific_type_functions(Gogo*, Named_type*,
const std::string& hash_name,
Function_type* hash_fntype,
const std::string& equal_name,
Function_type* equal_fntype);
// Export the type. // Export the type.
void void
export_type(Export* exp) const export_type(Export* exp) const
...@@ -866,6 +902,9 @@ class Type ...@@ -866,6 +902,9 @@ class Type
do_has_pointer() const do_has_pointer() const
{ return false; } { return false; }
virtual bool
do_compare_is_identity() const = 0;
virtual unsigned int virtual unsigned int
do_hash_for_method(Gogo*) const; do_hash_for_method(Gogo*) const;
...@@ -878,7 +917,6 @@ class Type ...@@ -878,7 +917,6 @@ class Type
virtual void virtual void
do_reflection(Gogo*, std::string*) const = 0; do_reflection(Gogo*, std::string*) const = 0;
virtual void virtual void
do_mangled_name(Gogo*, std::string*) const = 0; do_mangled_name(Gogo*, std::string*) const = 0;
...@@ -1002,18 +1040,24 @@ class Type ...@@ -1002,18 +1040,24 @@ class Type
void void
make_type_descriptor_var(Gogo*); make_type_descriptor_var(Gogo*);
// Return the name of the type descriptor variable for an unnamed // Return the name of the type descriptor variable. If NAME is not
// type. // NULL, it is the name to use.
std::string std::string
unnamed_type_descriptor_var_name(Gogo*); type_descriptor_var_name(Gogo*, Named_type* name);
// Return the name of the type descriptor variable for a named type. // Return true if the type descriptor for this type should be
std::string // defined in some other package. If NAME is not NULL, it is the
type_descriptor_var_name(Gogo*); // name of this type. If this returns true it sets *PACKAGE to the
// package where the type descriptor is defined.
bool
type_descriptor_defined_elsewhere(Named_type* name, const Package** package);
// Get the hash and equality functions for a type. // Build the hash and equality type functions for a type which needs
// specific functions.
void void
type_functions(const char** hash_fn, const char** equal_fn) const; specific_type_functions(Gogo*, Named_type*, Function_type* hash_fntype,
Function_type* equal_fntype, Named_object** hash_fn,
Named_object** equal_fn);
// Build a composite literal for the uncommon type information. // Build a composite literal for the uncommon type information.
Expression* Expression*
...@@ -1097,6 +1141,14 @@ class Type ...@@ -1097,6 +1141,14 @@ class Type
// A list of builtin named types. // A list of builtin named types.
static std::vector<Named_type*> named_builtin_types; static std::vector<Named_type*> named_builtin_types;
// A map from types which need specific type functions to the type
// functions themselves.
typedef std::pair<Named_object*, Named_object*> Hash_equal_fn;
typedef Unordered_map_hash(const Type*, Hash_equal_fn, Type_hash_identical,
Type_identical) Type_functions;
static Type_functions type_functions_table;
// The type classification. // The type classification.
Type_classification classification_; Type_classification classification_;
// The backend representation of the type, once it has been // The backend representation of the type, once it has been
...@@ -1314,6 +1366,10 @@ class Integer_type : public Type ...@@ -1314,6 +1366,10 @@ class Integer_type : public Type
is_identical(const Integer_type* t) const; is_identical(const Integer_type* t) const;
protected: protected:
bool
do_compare_is_identity() const
{ return true; }
unsigned int unsigned int
do_hash_for_method(Gogo*) const; do_hash_for_method(Gogo*) const;
...@@ -1383,6 +1439,10 @@ class Float_type : public Type ...@@ -1383,6 +1439,10 @@ class Float_type : public Type
is_identical(const Float_type* t) const; is_identical(const Float_type* t) const;
protected: protected:
bool
do_compare_is_identity() const
{ return false; }
unsigned int unsigned int
do_hash_for_method(Gogo*) const; do_hash_for_method(Gogo*) const;
...@@ -1448,6 +1508,10 @@ class Complex_type : public Type ...@@ -1448,6 +1508,10 @@ class Complex_type : public Type
is_identical(const Complex_type* t) const; is_identical(const Complex_type* t) const;
protected: protected:
bool
do_compare_is_identity() const
{ return false; }
unsigned int unsigned int
do_hash_for_method(Gogo*) const; do_hash_for_method(Gogo*) const;
...@@ -1504,6 +1568,10 @@ class String_type : public Type ...@@ -1504,6 +1568,10 @@ class String_type : public Type
do_has_pointer() const do_has_pointer() const
{ return true; } { return true; }
bool
do_compare_is_identity() const
{ return false; }
Btype* Btype*
do_get_backend(Gogo*); do_get_backend(Gogo*);
...@@ -1618,6 +1686,10 @@ class Function_type : public Type ...@@ -1618,6 +1686,10 @@ class Function_type : public Type
do_has_pointer() const do_has_pointer() const
{ return true; } { return true; }
bool
do_compare_is_identity() const
{ return false; }
unsigned int unsigned int
do_hash_for_method(Gogo*) const; do_hash_for_method(Gogo*) const;
...@@ -1699,6 +1771,10 @@ class Pointer_type : public Type ...@@ -1699,6 +1771,10 @@ class Pointer_type : public Type
do_has_pointer() const do_has_pointer() const
{ return true; } { return true; }
bool
do_compare_is_identity() const
{ return true; }
unsigned int unsigned int
do_hash_for_method(Gogo*) const; do_hash_for_method(Gogo*) const;
...@@ -1944,6 +2020,14 @@ class Struct_type : public Type ...@@ -1944,6 +2020,14 @@ class Struct_type : public Type
static Type* static Type*
make_struct_type_descriptor_type(); make_struct_type_descriptor_type();
// Write the hash function for this type.
void
write_hash_function(Gogo*, Named_type*, Function_type*, Function_type*);
// Write the equality function for this type.
void
write_equal_function(Gogo*, Named_type*);
protected: protected:
int int
do_traverse(Traverse*); do_traverse(Traverse*);
...@@ -1954,6 +2038,9 @@ class Struct_type : public Type ...@@ -1954,6 +2038,9 @@ class Struct_type : public Type
bool bool
do_has_pointer() const; do_has_pointer() const;
bool
do_compare_is_identity() const;
unsigned int unsigned int
do_hash_for_method(Gogo*) const; do_hash_for_method(Gogo*) const;
...@@ -2022,6 +2109,10 @@ class Array_type : public Type ...@@ -2022,6 +2109,10 @@ class Array_type : public Type
array_has_hidden_fields(const Named_type* within, std::string* reason) const array_has_hidden_fields(const Named_type* within, std::string* reason) const
{ return this->element_type_->has_hidden_fields(within, reason); } { return this->element_type_->has_hidden_fields(within, reason); }
// Build the hash and equality functions if necessary.
void
finalize_methods(Gogo*);
// Return a tree for the pointer to the values in an array. // Return a tree for the pointer to the values in an array.
tree tree
value_pointer_tree(Gogo*, tree array) const; value_pointer_tree(Gogo*, tree array) const;
...@@ -2052,6 +2143,14 @@ class Array_type : public Type ...@@ -2052,6 +2143,14 @@ class Array_type : public Type
static Type* static Type*
make_slice_type_descriptor_type(); make_slice_type_descriptor_type();
// Write the hash function for this type.
void
write_hash_function(Gogo*, Named_type*, Function_type*, Function_type*);
// Write the equality function for this type.
void
write_equal_function(Gogo*, Named_type*);
protected: protected:
int int
do_traverse(Traverse* traverse); do_traverse(Traverse* traverse);
...@@ -2065,6 +2164,13 @@ class Array_type : public Type ...@@ -2065,6 +2164,13 @@ class Array_type : public Type
return this->length_ == NULL || this->element_type_->has_pointer(); return this->length_ == NULL || this->element_type_->has_pointer();
} }
bool
do_compare_is_identity() const
{
return (this->length_ != NULL
&& this->element_type_->compare_is_identity());
}
unsigned int unsigned int
do_hash_for_method(Gogo*) const; do_hash_for_method(Gogo*) const;
...@@ -2155,6 +2261,10 @@ class Map_type : public Type ...@@ -2155,6 +2261,10 @@ class Map_type : public Type
do_has_pointer() const do_has_pointer() const
{ return true; } { return true; }
bool
do_compare_is_identity() const
{ return false; }
unsigned int unsigned int
do_hash_for_method(Gogo*) const; do_hash_for_method(Gogo*) const;
...@@ -2237,6 +2347,10 @@ class Channel_type : public Type ...@@ -2237,6 +2347,10 @@ class Channel_type : public Type
do_has_pointer() const do_has_pointer() const
{ return true; } { return true; }
bool
do_compare_is_identity() const
{ return true; }
unsigned int unsigned int
do_hash_for_method(Gogo*) const; do_hash_for_method(Gogo*) const;
...@@ -2348,6 +2462,10 @@ class Interface_type : public Type ...@@ -2348,6 +2462,10 @@ class Interface_type : public Type
do_has_pointer() const do_has_pointer() const
{ return true; } { return true; }
bool
do_compare_is_identity() const
{ return false; }
unsigned int unsigned int
do_hash_for_method(Gogo*) const; do_hash_for_method(Gogo*) const;
...@@ -2480,6 +2598,11 @@ class Named_type : public Type ...@@ -2480,6 +2598,11 @@ class Named_type : public Type
bool bool
is_named_error_type() const; is_named_error_type() const;
// Return whether this type is comparable. If REASON is not NULL,
// set *REASON when returning false.
bool
named_type_is_comparable(std::string* reason) const;
// Add a method to this type. // Add a method to this type.
Named_object* Named_object*
add_method(const std::string& name, Function*); add_method(const std::string& name, Function*);
...@@ -2572,6 +2695,9 @@ class Named_type : public Type ...@@ -2572,6 +2695,9 @@ class Named_type : public Type
bool bool
do_has_pointer() const; do_has_pointer() const;
bool
do_compare_is_identity() const;
unsigned int unsigned int
do_hash_for_method(Gogo*) const; do_hash_for_method(Gogo*) const;
...@@ -2704,6 +2830,10 @@ class Forward_declaration_type : public Type ...@@ -2704,6 +2830,10 @@ class Forward_declaration_type : public Type
do_has_pointer() const do_has_pointer() const
{ return this->real_type()->has_pointer(); } { return this->real_type()->has_pointer(); }
bool
do_compare_is_identity() const
{ return this->real_type()->compare_is_identity(); }
unsigned int unsigned int
do_hash_for_method(Gogo* gogo) const do_hash_for_method(Gogo* gogo) const
{ return this->real_type()->hash_for_method(gogo); } { return this->real_type()->hash_for_method(gogo); }
......
...@@ -11,7 +11,7 @@ func use(bool) {} ...@@ -11,7 +11,7 @@ func use(bool) {}
type T1 *int type T1 *int
type T2 *int type T2 *int
type T3 struct {} type T3 struct{ z []int }
var t3 T3 var t3 T3
...@@ -21,12 +21,12 @@ func main() { ...@@ -21,12 +21,12 @@ func main() {
// so chan int can be compared against // so chan int can be compared against
// directional channels but channel of different // directional channels but channel of different
// direction cannot be compared against each other. // direction cannot be compared against each other.
var c1 chan <-int var c1 chan<- int
var c2 <-chan int var c2 <-chan int
var c3 chan int var c3 chan int
use(c1 == c2) // ERROR "invalid operation|incompatible" use(c1 == c2) // ERROR "invalid operation|incompatible"
use(c2 == c1) // ERROR "invalid operation|incompatible" use(c2 == c1) // ERROR "invalid operation|incompatible"
use(c1 == c3) use(c1 == c3)
use(c2 == c2) use(c2 == c2)
use(c3 == c1) use(c3 == c1)
...@@ -36,14 +36,32 @@ func main() { ...@@ -36,14 +36,32 @@ func main() {
var p1 T1 var p1 T1
var p2 T2 var p2 T2
var p3 *int var p3 *int
use(p1 == p2) // ERROR "invalid operation|incompatible" use(p1 == p2) // ERROR "invalid operation|incompatible"
use(p2 == p1) // ERROR "invalid operation|incompatible" use(p2 == p1) // ERROR "invalid operation|incompatible"
use(p1 == p3) use(p1 == p3)
use(p2 == p2) use(p2 == p2)
use(p3 == p1) use(p3 == p1)
use(p3 == p2) use(p3 == p2)
// Comparison of structs should have a good message // Comparison of structs should have a good message
use(t3 == t3) // ERROR "struct|expected" use(t3 == t3) // ERROR "struct|expected"
// Slices, functions, and maps too.
var x []int
var f func()
var m map[int]int
use(x == x) // ERROR "slice can only be compared to nil"
use(f == f) // ERROR "func can only be compared to nil"
use(m == m) // ERROR "map can only be compared to nil"
// Comparison with interface that cannot return true
// (would panic).
var i interface{}
use(i == x) // ERROR "invalid operation"
use(x == i) // ERROR "invalid operation"
use(i == f) // ERROR "invalid operation"
use(f == i) // ERROR "invalid operation"
use(i == m) // ERROR "invalid operation"
use(m == i) // ERROR "invalid operation"
} }
...@@ -60,6 +60,7 @@ func test4() { ...@@ -60,6 +60,7 @@ func test4() {
type T struct { type T struct {
a, b int a, b int
c []int
} }
func test5() { func test5() {
......
...@@ -460,8 +460,10 @@ runtime_files = \ ...@@ -460,8 +460,10 @@ runtime_files = \
runtime/go-strplus.c \ runtime/go-strplus.c \
runtime/go-strslice.c \ runtime/go-strslice.c \
runtime/go-trampoline.c \ runtime/go-trampoline.c \
runtime/go-type-complex.c \
runtime/go-type-eface.c \ runtime/go-type-eface.c \
runtime/go-type-error.c \ runtime/go-type-error.c \
runtime/go-type-float.c \
runtime/go-type-identity.c \ runtime/go-type-identity.c \
runtime/go-type-interface.c \ runtime/go-type-interface.c \
runtime/go-type-string.c \ runtime/go-type-string.c \
......
...@@ -208,7 +208,8 @@ am__libgo_la_SOURCES_DIST = runtime/go-append.c runtime/go-assert.c \ ...@@ -208,7 +208,8 @@ am__libgo_la_SOURCES_DIST = runtime/go-append.c runtime/go-assert.c \
runtime/go-string-to-byte-array.c \ runtime/go-string-to-byte-array.c \
runtime/go-string-to-int-array.c runtime/go-strplus.c \ runtime/go-string-to-int-array.c runtime/go-strplus.c \
runtime/go-strslice.c runtime/go-trampoline.c \ runtime/go-strslice.c runtime/go-trampoline.c \
runtime/go-type-eface.c runtime/go-type-error.c \ runtime/go-type-complex.c runtime/go-type-eface.c \
runtime/go-type-error.c runtime/go-type-float.c \
runtime/go-type-identity.c runtime/go-type-interface.c \ runtime/go-type-identity.c runtime/go-type-interface.c \
runtime/go-type-string.c runtime/go-typedesc-equal.c \ runtime/go-type-string.c runtime/go-typedesc-equal.c \
runtime/go-typestring.c runtime/go-unreflect.c \ runtime/go-typestring.c runtime/go-unreflect.c \
...@@ -242,13 +243,14 @@ am__objects_4 = go-append.lo go-assert.lo go-assert-interface.lo \ ...@@ -242,13 +243,14 @@ am__objects_4 = go-append.lo go-assert.lo go-assert-interface.lo \
go-reflect-map.lo go-rune.lo go-runtime-error.lo go-setenv.lo \ go-reflect-map.lo go-rune.lo go-runtime-error.lo go-setenv.lo \
go-signal.lo go-strcmp.lo go-string-to-byte-array.lo \ go-signal.lo go-strcmp.lo go-string-to-byte-array.lo \
go-string-to-int-array.lo go-strplus.lo go-strslice.lo \ go-string-to-int-array.lo go-strplus.lo go-strslice.lo \
go-trampoline.lo go-type-eface.lo go-type-error.lo \ go-trampoline.lo go-type-complex.lo go-type-eface.lo \
go-type-identity.lo go-type-interface.lo go-type-string.lo \ go-type-error.lo go-type-float.lo go-type-identity.lo \
go-typedesc-equal.lo go-typestring.lo go-unreflect.lo \ go-type-interface.lo go-type-string.lo go-typedesc-equal.lo \
go-unsafe-new.lo go-unsafe-newarray.lo go-unsafe-pointer.lo \ go-typestring.lo go-unreflect.lo go-unsafe-new.lo \
go-unwind.lo chan.lo cpuprof.lo $(am__objects_1) mcache.lo \ go-unsafe-newarray.lo go-unsafe-pointer.lo go-unwind.lo \
mcentral.lo $(am__objects_2) mfinal.lo mfixalloc.lo mgc0.lo \ chan.lo cpuprof.lo $(am__objects_1) mcache.lo mcentral.lo \
mheap.lo msize.lo proc.lo runtime.lo thread.lo yield.lo \ $(am__objects_2) mfinal.lo mfixalloc.lo mgc0.lo mheap.lo \
msize.lo proc.lo runtime.lo thread.lo yield.lo \
$(am__objects_3) iface.lo malloc.lo map.lo mprof.lo reflect.lo \ $(am__objects_3) iface.lo malloc.lo map.lo mprof.lo reflect.lo \
runtime1.lo sema.lo sigqueue.lo string.lo time.lo runtime1.lo sema.lo sigqueue.lo string.lo time.lo
am_libgo_la_OBJECTS = $(am__objects_4) am_libgo_la_OBJECTS = $(am__objects_4)
...@@ -881,8 +883,10 @@ runtime_files = \ ...@@ -881,8 +883,10 @@ runtime_files = \
runtime/go-strplus.c \ runtime/go-strplus.c \
runtime/go-strslice.c \ runtime/go-strslice.c \
runtime/go-trampoline.c \ runtime/go-trampoline.c \
runtime/go-type-complex.c \
runtime/go-type-eface.c \ runtime/go-type-eface.c \
runtime/go-type-error.c \ runtime/go-type-error.c \
runtime/go-type-float.c \
runtime/go-type-identity.c \ runtime/go-type-identity.c \
runtime/go-type-interface.c \ runtime/go-type-interface.c \
runtime/go-type-string.c \ runtime/go-type-string.c \
...@@ -2523,8 +2527,10 @@ distclean-compile: ...@@ -2523,8 +2527,10 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-strplus.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-strplus.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-strslice.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-strslice.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-trampoline.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-trampoline.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-type-complex.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-type-eface.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-type-eface.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-type-error.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-type-error.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-type-float.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-type-identity.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-type-identity.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-type-interface.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-type-interface.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-type-string.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-type-string.Plo@am__quote@
...@@ -2928,6 +2934,13 @@ go-trampoline.lo: runtime/go-trampoline.c ...@@ -2928,6 +2934,13 @@ go-trampoline.lo: runtime/go-trampoline.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o go-trampoline.lo `test -f 'runtime/go-trampoline.c' || echo '$(srcdir)/'`runtime/go-trampoline.c @am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o go-trampoline.lo `test -f 'runtime/go-trampoline.c' || echo '$(srcdir)/'`runtime/go-trampoline.c
go-type-complex.lo: runtime/go-type-complex.c
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT go-type-complex.lo -MD -MP -MF $(DEPDIR)/go-type-complex.Tpo -c -o go-type-complex.lo `test -f 'runtime/go-type-complex.c' || echo '$(srcdir)/'`runtime/go-type-complex.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-type-complex.Tpo $(DEPDIR)/go-type-complex.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/go-type-complex.c' object='go-type-complex.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o go-type-complex.lo `test -f 'runtime/go-type-complex.c' || echo '$(srcdir)/'`runtime/go-type-complex.c
go-type-eface.lo: runtime/go-type-eface.c go-type-eface.lo: runtime/go-type-eface.c
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT go-type-eface.lo -MD -MP -MF $(DEPDIR)/go-type-eface.Tpo -c -o go-type-eface.lo `test -f 'runtime/go-type-eface.c' || echo '$(srcdir)/'`runtime/go-type-eface.c @am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT go-type-eface.lo -MD -MP -MF $(DEPDIR)/go-type-eface.Tpo -c -o go-type-eface.lo `test -f 'runtime/go-type-eface.c' || echo '$(srcdir)/'`runtime/go-type-eface.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-type-eface.Tpo $(DEPDIR)/go-type-eface.Plo @am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-type-eface.Tpo $(DEPDIR)/go-type-eface.Plo
...@@ -2942,6 +2955,13 @@ go-type-error.lo: runtime/go-type-error.c ...@@ -2942,6 +2955,13 @@ go-type-error.lo: runtime/go-type-error.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o go-type-error.lo `test -f 'runtime/go-type-error.c' || echo '$(srcdir)/'`runtime/go-type-error.c @am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o go-type-error.lo `test -f 'runtime/go-type-error.c' || echo '$(srcdir)/'`runtime/go-type-error.c
go-type-float.lo: runtime/go-type-float.c
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT go-type-float.lo -MD -MP -MF $(DEPDIR)/go-type-float.Tpo -c -o go-type-float.lo `test -f 'runtime/go-type-float.c' || echo '$(srcdir)/'`runtime/go-type-float.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-type-float.Tpo $(DEPDIR)/go-type-float.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/go-type-float.c' object='go-type-float.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o go-type-float.lo `test -f 'runtime/go-type-float.c' || echo '$(srcdir)/'`runtime/go-type-float.c
go-type-identity.lo: runtime/go-type-identity.c go-type-identity.lo: runtime/go-type-identity.c
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT go-type-identity.lo -MD -MP -MF $(DEPDIR)/go-type-identity.Tpo -c -o go-type-identity.lo `test -f 'runtime/go-type-identity.c' || echo '$(srcdir)/'`runtime/go-type-identity.c @am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT go-type-identity.lo -MD -MP -MF $(DEPDIR)/go-type-identity.Tpo -c -o go-type-identity.lo `test -f 'runtime/go-type-identity.c' || echo '$(srcdir)/'`runtime/go-type-identity.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-type-identity.Tpo $(DEPDIR)/go-type-identity.Plo @am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-type-identity.Tpo $(DEPDIR)/go-type-identity.Plo
......
/* go-type-complex.c -- hash and equality complex functions.
Copyright 2012 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 "runtime.h"
#include "go-type.h"
/* The 64-bit type. */
typedef unsigned int DItype __attribute__ ((mode (DI)));
/* Hash function for float types. */
uintptr_t
__go_type_hash_complex (const void *vkey, uintptr_t key_size)
{
if (key_size == 8)
{
union
{
unsigned char a[8];
__complex float cf;
DItype di;
} ucf;
__complex float cf;
float cfr;
float cfi;
__builtin_memcpy (ucf.a, vkey, 8);
cf = ucf.cf;
cfr = __builtin_crealf (cf);
cfi = __builtin_cimagf (cf);
if (__builtin_isinff (cfr) || __builtin_isinff (cfi)
|| __builtin_isnanf (cfr) || __builtin_isnanf (cfi))
return 0;
/* Avoid negative zero. */
if (cfr == 0 && cfi == 0)
return 0;
else if (cfr == 0)
ucf.cf = cfi * 1.0iF;
else if (cfi == 0)
ucf.cf = cfr;
return ucf.di;
}
else if (key_size == 16)
{
union
{
unsigned char a[16];
__complex double cd;
DItype adi[2];
} ucd;
__complex double cd;
double cdr;
double cdi;
__builtin_memcpy (ucd.a, vkey, 16);
cd = ucd.cd;
cdr = __builtin_crealf (cd);
cdi = __builtin_cimagf (cd);
if (__builtin_isinf (cdr) || __builtin_isinf (cdi)
|| __builtin_isnan (cdr) || __builtin_isnan (cdi))
return 0;
/* Avoid negative zero. */
if (cdr == 0 && cdi == 0)
return 0;
else if (cdr == 0)
ucd.cd = cdi * 1.0i;
else if (cdi == 0)
ucd.cd = cdr;
return ucd.adi[0] ^ ucd.adi[1];
}
else
runtime_throw ("__go_type_hash_complex: invalid complex size");
}
/* Equality function for complex types. */
_Bool
__go_type_equal_complex (const void *vk1, const void *vk2, uintptr_t key_size)
{
if (key_size == 8)
{
union
{
unsigned char a[8];
__complex float cf;
} ucf;
__complex float cf1;
__complex float cf2;
__builtin_memcpy (ucf.a, vk1, 8);
cf1 = ucf.cf;
__builtin_memcpy (ucf.a, vk2, 8);
cf2 = ucf.cf;
return cf1 == cf2;
}
else if (key_size == 16)
{
union
{
unsigned char a[16];
__complex double cd;
} ucd;
__complex double cd1;
__complex double cd2;
__builtin_memcpy (ucd.a, vk1, 16);
cd1 = ucd.cd;
__builtin_memcpy (ucd.a, vk2, 16);
cd2 = ucd.cd;
return cd1 == cd2;
}
else
runtime_throw ("__go_type_equal_complex: invalid complex size");
}
...@@ -10,13 +10,13 @@ ...@@ -10,13 +10,13 @@
/* A hash function for an empty interface. */ /* A hash function for an empty interface. */
size_t uintptr_t
__go_type_hash_empty_interface (const void *vval, __go_type_hash_empty_interface (const void *vval,
size_t key_size __attribute__ ((unused))) uintptr_t key_size __attribute__ ((unused)))
{ {
const struct __go_empty_interface *val; const struct __go_empty_interface *val;
const struct __go_type_descriptor *descriptor; const struct __go_type_descriptor *descriptor;
size_t size; uintptr_t size;
val = (const struct __go_empty_interface *) vval; val = (const struct __go_empty_interface *) vval;
descriptor = val->__type_descriptor; descriptor = val->__type_descriptor;
...@@ -33,7 +33,7 @@ __go_type_hash_empty_interface (const void *vval, ...@@ -33,7 +33,7 @@ __go_type_hash_empty_interface (const void *vval,
_Bool _Bool
__go_type_equal_empty_interface (const void *vv1, const void *vv2, __go_type_equal_empty_interface (const void *vv1, const void *vv2,
size_t key_size __attribute__ ((unused))) uintptr_t key_size __attribute__ ((unused)))
{ {
const struct __go_empty_interface *v1; const struct __go_empty_interface *v1;
const struct __go_empty_interface *v2; const struct __go_empty_interface *v2;
......
...@@ -10,9 +10,9 @@ ...@@ -10,9 +10,9 @@
/* A hash function used for a type which does not support hash /* A hash function used for a type which does not support hash
functions. */ functions. */
size_t uintptr_t
__go_type_hash_error (const void *val __attribute__ ((unused)), __go_type_hash_error (const void *val __attribute__ ((unused)),
size_t key_size __attribute__ ((unused))) uintptr_t key_size __attribute__ ((unused)))
{ {
runtime_panicstring ("hash of unhashable type"); runtime_panicstring ("hash of unhashable type");
} }
...@@ -22,7 +22,7 @@ __go_type_hash_error (const void *val __attribute__ ((unused)), ...@@ -22,7 +22,7 @@ __go_type_hash_error (const void *val __attribute__ ((unused)),
_Bool _Bool
__go_type_equal_error (const void *v1 __attribute__ ((unused)), __go_type_equal_error (const void *v1 __attribute__ ((unused)),
const void *v2 __attribute__ ((unused)), const void *v2 __attribute__ ((unused)),
size_t key_size __attribute__ ((unused))) uintptr_t key_size __attribute__ ((unused)))
{ {
runtime_panicstring ("comparing uncomparable types"); runtime_panicstring ("comparing uncomparable types");
} }
/* go-type-float.c -- hash and equality float functions.
Copyright 2012 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 "runtime.h"
#include "go-type.h"
/* The 32-bit and 64-bit types. */
typedef unsigned int SItype __attribute__ ((mode (SI)));
typedef unsigned int DItype __attribute__ ((mode (DI)));
/* Hash function for float types. */
uintptr_t
__go_type_hash_float (const void *vkey, uintptr_t key_size)
{
if (key_size == 4)
{
union
{
unsigned char a[4];
float f;
SItype si;
} uf;
float f;
__builtin_memcpy (uf.a, vkey, 4);
f = uf.f;
if (__builtin_isinff (f) || __builtin_isnanf (f) || f == 0)
return 0;
return (uintptr_t) uf.si;
}
else if (key_size == 8)
{
union
{
unsigned char a[8];
double d;
DItype di;
} ud;
double d;
__builtin_memcpy (ud.a, vkey, 8);
d = ud.d;
if (__builtin_isinf (d) || __builtin_isnan (d) || d == 0)
return 0;
return (uintptr_t) ud.di;
}
else
runtime_throw ("__go_type_hash_float: invalid float size");
}
/* Equality function for float types. */
_Bool
__go_type_equal_float (const void *vk1, const void *vk2, uintptr_t key_size)
{
if (key_size == 4)
{
union
{
unsigned char a[4];
float f;
} uf;
float f1;
float f2;
__builtin_memcpy (uf.a, vk1, 4);
f1 = uf.f;
__builtin_memcpy (uf.a, vk2, 4);
f2 = uf.f;
return f1 == f2;
}
else if (key_size == 8)
{
union
{
unsigned char a[8];
double d;
DItype di;
} ud;
double d1;
double d2;
__builtin_memcpy (ud.a, vk1, 8);
d1 = ud.d;
__builtin_memcpy (ud.a, vk2, 8);
d2 = ud.d;
return d1 == d2;
}
else
runtime_throw ("__go_type_equal_float: invalid float size");
}
...@@ -8,35 +8,37 @@ ...@@ -8,35 +8,37 @@
#include "go-type.h" #include "go-type.h"
/* Typedefs for accesses of different sizes. */ /* The 64-bit type. */
typedef int QItype __attribute__ ((mode (QI))); typedef unsigned int DItype __attribute__ ((mode (DI)));
typedef int HItype __attribute__ ((mode (HI)));
typedef int SItype __attribute__ ((mode (SI)));
typedef int DItype __attribute__ ((mode (DI)));
/* An identity hash function for a type. This is used for types where /* An identity hash function for a type. This is used for types where
we can simply use the type value itself as a hash code. This is we can simply use the type value itself as a hash code. This is
true of, e.g., integers and pointers. */ true of, e.g., integers and pointers. */
size_t uintptr_t
__go_type_hash_identity (const void *key, size_t key_size) __go_type_hash_identity (const void *key, uintptr_t key_size)
{ {
switch (key_size) uintptr_t ret;
uintptr_t i;
const unsigned char *p;
if (key_size <= 8)
{ {
case 1: union
return *(const QItype *) key; {
case 2: DItype v;
return *(const HItype *) key; unsigned char a[8];
case 3: } u;
case 4: u.v = 0;
case 5: __builtin_memcpy (&u.a, key, key_size);
case 6: return (uintptr_t) u.v;
case 7:
return *(const SItype *) key;
default:
return *(const DItype *) key;
} }
ret = 5381;
for (i = 0, p = (const unsigned char *) key; i < key_size; i++, p++)
ret = ret * 33 + *p;
return ret;
} }
/* An identity equality function for a type. This is used for types /* An identity equality function for a type. This is used for types
...@@ -44,7 +46,7 @@ __go_type_hash_identity (const void *key, size_t key_size) ...@@ -44,7 +46,7 @@ __go_type_hash_identity (const void *key, size_t key_size)
the same bits. */ the same bits. */
_Bool _Bool
__go_type_equal_identity (const void *k1, const void *k2, size_t key_size) __go_type_equal_identity (const void *k1, const void *k2, uintptr_t key_size)
{ {
return __builtin_memcmp (k1, k2, key_size) == 0; return __builtin_memcmp (k1, k2, key_size) == 0;
} }
...@@ -9,13 +9,13 @@ ...@@ -9,13 +9,13 @@
/* A hash function for an interface. */ /* A hash function for an interface. */
size_t uintptr_t
__go_type_hash_interface (const void *vval, __go_type_hash_interface (const void *vval,
size_t key_size __attribute__ ((unused))) uintptr_t key_size __attribute__ ((unused)))
{ {
const struct __go_interface *val; const struct __go_interface *val;
const struct __go_type_descriptor *descriptor; const struct __go_type_descriptor *descriptor;
size_t size; uintptr_t size;
val = (const struct __go_interface *) vval; val = (const struct __go_interface *) vval;
if (val->__methods == NULL) if (val->__methods == NULL)
...@@ -32,7 +32,7 @@ __go_type_hash_interface (const void *vval, ...@@ -32,7 +32,7 @@ __go_type_hash_interface (const void *vval,
_Bool _Bool
__go_type_equal_interface (const void *vv1, const void *vv2, __go_type_equal_interface (const void *vv1, const void *vv2,
size_t key_size __attribute__ ((unused))) uintptr_t key_size __attribute__ ((unused)))
{ {
const struct __go_interface *v1; const struct __go_interface *v1;
const struct __go_interface *v2; const struct __go_interface *v2;
......
...@@ -11,14 +11,14 @@ ...@@ -11,14 +11,14 @@
/* A string hash function for a map. */ /* A string hash function for a map. */
size_t uintptr_t
__go_type_hash_string (const void *vkey, __go_type_hash_string (const void *vkey,
size_t key_size __attribute__ ((unused))) uintptr_t key_size __attribute__ ((unused)))
{ {
size_t ret; uintptr_t ret;
const struct __go_string *key; const struct __go_string *key;
size_t len; int len;
size_t i; int i;
const unsigned char *p; const unsigned char *p;
ret = 5381; ret = 5381;
...@@ -33,7 +33,7 @@ __go_type_hash_string (const void *vkey, ...@@ -33,7 +33,7 @@ __go_type_hash_string (const void *vkey,
_Bool _Bool
__go_type_equal_string (const void *vk1, const void *vk2, __go_type_equal_string (const void *vk1, const void *vk2,
size_t key_size __attribute__ ((unused))) uintptr_t key_size __attribute__ ((unused)))
{ {
const struct __go_string *k1; const struct __go_string *k1;
const struct __go_string *k2; const struct __go_string *k2;
......
...@@ -86,11 +86,11 @@ struct __go_type_descriptor ...@@ -86,11 +86,11 @@ struct __go_type_descriptor
size of this type, and returns a hash code. We pass the size size of this type, and returns a hash code. We pass the size
explicitly becaues it means that we can share a single instance explicitly becaues it means that we can share a single instance
of this function for various different types. */ of this function for various different types. */
size_t (*__hashfn) (const void *, size_t); uintptr_t (*__hashfn) (const void *, uintptr_t);
/* This function takes two pointers to values of this type, and the /* This function takes two pointers to values of this type, and the
size of this type, and returns whether the values are equal. */ size of this type, and returns whether the values are equal. */
_Bool (*__equalfn) (const void *, const void *, size_t); _Bool (*__equalfn) (const void *, const void *, uintptr_t);
/* A string describing this type. This is only used for /* A string describing this type. This is only used for
debugging. */ debugging. */
...@@ -317,13 +317,17 @@ extern _Bool ...@@ -317,13 +317,17 @@ extern _Bool
__go_type_descriptors_equal(const struct __go_type_descriptor*, __go_type_descriptors_equal(const struct __go_type_descriptor*,
const struct __go_type_descriptor*); const struct __go_type_descriptor*);
extern size_t __go_type_hash_identity (const void *, size_t); extern uintptr_t __go_type_hash_identity (const void *, uintptr_t);
extern _Bool __go_type_equal_identity (const void *, const void *, size_t); extern _Bool __go_type_equal_identity (const void *, const void *, uintptr_t);
extern size_t __go_type_hash_string (const void *, size_t); extern uintptr_t __go_type_hash_string (const void *, uintptr_t);
extern _Bool __go_type_equal_string (const void *, const void *, size_t); extern _Bool __go_type_equal_string (const void *, const void *, uintptr_t);
extern size_t __go_type_hash_interface (const void *, size_t); extern uintptr_t __go_type_hash_float (const void *, uintptr_t);
extern _Bool __go_type_equal_interface (const void *, const void *, size_t); extern _Bool __go_type_equal_float (const void *, const void *, uintptr_t);
extern size_t __go_type_hash_error (const void *, size_t); extern uintptr_t __go_type_hash_complex (const void *, uintptr_t);
extern _Bool __go_type_equal_error (const void *, const void *, size_t); extern _Bool __go_type_equal_complex (const void *, const void *, uintptr_t);
extern uintptr_t __go_type_hash_interface (const void *, uintptr_t);
extern _Bool __go_type_equal_interface (const void *, const void *, uintptr_t);
extern uintptr_t __go_type_hash_error (const void *, uintptr_t);
extern _Bool __go_type_equal_error (const void *, const void *, uintptr_t);
#endif /* !defined(LIBGO_GO_TYPE_H) */ #endif /* !defined(LIBGO_GO_TYPE_H) */
...@@ -322,18 +322,18 @@ localname() { ...@@ -322,18 +322,18 @@ localname() {
pattern='Test([^a-z].*)?' pattern='Test([^a-z].*)?'
# The -p option tells GNU nm not to sort. # The -p option tells GNU nm not to sort.
# The -v option tells Solaris nm to sort by value. # The -v option tells Solaris nm to sort by value.
tests=$($NM -p -v _gotest_.o $xofile | egrep ' T .*\.'$pattern'$' | grep -v '\..*\..*\.' | sed 's/.* //' | sed 's/.*\.\(.*\.\)/\1/') tests=$($NM -p -v _gotest_.o $xofile | egrep ' T .*\.'$pattern'$' | grep -v '\..*\..*\.' | fgrep -v '$' | sed 's/.* //' | sed 's/.*\.\(.*\.\)/\1/')
if [ "x$tests" = x ]; then if [ "x$tests" = x ]; then
echo 'gotest: warning: no tests matching '$pattern in _gotest_.o $xofile 1>&2 echo 'gotest: warning: no tests matching '$pattern in _gotest_.o $xofile 1>&2
exit 2 exit 2
fi fi
# benchmarks are named BenchmarkFoo. # benchmarks are named BenchmarkFoo.
pattern='Benchmark([^a-z].*)?' pattern='Benchmark([^a-z].*)?'
benchmarks=$($NM -p -v _gotest_.o $xofile | egrep ' T .*\.'$pattern'$' | grep -v '\..*\..*\.' | sed 's/.* //' | sed 's/.*\.\(.*\.\)/\1/') benchmarks=$($NM -p -v _gotest_.o $xofile | egrep ' T .*\.'$pattern'$' | grep -v '\..*\..*\.' | fgrep -v '$' | sed 's/.* //' | sed 's/.*\.\(.*\.\)/\1/')
# examples are named ExampleFoo # examples are named ExampleFoo
pattern='Example([^a-z].*)?' pattern='Example([^a-z].*)?'
examples=$($NM -p -v _gotest_.o $xofile | egrep ' T .*\.'$pattern'$' | grep -v '\..*\..*\.' | sed 's/.* //' | sed 's/.*\.\(.*\.\)/\1/') examples=$($NM -p -v _gotest_.o $xofile | egrep ' T .*\.'$pattern'$' | grep -v '\..*\..*\.' | fgrep -v '$' | sed 's/.* //' | sed 's/.*\.\(.*\.\)/\1/')
# package spec # package spec
echo 'package main' echo 'package main'
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment