aboutsummaryrefslogtreecommitdiff
path: root/gcc/go
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2022-07-27 10:15:41 -0700
committerIan Lance Taylor <iant@golang.org>2022-07-27 10:15:41 -0700
commit9f62ed218fa656607740b386c0caa03e65dcd283 (patch)
tree6bde49bc5e4c4241266b108e4277baef4b85535d /gcc/go
parent71e955da39cea0ebffcfee3432effa622d14ca99 (diff)
parent5eb9f117a361538834b9740d59219911680717d1 (diff)
downloadgcc-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/ChangeLog19
-rw-r--r--gcc/go/Make-lang.in2
-rw-r--r--gcc/go/go-gcc.cc20
-rw-r--r--gcc/go/go-lang.cc11
-rw-r--r--gcc/go/gofrontend/MERGE2
-rw-r--r--gcc/go/gofrontend/export.cc346
-rw-r--r--gcc/go/gofrontend/expressions.cc165
-rw-r--r--gcc/go/gofrontend/expressions.h121
-rw-r--r--gcc/go/gofrontend/go.cc3
-rw-r--r--gcc/go/gofrontend/gogo.cc253
-rw-r--r--gcc/go/gofrontend/gogo.h23
-rw-r--r--gcc/go/gofrontend/names.cc29
-rw-r--r--gcc/go/gofrontend/parse.cc113
-rw-r--r--gcc/go/gofrontend/parse.h1
-rw-r--r--gcc/go/gofrontend/statements.cc28
-rw-r--r--gcc/go/gofrontend/types.cc89
-rw-r--r--gcc/go/gofrontend/types.h11
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(&copy);
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*);