-e0b906b13cbc947406c634aaf8b06270292bd7e0
+b42744825e3f2d1d2981eedbb67d6ac6419b8122
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.
}
Expression* obj;
- if (rhs_type->points_to() != NULL)
+ if (rhs_type->is_direct_iface_type())
{
// We are assigning a pointer to the interface; the interface
// holds the pointer itself.
- obj = rhs;
+ obj = unpack_direct_iface(rhs, location);
}
else
{
return Expression::make_interface_value(lhs_type, first_field, obj, location);
}
+// Return an expression for the pointer-typed value of a direct interface
+// type. Specifically, for single field struct or array, get the single
+// field, and do this recursively. The reason for this is that we don't
+// want to assign a struct or an array to a pointer-typed field. The
+// backend may not like that.
+
+Expression*
+Expression::unpack_direct_iface(Expression* rhs, Location loc)
+{
+ Struct_type* st = rhs->type()->struct_type();
+ if (st != NULL)
+ {
+ go_assert(st->field_count() == 1);
+ Expression* field = Expression::make_field_reference(rhs, 0, loc);
+ return unpack_direct_iface(field, loc);
+ }
+ Array_type* at = rhs->type()->array_type();
+ if (at != NULL)
+ {
+ int64_t len;
+ bool ok = at->int_length(&len);
+ go_assert(ok && len == 1);
+ Type* int_type = Type::lookup_integer_type("int");
+ Expression* index = Expression::make_integer_ul(0, int_type, loc);
+ Expression* elem = Expression::make_array_index(rhs, index, NULL, NULL, loc);
+ return unpack_direct_iface(elem, loc);
+ }
+ return rhs;
+}
+
+// The opposite of unpack_direct_iface.
+
+Expression*
+Expression::pack_direct_iface(Type* t, Expression* rhs, Location loc)
+{
+ if (rhs->type() == t)
+ return rhs;
+ Struct_type* st = t->struct_type();
+ if (st != NULL)
+ {
+ Expression_list* vals = new Expression_list();
+ vals->push_back(pack_direct_iface(st->field(0)->type(), rhs, loc));
+ return Expression::make_struct_composite_literal(t, vals, loc);
+ }
+ Array_type* at = t->array_type();
+ if (at != NULL)
+ {
+ Expression_list* vals = new Expression_list();
+ vals->push_back(pack_direct_iface(at->element_type(), rhs, loc));
+ return Expression::make_array_composite_literal(t, vals, loc);
+ }
+ return Expression::make_unsafe_cast(t, rhs, loc);
+}
+
// Return an expression for the type descriptor of RHS.
Expression*
Expression* obj = Expression::make_interface_info(rhs, INTERFACE_INFO_OBJECT,
location);
- // If the value is a pointer, then it is the value we want.
+ // If the value is a direct interface, then it is the value we want.
// Otherwise it points to the value.
- if (lhs_type->points_to() == NULL)
+ if (lhs_type->is_direct_iface_type())
+ obj = Expression::pack_direct_iface(lhs_type, obj, location);
+ else
{
obj = Expression::make_unsafe_cast(Type::make_pointer_type(lhs_type), obj,
location);
&& Type::are_convertible(t, et, NULL));
}
else if (t->map_type() != NULL)
- go_assert(et->map_type() != NULL);
+ go_assert(et->map_type() != NULL || et->points_to() != NULL);
else if (t->channel_type() != NULL)
- go_assert(et->channel_type() != NULL);
+ go_assert(et->channel_type() != NULL || et->points_to() != NULL);
else if (t->points_to() != NULL)
go_assert(et->points_to() != NULL
|| et->channel_type() != NULL
|| et->function_type() != NULL
|| et->integer_type() != NULL
|| et->is_nil_type());
+ else if (t->function_type() != NULL)
+ go_assert(et->points_to() != NULL);
else if (et->is_unsafe_pointer_type())
go_assert(t->points_to() != NULL
|| (t->integer_type() != NULL
|| et->map_type() != NULL
|| et->channel_type() != NULL
|| et->is_nil_type());
- else if (t->function_type() != NULL)
- go_assert(et->points_to() != NULL);
else
go_unreachable();
}
// The right operand is not an interface. We need to take its
- // address if it is not a pointer.
+ // address if it is not a direct interface type.
Expression* pointer_arg = NULL;
- if (right_type->points_to() != NULL)
- pointer_arg = right;
+ if (right_type->is_direct_iface_type())
+ pointer_arg = Expression::unpack_direct_iface(right, location);
else
{
go_assert(right->is_addressable());
if (bme != NULL)
{
Named_object* methodfn = bme->function();
+ Function_type* mft = (methodfn->is_function()
+ ? methodfn->func_value()->type()
+ : methodfn->func_declaration_value()->type());
Expression* first_arg = bme->first_argument();
- // We always pass a pointer when calling a method.
- if (first_arg->type()->points_to() == NULL
- && !first_arg->type()->is_error())
+ // We always pass a pointer when calling a method, except for
+ // direct interface types when calling a value method.
+ if (!first_arg->type()->is_error()
+ && !first_arg->type()->is_direct_iface_type())
{
first_arg = Expression::make_unary(OPERATOR_AND, first_arg, loc);
// We may need to create a temporary variable so that we can
Unary_expression* ue = static_cast<Unary_expression*>(first_arg);
ue->set_create_temp();
}
+ else if (mft->receiver()->type()->points_to() == NULL
+ && first_arg->type()->points_to() != NULL
+ && first_arg->type()->points_to()->is_direct_iface_type())
+ first_arg = Expression::make_dereference(first_arg,
+ Expression::NIL_CHECK_DEFAULT,
+ loc);
// If we are calling a method which was inherited from an
// embedded struct, and the method did not get a stub, then the
else
m = st->method_function(p->name(), &is_ambiguous);
go_assert(m != NULL);
- Named_object* no = m->named_object();
+ Named_object* no =
+ (this->is_pointer_
+ && this->type_->is_direct_iface_type()
+ && m->is_value_method()
+ ? m->iface_stub_object()
+ : m->named_object());
go_assert(no->is_function() || no->is_function_declaration());
- Btype* fcn_btype = m->type()->get_backend_fntype(gogo);
+ Function_type* fcn_type = (no->is_function()
+ ? no->func_value()->type()
+ : no->func_declaration_value()->type());
+ Btype* fcn_btype = fcn_type->get_backend_fntype(gogo);
Backend::Btyped_identifier bmtype(p->name(), fcn_btype, loc);
bstructfields.push_back(bmtype);
static Expression*
check_bounds(Expression* val, Location);
+ // Return an expression for constructing a direct interface type from a
+ // pointer.
+ static Expression*
+ pack_direct_iface(Type*, Expression*, Location);
+
// Dump an expression to a dump constext.
void
dump_expression(Ast_dump_context*) const;
static Expression*
convert_type_to_interface(Type*, Expression*, Location);
+ static Expression*
+ unpack_direct_iface(Expression*, Location);
+
static Expression*
get_interface_type_descriptor(Expression*);
// the receiver is declared as a non-pointer type, then we
// copy the value into a local variable.
if ((*p)->var_value()->is_receiver()
- && (*p)->var_value()->type()->points_to() == NULL)
+ && !(*p)->var_value()->type()->is_direct_iface_type())
{
std::string name = (*p)->name() + ".pointer";
Type* var_type = (*p)->var_value()->type();
else
{
bool is_parameter = this->is_parameter_;
- if (this->is_receiver_ && type->points_to() == NULL)
+ if (this->is_receiver_ && !type->is_direct_iface_type())
is_parameter = false;
if (this->is_in_heap())
{
gogo->add_statement(s);
}
+// Return whether this type is stored directly in an interface's
+// data word.
+//
+// Since finalize_methods runs before type checking, we may see a
+// malformed type like 'type T struct { x T }'. Use a visited map
+// to avoid infinite recursion.
+
+bool
+Type::is_direct_iface_type() const
+{
+ Unordered_set(const Type*) visited;
+ return this->is_direct_iface_type_helper(&visited);
+}
+
+bool
+Type::is_direct_iface_type_helper(Unordered_set(const Type*)* visited) const
+{
+ if (this->points_to() != NULL
+ || this->channel_type() != NULL
+ || this->function_type() != NULL
+ || this->map_type() != NULL)
+ return true;
+
+ std::pair<Unordered_set(const Type*)::iterator, bool> ins
+ = visited->insert(this);
+ if (!ins.second)
+ // malformed circular type
+ return false;
+
+ const Struct_type* st = this->struct_type();
+ if (st != NULL)
+ return (st->field_count() == 1
+ && st->field(0)->type()->is_direct_iface_type_helper(visited));
+ const Array_type* at = this->array_type();
+ if (at != NULL && !at->is_slice_type())
+ {
+ int64_t len;
+ return (at->int_length(&len) && len == 1
+ && at->element_type()->is_direct_iface_type_helper(visited));
+ }
+ return false;
+}
+
// Return a composite literal for the type descriptor for a plain type
// of kind RUNTIME_TYPE_KIND named NAME.
if (!this->has_pointer())
runtime_type_kind |= RUNTIME_TYPE_KIND_NO_POINTERS;
- if (this->points_to() != NULL)
+ if (this->is_direct_iface_type())
runtime_type_kind |= RUNTIME_TYPE_KIND_DIRECT_IFACE;
int64_t ptrsize;
int64_t ptrdata;
vals->push_back(Expression::make_unary(OPERATOR_AND, s, bloc));
}
- Named_object* no = (m->needs_stub_method()
- ? m->stub_object()
- : m->named_object());
+ Named_object* no =
+ ((this->points_to() != NULL
+ && this->points_to()->is_direct_iface_type()
+ && m->is_value_method())
+ ? m->iface_stub_object()
+ : (m->needs_stub_method()
+ ? m->stub_object()
+ : m->named_object()));
Function_type* mtype;
if (no->is_function())
breceiver.name = Gogo::unpack_hidden_name(this->receiver_->name());
// We always pass the address of the receiver parameter, in
- // order to make interface calls work with unknown types.
+ // order to make interface calls work with unknown types,
+ // except for direct interface types where the interface call
+ // actually passes value.
Type* rtype = this->receiver_->type();
- if (rtype->points_to() == NULL)
+ if (!rtype->is_direct_iface_type())
rtype = Type::make_pointer_type(rtype);
breceiver.btype = rtype->get_backend(gogo);
breceiver.location = this->receiver_->location();
*all_methods = NULL;
}
Type::build_stub_methods(gogo, type, *all_methods, location);
+ if (type->is_direct_iface_type())
+ Type::build_direct_iface_stub_methods(gogo, type, *all_methods, location);
}
// Add the methods for TYPE to *METHODS. FIELD_INDEXES is used to
gogo->add_statement(Statement::make_return_from_call(call, location));
}
+// Build direct interface stub methods for TYPE as needed. METHODS
+// is the set of methods for the type. LOCATION is the location of
+// the type definition.
+//
+// This is for an interface holding a pointer to the type and invoking
+// a value method. The interface data is the pointer, and is passed
+// to the stub, which dereferences it and passes to the actual method.
+
+void
+Type::build_direct_iface_stub_methods(Gogo* gogo, const Type* type,
+ Methods* methods, Location loc)
+{
+ if (methods == NULL)
+ return;
+
+ for (Methods::const_iterator p = methods->begin();
+ p != methods->end();
+ ++p)
+ {
+ Method* m = p->second;
+ if (!m->is_value_method())
+ continue;
+
+ Type* receiver_type = const_cast<Type*>(type);
+ receiver_type = Type::make_pointer_type(receiver_type);
+ const std::string& name(p->first);
+ Function_type* fntype = m->type();
+
+ static unsigned int counter;
+ char buf[100];
+ snprintf(buf, sizeof buf, "$ptr%u", counter);
+ ++counter;
+ Typed_identifier* receiver =
+ new Typed_identifier(buf, receiver_type, m->receiver_location());
+
+ const Typed_identifier_list* params = fntype->parameters();
+ Typed_identifier_list* stub_params;
+ if (params == NULL || params->empty())
+ stub_params = NULL;
+ else
+ {
+ // We give each stub parameter a unique name.
+ stub_params = new Typed_identifier_list();
+ for (Typed_identifier_list::const_iterator pp = params->begin();
+ pp != params->end();
+ ++pp)
+ {
+ char pbuf[100];
+ snprintf(pbuf, sizeof pbuf, "$p%u", counter);
+ stub_params->push_back(Typed_identifier(pbuf, pp->type(),
+ pp->location()));
+ ++counter;
+ }
+ }
+
+ const Typed_identifier_list* fnresults = fntype->results();
+ Typed_identifier_list* stub_results;
+ if (fnresults == NULL || fnresults->empty())
+ stub_results = NULL;
+ else
+ {
+ // We create the result parameters without any names, since
+ // we won't refer to them.
+ stub_results = new Typed_identifier_list();
+ for (Typed_identifier_list::const_iterator pr = fnresults->begin();
+ pr != fnresults->end();
+ ++pr)
+ stub_results->push_back(Typed_identifier("", pr->type(),
+ pr->location()));
+ }
+
+ Function_type* stub_type = Type::make_function_type(receiver,
+ stub_params,
+ stub_results,
+ fntype->location());
+ if (fntype->is_varargs())
+ stub_type->set_is_varargs();
+
+ // We only create the function in the package which creates the
+ // type.
+ const Package* package;
+ if (type->named_type() == NULL)
+ package = NULL;
+ else
+ package = type->named_type()->named_object()->package();
+
+ std::string stub_name = gogo->stub_method_name(package, name) + "2";
+ Named_object* stub;
+ if (package != NULL)
+ stub = Named_object::make_function_declaration(stub_name, package,
+ stub_type, loc);
+ else
+ {
+ stub = gogo->start_function(stub_name, stub_type, false,
+ fntype->location());
+ Type::build_one_iface_stub_method(gogo, m, buf, stub_params,
+ fntype->is_varargs(), loc);
+ gogo->finish_function(fntype->location());
+
+ if (type->named_type() == NULL && stub->is_function())
+ stub->func_value()->set_is_unnamed_type_stub_method();
+ if (m->nointerface() && stub->is_function())
+ stub->func_value()->set_nointerface();
+ }
+
+ m->set_iface_stub_object(stub);
+ }
+}
+
+// Build a stub method for METHOD of direct interface type T.
+// RECEIVER_NAME is the name we used for the receiver.
+// PARAMS is the list of function parameters.
+//
+// The stub looks like
+//
+// func ($ptr *T, PARAMS) {
+// (*$ptr).METHOD(PARAMS)
+// }
+
+void
+Type::build_one_iface_stub_method(Gogo* gogo, Method* method,
+ const char* receiver_name,
+ const Typed_identifier_list* params,
+ bool is_varargs, Location loc)
+{
+ Named_object* receiver_object = gogo->lookup(receiver_name, NULL);
+ go_assert(receiver_object != NULL);
+
+ Expression* expr = Expression::make_var_reference(receiver_object, loc);
+ expr = Expression::make_dereference(expr,
+ Expression::NIL_CHECK_DEFAULT,
+ loc);
+
+ Expression_list* arguments;
+ if (params == NULL || params->empty())
+ arguments = NULL;
+ else
+ {
+ arguments = new Expression_list();
+ for (Typed_identifier_list::const_iterator p = params->begin();
+ p != params->end();
+ ++p)
+ {
+ Named_object* param = gogo->lookup(p->name(), NULL);
+ go_assert(param != NULL);
+ Expression* param_ref = Expression::make_var_reference(param,
+ loc);
+ arguments->push_back(param_ref);
+ }
+ }
+
+ Expression* func = method->bind_method(expr, loc);
+ go_assert(func != NULL);
+ Call_expression* call = Expression::make_call(func, arguments, is_varargs,
+ loc);
+
+ gogo->add_statement(Statement::make_return_from_call(call, loc));
+}
+
// Apply FIELD_INDEXES to EXPR. The field indexes have to be applied
// in reverse order.
this->stub_ = no;
}
+ // Get the direct interface method stub object.
+ Named_object*
+ iface_stub_object() const
+ {
+ go_assert(this->iface_stub_ != NULL);
+ return this->iface_stub_;
+ }
+
+ // Set the direct interface method stub object.
+ void
+ set_iface_stub_object(Named_object* no)
+ {
+ go_assert(this->iface_stub_ == NULL);
+ this->iface_stub_ = no;
+ }
+
// Return true if this method should not participate in any
// interfaces.
bool
// These objects are only built by the child classes.
Method(const Field_indexes* field_indexes, unsigned int depth,
bool is_value_method, bool needs_stub_method)
- : field_indexes_(field_indexes), depth_(depth), stub_(NULL),
+ : field_indexes_(field_indexes), depth_(depth), stub_(NULL), iface_stub_(NULL),
is_value_method_(is_value_method), needs_stub_method_(needs_stub_method),
is_ambiguous_(false)
{ }
// If a stub method is required, this is its object. This is only
// set after stub methods are built in finalize_methods.
Named_object* stub_;
+ // Stub object for direct interface type. This is only set after
+ // stub methods are built in finalize_methods.
+ Named_object* iface_stub_;
// Whether this is a value method--a method that does not require a
// pointer.
bool is_value_method_;
is_unsafe_pointer_type() const
{ return this->points_to() != NULL && this->points_to()->is_void_type(); }
+ // Return whether this type is stored directly in an interface's
+ // data word.
+ bool
+ is_direct_iface_type() const;
+
// Return a version of this type with any expressions copied, but
// only if copying the expressions will affect the size of the type.
// If there are no such expressions in the type (expressions can
const Typed_identifier_list*, bool is_varargs,
Location);
+ // Build direct interface stub methods for a type.
+ static void
+ build_direct_iface_stub_methods(Gogo*, const Type*, Methods*, Location);
+
+ static void
+ build_one_iface_stub_method(Gogo*, Method*, const char*,
+ const Typed_identifier_list*,
+ bool, Location);
+
static Expression*
apply_field_indexes(Expression*, const Method::Field_indexes*,
Location);
bool* is_method, bool* found_pointer_method,
std::string* ambig1, std::string* ambig2);
+ // Helper function for is_direct_iface_type, to prevent infinite
+ // recursion.
+ bool
+ is_direct_iface_type_helper(Unordered_set(const Type*)*) const;
+
// Get the backend representation for a type without looking in the
// hash table for identical types.
Btype*
}
func TestArrayOfDirectIface(t *testing.T) {
- t.Skip("skipping test because gccgo uses a different directiface value")
{
type T [1]*byte
i1 := Zero(TypeOf(T{})).Interface()
}
}
-/*
-gccgo does not use the same directiface settings as gc.
-
func TestStructOfDirectIface(t *testing.T) {
{
type T struct{ X [1]*byte }
}
}
}
-*/
type StructI int
typ.equalfn = nil
}
- typ.kind &^= kindDirectIface
+ switch {
+ case len(fs) == 1 && !ifaceIndir(fs[0].typ):
+ // structs of 1 direct iface type can be direct
+ typ.kind |= kindDirectIface
+ default:
+ typ.kind &^= kindDirectIface
+ }
+
typ.uncommonType = nil
typ.ptrToThis = nil
array.ptrdata = array.size // overestimate but ok; must match program
}
- array.kind &^= kindDirectIface
+ switch {
+ case count == 1 && !ifaceIndir(typ):
+ // array of 1 direct iface type can be direct
+ array.kind |= kindDirectIface
+ default:
+ array.kind &^= kindDirectIface
+ }
esize := typ.size
// pointer to memory that holds the value. It follows from this that
// kindDirectIface can only be set for a type whose representation is
// simply a pointer. In the current gccgo implementation, this is set
-// only for pointer types (including unsafe.Pointer). In the future it
-// could also be set for other types: channels, maps, functions,
-// single-field structs and single-element arrays whose single field
-// is simply a pointer.
+// for types that are pointer-shaped, including unsafe.Pointer, channels,
+// maps, functions, single-field structs and single-element arrays whose
+// single field is simply a pointer-shaped type.
// For a nil interface value both fields in the interface struct are nil.
typedmemclr(t, ret)
return false
} else {
- typedmemmove(t, ret, e.data)
+ if isDirectIface(t) {
+ *(*unsafe.Pointer)(ret) = e.data
+ } else {
+ typedmemmove(t, ret, e.data)
+ }
return true
}
}
typedmemclr(t, ret)
return false
} else {
- typedmemmove(t, ret, i.data)
+ if isDirectIface(t) {
+ *(*unsafe.Pointer)(ret) = i.data
+ } else {
+ typedmemmove(t, ret, i.data)
+ }
return true
}
}
data unsafe.Pointer
}
i := (*iface)(unsafe.Pointer(&f))
- r := **(**uintptr)(i.data)
+ r := *(*uintptr)(i.data)
if internalcpu.FunctionDescriptors {
// With PPC64 ELF ABI v1 function descriptors the
// function address is a pointer to a struct whose
//go:nosplit
func funcPC(f interface{}) uintptr {
i := (*iface)(unsafe.Pointer(&f))
- r := **(**uintptr)(i.data)
+ r := *(*uintptr)(i.data)
if cpu.FunctionDescriptors {
// With PPC64 ELF ABI v1 function descriptors the
// function address is a pointer to a struct whose
return 0;
if (p - function > 3 && __builtin_strcmp (p - 3, "..r") == 0)
return 0;
- if (p - function > 6 && __builtin_strcmp (p - 6, "..stub") == 0)
+ if (p - function > 6 && __builtin_strncmp (p - 6, "..stub", 6) == 0)
return 0;
}