}
}
- // Lower struct and array comparisons.
+ // Lower struct, array, and some interface comparisons.
if (op == OPERATOR_EQEQ || op == OPERATOR_NOTEQ)
{
if (left->type()->struct_type() != NULL)
else if (left->type()->array_type() != NULL
&& !left->type()->is_slice_type())
return this->lower_array_comparison(gogo, inserter);
+ else if ((left->type()->interface_type() != NULL
+ && right->type()->interface_type() == NULL)
+ || (left->type()->interface_type() == NULL
+ && right->type()->interface_type() != NULL))
+ return this->lower_interface_value_comparison(gogo, inserter);
}
return this;
return ret;
}
+// Lower an interface to value comparison.
+
+Expression*
+Binary_expression::lower_interface_value_comparison(Gogo*,
+ Statement_inserter* inserter)
+{
+ Type* left_type = this->left_->type();
+ Type* right_type = this->right_->type();
+ Interface_type* ift;
+ if (left_type->interface_type() != NULL)
+ {
+ ift = left_type->interface_type();
+ if (!ift->implements_interface(right_type, NULL))
+ return this;
+ }
+ else
+ {
+ ift = right_type->interface_type();
+ if (!ift->implements_interface(left_type, NULL))
+ return this;
+ }
+ if (!Type::are_compatible_for_comparison(true, left_type, right_type, NULL))
+ return this;
+
+ Location loc = this->location();
+
+ if (left_type->interface_type() == NULL
+ && left_type->points_to() == NULL
+ && !this->left_->is_addressable())
+ {
+ Temporary_statement* temp =
+ Statement::make_temporary(left_type, NULL, loc);
+ inserter->insert(temp);
+ this->left_ =
+ Expression::make_set_and_use_temporary(temp, this->left_, loc);
+ }
+
+ if (right_type->interface_type() == NULL
+ && right_type->points_to() == NULL
+ && !this->right_->is_addressable())
+ {
+ Temporary_statement* temp =
+ Statement::make_temporary(right_type, NULL, loc);
+ inserter->insert(temp);
+ this->right_ =
+ Expression::make_set_and_use_temporary(temp, this->right_, loc);
+ }
+
+ return this;
+}
+
// Lower a struct or array comparison to a call to memcmp.
Expression*
case OPERATOR_GT:
case OPERATOR_GE:
return Expression::comparison_tree(context, this->type_, this->op_,
- this->left_->type(), left,
- this->right_->type(), right,
+ this->left_, this->right_,
this->location());
case OPERATOR_OROR:
tree
Expression::comparison_tree(Translate_context* context, Type* result_type,
- Operator op, Type* left_type, tree left_tree,
- Type* right_type, tree right_tree,
- Location location)
+ Operator op, Expression* left_expr,
+ Expression* right_expr, Location location)
{
- Type* int_type = Type::lookup_integer_type("int");
- tree int_type_tree = type_to_tree(int_type->get_backend(context->gogo()));
+ Type* left_type = left_expr->type();
+ Type* right_type = right_expr->type();
+
+ mpz_t zval;
+ mpz_init_set_ui(zval, 0UL);
+ Expression* zexpr = Expression::make_integer(&zval, NULL, location);
+ mpz_clear(zval);
enum tree_code code;
switch (op)
go_unreachable();
}
+ // FIXME: Computing the tree here means it will be computed multiple times,
+ // which is wasteful. This is a temporary modification until all tree code
+ // here can be replaced with frontend expressions.
+ tree left_tree = left_expr->get_tree(context);
+ tree right_tree = right_expr->get_tree(context);
if (left_type->is_string_type() && right_type->is_string_type())
{
- Type* st = Type::make_string_type();
- tree string_type = type_to_tree(st->get_backend(context->gogo()));
- static tree string_compare_decl;
- left_tree = Gogo::call_builtin(&string_compare_decl,
- location,
- "__go_strcmp",
- 2,
- int_type_tree,
- string_type,
- left_tree,
- string_type,
- right_tree);
- right_tree = build_int_cst_type(int_type_tree, 0);
+ Expression* strcmp_call = Runtime::make_call(Runtime::STRCMP, location, 2,
+ left_expr, right_expr);
+ left_tree = strcmp_call->get_tree(context);
+ right_tree = zexpr->get_tree(context);
}
else if ((left_type->interface_type() != NULL
&& right_type->interface_type() == NULL
if (left_type->interface_type() == NULL)
{
std::swap(left_type, right_type);
- std::swap(left_tree, right_tree);
+ std::swap(left_expr, right_expr);
}
// The right operand is not an interface. We need to take its
// address if it is not a pointer.
- tree make_tmp;
- tree arg;
+ Expression* pointer_arg = NULL;
if (right_type->points_to() != NULL)
- {
- make_tmp = NULL_TREE;
- arg = right_tree;
- }
- else if (TREE_ADDRESSABLE(TREE_TYPE(right_tree))
- || (TREE_CODE(right_tree) != CONST_DECL
- && DECL_P(right_tree)))
- {
- make_tmp = NULL_TREE;
- arg = build_fold_addr_expr_loc(location.gcc_location(), right_tree);
- if (DECL_P(right_tree))
- TREE_ADDRESSABLE(right_tree) = 1;
- }
- else
- {
- tree tmp = create_tmp_var(TREE_TYPE(right_tree),
- get_name(right_tree));
- DECL_IGNORED_P(tmp) = 0;
- DECL_INITIAL(tmp) = right_tree;
- TREE_ADDRESSABLE(tmp) = 1;
- make_tmp = build1(DECL_EXPR, void_type_node, tmp);
- SET_EXPR_LOCATION(make_tmp, location.gcc_location());
- arg = build_fold_addr_expr_loc(location.gcc_location(), tmp);
- }
- arg = fold_convert_loc(location.gcc_location(), ptr_type_node, arg);
-
- Bexpression* descriptor_bexpr =
- right_type->type_descriptor_pointer(context->gogo(), location);
- tree descriptor = expr_to_tree(descriptor_bexpr);
-
- if (left_type->interface_type()->is_empty())
- {
- static tree empty_interface_value_compare_decl;
- left_tree = Gogo::call_builtin(&empty_interface_value_compare_decl,
- location,
- "__go_empty_interface_value_compare",
- 3,
- int_type_tree,
- TREE_TYPE(left_tree),
- left_tree,
- TREE_TYPE(descriptor),
- descriptor,
- ptr_type_node,
- arg);
- if (left_tree == error_mark_node)
- return error_mark_node;
- // This can panic if the type is not comparable.
- TREE_NOTHROW(empty_interface_value_compare_decl) = 0;
- }
+ pointer_arg = right_expr;
else
{
- static tree interface_value_compare_decl;
- left_tree = Gogo::call_builtin(&interface_value_compare_decl,
- location,
- "__go_interface_value_compare",
- 3,
- int_type_tree,
- TREE_TYPE(left_tree),
- left_tree,
- TREE_TYPE(descriptor),
- descriptor,
- ptr_type_node,
- arg);
- if (left_tree == error_mark_node)
- return error_mark_node;
- // This can panic if the type is not comparable.
- TREE_NOTHROW(interface_value_compare_decl) = 0;
+ go_assert(right_expr->is_addressable());
+ pointer_arg = Expression::make_unary(OPERATOR_AND, right_expr,
+ location);
}
- right_tree = build_int_cst_type(int_type_tree, 0);
- if (make_tmp != NULL_TREE)
- left_tree = build2(COMPOUND_EXPR, TREE_TYPE(left_tree), make_tmp,
- left_tree);
+ Expression* descriptor_expr = Expression::make_type_descriptor(right_type,
+ location);
+ Call_expression* iface_valcmp =
+ Runtime::make_call((left_type->interface_type()->is_empty()
+ ? Runtime::EMPTY_INTERFACE_VALUE_COMPARE
+ : Runtime::INTERFACE_VALUE_COMPARE),
+ location, 3, left_expr, descriptor_expr,
+ pointer_arg);
+ left_tree = iface_valcmp->get_tree(context);
+ right_tree = zexpr->get_tree(context);
}
else if (left_type->interface_type() != NULL
&& right_type->interface_type() != NULL)
{
+ Runtime::Function compare_function;
if (left_type->interface_type()->is_empty()
&& right_type->interface_type()->is_empty())
- {
- static tree empty_interface_compare_decl;
- left_tree = Gogo::call_builtin(&empty_interface_compare_decl,
- location,
- "__go_empty_interface_compare",
- 2,
- int_type_tree,
- TREE_TYPE(left_tree),
- left_tree,
- TREE_TYPE(right_tree),
- right_tree);
- if (left_tree == error_mark_node)
- return error_mark_node;
- // This can panic if the type is uncomparable.
- TREE_NOTHROW(empty_interface_compare_decl) = 0;
- }
+ compare_function = Runtime::EMPTY_INTERFACE_COMPARE;
else if (!left_type->interface_type()->is_empty()
&& !right_type->interface_type()->is_empty())
- {
- static tree interface_compare_decl;
- left_tree = Gogo::call_builtin(&interface_compare_decl,
- location,
- "__go_interface_compare",
- 2,
- int_type_tree,
- TREE_TYPE(left_tree),
- left_tree,
- TREE_TYPE(right_tree),
- right_tree);
- if (left_tree == error_mark_node)
- return error_mark_node;
- // This can panic if the type is uncomparable.
- TREE_NOTHROW(interface_compare_decl) = 0;
- }
+ compare_function = Runtime::INTERFACE_COMPARE;
else
{
if (left_type->interface_type()->is_empty())
{
go_assert(op == OPERATOR_EQEQ || op == OPERATOR_NOTEQ);
std::swap(left_type, right_type);
- std::swap(left_tree, right_tree);
+ std::swap(left_expr, right_expr);
}
go_assert(!left_type->interface_type()->is_empty());
go_assert(right_type->interface_type()->is_empty());
- static tree interface_empty_compare_decl;
- left_tree = Gogo::call_builtin(&interface_empty_compare_decl,
- location,
- "__go_interface_empty_compare",
- 2,
- int_type_tree,
- TREE_TYPE(left_tree),
- left_tree,
- TREE_TYPE(right_tree),
- right_tree);
- if (left_tree == error_mark_node)
- return error_mark_node;
- // This can panic if the type is uncomparable.
- TREE_NOTHROW(interface_empty_compare_decl) = 0;
+ compare_function = Runtime::INTERFACE_EMPTY_COMPARE;
}
- right_tree = build_int_cst_type(int_type_tree, 0);
+ Call_expression* ifacecmp_call =
+ Runtime::make_call(compare_function, location, 2,
+ left_expr, right_expr);
+
+ left_tree = ifacecmp_call->get_tree(context);
+ right_tree = zexpr->get_tree(context);
}
if (left_type->is_nil_type()
// Note that we are evaluating this->expr_ twice, but that is OK
// because in the lowering pass we forced it into a temporary
// variable.
- tree expr_tree = this->expr_->get_tree(context);
tree nil_check_tree = Expression::comparison_tree(context,
Type::lookup_bool_type(),
OPERATOR_EQEQ,
- this->expr_->type(),
- expr_tree,
- Type::make_nil_type(),
- null_pointer_node,
+ this->expr_,
+ Expression::make_nil(loc),
loc);
tree crash = context->gogo()->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE,
loc);