This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[gccgo] Lower tuple type assertion assignment statement
- From: Ian Lance Taylor <iant at google dot com>
- To: gcc-patches at gcc dot gnu dot org
- Date: Sat, 16 Jan 2010 21:15:22 -0800
- Subject: [gccgo] Lower tuple type assertion assignment statement
I committed this patch to the gccgo branch to lower a tuple type
assertion statement (r, ok = v.(type)).
Ian
Index: libgo/runtime/iface.cgo
===================================================================
--- libgo/runtime/iface.cgo (revision 0)
+++ libgo/runtime/iface.cgo (revision 0)
@@ -0,0 +1,36 @@
+// Copyright 2010 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.
+
+package runtime
+#include "go-type.h"
+#include "interface.h"
+#define nil NULL
+
+typedef _Bool bool;
+typedef struct __go_type_descriptor descriptor;
+typedef struct __go_interface interface;
+
+func ifaceI2I2(inter *descriptor, i *interface) (ret *interface, ok bool) {
+ ret = __go_convert_interface(inter, i, &ok);
+}
+
+func ifaceI2T2P(inter *descriptor, i *interface) (ret *void, ok bool) {
+ if (i != nil && __go_type_descriptors_equal(inter, i->__type_descriptor)) {
+ ret = i->__object;
+ ok = 1;
+ } else {
+ ret = nil;
+ ok = 0;
+ }
+}
+
+func ifaceI2T2(inter *descriptor, i *interface, ret *void) (ok bool) {
+ if (i != nil && __go_type_descriptors_equal(inter, i->__type_descriptor)) {
+ __builtin_memcpy(ret, i->__object, inter->__size);
+ ok = 1;
+ } else {
+ __builtin_memset(ret, 0, inter->__size);
+ ok = 0;
+ }
+}
Index: libgo/Makefile.am
===================================================================
--- libgo/Makefile.am (revision 155971)
+++ libgo/Makefile.am (working copy)
@@ -311,6 +311,7 @@ runtime_files = \
runtime/proc.c \
malloc_go.c \
chan.c \
+ iface.c \
map.c \
sigqueue.c \
string.c
Index: gcc/go/types.h
===================================================================
--- gcc/go/types.h (revision 155628)
+++ gcc/go/types.h (working copy)
@@ -201,6 +201,9 @@ class Type
static Interface_type*
make_interface_type(Typed_identifier_list* methods, source_location);
+ static Type*
+ make_type_descriptor_ptr_type();
+
static Named_type*
make_named_type(Named_object*, Type*, source_location);
Index: gcc/go/expressions.h
===================================================================
--- gcc/go/expressions.h (revision 155952)
+++ gcc/go/expressions.h (working copy)
@@ -90,7 +90,8 @@ class Expression
EXPRESSION_RECEIVE,
EXPRESSION_SEND,
EXPRESSION_REFCOUNT_ADJUST,
- EXPRESSION_REFCOUNT_DECREMENT_LVALUE
+ EXPRESSION_REFCOUNT_DECREMENT_LVALUE,
+ EXPRESSION_TYPE_DESCRIPTOR
};
Expression(Expression_classification, source_location);
@@ -275,6 +276,11 @@ class Expression
static Expression*
make_refcount_decrement_lvalue(Refcounts*, Expression*);
+ // Make an expression which evaluates to the type descriptor of a
+ // type.
+ static Expression*
+ make_type_descriptor(Type* type, source_location);
+
// Return the expression classification.
Expression_classification
classification() const
Index: gcc/go/statements.cc
===================================================================
--- gcc/go/statements.cc (revision 155971)
+++ gcc/go/statements.cc (working copy)
@@ -1350,7 +1350,7 @@ Statement::make_tuple_receive_assignment
}
// An assignment to a pair of values from a type guard. This is a
-// conditional type guard.
+// conditional type guard. v, ok = i.(type).
class Tuple_type_guard_assignment_statement : public Statement
{
@@ -1367,18 +1367,26 @@ class Tuple_type_guard_assignment_statem
do_traverse(Traverse*);
bool
- do_traverse_assignments(Traverse_assignments*);
+ do_traverse_assignments(Traverse_assignments*)
+ { gcc_unreachable(); }
+
+ Statement*
+ do_lower(Gogo*, Block*);
+
+ tree
+ do_get_tree(Translate_context*)
+ { gcc_unreachable(); }
+ private:
void
- do_determine_types();
+ lower_to_interface(Block*);
void
- do_check_types(Gogo*);
+ lower_to_pointer_type(Block*);
- tree
- do_get_tree(Translate_context*);
+ void
+ lower_to_type(Block*);
- private:
// The variable which recieves the converted value.
Expression* val_;
// The variable which receives the indication of success.
@@ -1401,225 +1409,161 @@ Tuple_type_guard_assignment_statement::d
return this->traverse_expression(traverse, &this->expr_);
}
-bool
-Tuple_type_guard_assignment_statement::do_traverse_assignments(
- Traverse_assignments* tassign)
+// Lower to a function call.
+
+Statement*
+Tuple_type_guard_assignment_statement::do_lower(Gogo*, Block* enclosing)
{
- tassign->assignment(&this->val_, NULL);
- tassign->assignment(&this->ok_, NULL);
- tassign->value(&this->expr_, false, this->val_->is_local_variable());
- return true;
-}
+ source_location loc = this->location();
-// Determine types of a type guard tuple assignment.
+ if (this->expr_->type()->interface_type() == NULL)
+ {
+ this->report_error(_("type assertion only valid for interface types"));
+ return Statement::make_error_statement(loc);
+ }
-void
-Tuple_type_guard_assignment_statement::do_determine_types()
-{
- this->val_->determine_type_no_context();
- Type_context subcontext(Type::lookup_bool_type(), false);
- this->ok_->determine_type(&subcontext);
- this->expr_->determine_type_no_context();
+ Block* b = new Block(enclosing, loc);
+
+ // Make sure that any subexpressions on the left hand side are
+ // evaluated in the right order.
+ Move_ordered_evals moe(b);
+ this->val_->traverse_subexpressions(&moe);
+ this->ok_->traverse_subexpressions(&moe);
+
+ if (this->type_->interface_type() != NULL)
+ this->lower_to_interface(b);
+ else if (this->type_->points_to() != NULL)
+ this->lower_to_pointer_type(b);
+ else
+ this->lower_to_type(b);
+
+ return Statement::make_block_statement(b, loc);
}
-// Check types of a type guard tuple assignment.
+// Lower a conversion to an interface type.
void
-Tuple_type_guard_assignment_statement::do_check_types(Gogo*)
+Tuple_type_guard_assignment_statement::lower_to_interface(Block* b)
{
- if (this->expr_->type()->interface_type() == NULL)
- {
- this->report_error(_("type guard only valid for interface types"));
- return;
- }
+ source_location loc = this->location();
- if (!this->val_->is_lvalue() || !this->ok_->is_lvalue())
- {
- this->report_error(_("invalid left hand side of assignment"));
- return;
- }
+ // func ifaceI2I2(*descriptor, *interface) (*interface, bool)
+ source_location bloc = BUILTINS_LOCATION;
+ Typed_identifier_list* param_types = new Typed_identifier_list();
+ param_types->push_back(Typed_identifier("inter",
+ Type::make_type_descriptor_ptr_type(),
+ bloc));
+ param_types->push_back(Typed_identifier("i", this->expr_->type(), bloc));
+ Typed_identifier_list* ret_types = new Typed_identifier_list();
+ ret_types->push_back(Typed_identifier("ret", this->type_, bloc));
+ ret_types->push_back(Typed_identifier("ok", Type::lookup_bool_type(), bloc));
+ Function_type* fntype = Type::make_function_type(NULL, param_types,
+ ret_types, bloc);
+ Named_object* ifaceI2I2 =
+ Named_object::make_function_declaration("ifaceI2I2", NULL, fntype, bloc);
+ ifaceI2I2->func_declaration_value()->set_asm_name("runtime.ifaceI2I2");
- std::string reason;
- if (!Type::are_compatible_for_assign(this->val_->type(), this->type_,
- &reason))
- {
- if (reason.empty())
- error_at(this->val_->location(),
- "incompatible types for type guard value");
- else
- error_at(this->val_->location(),
- "incompatible types for type guard value (%s)",
- reason.c_str());
- this->set_is_error();
- }
+ // val, ok = ifaceI2I2(type_descriptor, expr)
+ Expression* func = Expression::make_func_reference(ifaceI2I2, NULL, loc);
+ Expression_list* params = new Expression_list();
+ params->push_back(Expression::make_type_descriptor(this->type_, loc));
+ params->push_back(this->expr_);
+ Call_expression* call = Expression::make_call(func, params, loc);
- if (!Type::are_compatible_for_assign(this->ok_->type(),
- Type::lookup_bool_type(), &reason))
- {
- if (reason.empty())
- error_at(this->ok_->location(), "incompatible type for type guard");
- else
- error_at(this->ok_->location(), "incompatible type for type guard (%s)",
- reason.c_str());
- this->set_is_error();
- }
+ Expression* res = Expression::make_call_result(call, 0);
+ Statement* s = Statement::make_assignment(OPERATOR_EQ, this->val_, res,
+ loc);
+ b->add_statement(s);
+
+ res = Expression::make_call_result(call, 1);
+ s = Statement::make_assignment(OPERATOR_EQ, this->ok_, res, loc);
+ b->add_statement(s);
}
-// Return the tree for a type guard tuple statement. The right hand
-// side (THIS->EXPR_) is an interface type. There are two cases to
-// consider: whether or not we are converting to an interface type.
+// Lower a conversion to a pointer type.
-tree
-Tuple_type_guard_assignment_statement::do_get_tree(Translate_context* context)
+void
+Tuple_type_guard_assignment_statement::lower_to_pointer_type(Block* b)
{
- Gogo* gogo = context->gogo();
+ source_location loc = this->location();
- tree expr_tree = this->expr_->get_tree(context);
- if (expr_tree == error_mark_node)
- return error_mark_node;
+ // func ifaceI2T2P(*descriptor, *interface) (T, bool)
+ source_location bloc = BUILTINS_LOCATION;
+ Typed_identifier_list* param_types = new Typed_identifier_list();
+ param_types->push_back(Typed_identifier("inter",
+ Type::make_type_descriptor_ptr_type(),
+ bloc));
+ param_types->push_back(Typed_identifier("i", this->expr_->type(), bloc));
+ Typed_identifier_list* ret_types = new Typed_identifier_list();
+ ret_types->push_back(Typed_identifier("ret", this->type_, bloc));
+ ret_types->push_back(Typed_identifier("ok", Type::lookup_bool_type(), bloc));
+ Function_type* fntype = Type::make_function_type(NULL, param_types,
+ ret_types, bloc);
+ Named_object* ifaceI2T2P =
+ Named_object::make_function_declaration("ifaceI2T2P", NULL, fntype, bloc);
+ ifaceI2T2P->func_declaration_value()->set_asm_name("runtime.ifaceI2T2P");
- source_location location = this->location();
+ // val, ok = ifaceI2T2P(type_descriptor, expr)
+ Expression* func = Expression::make_func_reference(ifaceI2T2P, NULL, loc);
+ Expression_list* params = new Expression_list();
+ params->push_back(Expression::make_type_descriptor(this->type_, loc));
+ params->push_back(this->expr_);
+ Call_expression* call = Expression::make_call(func, params, loc);
- gcc_assert(this->expr_->type()->interface_type() != NULL);
+ Expression* res = Expression::make_call_result(call, 0);
+ Statement* s = Statement::make_assignment(OPERATOR_EQ, this->val_, res,
+ loc);
+ b->add_statement(s);
- Interface_type* interface_type = this->type_->interface_type();
- if (interface_type != NULL)
- {
- tree lhs_type_descriptor = interface_type->type_descriptor(gogo);
- gcc_assert(POINTER_TYPE_P(TREE_TYPE(expr_tree)));
+ res = Expression::make_call_result(call, 1);
+ s = Statement::make_assignment(OPERATOR_EQ, this->ok_, res, loc);
+ b->add_statement(s);
+}
- tree stmt_list = NULL_TREE;
+// Lower a conversion to a non-interface non-pointer type.
- tree ok_tree = this->ok_->get_tree(context);
- if (ok_tree == error_mark_node)
- return error_mark_node;
-
- tree tmp;
- tree ok_addr;
- if (TREE_CODE(ok_tree) == VAR_DECL)
- {
- tmp = NULL_TREE;
- gcc_assert(TREE_CODE(TREE_TYPE(ok_tree)) == BOOLEAN_TYPE);
- ok_addr = build_fold_addr_expr(ok_tree);
- TREE_ADDRESSABLE(ok_tree) = 1;
- }
- else
- {
- tmp = create_tmp_var(boolean_type_node, get_name(boolean_type_node));
- DECL_IGNORED_P(tmp) = 0;
- TREE_ADDRESSABLE(tmp) = 1;
- tree make_tmp = build1(DECL_EXPR, void_type_node, tmp);
- SET_EXPR_LOCATION(make_tmp, location);
- append_to_statement_list(make_tmp, &stmt_list);
- ok_addr = build_fold_addr_expr(tmp);
- }
+void
+Tuple_type_guard_assignment_statement::lower_to_type(Block* b)
+{
+ source_location loc = this->location();
- static tree convert_interface_decl;
- tree call = Gogo::call_builtin(&convert_interface_decl,
- location,
- "__go_convert_interface",
- 3,
- ptr_type_node,
- TREE_TYPE(lhs_type_descriptor),
- lhs_type_descriptor,
- ptr_type_node,
- fold_convert(ptr_type_node, expr_tree),
- build_pointer_type(boolean_type_node),
- ok_addr);
- call = save_expr(call);
- append_to_statement_list(call, &stmt_list);
- if (tmp != NULL_TREE)
- append_to_statement_list(build2(MODIFY_EXPR, void_type_node,
- ok_tree, tmp),
- &stmt_list);
- else
- tmp = ok_tree;
+ // var val_temp TYPE
+ Temporary_statement* val_temp = Statement::make_temporary(this->type_, NULL,
+ loc);
+ b->add_statement(val_temp);
- call = fold_convert(interface_type->get_tree(gogo), call);
- tree assign = Assignment_statement::get_assignment_tree(context,
- OPERATOR_EQ,
- this->val_,
- NULL_TREE,
- NULL,
- this->type_,
- call,
- location);
+ // func ifaceI2T2(*descriptor, *interface, *T) bool
+ source_location bloc = BUILTINS_LOCATION;
+ Typed_identifier_list* param_types = new Typed_identifier_list();
+ param_types->push_back(Typed_identifier("inter",
+ Type::make_type_descriptor_ptr_type(),
+ bloc));
+ param_types->push_back(Typed_identifier("i", this->expr_->type(), bloc));
+ Type* ptype = Type::make_pointer_type(this->type_);
+ param_types->push_back(Typed_identifier("v", ptype, bloc));
+ Typed_identifier_list* ret_types = new Typed_identifier_list();
+ ret_types->push_back(Typed_identifier("ok", Type::lookup_bool_type(), bloc));
+ Function_type* fntype = Type::make_function_type(NULL, param_types,
+ ret_types, bloc);
+ Named_object* ifaceI2T2 =
+ Named_object::make_function_declaration("ifaceI2T2", NULL, fntype, bloc);
+ ifaceI2T2->func_declaration_value()->set_asm_name("runtime.ifaceI2T2");
- append_to_statement_list(build3(COND_EXPR, void_type_node, tmp, assign,
- NULL_TREE),
- &stmt_list);
+ // ok = ifaceI2T2(type_descriptor, expr, &val_temp)
+ Expression* func = Expression::make_func_reference(ifaceI2T2, NULL, loc);
+ Expression_list* params = new Expression_list();
+ params->push_back(Expression::make_type_descriptor(this->type_, loc));
+ params->push_back(this->expr_);
+ Expression* ref = Expression::make_temporary_reference(val_temp, loc);
+ params->push_back(Expression::make_unary(OPERATOR_AND, ref, loc));
+ Expression* call = Expression::make_call(func, params, loc);
+ Statement* s = Statement::make_assignment(OPERATOR_EQ, this->ok_, call, loc);
+ b->add_statement(s);
- return stmt_list;
- }
- else
- {
- // We are converting from an interface to a plain type. This is
- // OK if the type descriptors are the same.
- expr_tree = save_expr(expr_tree);
- gcc_assert(POINTER_TYPE_P(TREE_TYPE(expr_tree)));
- tree struct_type = TREE_TYPE(TREE_TYPE(expr_tree));
- gcc_assert(TREE_CODE(struct_type) == RECORD_TYPE);
- tree field = TYPE_FIELDS(struct_type);
- gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)),
- "__type_descriptor") == 0);
- tree rhs_td = build3(COMPONENT_REF, TREE_TYPE(field),
- build_fold_indirect_ref(expr_tree),
- field, NULL_TREE);
-
- tree lhs_td = this->type_->type_descriptor(gogo);
-
- tree ok_true =
- Assignment_statement::get_assignment_tree(context,
- OPERATOR_EQ,
- this->ok_,
- NULL_TREE,
- NULL,
- Type::lookup_bool_type(),
- boolean_true_node,
- location);
- tree ok_false =
- Assignment_statement::get_assignment_tree(context,
- OPERATOR_EQ,
- this->ok_,
- NULL_TREE,
- NULL,
- Type::lookup_bool_type(),
- boolean_false_node,
- location);
- tree val_true =
- Assignment_statement::get_assignment_tree(context,
- OPERATOR_EQ,
- this->val_,
- NULL_TREE,
- this->expr_,
- NULL,
- expr_tree,
- location);
-
- static tree type_descriptors_equal_fndecl;
- tree compare = Gogo::call_builtin(&type_descriptors_equal_fndecl,
- location,
- "__go_type_descriptors_equal",
- 2,
- boolean_type_node,
- TREE_TYPE(lhs_td),
- lhs_td,
- TREE_TYPE(rhs_td),
- rhs_td);
-
- tree is_nil = fold_build2(EQ_EXPR, boolean_type_node, expr_tree,
- fold_convert(TREE_TYPE(expr_tree),
- null_pointer_node));
-
- return fold_build3(COND_EXPR, void_type_node,
- is_nil,
- ok_false,
- build3(COND_EXPR, void_type_node,
- compare,
- build2(COMPOUND_EXPR, void_type_node, ok_true,
- val_true),
- ok_false));
- }
+ // val = val_temp
+ ref = Expression::make_temporary_reference(val_temp, loc);
+ s = Statement::make_assignment(OPERATOR_EQ, this->val_, ref, loc);
+ b->add_statement(s);
}
// Make an assignment from a type guard to a pair of variables.
Index: gcc/go/statements.h
===================================================================
--- gcc/go/statements.h (revision 155971)
+++ gcc/go/statements.h (working copy)
@@ -91,7 +91,6 @@ class Statement
STATEMENT_TEMPORARY,
STATEMENT_DESTROY_TEMPORARY,
STATEMENT_ASSIGNMENT,
- STATEMENT_TUPLE_TYPE_GUARD_ASSIGNMENT,
STATEMENT_EXPRESSION,
STATEMENT_BLOCK,
STATEMENT_INCDEC,
@@ -115,6 +114,7 @@ class Statement
STATEMENT_TUPLE_MAP_ASSIGNMENT,
STATEMENT_MAP_ASSIGNMENT,
STATEMENT_TUPLE_RECEIVE_ASSIGNMENT,
+ STATEMENT_TUPLE_TYPE_GUARD_ASSIGNMENT,
STATEMENT_FOR,
STATEMENT_FOR_RANGE
};
Index: gcc/go/types.cc
===================================================================
--- gcc/go/types.cc (revision 155628)
+++ gcc/go/types.cc (working copy)
@@ -4632,6 +4632,39 @@ Type::make_interface_type(Typed_identifi
return new Interface_type(methods, location);
}
+// Make the type of a pointer to a type descriptor as represented in
+// Go. We should really tie this to runtime.Type rather than copying
+// it.
+
+Type*
+Type::make_type_descriptor_ptr_type()
+{
+ static Type* ret;
+ if (ret == NULL)
+ {
+ source_location bloc = BUILTINS_LOCATION;
+ Struct_field_list* sfl = new Struct_field_list();
+ Type* uint8_type = Type::lookup_integer_type("uint8");
+ Type* uintptr_type = Type::lookup_integer_type("uintptr");
+ sfl->push_back(Struct_field(Typed_identifier("Code", uint8_type, bloc)));
+ sfl->push_back(Struct_field(Typed_identifier("align", uint8_type, bloc)));
+ sfl->push_back(Struct_field(Typed_identifier("fieldAlign", uint8_type,
+ bloc)));
+ sfl->push_back(Struct_field(Typed_identifier("size", uintptr_type,
+ bloc)));
+ // We don't try to represent the real function type.
+ Type* fntype = Type::make_function_type(NULL, NULL, NULL, bloc);
+ sfl->push_back(Struct_field(Typed_identifier("hash", fntype, bloc)));
+ sfl->push_back(Struct_field(Typed_identifier("equal", fntype, bloc)));
+ Type* stype = Type::make_pointer_type(Type::lookup_string_type());
+ sfl->push_back(Struct_field(Typed_identifier("string", stype, bloc)));
+ // We omit the pointer to uncommonType.
+ Type* t = Type::make_struct_type(sfl, bloc);
+ ret = Type::make_pointer_type(t);
+ }
+ return ret;
+}
+
// Class Method.
// Bind a method to an object.
Index: gcc/go/expressions.cc
===================================================================
--- gcc/go/expressions.cc (revision 155952)
+++ gcc/go/expressions.cc (working copy)
@@ -10562,6 +10562,47 @@ Refcount_decrement_lvalue_expression::se
return fold_build2(COMPOUND_EXPR, void_type_node, save, set);
}
+// An expression which evaluates to a pointer to the type descriptor
+// of a type.
+
+class Type_descriptor_expression : public Expression
+{
+ public:
+ Type_descriptor_expression(Type* type, source_location location)
+ : Expression(EXPRESSION_TYPE_DESCRIPTOR, location),
+ type_(type)
+ { }
+
+ protected:
+ Type*
+ do_type()
+ { return Type::make_type_descriptor_ptr_type(); }
+
+ void
+ do_determine_type(const Type_context*)
+ { }
+
+ Expression*
+ do_copy()
+ { return this; }
+
+ tree
+ do_get_tree(Translate_context* context)
+ { return this->type_->type_descriptor(context->gogo()); }
+
+ private:
+ // The type for which this is the descriptor.
+ Type* type_;
+};
+
+// Make a type descriptor expression.
+
+Expression*
+Expression::make_type_descriptor(Type* type, source_location location)
+{
+ return new Type_descriptor_expression(type, location);
+}
+
// Make a reference count decrement of an lvalue.
Expression*