diff options
author | Ian Lance Taylor <iant@golang.org> | 2022-07-27 10:15:41 -0700 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2022-07-27 10:15:41 -0700 |
commit | 9f62ed218fa656607740b386c0caa03e65dcd283 (patch) | |
tree | 6bde49bc5e4c4241266b108e4277baef4b85535d /gcc/go | |
parent | 71e955da39cea0ebffcfee3432effa622d14ca99 (diff) | |
parent | 5eb9f117a361538834b9740d59219911680717d1 (diff) | |
download | gcc-9f62ed218fa656607740b386c0caa03e65dcd283.zip gcc-9f62ed218fa656607740b386c0caa03e65dcd283.tar.gz gcc-9f62ed218fa656607740b386c0caa03e65dcd283.tar.bz2 |
Merge from trunk revision 5eb9f117a361538834b9740d59219911680717d1.
Diffstat (limited to 'gcc/go')
-rw-r--r-- | gcc/go/ChangeLog | 19 | ||||
-rw-r--r-- | gcc/go/Make-lang.in | 2 | ||||
-rw-r--r-- | gcc/go/go-gcc.cc | 20 | ||||
-rw-r--r-- | gcc/go/go-lang.cc | 11 | ||||
-rw-r--r-- | gcc/go/gofrontend/MERGE | 2 | ||||
-rw-r--r-- | gcc/go/gofrontend/export.cc | 346 | ||||
-rw-r--r-- | gcc/go/gofrontend/expressions.cc | 165 | ||||
-rw-r--r-- | gcc/go/gofrontend/expressions.h | 121 | ||||
-rw-r--r-- | gcc/go/gofrontend/go.cc | 3 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo.cc | 253 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo.h | 23 | ||||
-rw-r--r-- | gcc/go/gofrontend/names.cc | 29 | ||||
-rw-r--r-- | gcc/go/gofrontend/parse.cc | 113 | ||||
-rw-r--r-- | gcc/go/gofrontend/parse.h | 1 | ||||
-rw-r--r-- | gcc/go/gofrontend/statements.cc | 28 | ||||
-rw-r--r-- | gcc/go/gofrontend/types.cc | 89 | ||||
-rw-r--r-- | gcc/go/gofrontend/types.h | 11 |
17 files changed, 871 insertions, 365 deletions
diff --git a/gcc/go/ChangeLog b/gcc/go/ChangeLog index f50a60b7..5dbcb29 100644 --- a/gcc/go/ChangeLog +++ b/gcc/go/ChangeLog @@ -1,3 +1,22 @@ +2022-07-16 Ian Lance Taylor <iant@golang.org> + + * go-gcc.cc (Gcc_backend::struct_field_expression): Handle a void + expression, as for f().x where f returns a zero-sized type. + +2022-07-15 Ian Lance Taylor <iant@golang.org> + + * go-gcc.cc (Gcc_backend::call_expression): Handle a void + argument, as for f(g()) where g returns a zero-sized type. + +2022-06-02 David Malcolm <dmalcolm@redhat.com> + + * go-lang.cc (go_get_sarif_source_language): New. + (LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE): Redefine. + +2022-05-31 Jason Merrill <jason@redhat.com> + + * Make-lang.in (go.tags): Look at *.cc. + 2022-02-13 Ian Lance Taylor <iant@golang.org> * gospec.cc: Revert 2022-02-09 change: diff --git a/gcc/go/Make-lang.in b/gcc/go/Make-lang.in index 31c6773..0e81268 100644 --- a/gcc/go/Make-lang.in +++ b/gcc/go/Make-lang.in @@ -133,7 +133,7 @@ go.srcinfo: doc/gccgo.info go.srcextra: go.tags: force cd $(srcdir)/go; \ - $(ETAGS) -o TAGS.sub *.c *.h gofrontend/*.h gofrontend/*.cc; \ + $(ETAGS) -o TAGS.sub *.cc *.h gofrontend/*.h gofrontend/*.cc; \ $(ETAGS) --include TAGS.sub --include ../TAGS.sub go.man: doc/gccgo.1 go.srcman: doc/gccgo.1 diff --git a/gcc/go/go-gcc.cc b/gcc/go/go-gcc.cc index f3de7a8..1ba7206 100644 --- a/gcc/go/go-gcc.cc +++ b/gcc/go/go-gcc.cc @@ -1707,6 +1707,13 @@ Gcc_backend::struct_field_expression(Bexpression* bstruct, size_t index, if (struct_tree == error_mark_node || TREE_TYPE(struct_tree) == error_mark_node) return this->error_expression(); + + // A function call that returns a zero-sized object will have been + // changed to return void. A zero-sized object can have a + // (zero-sized) field, so support that case. + if (TREE_TYPE(struct_tree) == void_type_node) + return bstruct; + gcc_assert(TREE_CODE(TREE_TYPE(struct_tree)) == RECORD_TYPE); tree field = TYPE_FIELDS(TREE_TYPE(struct_tree)); if (field == NULL_TREE) @@ -2112,6 +2119,19 @@ Gcc_backend::call_expression(Bfunction*, // containing fcn for call args[i] = fn_args.at(i)->get_tree(); if (args[i] == error_mark_node) return this->error_expression(); + if (TREE_TYPE(args[i]) == void_type_node) + { + // This can happen for a case like f(g()) where g returns a + // zero-sized type, because in that case we've changed g to + // return void. + tree t = TYPE_ARG_TYPES(TREE_TYPE(TREE_TYPE(fn))); + for (size_t j = 0; j < i; ++j) + t = TREE_CHAIN(t); + tree arg_type = TREE_TYPE(TREE_VALUE(t)); + args[i] = fold_build2_loc(EXPR_LOCATION(args[i]), COMPOUND_EXPR, + arg_type, args[i], + build_zero_cst(arg_type)); + } } tree fndecl = fn; diff --git a/gcc/go/go-lang.cc b/gcc/go/go-lang.cc index c8365d2..84cd623 100644 --- a/gcc/go/go-lang.cc +++ b/gcc/go/go-lang.cc @@ -545,6 +545,15 @@ go_langhook_eh_personality (void) return personality_decl; } +/* Get a value for the SARIF v2.1.0 "artifact.sourceLanguage" property, + based on the list in SARIF v2.1.0 Appendix J. */ + +static const char * +go_get_sarif_source_language (const char *) +{ + return "go"; +} + /* Functions called directly by the generic backend. */ tree @@ -615,6 +624,7 @@ go_localize_identifier (const char *ident) #undef LANG_HOOKS_GETDECLS #undef LANG_HOOKS_GIMPLIFY_EXPR #undef LANG_HOOKS_EH_PERSONALITY +#undef LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE #define LANG_HOOKS_NAME "GNU Go" #define LANG_HOOKS_INIT go_langhook_init @@ -631,6 +641,7 @@ go_localize_identifier (const char *ident) #define LANG_HOOKS_GETDECLS go_langhook_getdecls #define LANG_HOOKS_GIMPLIFY_EXPR go_langhook_gimplify_expr #define LANG_HOOKS_EH_PERSONALITY go_langhook_eh_personality +#define LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE go_get_sarif_source_language struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER; diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 3742414..2f2fafd 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -3742e8a154bfec805054b4ebf0809f12dc7694da +a62f20ae78ddd41be682dde8cab075ca4f5dbb2a The first line of this file holds the git revision number of the last merge done from the gofrontend repository. diff --git a/gcc/go/gofrontend/export.cc b/gcc/go/gofrontend/export.cc index 3d11334..7373dee 100644 --- a/gcc/go/gofrontend/export.cc +++ b/gcc/go/gofrontend/export.cc @@ -124,6 +124,11 @@ class Collect_export_references : public Traverse void prepare_types(const std::vector<Named_object*>& sorted_exports); + // Third entry point (called after the method above), to find + // all types in expressions referenced by exports. + void + prepare_expressions(const std::vector<Named_object*>& sorted_exports); + protected: // Override of parent class method. int @@ -281,6 +286,28 @@ Collect_export_references::expression(Expression** pexpr) return TRAVERSE_CONTINUE; } +// Collect up the set of types mentioned in expressions of things we're exporting, +// and collect all the packages encountered during type traversal, to make sure +// we can declare things referered to indirectly (for example, in the body of an +// exported inline function from another package). + +void +Collect_export_references::prepare_expressions(const std::vector<Named_object*>& sorted_exports) +{ + for (std::vector<Named_object*>::const_iterator p = sorted_exports.begin(); + p != sorted_exports.end(); + ++p) + { + Named_object* no = *p; + if (no->classification() == Named_object::NAMED_OBJECT_CONST) + { + Expression* e = no->const_value()->expr(); + if (e != NULL) + Expression::traverse(&e, this); + } + } +} + // Collect up the set of types mentioned in things we're exporting, and collect // all the packages encountered during type traversal, to make sure we can // declare things referered to indirectly (for example, in the body of an @@ -360,16 +387,6 @@ Collect_export_references::type(Type* type) if (type->is_abstract()) return TRAVERSE_SKIP_COMPONENTS; - // For interfaces make sure that embedded methods are sorted, since the - // comparison function we use for indexing types relies on it (this call has - // to happen before the record_type call below). - if (type->classification() == Type::TYPE_INTERFACE) - { - Interface_type* it = type->interface_type(); - if (it != NULL) - it->sort_embedded(); - } - if (!this->exp_->record_type(type)) { // We've already seen this type. @@ -501,6 +518,11 @@ should_export(Named_object* no) return true; } +// Compare Typed_identifier_list's. + +static int +compare_til(const Typed_identifier_list*, const Typed_identifier_list*); + // A functor to sort Named_object pointers by name. struct Sort_bindings @@ -508,16 +530,66 @@ struct Sort_bindings bool operator()(const Named_object* n1, const Named_object* n2) const { + if (n1 == n2) + return false; + if (n1->package() != n2->package()) { if (n1->package() == NULL) return true; if (n2->package() == NULL) return false; - return n1->package()->pkgpath() < n2->package()->pkgpath(); + + // Make sure we don't see the same pkgpath twice. + const std::string& p1(n1->package()->pkgpath()); + const std::string& p2(n2->package()->pkgpath()); + go_assert(p1 != p2); + + return p1 < p2; } - return n1->name() < n2->name(); + if (n1->name() != n2->name()) + return n1->name() < n2->name(); + + // We shouldn't see the same name twice, but it can happen for + // nested type names. + + go_assert(n1->is_type() && n2->is_type()); + + unsigned int ind1; + const Named_object* g1 = n1->type_value()->in_function(&ind1); + unsigned int ind2; + const Named_object* g2 = n2->type_value()->in_function(&ind2); + + if (g1 == NULL) + { + go_assert(g2 != NULL); + return true; + } + else if (g2 == NULL) + return false; + else if (g1 == g2) + { + go_assert(ind1 != ind2); + return ind1 < ind2; + } + else if ((g1->package() != g2->package()) || (g1->name() != g2->name())) + return Sort_bindings()(g1, g2); + else + { + // This case can happen if g1 or g2 is a method. + if (g1 != NULL && g1->func_value()->is_method()) + { + const Typed_identifier* r = g1->func_value()->type()->receiver(); + g1 = r->type()->named_type()->named_object(); + } + if (g2 != NULL && g2->func_value()->is_method()) + { + const Typed_identifier* r = g2->func_value()->type()->receiver(); + g2 = r->type()->named_type()->named_object(); + } + return Sort_bindings()(g1, g2); + } } }; @@ -528,17 +600,20 @@ struct Sort_types bool operator()(const Type* t1, const Type* t2) const { + t1 = t1->forwarded(); + t2 = t2->forwarded(); + const Named_type* nt1 = t1->named_type(); const Named_type* nt2 = t2->named_type(); if (nt1 != NULL) { - if (nt2 != NULL) - { - Sort_bindings sb; - return sb(nt1->named_object(), nt2->named_object()); - } - else - return true; + if (nt2 != NULL) + { + Sort_bindings sb; + return sb(nt1->named_object(), nt2->named_object()); + } + else + return true; } else if (nt2 != NULL) return false; @@ -549,10 +624,218 @@ struct Sort_types gogo->type_descriptor_backend_name(t1, NULL, &b1); Backend_name b2; gogo->type_descriptor_backend_name(t2, NULL, &b2); - return b1.name() < b2.name(); + + std::string n1 = b1.name(); + std::string n2 = b2.name(); + if (n1 != n2) + return n1 < n2; + + // We should never see equal types here. If we do, we may not + // generate an identical output file for identical input. But the + // backend names can be equal because we want to treat aliases + // differently while type_descriptor_backend_name does not. In + // that case we need to traverse the type elements. + + // t1 == t2 in case std::sort compares elements to themselves. + if (t1 == t2) + return false; + + Sort_types sort; + Type_alias_identical identical; + go_assert(!identical(t1, t2)); + + switch (t1->classification()) + { + case Type::TYPE_ERROR: + return false; + + case Type::TYPE_VOID: + case Type::TYPE_BOOLEAN: + case Type::TYPE_INTEGER: + case Type::TYPE_FLOAT: + case Type::TYPE_COMPLEX: + case Type::TYPE_STRING: + case Type::TYPE_SINK: + case Type::TYPE_NIL: + case Type::TYPE_CALL_MULTIPLE_RESULT: + case Type::TYPE_NAMED: + case Type::TYPE_FORWARD: + default: + go_unreachable(); + + case Type::TYPE_FUNCTION: + { + const Function_type* ft1 = t1->function_type(); + const Function_type* ft2 = t2->function_type(); + const Typed_identifier* r1 = ft1->receiver(); + const Typed_identifier* r2 = ft2->receiver(); + if (r1 == NULL) + go_assert(r2 == NULL); + else + { + go_assert(r2 != NULL); + const Type* rt1 = r1->type()->forwarded(); + const Type* rt2 = r2->type()->forwarded(); + if (!identical(rt1, rt2)) + return sort(rt1, rt2); + } + + const Typed_identifier_list* p1 = ft1->parameters(); + const Typed_identifier_list* p2 = ft2->parameters(); + if (p1 == NULL || p1->empty()) + go_assert(p2 == NULL || p2->empty()); + else + { + go_assert(p2 != NULL && !p2->empty()); + int i = compare_til(p1, p2); + if (i < 0) + return false; + else if (i > 0) + return true; + } + + p1 = ft1->results(); + p2 = ft2->results(); + if (p1 == NULL || p1->empty()) + go_assert(p2 == NULL || p2->empty()); + else + { + go_assert(p2 != NULL && !p2->empty()); + int i = compare_til(p1, p2); + if (i < 0) + return false; + else if (i > 0) + return true; + } + + go_unreachable(); + } + + case Type::TYPE_POINTER: + { + const Type* p1 = t1->points_to()->forwarded(); + const Type* p2 = t2->points_to()->forwarded(); + go_assert(!identical(p1, p2)); + return sort(p1, p2); + } + + case Type::TYPE_STRUCT: + { + const Struct_type* s1 = t1->struct_type(); + const Struct_type* s2 = t2->struct_type(); + const Struct_field_list* f1 = s1->fields(); + const Struct_field_list* f2 = s2->fields(); + go_assert(f1 != NULL && f2 != NULL); + Struct_field_list::const_iterator p1 = f1->begin(); + Struct_field_list::const_iterator p2 = f2->begin(); + for (; p2 != f2->end(); ++p1, ++p2) + { + go_assert(p1 != f1->end()); + go_assert(p1->field_name() == p2->field_name()); + go_assert(p1->is_anonymous() == p2->is_anonymous()); + const Type* ft1 = p1->type()->forwarded(); + const Type* ft2 = p2->type()->forwarded(); + if (!identical(ft1, ft2)) + return sort(ft1, ft2); + } + go_assert(p1 == f1->end()); + go_unreachable(); + } + + case Type::TYPE_ARRAY: + { + const Type* e1 = t1->array_type()->element_type()->forwarded(); + const Type* e2 = t2->array_type()->element_type()->forwarded(); + go_assert(!identical(e1, e2)); + return sort(e1, e2); + } + + case Type::TYPE_MAP: + { + const Map_type* m1 = t1->map_type(); + const Map_type* m2 = t2->map_type(); + const Type* k1 = m1->key_type()->forwarded(); + const Type* k2 = m2->key_type()->forwarded(); + if (!identical(k1, k2)) + return sort(k1, k2); + const Type* v1 = m1->val_type()->forwarded(); + const Type* v2 = m2->val_type()->forwarded(); + go_assert(!identical(v1, v2)); + return sort(v1, v2); + } + + case Type::TYPE_CHANNEL: + { + const Type* e1 = t1->channel_type()->element_type()->forwarded(); + const Type* e2 = t2->channel_type()->element_type()->forwarded(); + go_assert(!identical(e1, e2)); + return sort(e1, e2); + } + + case Type::TYPE_INTERFACE: + { + const Interface_type* it1 = t1->interface_type(); + const Interface_type* it2 = t2->interface_type(); + const Typed_identifier_list* m1 = it1->local_methods(); + const Typed_identifier_list* m2 = it2->local_methods(); + + // We know the full method lists are the same, because the + // mangled type names were the same, but here we are looking + // at the local method lists, which include embedded + // interfaces, and we can have an embedded empty interface. + if (m1 == NULL || m1->empty()) + { + go_assert(m2 != NULL && !m2->empty()); + return true; + } + else if (m2 == NULL || m2->empty()) + { + go_assert(m1 != NULL && !m1->empty()); + return false; + } + + int i = compare_til(m1, m2); + if (i < 0) + return false; + else if (i > 0) + return true; + else + go_unreachable(); + } + } } }; +// Compare Typed_identifier_list's with Sort_types, returning -1, 0, +1. + +static int +compare_til( + const Typed_identifier_list* til1, + const Typed_identifier_list* til2) +{ + Type_alias_identical identical; + Sort_types sort; + Typed_identifier_list::const_iterator p1 = til1->begin(); + Typed_identifier_list::const_iterator p2 = til2->begin(); + for (; p2 != til2->end(); ++p1, ++p2) + { + if (p1 == til1->end()) + return -1; + const Type* t1 = p1->type()->forwarded(); + const Type* t2 = p2->type()->forwarded(); + if (!identical(t1, t2)) + { + if (sort(t1, t2)) + return -1; + else + return +1; + } + } + if (p1 != til1->end()) + return +1; + return 0; +} + // Export those identifiers marked for exporting. void @@ -638,6 +921,7 @@ Export::export_globals(const std::string& package_name, // Collect up the set of types mentioned in things we're exporting, // and any packages that may be referred to indirectly. collect.prepare_types(sorted_exports); + collect.prepare_expressions(sorted_exports); // Assign indexes to all exported types and types referenced by // things we're exporting. Return value is index of first non-exported @@ -714,17 +998,9 @@ bool Export::record_type(Type* type) { type = type->forwarded(); - std::pair<Type_refs::iterator, bool> ins = this->impl_->type_refs.insert(std::make_pair(type, 0)); - if (!ins.second) - { - // We've already seen this type. - return false; - } - ins.first->second = 0; - - return true; + return ins.second; } // Assign the specified type an index. @@ -733,13 +1009,12 @@ void Export::set_type_index(const Type* type) { type = type->forwarded(); - std::pair<Type_refs::iterator, bool> ins = - this->impl_->type_refs.insert(std::make_pair(type, 0)); - go_assert(!ins.second); + Type_refs::iterator p = this->impl_->type_refs.find(type); + go_assert(p != this->impl_->type_refs.end()); int index = this->type_index_; ++this->type_index_; - go_assert(ins.first->second == 0); - ins.first->second = index; + go_assert(p->second == 0); + p->second = index; } // This helper assigns type indices to all types mentioned directly or @@ -758,9 +1033,6 @@ Export::assign_type_indices(const std::vector<Named_object*>& sorted_exports) { if (!(*p)->is_type()) continue; - Interface_type* it = (*p)->type_value()->interface_type(); - if (it != NULL) - it->sort_embedded(); this->record_type((*p)->type_value()); this->set_type_index((*p)->type_value()); } diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index 1b3b3bf..2492d9f 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -1426,7 +1426,12 @@ Sink_expression::do_get_backend(Translate_context* context) Gogo* gogo = context->gogo(); if (this->bvar_ == NULL) { - go_assert(this->type_ != NULL && !this->type_->is_sink_type()); + if (this->type_ == NULL || this->type_->is_sink_type()) + { + go_assert(saw_errors()); + return gogo->backend()->error_expression(); + } + Named_object* fn = context->function(); go_assert(fn != NULL); Bfunction* fn_ctx = fn->func_value()->get_or_make_decl(gogo, fn); @@ -2715,7 +2720,7 @@ Integer_expression::do_import(Import_expression* imp, Location loc) return Expression::make_error(loc); } if (pos == std::string::npos) - mpfr_set_ui(real, 0, MPFR_RNDN); + mpfr_init_set_ui(real, 0, MPFR_RNDN); else { std::string real_str = num.substr(0, pos); @@ -3347,97 +3352,7 @@ class Find_named_object : public Traverse bool found_; }; -// A reference to a const in an expression. - -class Const_expression : public Expression -{ - public: - Const_expression(Named_object* constant, Location location) - : Expression(EXPRESSION_CONST_REFERENCE, location), - constant_(constant), type_(NULL), seen_(false) - { } - - Named_object* - named_object() - { return this->constant_; } - - const Named_object* - named_object() const - { return this->constant_; } - - // Check that the initializer does not refer to the constant itself. - void - check_for_init_loop(); - - protected: - int - do_traverse(Traverse*); - - Expression* - do_lower(Gogo*, Named_object*, Statement_inserter*, int); - - bool - do_is_constant() const - { return true; } - - bool - do_is_zero_value() const - { return this->constant_->const_value()->expr()->is_zero_value(); } - - bool - do_is_static_initializer() const - { return true; } - - bool - do_numeric_constant_value(Numeric_constant* nc) const; - - bool - do_string_constant_value(std::string* val) const; - - bool - do_boolean_constant_value(bool* val) const; - - Type* - do_type(); - - // The type of a const is set by the declaration, not the use. - void - do_determine_type(const Type_context*); - - void - do_check_types(Gogo*); - - Expression* - do_copy() - { return this; } - - Bexpression* - do_get_backend(Translate_context* context); - - int - do_inlining_cost() const - { return 1; } - - // When exporting a reference to a const as part of a const - // expression, we export the value. We ignore the fact that it has - // a name. - void - do_export(Export_function_body* efb) const - { this->constant_->const_value()->expr()->export_expression(efb); } - - void - do_dump_expression(Ast_dump_context*) const; - - private: - // The constant. - Named_object* constant_; - // The type of this reference. This is used if the constant has an - // abstract type. - Type* type_; - // Used to prevent infinite recursion when a constant incorrectly - // refers to itself. - mutable bool seen_; -}; +// Class Const_expression. // Traversal. @@ -3449,6 +3364,14 @@ Const_expression::do_traverse(Traverse* traverse) return TRAVERSE_CONTINUE; } +// Whether this is the zero value. + +bool +Const_expression::do_is_zero_value() const +{ + return this->constant_->const_value()->expr()->is_zero_value(); +} + // Lower a constant expression. This is where we convert the // predeclared constant iota into an integer value. @@ -3703,6 +3626,16 @@ Const_expression::do_get_backend(Translate_context* context) return expr->get_backend(context); } +// When exporting a reference to a const as part of a const +// expression, we export the value. We ignore the fact that it has +// a name. + +void +Const_expression::do_export(Export_function_body* efb) const +{ + this->constant_->const_value()->expr()->export_expression(efb); +} + // Dump ast representation for constant expression. void @@ -6824,11 +6757,12 @@ Binary_expression::do_determine_type(const Type_context* context) { if ((tleft->integer_type() != NULL && tright->integer_type() != NULL) || (tleft->float_type() != NULL && tright->float_type() != NULL) - || (tleft->complex_type() != NULL && tright->complex_type() != NULL)) + || (tleft->complex_type() != NULL && tright->complex_type() != NULL) + || (tleft->is_boolean_type() && tright->is_boolean_type())) { - // Both sides have an abstract integer, abstract float, or - // abstract complex type. Just let CONTEXT determine - // whether they may remain abstract or not. + // Both sides have an abstract integer, abstract float, + // abstract complex, or abstract boolean type. Just let + // CONTEXT determine whether they may remain abstract or not. } else if (tleft->complex_type() != NULL) subcontext.type = tleft; @@ -7671,8 +7605,7 @@ Expression::comparison(Translate_context* context, Type* result_type, && left_type->array_type()->length() == NULL) { Array_type* at = left_type->array_type(); - bool is_lvalue = false; - left = at->get_value_pointer(context->gogo(), left, is_lvalue); + left = at->get_value_pointer(context->gogo(), left); } else if (left_type->interface_type() != NULL) { @@ -8553,6 +8486,11 @@ Builtin_call_expression::do_flatten(Gogo* gogo, Named_object* function, pa != this->args()->end(); ++pa) { + if ((*pa)->is_error_expression()) + { + go_assert(saw_errors()); + return Expression::make_error(loc); + } if ((*pa)->is_nil_expression()) { Expression* nil = Expression::make_nil(loc); @@ -9276,7 +9214,7 @@ Builtin_call_expression::flatten_append(Gogo* gogo, Named_object* function, Type* unsafe_ptr_type = Type::make_pointer_type(Type::make_void_type()); Expression* a1 = Expression::make_type_descriptor(element_type, loc); Expression* a2 = Expression::make_temporary_reference(s1tmp, loc); - a2 = slice_type->array_type()->get_value_pointer(gogo, a2, false); + a2 = slice_type->array_type()->get_value_pointer(gogo, a2); a2 = Expression::make_cast(unsafe_ptr_type, a2, loc); Expression* a3 = Expression::make_temporary_reference(l1tmp, loc); Expression* a4 = Expression::make_temporary_reference(c1tmp, loc); @@ -13458,6 +13396,7 @@ Array_index_expression::do_check_types(Gogo*) if (array_type == NULL) { go_assert(this->array_->type()->is_error()); + this->set_is_error(); return; } @@ -13848,9 +13787,8 @@ Array_index_expression::do_get_backend(Translate_context* context) } else { - Expression* valptr = - array_type->get_value_pointer(gogo, this->array_, - this->is_lvalue_); + Expression* valptr = array_type->get_value_pointer(gogo, + this->array_); Bexpression* ptr = valptr->get_backend(context); ptr = gogo->backend()->pointer_offset_expression(ptr, start, loc); @@ -13891,8 +13829,7 @@ Array_index_expression::do_get_backend(Translate_context* context) Bexpression* offset = gogo->backend()->conditional_expression(bfn, int_btype, cond, zero, start, loc); - Expression* valptr = array_type->get_value_pointer(gogo, this->array_, - this->is_lvalue_); + Expression* valptr = array_type->get_value_pointer(gogo, this->array_); Bexpression* val = valptr->get_backend(context); val = gogo->backend()->pointer_offset_expression(val, offset, loc); @@ -15238,7 +15175,7 @@ Selector_expression::lower_method_expression(Gogo* gogo) p != method_parameters->end(); ++p, ++i) { - if (!p->name().empty()) + if (!p->name().empty() && !Gogo::is_sink_name(p->name())) parameters->push_back(*p); else { @@ -17266,6 +17203,8 @@ Composite_literal_expression::lower_map(Gogo* gogo, Named_object* function, Location location = this->location(); Unordered_map(unsigned int, std::vector<Expression*>) st; Unordered_map(unsigned int, std::vector<Expression*>) nt; + bool saw_false = false; + bool saw_true = false; if (this->vals_ != NULL) { if (!this->has_keys_) @@ -17300,6 +17239,7 @@ Composite_literal_expression::lower_map(Gogo* gogo, Named_object* function, continue; std::string sval; Numeric_constant nval; + bool bval; if ((*p)->string_constant_value(&sval)) // Check string keys. { unsigned int h = Gogo::hash_string(sval, 0); @@ -17373,6 +17313,19 @@ Composite_literal_expression::lower_map(Gogo* gogo, Named_object* function, mit->second.push_back(*p); } } + else if ((*p)->boolean_constant_value(&bval)) + { + if ((bval && saw_true) || (!bval && saw_false)) + { + go_error_at((*p)->location(), + "duplicate key in map literal"); + return Expression::make_error(location); + } + if (bval) + saw_true = true; + else + saw_false = true; + } } } diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h index 92e8d8d..a1e3733 100644 --- a/gcc/go/gofrontend/expressions.h +++ b/gcc/go/gofrontend/expressions.h @@ -28,6 +28,7 @@ class Map_type; class Struct_type; class Struct_field; class Expression_list; +class Const_expression; class Var_expression; class Enclosed_var_expression; class Temporary_reference_expression; @@ -626,6 +627,20 @@ class Expression is_type_expression() const { return this->classification_ == EXPRESSION_TYPE; } + // If this is a const reference, return the Const_expression + // structure. Otherwise, return NULL. This is a controlled dynamic + // cast. + Const_expression* + const_expression() + { return this->convert<Const_expression, EXPRESSION_CONST_REFERENCE>(); } + + const Const_expression* + const_expression() const + { + return this->convert<const Const_expression, + EXPRESSION_CONST_REFERENCE>(); + } + // If this is a variable reference, return the Var_expression // structure. Otherwise, return NULL. This is a controlled dynamic // cast. @@ -1453,6 +1468,96 @@ class Parser_expression : public Expression { go_unreachable(); } }; +// A reference to a const in an expression. + +class Const_expression : public Expression +{ + public: + Const_expression(Named_object* constant, Location location) + : Expression(EXPRESSION_CONST_REFERENCE, location), + constant_(constant), type_(NULL), seen_(false) + { } + + Named_object* + named_object() + { return this->constant_; } + + const Named_object* + named_object() const + { return this->constant_; } + + // Check that the initializer does not refer to the constant itself. + void + check_for_init_loop(); + + protected: + int + do_traverse(Traverse*); + + Expression* + do_lower(Gogo*, Named_object*, Statement_inserter*, int); + + bool + do_is_constant() const + { return true; } + + bool + do_is_zero_value() const; + + bool + do_is_static_initializer() const + { return true; } + + bool + do_numeric_constant_value(Numeric_constant* nc) const; + + bool + do_string_constant_value(std::string* val) const; + + bool + do_boolean_constant_value(bool* val) const; + + Type* + do_type(); + + // The type of a const is set by the declaration, not the use. + void + do_determine_type(const Type_context*); + + void + do_check_types(Gogo*); + + Expression* + do_copy() + { return this; } + + Bexpression* + do_get_backend(Translate_context* context); + + int + do_inlining_cost() const + { return 1; } + + // When exporting a reference to a const as part of a const + // expression, we export the value. We ignore the fact that it has + // a name. + void + do_export(Export_function_body* efb) const; + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The constant. + Named_object* constant_; + // The type of this reference. This is used if the constant has an + // abstract type. + Type* type_; + // Used to prevent infinite recursion when a constant incorrectly + // refers to itself. + mutable bool seen_; +}; + // An expression which is simply a variable. class Var_expression : public Expression @@ -3055,7 +3160,7 @@ class Array_index_expression : public Expression Expression* end, Expression* cap, Location location) : Expression(EXPRESSION_ARRAY_INDEX, location), array_(array), start_(start), end_(end), cap_(cap), type_(NULL), - is_lvalue_(false), needs_bounds_check_(true), is_flattened_(false) + needs_bounds_check_(true), is_flattened_(false) { } // Return the array. @@ -3087,18 +3192,6 @@ class Array_index_expression : public Expression end() const { return this->end_; } - // Return whether this array index expression appears in an lvalue - // (left hand side of assignment) context. - bool - is_lvalue() const - { return this->is_lvalue_; } - - // Update this array index expression to indicate that it appears - // in a left-hand-side or lvalue context. - void - set_is_lvalue() - { this->is_lvalue_ = true; } - void set_needs_bounds_check(bool b) { this->needs_bounds_check_ = b; } @@ -3174,8 +3267,6 @@ class Array_index_expression : public Expression Expression* cap_; // The type of the expression. Type* type_; - // Whether expr appears in an lvalue context. - bool is_lvalue_; // Whether bounds check is needed. bool needs_bounds_check_; // Whether this has already been flattened. diff --git a/gcc/go/gofrontend/go.cc b/gcc/go/gofrontend/go.cc index 404cb12..1512770 100644 --- a/gcc/go/gofrontend/go.cc +++ b/gcc/go/gofrontend/go.cc @@ -146,6 +146,9 @@ go_parse_input_files(const char** filenames, unsigned int filename_count, if (only_check_syntax) return; + // Record global variable initializer dependencies. + ::gogo->record_global_init_refs(); + // Do simple deadcode elimination. ::gogo->remove_deadcode(); diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc index 30d5c9f..9197eef 100644 --- a/gcc/go/gofrontend/gogo.cc +++ b/gcc/go/gofrontend/gogo.cc @@ -1086,8 +1086,8 @@ class Find_vars : public Traverse public: Find_vars() - : Traverse(traverse_expressions), - vars_(), seen_objects_() + : Traverse(traverse_expressions | traverse_statements), + vars_(), seen_objects_(), lhs_is_ref_(false) { } // An iterator through the variables found, after the traversal. @@ -1104,11 +1104,16 @@ class Find_vars : public Traverse int expression(Expression**); + int + statement(Block*, size_t* index, Statement*); + private: // Accumulated variables. Vars vars_; // Objects we have already seen. Seen_objects seen_objects_; + // Whether an assignment to a variable counts as a reference. + bool lhs_is_ref_; }; // Collect global variables referenced by EXPR. Look through function @@ -1164,7 +1169,11 @@ Find_vars::expression(Expression** pexpr) if (ins.second) { // This is the first time we have seen this name. - if (f->func_value()->block()->traverse(this) == TRAVERSE_EXIT) + bool hold = this->lhs_is_ref_; + this->lhs_is_ref_ = true; + int r = f->func_value()->block()->traverse(this); + this->lhs_is_ref_ = hold; + if (r == TRAVERSE_EXIT) return TRAVERSE_EXIT; } } @@ -1192,6 +1201,29 @@ Find_vars::expression(Expression** pexpr) return TRAVERSE_CONTINUE; } +// Check a statement while searching for variables. This is where we +// skip variables on the left hand side of assigments if appropriate. + +int +Find_vars::statement(Block*, size_t*, Statement* s) +{ + if (this->lhs_is_ref_) + return TRAVERSE_CONTINUE; + Assignment_statement* as = s->assignment_statement(); + if (as == NULL) + return TRAVERSE_CONTINUE; + + // Only traverse subexpressions of the LHS. + if (as->lhs()->traverse_subexpressions(this) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + + Expression* rhs = as->rhs(); + if (Expression::traverse(&rhs, this) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + + return TRAVERSE_SKIP_COMPONENTS; +} + // Return true if EXPR, PREINIT, or DEP refers to VAR. static bool @@ -1230,11 +1262,11 @@ class Var_init { public: Var_init() - : var_(NULL), init_(NULL), refs_(NULL), dep_count_(0) + : var_(NULL), init_(NULL), dep_count_(0) { } Var_init(Named_object* var, Bstatement* init) - : var_(var), init_(init), refs_(NULL), dep_count_(0) + : var_(var), init_(init), dep_count_(0) { } // Return the variable. @@ -1247,19 +1279,6 @@ class Var_init init() const { return this->init_; } - // Add a reference. - void - add_ref(Named_object* var); - - // The variables which this variable's initializers refer to. - const std::vector<Named_object*>* - refs() - { return this->refs_; } - - // Clear the references, if any. - void - clear_refs(); - // Return the number of remaining dependencies. size_t dep_count() const @@ -1280,36 +1299,12 @@ class Var_init Named_object* var_; // The backend initialization statement. Bstatement* init_; - // Variables this refers to. - std::vector<Named_object*>* refs_; // The number of initializations this is dependent on. A variable // initialization should not be emitted if any of its dependencies // have not yet been resolved. size_t dep_count_; }; -// Add a reference. - -void -Var_init::add_ref(Named_object* var) -{ - if (this->refs_ == NULL) - this->refs_ = new std::vector<Named_object*>; - this->refs_->push_back(var); -} - -// Clear the references, if any. - -void -Var_init::clear_refs() -{ - if (this->refs_ != NULL) - { - delete this->refs_; - this->refs_ = NULL; - } -} - // For comparing Var_init keys in a map. inline bool @@ -1324,7 +1319,7 @@ typedef std::list<Var_init> Var_inits; // variable V2 then we initialize V1 after V2. static void -sort_var_inits(Gogo* gogo, Var_inits* var_inits) +sort_var_inits(Var_inits* var_inits) { if (var_inits->empty()) return; @@ -1337,33 +1332,13 @@ sort_var_inits(Gogo* gogo, Var_inits* var_inits) Init_deps init_deps; bool init_loop = false; - // Compute all variable references. + // Map from variable to Var_init. for (Var_inits::iterator pvar = var_inits->begin(); pvar != var_inits->end(); ++pvar) { Named_object* var = pvar->var(); var_to_init[var] = &*pvar; - - Find_vars find_vars; - Expression* init = var->var_value()->init(); - if (init != NULL) - Expression::traverse(&init, &find_vars); - if (var->var_value()->has_pre_init()) - var->var_value()->preinit()->traverse(&find_vars); - Named_object* dep = gogo->var_depends_on(var->var_value()); - if (dep != NULL) - { - Expression* dinit = dep->var_value()->init(); - if (dinit != NULL) - Expression::traverse(&dinit, &find_vars); - if (dep->var_value()->has_pre_init()) - dep->var_value()->preinit()->traverse(&find_vars); - } - for (Find_vars::const_iterator p = find_vars.begin(); - p != find_vars.end(); - ++p) - pvar->add_ref(*p); } // Add dependencies to init_deps, and check for cycles. @@ -1373,7 +1348,8 @@ sort_var_inits(Gogo* gogo, Var_inits* var_inits) { Named_object* var = pvar->var(); - const std::vector<Named_object*>* refs = pvar->refs(); + const std::vector<Named_object*>* refs = + pvar->var()->var_value()->init_refs(); if (refs == NULL) continue; for (std::vector<Named_object*>::const_iterator pdep = refs->begin(); @@ -1383,19 +1359,11 @@ sort_var_inits(Gogo* gogo, Var_inits* var_inits) Named_object* dep = *pdep; if (var == dep) { - // This is a reference from a variable to itself, which - // may indicate a loop. We only report an error if - // there is an initializer and there is no dependency. - // When there is no initializer, it means that the - // preinitializer sets the variable, which will appear - // to be a loop here. - if (var->var_value()->init() != NULL - && gogo->var_depends_on(var->var_value()) == NULL) - go_error_at(var->location(), - ("initialization expression for %qs " - "depends upon itself"), - var->message_name().c_str()); - + // This is a reference from a variable to itself. + go_error_at(var->location(), + ("initialization expression for %qs " + "depends upon itself"), + var->message_name().c_str()); continue; } @@ -1412,7 +1380,8 @@ sort_var_inits(Gogo* gogo, Var_inits* var_inits) pvar->add_dependency(); // Check for cycles. - const std::vector<Named_object*>* deprefs = dep_init->refs(); + const std::vector<Named_object*>* deprefs = + dep_init->var()->var_value()->init_refs(); if (deprefs == NULL) continue; for (std::vector<Named_object*>::const_iterator pdepdep = @@ -1437,10 +1406,6 @@ sort_var_inits(Gogo* gogo, Var_inits* var_inits) } var_to_init.clear(); - for (Var_inits::iterator pvar = var_inits->begin(); - pvar != var_inits->end(); - ++pvar) - pvar->clear_refs(); // If there are no dependencies then the declaration order is sorted. if (!init_deps.empty() && !init_loop) @@ -1612,31 +1577,16 @@ Gogo::write_globals() // The initializer is constant if it is the zero-value of the // variable's type or if the initial value is an immutable value // that is not copied to the heap. - Expression* init = var->init(); - - // If we see "a = b; b = x", and x is a static - // initializer, just set a to x. - while (init != NULL && init->var_expression() != NULL) - { - Named_object* ino = init->var_expression()->named_object(); - if (!ino->is_variable() || ino->package() != NULL) - break; - Expression* ino_init = ino->var_value()->init(); - if (ino->var_value()->has_pre_init() - || ino_init == NULL - || !ino_init->is_static_initializer()) - break; - init = ino_init; - } - - bool is_static_initializer; - if (init == NULL) + bool is_static_initializer = false; + if (var->init() == NULL) is_static_initializer = true; else { Type* var_type = var->type(); - init = Expression::make_cast(var_type, init, var->location()); - is_static_initializer = init->is_static_initializer(); + Expression* init = var->init(); + Expression* init_cast = + Expression::make_cast(var_type, init, var->location()); + is_static_initializer = init_cast->is_static_initializer(); } // Non-constant variable initializations might need to create @@ -1655,15 +1605,7 @@ Gogo::write_globals() } var_init_fn = init_fndecl; } - - Bexpression* var_binit; - if (init == NULL) - var_binit = NULL; - else - { - Translate_context context(this, var_init_fn, NULL, NULL); - var_binit = init->get_backend(&context); - } + Bexpression* var_binit = var->get_init(this, var_init_fn); if (var_binit == NULL) ; @@ -1771,7 +1713,7 @@ Gogo::write_globals() // workable order. if (!var_inits.empty()) { - sort_var_inits(this, &var_inits); + sort_var_inits(&var_inits); for (Var_inits::const_iterator p = var_inits.begin(); p != var_inits.end(); ++p) @@ -2834,7 +2776,7 @@ Specific_type_functions::type(Type* t) case Type::TYPE_MAP: { - Type* key_type = t->map_type()->key_type(); + Type* key_type = t->map_type()->key_type()->unalias(); if (key_type->needs_specific_type_functions(this->gogo_)) key_type->hash_function(this->gogo_, NULL); } @@ -3863,6 +3805,51 @@ Gogo::check_types_in_block(Block* block) block->traverse(&traverse); } +// For each global variable defined in the current package, record the +// set of variables that its initializer depends on. We do this after +// lowering so that all unknown names are resolved to their final +// locations. We do this before write barrier insertion because that +// makes it harder to distinguish references from assignments in +// preinit blocks. + +void +Gogo::record_global_init_refs() +{ + Bindings* bindings = this->package_->bindings(); + for (Bindings::const_definitions_iterator pb = bindings->begin_definitions(); + pb != bindings->end_definitions(); + pb++) + { + Named_object* no = *pb; + if (!no->is_variable()) + continue; + + Variable* var = no->var_value(); + go_assert(var->is_global()); + + Find_vars find_vars; + Expression* init = var->init(); + if (init != NULL) + Expression::traverse(&init, &find_vars); + if (var->has_pre_init()) + var->preinit()->traverse(&find_vars); + Named_object* dep = this->var_depends_on(var); + if (dep != NULL) + { + Expression* dinit = dep->var_value()->init(); + if (dinit != NULL) + Expression::traverse(&dinit, &find_vars); + if (dep->var_value()->has_pre_init()) + dep->var_value()->preinit()->traverse(&find_vars); + } + + for (Find_vars::const_iterator pv = find_vars.begin(); + pv != find_vars.end(); + ++pv) + var->add_init_ref(*pv); + } +} + // A traversal class which finds all the expressions which must be // evaluated in order within a statement or larger expression. This // is used to implement the rules about order of evaluation. @@ -5325,16 +5312,16 @@ Gogo::write_c_header() Named_object* no = types.front(); types.pop_front(); - std::vector<const Named_object*> requires; + std::vector<const Named_object*> needs; std::vector<const Named_object*> declare; - if (!no->type_value()->struct_type()->can_write_to_c_header(&requires, + if (!no->type_value()->struct_type()->can_write_to_c_header(&needs, &declare)) continue; bool ok = true; for (std::vector<const Named_object*>::const_iterator pr - = requires.begin(); - pr != requires.end() && ok; + = needs.begin(); + pr != needs.end() && ok; ++pr) { for (std::list<Named_object*>::const_iterator pt = types.begin(); @@ -5365,10 +5352,10 @@ Gogo::write_c_header() if (*pd == no) continue; - std::vector<const Named_object*> drequires; + std::vector<const Named_object*> dneeds; std::vector<const Named_object*> ddeclare; if (!(*pd)->type_value()->struct_type()-> - can_write_to_c_header(&drequires, &ddeclare)) + can_write_to_c_header(&dneeds, &ddeclare)) continue; bool done = false; @@ -7445,16 +7432,16 @@ Variable::Variable(Type* type, Expression* init, bool is_global, bool is_parameter, bool is_receiver, Location location) : type_(type), init_(init), preinit_(NULL), location_(location), - embeds_(NULL), backend_(NULL), is_global_(is_global), - is_parameter_(is_parameter), is_closure_(false), is_receiver_(is_receiver), - is_varargs_parameter_(false), is_global_sink_(false), is_used_(false), - is_address_taken_(false), is_non_escaping_address_taken_(false), - seen_(false), init_is_lowered_(false), init_is_flattened_(false), + toplevel_decl_(NULL), init_refs_(NULL), embeds_(NULL), backend_(NULL), + is_global_(is_global), is_parameter_(is_parameter), is_closure_(false), + is_receiver_(is_receiver), is_varargs_parameter_(false), + is_global_sink_(false), is_used_(false), is_address_taken_(false), + is_non_escaping_address_taken_(false), seen_(false), + init_is_lowered_(false), init_is_flattened_(false), type_from_init_tuple_(false), type_from_range_index_(false), type_from_range_value_(false), type_from_chan_element_(false), is_type_switch_var_(false), determined_type_(false), - in_unique_section_(false), is_referenced_by_inline_(false), - toplevel_decl_(NULL) + in_unique_section_(false), is_referenced_by_inline_(false) { go_assert(type != NULL || init != NULL); go_assert(!is_parameter || init == NULL); @@ -7944,6 +7931,16 @@ Variable::get_init_block(Gogo* gogo, Named_object* function, return block_stmt; } +// Add an initializer reference. + +void +Variable::add_init_ref(Named_object* var) +{ + if (this->init_refs_ == NULL) + this->init_refs_ = new std::vector<Named_object*>; + this->init_refs_->push_back(var); +} + // Export the variable void diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h index 2ee0fda..433fdae 100644 --- a/gcc/go/gofrontend/gogo.h +++ b/gcc/go/gofrontend/gogo.h @@ -842,6 +842,11 @@ class Gogo void check_return_statements(); + // Gather references from global variables initializers to other + // variables. + void + record_global_init_refs(); + // Remove deadcode. void remove_deadcode(); @@ -2333,6 +2338,15 @@ class Variable this->toplevel_decl_ = s; } + // Note that the initializer of this global variable refers to VAR. + void + add_init_ref(Named_object* var); + + // The variables that this variable's initializers refer to. + const std::vector<Named_object*>* + init_refs() const + { return this->init_refs_; } + // Traverse the initializer expression. int traverse_expression(Traverse*, unsigned int traverse_mask); @@ -2389,6 +2403,12 @@ class Variable Block* preinit_; // Location of variable definition. Location location_; + // The top-level declaration for this variable. Only used for local + // variables. Must be a Temporary_statement if not NULL. + Statement* toplevel_decl_; + // Variables that the initializer of a global variable refers to. + // Used for initializer ordering. + std::vector<Named_object*>* init_refs_; // Any associated go:embed comments. std::vector<std::string>* embeds_; // Backend representation. @@ -2439,9 +2459,6 @@ class Variable // True if this variable is referenced from an inlined body that // will be put into the export data. bool is_referenced_by_inline_ : 1; - // The top-level declaration for this variable. Only used for local - // variables. Must be a Temporary_statement if not NULL. - Statement* toplevel_decl_; }; // A variable which is really the name for a function return value, or diff --git a/gcc/go/gofrontend/names.cc b/gcc/go/gofrontend/names.cc index f85d84c..dac7f20 100644 --- a/gcc/go/gofrontend/names.cc +++ b/gcc/go/gofrontend/names.cc @@ -831,15 +831,28 @@ Struct_type::do_mangled_name(Gogo* gogo, std::string* ret, ret->push_back(' '); } - // For an anonymous field with an alias type, the field name - // is the alias name. - if (p->is_anonymous() - && p->type()->named_type() != NULL - && p->type()->named_type()->is_alias()) - p->type()->named_type()->append_symbol_type_name(gogo, true, ret, - is_non_identifier); + const Type* ft = p->type(); + const Named_type* nt = ft->named_type(); + + if (p->is_anonymous() && nt != NULL && nt->is_builtin()) + { + // For an embedded field with a builtin type, we must + // include a package path. Otherwise embedding builtin + // types in different packages will produce identical + // types, which shouldn't happen because the builtin + // types are not exported. + ret->append(gogo->pkgpath()); + ret->push_back('.'); + nt->append_symbol_type_name(gogo, true, ret, is_non_identifier); + } + else if (p->is_anonymous() && nt != NULL && nt->is_alias()) + { + // For an anonymous field with an alias type, the field name + // is the alias name. + nt->append_symbol_type_name(gogo, true, ret, is_non_identifier); + } else - this->append_mangled_name(p->type(), gogo, ret, is_non_identifier); + this->append_mangled_name(ft, gogo, ret, is_non_identifier); if (p->has_tag()) { diff --git a/gcc/go/gofrontend/parse.cc b/gcc/go/gofrontend/parse.cc index cc197e5..c93d82b 100644 --- a/gcc/go/gofrontend/parse.cc +++ b/gcc/go/gofrontend/parse.cc @@ -191,7 +191,11 @@ Parse::qualified_ident(std::string* pname, Named_object** ppackage) Named_object* package = this->gogo_->lookup(name, NULL); if (package == NULL || !package->is_package()) { - go_error_at(this->location(), "expected package"); + if (package == NULL) + go_error_at(this->location(), "reference to undefined name %qs", + Gogo::message_name(name).c_str()); + else + go_error_at(this->location(), "expected package"); // We expect . IDENTIFIER; skip both. if (this->advance_token()->is_identifier()) this->advance_token(); @@ -1468,6 +1472,7 @@ Parse::const_spec(int iota, Type** last_type, Expression_list** last_expr_list) { Expression* copy = (*p)->copy(); copy->set_location(loc); + this->update_references(©); expr_list->push_back(copy); } } @@ -1513,6 +1518,94 @@ Parse::const_spec(int iota, Type** last_type, Expression_list** last_expr_list) return; } +// Update any references to names to refer to the current names, +// for weird cases like +// +// const X = 1 +// func F() { +// const ( +// X = X + X +// Y +// ) +// } +// +// where the X + X for the first X is the outer X, but the X + X +// copied for Y is the inner X. + +class Update_references : public Traverse +{ + public: + Update_references(Gogo* gogo) + : Traverse(traverse_expressions), + gogo_(gogo) + { } + + int + expression(Expression**); + + private: + Gogo* gogo_; +}; + +int +Update_references::expression(Expression** pexpr) +{ + Named_object* old_no; + switch ((*pexpr)->classification()) + { + case Expression::EXPRESSION_CONST_REFERENCE: + old_no = (*pexpr)->const_expression()->named_object(); + break; + case Expression::EXPRESSION_VAR_REFERENCE: + old_no = (*pexpr)->var_expression()->named_object(); + break; + case Expression::EXPRESSION_ENCLOSED_VAR_REFERENCE: + old_no = (*pexpr)->enclosed_var_expression()->variable(); + break; + case Expression::EXPRESSION_FUNC_REFERENCE: + old_no = (*pexpr)->func_expression()->named_object(); + break; + case Expression::EXPRESSION_UNKNOWN_REFERENCE: + old_no = (*pexpr)->unknown_expression()->named_object(); + break; + default: + return TRAVERSE_CONTINUE; + } + + if (old_no->package() != NULL) + { + // This is a qualified reference, so it can't have changed in + // scope. FIXME: This probably doesn't handle dot imports + // correctly. + return TRAVERSE_CONTINUE; + } + + Named_object* in_function; + Named_object* new_no = this->gogo_->lookup(old_no->name(), &in_function); + if (new_no == old_no) + return TRAVERSE_CONTINUE; + + // The new name must be a constant, since that is all we have + // introduced into scope. + if (!new_no->is_const()) + { + go_assert(saw_errors()); + return TRAVERSE_CONTINUE; + } + + *pexpr = Expression::make_const_reference(new_no, (*pexpr)->location()); + + return TRAVERSE_CONTINUE; +} + +void +Parse::update_references(Expression** pexpr) +{ + Update_references ur(this->gogo_); + ur.expression(pexpr); + (*pexpr)->traverse_subexpressions(&ur); +} + // TypeDecl = "type" Decl<TypeSpec> . void @@ -1888,7 +1981,11 @@ Parse::init_vars_from_map(const Typed_identifier_list* vars, Type* type, else if (!val_no->is_sink()) { if (val_no->is_variable()) - val_no->var_value()->add_preinit_statement(this->gogo_, s); + { + val_no->var_value()->add_preinit_statement(this->gogo_, s); + if (no->is_variable()) + this->gogo_->record_var_depends_on(no->var_value(), val_no); + } } else if (!no->is_sink()) { @@ -1955,7 +2052,11 @@ Parse::init_vars_from_receive(const Typed_identifier_list* vars, Type* type, else if (!val_no->is_sink()) { if (val_no->is_variable()) - val_no->var_value()->add_preinit_statement(this->gogo_, s); + { + val_no->var_value()->add_preinit_statement(this->gogo_, s); + if (no->is_variable()) + this->gogo_->record_var_depends_on(no->var_value(), val_no); + } } else if (!no->is_sink()) { @@ -2021,7 +2122,11 @@ Parse::init_vars_from_type_guard(const Typed_identifier_list* vars, else if (!val_no->is_sink()) { if (val_no->is_variable()) - val_no->var_value()->add_preinit_statement(this->gogo_, s); + { + val_no->var_value()->add_preinit_statement(this->gogo_, s); + if (no->is_variable()) + this->gogo_->record_var_depends_on(no->var_value(), val_no); + } } else if (!no->is_sink()) { diff --git a/gcc/go/gofrontend/parse.h b/gcc/go/gofrontend/parse.h index 6e300ef..cda0bee 100644 --- a/gcc/go/gofrontend/parse.h +++ b/gcc/go/gofrontend/parse.h @@ -185,6 +185,7 @@ class Parse void list(void (Parse::*)(), bool); void const_decl(); void const_spec(int, Type**, Expression_list**); + void update_references(Expression**); void type_decl(); void type_spec(); void var_decl(); diff --git a/gcc/go/gofrontend/statements.cc b/gcc/go/gofrontend/statements.cc index 95fa3c4..b442830 100644 --- a/gcc/go/gofrontend/statements.cc +++ b/gcc/go/gofrontend/statements.cc @@ -1260,6 +1260,16 @@ Assignment_operation_statement::do_lower(Gogo*, Named_object*, Move_ordered_evals moe(b); this->lhs_->traverse_subexpressions(&moe); + // We can still be left with subexpressions that have to be loaded + // even if they don't have side effects themselves, in case the RHS + // changes variables named on the LHS. + int i; + if (this->lhs_->must_eval_subexpressions_in_order(&i)) + { + Move_subexpressions ms(i, b); + this->lhs_->traverse_subexpressions(&ms); + } + Expression* lval = this->lhs_->copy(); Operator op; @@ -1584,9 +1594,9 @@ Tuple_map_assignment_statement::do_lower(Gogo* gogo, Named_object*, // var present_temp bool Temporary_statement* present_temp = - Statement::make_temporary((this->present_->type()->is_sink_type()) - ? Type::make_boolean_type() - : this->present_->type(), + Statement::make_temporary((this->present_->type()->is_boolean_type() + ? this->present_->type() + : Type::lookup_bool_type()), NULL, loc); b->add_statement(present_temp); @@ -1779,9 +1789,9 @@ Tuple_receive_assignment_statement::do_lower(Gogo*, Named_object*, // var closed_temp bool Temporary_statement* closed_temp = - Statement::make_temporary((this->closed_->type()->is_sink_type()) - ? Type::make_boolean_type() - : this->closed_->type(), + Statement::make_temporary((this->closed_->type()->is_boolean_type() + ? this->closed_->type() + : Type::lookup_bool_type()), NULL, loc); b->add_statement(closed_temp); @@ -1955,6 +1965,8 @@ Tuple_type_guard_assignment_statement::do_lower(Gogo*, Named_object*, b->add_statement(s); res = Expression::make_call_result(call, 1); + if (!this->ok_->type()->is_boolean_type()) + res = Expression::make_cast(Type::lookup_bool_type(), res, loc); s = Statement::make_assignment(this->ok_, res, loc); b->add_statement(s); } @@ -1991,7 +2003,9 @@ Tuple_type_guard_assignment_statement::lower_to_object_type( Temporary_statement* ok_temp = NULL; if (!this->ok_->is_sink_expression()) { - ok_temp = Statement::make_temporary(this->ok_->type(), + ok_temp = Statement::make_temporary((this->ok_->type()->is_boolean_type() + ? this->ok_->type() + : Type::lookup_bool_type()), NULL, loc); b->add_statement(ok_temp); } diff --git a/gcc/go/gofrontend/types.cc b/gcc/go/gofrontend/types.cc index 8267f15..9f34801 100644 --- a/gcc/go/gofrontend/types.cc +++ b/gcc/go/gofrontend/types.cc @@ -1764,6 +1764,9 @@ Type::needs_specific_type_functions(Gogo* gogo) Named_object* Type::hash_function(Gogo* gogo, Function_type* hash_fntype) { + if (this->named_type() != NULL) + go_assert(!this->named_type()->is_alias()); + if (!this->is_comparable()) return NULL; @@ -2067,6 +2070,9 @@ Type::write_identity_hash(Gogo* gogo, int64_t size) Named_object* Type::equal_function(Gogo* gogo, Named_type* name, Function_type* equal_fntype) { + if (this->named_type() != NULL) + go_assert(!this->named_type()->is_alias()); + // If the unaliased type is not a named type, then the type does not // have a name after all. if (name != NULL) @@ -4648,8 +4654,11 @@ class Sink_type : public Type { return false; } Btype* - do_get_backend(Gogo*) - { go_unreachable(); } + do_get_backend(Gogo* gogo) + { + go_assert(saw_errors()); + return gogo->backend()->error_type(); + } Expression* do_type_descriptor(Gogo*, Named_type*) @@ -5303,7 +5312,7 @@ Function_type::do_export(Export* exp) const if (results != NULL) { exp->write_c_string(" "); - if (results->size() == 1 && results->begin()->name().empty()) + if (results->size() == 1) exp->write_type(results->begin()->type()); else { @@ -6700,7 +6709,8 @@ Struct_type::write_hash_function(Gogo* gogo, Function_type* hash_fntype) subkey = Expression::make_cast(key_arg_type, subkey, bloc); // Get the hash function to use for the type of this field. - Named_object* hash_fn = pf->type()->hash_function(gogo, hash_fntype); + Named_object* hash_fn = + pf->type()->unalias()->hash_function(gogo, hash_fntype); // Call the hash function for the field, passing retval as the seed. ref = Expression::make_temporary_reference(retval, bloc); @@ -6957,7 +6967,7 @@ Struct_type::do_import(Import* imp) bool Struct_type::can_write_to_c_header( - std::vector<const Named_object*>* requires, + std::vector<const Named_object*>* needs, std::vector<const Named_object*>* declare) const { const Struct_field_list* fields = this->fields_; @@ -6968,7 +6978,7 @@ Struct_type::can_write_to_c_header( p != fields->end(); ++p) { - if (!this->can_write_type_to_c_header(p->type(), requires, declare)) + if (!this->can_write_type_to_c_header(p->type(), needs, declare)) return false; if (Gogo::message_name(p->field_name()) == "_") sinks++; @@ -6983,7 +6993,7 @@ Struct_type::can_write_to_c_header( bool Struct_type::can_write_type_to_c_header( const Type* t, - std::vector<const Named_object*>* requires, + std::vector<const Named_object*>* needs, std::vector<const Named_object*>* declare) const { t = t->forwarded(); @@ -7017,13 +7027,13 @@ Struct_type::can_write_type_to_c_header( return true; case TYPE_STRUCT: - return t->struct_type()->can_write_to_c_header(requires, declare); + return t->struct_type()->can_write_to_c_header(needs, declare); case TYPE_ARRAY: if (t->is_slice_type()) return true; return this->can_write_type_to_c_header(t->array_type()->element_type(), - requires, declare); + needs, declare); case TYPE_NAMED: { @@ -7039,10 +7049,10 @@ Struct_type::can_write_type_to_c_header( // We will accept empty struct fields, but not print them. if (t->struct_type()->total_field_count() == 0) return true; - requires->push_back(no); - return t->struct_type()->can_write_to_c_header(requires, declare); + needs->push_back(no); + return t->struct_type()->can_write_to_c_header(needs, declare); } - return this->can_write_type_to_c_header(t->base(), requires, declare); + return this->can_write_type_to_c_header(t->base(), needs, declare); } case TYPE_CALL_MULTIPLE_RESULT: @@ -7140,9 +7150,9 @@ Struct_type::write_field_to_c_header(std::ostream& os, const std::string& name, case TYPE_POINTER: { - std::vector<const Named_object*> requires; + std::vector<const Named_object*> needs; std::vector<const Named_object*> declare; - if (!this->can_write_type_to_c_header(t->points_to(), &requires, + if (!this->can_write_type_to_c_header(t->points_to(), &needs, &declare)) os << "void*"; else @@ -7419,7 +7429,10 @@ bool Array_type::do_verify() { if (this->element_type()->is_error_type()) - return false; + { + this->set_is_error(); + return false; + } if (!this->verify_length()) { this->length_ = Expression::make_error(this->length_->location()); @@ -7553,8 +7566,8 @@ Array_type::write_hash_function(Gogo* gogo, Function_type* hash_fntype) gogo->start_block(bloc); // Get the hash function for the element type. - Named_object* hash_fn = this->element_type_->hash_function(gogo, - hash_fntype); + Named_object* hash_fn = + this->element_type_->unalias()->hash_function(gogo, hash_fntype); // Get a pointer to this element in the loop. Expression* subkey = Expression::make_temporary_reference(key, bloc); @@ -7815,7 +7828,7 @@ Array_type::finish_backend_element(Gogo* gogo) // Return an expression for a pointer to the values in ARRAY. Expression* -Array_type::get_value_pointer(Gogo*, Expression* array, bool is_lvalue) const +Array_type::get_value_pointer(Gogo*, Expression* array) const { if (this->length() != NULL) { @@ -7828,25 +7841,6 @@ Array_type::get_value_pointer(Gogo*, Expression* array, bool is_lvalue) const } // Slice. - - if (is_lvalue) - { - Temporary_reference_expression* tref = - array->temporary_reference_expression(); - Var_expression* ve = array->var_expression(); - if (tref != NULL) - { - tref = tref->copy()->temporary_reference_expression(); - tref->set_is_lvalue(); - array = tref; - } - else if (ve != NULL) - { - ve = new Var_expression(ve->named_object(), ve->location()); - array = ve; - } - } - return Expression::make_slice_info(array, Expression::SLICE_INFO_VALUE_POINTER, array->location()); @@ -8460,8 +8454,8 @@ Map_type::do_type_descriptor(Gogo* gogo, Named_type* name) ++p; go_assert(p->is_field_name("hasher")); Function_type* hasher_fntype = p->type()->function_type(); - Named_object* hasher_fn = this->key_type_->hash_function(gogo, - hasher_fntype); + Named_object* hasher_fn = + this->key_type_->unalias()->hash_function(gogo, hasher_fntype); if (hasher_fn == NULL) vals->push_back(Expression::make_cast(hasher_fntype, Expression::make_nil(bloc), @@ -9050,6 +9044,9 @@ Interface_type::finalize_methods() if (this->parse_methods_ == NULL) return; + // The exporter uses parse_methods_. + this->parse_methods_->sort_by_name(); + this->all_methods_ = new Typed_identifier_list(); this->all_methods_->reserve(this->parse_methods_->size()); Typed_identifier_list inherit; @@ -9337,15 +9334,17 @@ Interface_type::is_compatible_for_assign(const Interface_type* t, // Hash code. unsigned int -Interface_type::do_hash_for_method(Gogo*, int) const +Interface_type::do_hash_for_method(Gogo*, int flags) const { go_assert(this->methods_are_finalized_); + Typed_identifier_list* methods = (((flags & COMPARE_EMBEDDED_INTERFACES) != 0) + ? this->parse_methods_ + : this->all_methods_); unsigned int ret = 0; - if (this->all_methods_ != NULL) + if (methods != NULL) { - for (Typed_identifier_list::const_iterator p = - this->all_methods_->begin(); - p != this->all_methods_->end(); + for (Typed_identifier_list::const_iterator p = methods->begin(); + p != methods->end(); ++p) { ret = Gogo::hash_string(p->name(), ret); @@ -11905,7 +11904,7 @@ Type::build_direct_iface_stub_methods(Gogo* gogo, const Type* type, need_stub = true; if (!in_heap && !m->is_value_method()) need_stub = true; - if (!need_stub) + if (!need_stub || m->is_ambiguous()) continue; Type* receiver_type = const_cast<Type*>(type); diff --git a/gcc/go/gofrontend/types.h b/gcc/go/gofrontend/types.h index c55345a..49404bd 100644 --- a/gcc/go/gofrontend/types.h +++ b/gcc/go/gofrontend/types.h @@ -2800,7 +2800,7 @@ class Array_type : public Type // Return an expression for the pointer to the values in an array. Expression* - get_value_pointer(Gogo*, Expression* array, bool is_lvalue) const; + get_value_pointer(Gogo*, Expression* array) const; // Return an expression for the length of an array with this type. Expression* @@ -3272,15 +3272,6 @@ class Interface_type : public Type methods_are_finalized() const { return this->methods_are_finalized_; } - // Sort embedded interfaces by name. Needed when we are preparing - // to emit types into the export data. - void - sort_embedded() - { - if (parse_methods_ != NULL) - parse_methods_->sort_by_name(); - } - protected: int do_traverse(Traverse*); |