Commit 7c0434e5 by Ian Lance Taylor Committed by Ian Lance Taylor

compiler: Handle recursive interfaces.

	* go-gcc.cc (Gcc_backend::placeholder_struct_type): Permit name to
	be empty.
	(Gcc_backend::set_placeholder_struct_type): Likewise.

From-SVN: r183340
parent 0ab6e1ec
2012-01-20 Ian Lance Taylor <iant@google.com>
* go-gcc.cc (Gcc_backend::placeholder_struct_type): Permit name to
be empty.
(Gcc_backend::set_placeholder_struct_type): Likewise.
2012-01-17 Ian Lance Taylor <iant@google.com>
* gospec.c (lang_specific_driver): If we see -S without -o, add -o
......
......@@ -656,10 +656,13 @@ Gcc_backend::placeholder_struct_type(const std::string& name,
Location location)
{
tree ret = make_node(RECORD_TYPE);
tree decl = build_decl(location.gcc_location(), TYPE_DECL,
get_identifier_from_string(name),
ret);
TYPE_NAME(ret) = decl;
if (!name.empty())
{
tree decl = build_decl(location.gcc_location(), TYPE_DECL,
get_identifier_from_string(name),
ret);
TYPE_NAME(ret) = decl;
}
return this->make_type(ret);
}
......@@ -674,10 +677,13 @@ Gcc_backend::set_placeholder_struct_type(
gcc_assert(TREE_CODE(t) == RECORD_TYPE && TYPE_FIELDS(t) == NULL_TREE);
Btype* r = this->fill_in_struct(placeholder, fields);
// Build the data structure gcc wants to see for a typedef.
tree copy = build_distinct_type_copy(t);
TYPE_NAME(copy) = NULL_TREE;
DECL_ORIGINAL_TYPE(TYPE_NAME(t)) = copy;
if (TYPE_NAME(t) != NULL_TREE)
{
// Build the data structure gcc wants to see for a typedef.
tree copy = build_distinct_type_copy(t);
TYPE_NAME(copy) = NULL_TREE;
DECL_ORIGINAL_TYPE(TYPE_NAME(t)) = copy;
}
return r->get_tree() != error_mark_node;
}
......
......@@ -139,7 +139,8 @@ class Backend
set_placeholder_function_type(Btype* placeholder, Btype* ft) = 0;
// Create a placeholder struct type. This is used for a named
// struct type, as with placeholder_pointer_type.
// struct type, as with placeholder_pointer_type. It is also used
// for interface types, in which case NAME will be the empty string.
virtual Btype*
placeholder_struct_type(const std::string& name, Location) = 0;
......
......@@ -7566,7 +7566,7 @@ Builtin_call_expression::do_lower(Gogo* gogo, Named_object* function,
{
// Calling recover outside of a function always returns the
// nil empty interface.
Type* eface = Type::make_interface_type(NULL, loc);
Type* eface = Type::make_empty_interface_type(loc);
return Expression::make_cast(eface, Expression::make_nil(loc), loc);
}
break;
......@@ -8189,7 +8189,7 @@ Builtin_call_expression::do_type()
return Type::make_void_type();
case BUILTIN_RECOVER:
return Type::make_interface_type(NULL, Linemap::predeclared_location());
return Type::make_empty_interface_type(Linemap::predeclared_location());
case BUILTIN_APPEND:
{
......@@ -8883,7 +8883,7 @@ Builtin_call_expression::do_get_tree(Translate_context* context)
if (arg_tree == error_mark_node)
return error_mark_node;
Type *empty =
Type::make_interface_type(NULL, Linemap::predeclared_location());
Type::make_empty_interface_type(Linemap::predeclared_location());
arg_tree = Expression::convert_for_assignment(context, empty,
arg->type(),
arg_tree, location);
......@@ -8916,7 +8916,7 @@ Builtin_call_expression::do_get_tree(Translate_context* context)
return error_mark_node;
Type *empty =
Type::make_interface_type(NULL, Linemap::predeclared_location());
Type::make_empty_interface_type(Linemap::predeclared_location());
tree empty_tree = type_to_tree(empty->get_backend(context->gogo()));
Type* nil_type = Type::make_nil_type();
......
......@@ -110,7 +110,8 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int int_type_size,
results->push_back(Typed_identifier("", Type::lookup_string_type(), loc));
Type *method_type = Type::make_function_type(NULL, NULL, results, loc);
methods->push_back(Typed_identifier("Error", method_type, loc));
Type *error_iface = Type::make_interface_type(methods, loc);
Interface_type *error_iface = Type::make_interface_type(methods, loc);
error_iface->finalize_methods();
Named_type *error_type = Named_object::make_type("error", NULL, error_iface, loc)->type_value();
this->add_named_type(error_type);
}
......@@ -175,7 +176,7 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int int_type_size,
print_type->set_is_builtin();
this->globals_->add_function_declaration("println", NULL, print_type, loc);
Type *empty = Type::make_interface_type(NULL, loc);
Type *empty = Type::make_empty_interface_type(loc);
Typed_identifier_list* panic_parms = new Typed_identifier_list();
panic_parms->push_back(Typed_identifier("e", empty, loc));
Function_type *panic_type = Type::make_function_type(NULL, panic_parms,
......@@ -1564,7 +1565,8 @@ Finalize_methods::type(Type* t)
// finalize the methods of the field types, not of the struct
// type itself. We don't want to add methods to the struct,
// since it has a name.
Type* rt = t->named_type()->real_type();
Named_type* nt = t->named_type();
Type* rt = nt->real_type();
if (rt->classification() != Type::TYPE_STRUCT)
{
if (Type::traverse(rt, this) == TRAVERSE_EXIT)
......@@ -1576,7 +1578,21 @@ Finalize_methods::type(Type* t)
return TRAVERSE_EXIT;
}
t->named_type()->finalize_methods(this->gogo_);
nt->finalize_methods(this->gogo_);
// If this type is defined in a different package, then finalize the
// types of all the methods, since we won't see them otherwise.
if (nt->named_object()->package() != NULL && nt->has_any_methods())
{
const Methods* methods = nt->methods();
for (Methods::const_iterator p = methods->begin();
p != methods->end();
++p)
{
if (Type::traverse(p->second->type(), this) == TRAVERSE_EXIT)
return TRAVERSE_EXIT;
}
}
return TRAVERSE_SKIP_COMPONENTS;
}
......@@ -2622,6 +2638,9 @@ class Build_method_tables : public Traverse
void
Gogo::build_interface_method_tables()
{
if (saw_errors())
return;
std::vector<Interface_type*> hidden_interfaces;
hidden_interfaces.reserve(this->interface_types_.size());
for (std::vector<Interface_type*>::const_iterator pi =
......@@ -4922,10 +4941,7 @@ Bindings::traverse(Traverse* traverse, bool is_global)
| Traverse::traverse_statements
| Traverse::traverse_expressions
| Traverse::traverse_types)) != 0)
{
if (p->func_value()->traverse(traverse) == TRAVERSE_EXIT)
return TRAVERSE_EXIT;
}
t = p->func_value()->traverse(traverse);
break;
case Named_object::NAMED_OBJECT_PACKAGE:
......@@ -4952,6 +4968,26 @@ Bindings::traverse(Traverse* traverse, bool is_global)
return TRAVERSE_EXIT;
}
// If we need to traverse types, check the function declarations,
// which have types. We don't need to check the type declarations,
// as those are just names.
if ((traverse_mask & e_or_t) != 0)
{
for (Bindings::const_declarations_iterator p =
this->begin_declarations();
p != this->end_declarations();
++p)
{
if (p->second->is_function_declaration())
{
if (Type::traverse(p->second->func_declaration_value()->type(),
traverse)
== TRAVERSE_EXIT)
return TRAVERSE_EXIT;
}
}
}
return TRAVERSE_CONTINUE;
}
......@@ -5090,9 +5126,12 @@ Traverse::remember_type(const Type* type)
return true;
go_assert((this->traverse_mask() & traverse_types) != 0
|| (this->traverse_mask() & traverse_expressions) != 0);
// We only have to remember named types, as they are the only ones
// we can see multiple times in a traversal.
if (type->classification() != Type::TYPE_NAMED)
// We mostly only have to remember named types. But it turns out
// that an interface type can refer to itself without using a name
// by relying on interface inheritance, as in
// type I interface { F() interface{I} }
if (type->classification() != Type::TYPE_NAMED
&& type->classification() != Type::TYPE_INTERFACE)
return false;
if (this->types_seen_ == NULL)
this->types_seen_ = new Types_seen();
......
......@@ -2578,8 +2578,13 @@ class Traverse
type(Type*);
private:
typedef Unordered_set_hash(const Type*, Type_hash_identical,
Type_identical) Types_seen;
// A hash table for types we have seen during this traversal. Note
// that this uses the default hash functions for pointers rather
// than Type_hash_identical and Type_identical. This is because for
// traversal we care about seeing a specific type structure. If
// there are two separate instances of identical types, we want to
// traverse both.
typedef Unordered_set(const Type*) Types_seen;
typedef Unordered_set(const Expression*) Expressions_seen;
......
......@@ -151,12 +151,14 @@ runtime_function_type(Runtime_function_type bft)
Typed_identifier_list* methods = new Typed_identifier_list();
Type* mtype = Type::make_function_type(NULL, NULL, NULL, bloc);
methods->push_back(Typed_identifier("x", mtype, bloc));
t = Type::make_interface_type(methods, bloc);
Interface_type* it = Type::make_interface_type(methods, bloc);
it->finalize_methods();
t = it;
}
break;
case RFT_EFACE:
t = Type::make_interface_type(NULL, bloc);
t = Type::make_empty_interface_type(bloc);
break;
case RFT_FUNC_PTR:
......
......@@ -485,6 +485,9 @@ class Type
static Interface_type*
make_interface_type(Typed_identifier_list* methods, Location);
static Interface_type*
make_empty_interface_type(Location);
static Type*
make_type_descriptor_type();
......@@ -1319,6 +1322,10 @@ class Typed_identifier_list
Linemap::unknown_location()));
}
void
reserve(size_t c)
{ this->entries_.reserve(c); }
// Iterators.
typedef std::vector<Typed_identifier>::iterator iterator;
......@@ -2429,7 +2436,9 @@ class Interface_type : public Type
public:
Interface_type(Typed_identifier_list* methods, Location location)
: Type(TYPE_INTERFACE),
methods_(methods), location_(location)
parse_methods_(methods), all_methods_(NULL), location_(location),
interface_btype_(NULL), assume_identical_(NULL),
methods_are_finalized_(false), seen_(false)
{ go_assert(methods == NULL || !methods->empty()); }
// The location where the interface type was defined.
......@@ -2440,18 +2449,27 @@ class Interface_type : public Type
// Return whether this is an empty interface.
bool
is_empty() const
{ return this->methods_ == NULL; }
{
go_assert(this->methods_are_finalized_);
return this->all_methods_ == NULL;
}
// Return the list of methods. This will return NULL for an empty
// interface.
const Typed_identifier_list*
methods() const
{ return this->methods_; }
{
go_assert(this->methods_are_finalized_);
return this->all_methods_;
}
// Return the number of methods.
size_t
method_count() const
{ return this->methods_ == NULL ? 0 : this->methods_->size(); }
{
go_assert(this->methods_are_finalized_);
return this->all_methods_ == NULL ? 0 : this->all_methods_->size();
}
// Return the method NAME, or NULL.
const Typed_identifier*
......@@ -2461,7 +2479,8 @@ class Interface_type : public Type
size_t
method_index(const std::string& name) const;
// Finalize the methods. This handles interface inheritance.
// Finalize the methods. This sets all_methods_. This handles
// interface inheritance.
void
finalize_methods();
......@@ -2528,11 +2547,41 @@ class Interface_type : public Type
do_export(Export*) const;
private:
// The list of methods associated with the interface. This will be
// NULL for the empty interface.
Typed_identifier_list* methods_;
// This type guards against infinite recursion when comparing
// interface types. We keep a list of interface types assumed to be
// identical during comparison. We just keep the list on the stack.
// This permits us to compare cases like
// type I1 interface { F() interface{I1} }
// type I2 interface { F() interface{I2} }
struct Assume_identical
{
Assume_identical* next;
const Interface_type* t1;
const Interface_type* t2;
};
bool
assume_identical(const Interface_type*, const Interface_type*) const;
// The list of methods associated with the interface from the
// parser. This will be NULL for the empty interface. This may
// include unnamed interface types.
Typed_identifier_list* parse_methods_;
// The list of all methods associated with the interface. This
// expands any interface types listed in methods_. It is set by
// finalize_methods. This will be NULL for the empty interface.
Typed_identifier_list* all_methods_;
// The location where the interface was defined.
Location location_;
// The backend representation of this type during backend conversion.
Btype* interface_btype_;
// A list of interface types assumed to be identical during
// interface comparison.
mutable Assume_identical* assume_identical_;
// Whether the methods have been finalized.
bool methods_are_finalized_;
// Used to avoid endless recursion in do_mangled_name.
mutable bool seen_;
};
// The value we keep for a named type. This lets us get the right
......
......@@ -87,7 +87,7 @@ Gogo::import_unsafe(const std::string& local_name, bool is_local_name_exported,
this->add_named_object(no);
// Typeof.
Type* empty_interface = Type::make_interface_type(NULL, bloc);
Type* empty_interface = Type::make_empty_interface_type(bloc);
Typed_identifier_list* parameters = new Typed_identifier_list;
parameters->push_back(Typed_identifier("i", empty_interface, bloc));
results = new Typed_identifier_list;
......
......@@ -23,5 +23,5 @@ type I5 interface {
}
type I6 interface {
I5 // GC_ERROR "interface"
I5 // ERROR "interface"
}
......@@ -12,7 +12,7 @@ type I1 interface {
}
type I2 interface {
I1 // GC_ERROR "loop|interface"
I1 // ERROR "loop|interface"
}
......
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