* Go patch committed: Don't evaluate interface conversions multiple times
@ 2014-12-19 4:11 Ian Lance Taylor
0 siblings, 0 replies; only message in thread
From: Ian Lance Taylor @ 2014-12-19 4:11 UTC (permalink / raw)
To: gcc-patches, gofrontend-dev
[-- Attachment #1: Type: text/plain, Size: 712 bytes --]
There were a few cases where the Go compiler was evaluating value
multiple times when they were converted to or from interface types.
This happened when the conversion occurred in a composite literal, or
even, when implicitly converting from one interface type to another,
in a variable declaration. For this patch I added an assertion for
the cases where a value might be evaluated multiple times, and then
patched the compiler until it stopped crashing on the testsuite.
Bootstrapped and ran Go testsuite on x86_64-unknown-linux-gnu.
Committed to mainline. This bug does not occur on the 4.9 branch.
I've committed a test case to the master Go repository
(https://go-review.googlesource.com/#/c/1710/).
Ian
[-- Attachment #2: foo.txt --]
[-- Type: text/plain, Size: 8840 bytes --]
diff -r 47792391c17a go/expressions.cc
--- a/go/expressions.cc Wed Dec 17 06:26:14 2014 -0800
+++ b/go/expressions.cc Thu Dec 18 19:29:08 2014 -0800
@@ -302,6 +302,9 @@
bool for_type_guard,
Location location)
{
+ if (Type::are_identical(lhs_type, rhs->type(), false, NULL))
+ return rhs;
+
Interface_type* lhs_interface_type = lhs_type->interface_type();
bool lhs_is_empty = lhs_interface_type->is_empty();
@@ -313,6 +316,9 @@
// to do a runtime check, although we still need to build a new
// method table.
+ // We are going to evaluate RHS multiple times.
+ go_assert(rhs->is_variable());
+
// Get the type descriptor for the right hand side. This will be
// NULL for a nil interface.
Expression* rhs_type_expr = Expression::get_interface_type_descriptor(rhs);
@@ -355,6 +361,9 @@
Expression::convert_interface_to_type(Type *lhs_type, Expression* rhs,
Location location)
{
+ // We are going to evaluate RHS multiple times.
+ go_assert(rhs->is_variable());
+
// Call a function to check that the type is valid. The function
// will panic with an appropriate runtime type error if the type is
// not valid.
@@ -3155,8 +3164,7 @@
{
if (((this->type()->is_string_type()
&& this->expr_->type()->is_slice_type())
- || (this->type()->interface_type() != NULL
- && this->expr_->type()->interface_type() != NULL))
+ || this->expr_->type()->interface_type() != NULL)
&& !this->expr_->is_variable())
{
Temporary_statement* temp =
@@ -8782,12 +8790,17 @@
else
{
Location loc = (*pa)->location();
- Expression* arg =
- Expression::convert_for_assignment(gogo, pp->type(), *pa, loc);
- Temporary_statement* temp =
- Statement::make_temporary(pp->type(), arg, loc);
- inserter->insert(temp);
- args->push_back(Expression::make_temporary_reference(temp, loc));
+ Expression* arg = *pa;
+ if (!arg->is_variable())
+ {
+ Temporary_statement *temp =
+ Statement::make_temporary(NULL, arg, loc);
+ inserter->insert(temp);
+ arg = Expression::make_temporary_reference(temp, loc);
+ }
+ arg = Expression::convert_for_assignment(gogo, pp->type(), arg,
+ loc);
+ args->push_back(arg);
}
}
delete this->args_;
@@ -11602,6 +11615,9 @@
return ret;
}
+ Expression*
+ do_flatten(Gogo*, Named_object*, Statement_inserter*);
+
Bexpression*
do_get_backend(Translate_context*);
@@ -11776,6 +11792,39 @@
go_assert(pv == this->vals_->end());
}
+// Flatten a struct construction expression. Store the values into
+// temporaries in case they need interface conversion.
+
+Expression*
+Struct_construction_expression::do_flatten(Gogo*, Named_object*,
+ Statement_inserter* inserter)
+{
+ if (this->vals_ == NULL)
+ return this;
+
+ // If this is a constant struct, we don't need temporaries.
+ if (this->is_constant_struct())
+ return this;
+
+ Location loc = this->location();
+ for (Expression_list::iterator pv = this->vals_->begin();
+ pv != this->vals_->end();
+ ++pv)
+ {
+ if (*pv != NULL)
+ {
+ if (!(*pv)->is_variable())
+ {
+ Temporary_statement* temp =
+ Statement::make_temporary(NULL, *pv, loc);
+ inserter->insert(temp);
+ *pv = Expression::make_temporary_reference(temp, loc);
+ }
+ }
+ }
+ return this;
+}
+
// Return the backend representation for constructing a struct.
Bexpression*
@@ -11909,6 +11958,9 @@
vals()
{ return this->vals_; }
+ Expression*
+ do_flatten(Gogo*, Named_object*, Statement_inserter*);
+
// Get the backend constructor for the array values.
Bexpression*
get_constructor(Translate_context* context, Btype* btype);
@@ -12024,6 +12076,39 @@
}
}
+// Flatten an array construction expression. Store the values into
+// temporaries in case they need interface conversion.
+
+Expression*
+Array_construction_expression::do_flatten(Gogo*, Named_object*,
+ Statement_inserter* inserter)
+{
+ if (this->vals_ == NULL)
+ return this;
+
+ // If this is a constant array, we don't need temporaries.
+ if (this->is_constant_array())
+ return this;
+
+ Location loc = this->location();
+ for (Expression_list::iterator pv = this->vals_->begin();
+ pv != this->vals_->end();
+ ++pv)
+ {
+ if (*pv != NULL)
+ {
+ if (!(*pv)->is_variable())
+ {
+ Temporary_statement* temp =
+ Statement::make_temporary(NULL, *pv, loc);
+ inserter->insert(temp);
+ *pv = Expression::make_temporary_reference(temp, loc);
+ }
+ }
+ }
+ return this;
+}
+
// Get a constructor expression for the array values.
Bexpression*
diff -r 47792391c17a go/gogo.cc
--- a/go/gogo.cc Wed Dec 17 06:26:14 2014 -0800
+++ b/go/gogo.cc Thu Dec 18 19:29:08 2014 -0800
@@ -5830,6 +5830,21 @@
gogo->flatten_expression(function, inserter, &this->init_);
+ // If an interface conversion is needed, we need a temporary
+ // variable.
+ if (this->type_ != NULL
+ && !Type::are_identical(this->type_, this->init_->type(), false,
+ NULL)
+ && this->init_->type()->interface_type() != NULL
+ && !this->init_->is_variable())
+ {
+ Temporary_statement* temp =
+ Statement::make_temporary(NULL, this->init_, this->location_);
+ inserter->insert(temp);
+ this->init_ = Expression::make_temporary_reference(temp,
+ this->location_);
+ }
+
this->seen_ = false;
this->init_is_flattened_ = true;
}
diff -r 47792391c17a go/statements.cc
--- a/go/statements.cc Wed Dec 17 06:26:14 2014 -0800
+++ b/go/statements.cc Thu Dec 18 19:29:08 2014 -0800
@@ -521,6 +521,9 @@
void
do_check_types(Gogo*);
+ Statement*
+ do_flatten(Gogo*, Named_object*, Block*, Statement_inserter*);
+
Bstatement*
do_get_backend(Translate_context*);
@@ -606,6 +609,28 @@
this->set_is_error();
}
+// Flatten an assignment statement. We may need a temporary for
+// interface conversion.
+
+Statement*
+Assignment_statement::do_flatten(Gogo*, Named_object*, Block*,
+ Statement_inserter* inserter)
+{
+ if (!this->lhs_->is_sink_expression()
+ && !Type::are_identical(this->lhs_->type(), this->rhs_->type(),
+ false, NULL)
+ && this->rhs_->type()->interface_type() != NULL
+ && !this->rhs_->is_variable())
+ {
+ Temporary_statement* temp =
+ Statement::make_temporary(NULL, this->rhs_, this->location());
+ inserter->insert(temp);
+ this->rhs_ = Expression::make_temporary_reference(temp,
+ this->location());
+ }
+ return this;
+}
+
// Convert an assignment statement to the backend representation.
Bstatement*
@@ -2480,12 +2505,13 @@
gogo->add_block(b, location);
gogo->lower_block(function, b);
- gogo->flatten_block(function, b);
// We already ran the determine_types pass, so we need to run it
// just for the call statement now. The other types are known.
call_statement->determine_types();
+ gogo->flatten_block(function, b);
+
if (may_call_recover || recover_arg != NULL)
{
// Dig up the call expression, which may have been changed
@@ -4412,6 +4438,27 @@
}
}
+// Flatten a send statement. We may need a temporary for interface
+// conversion.
+
+Statement*
+Send_statement::do_flatten(Gogo*, Named_object*, Block*,
+ Statement_inserter* inserter)
+{
+ Type* element_type = this->channel_->type()->channel_type()->element_type();
+ if (!Type::are_identical(element_type, this->val_->type(), false, NULL)
+ && this->val_->type()->interface_type() != NULL
+ && !this->val_->is_variable())
+ {
+ Temporary_statement* temp =
+ Statement::make_temporary(NULL, this->val_, this->location());
+ inserter->insert(temp);
+ this->val_ = Expression::make_temporary_reference(temp,
+ this->location());
+ }
+ return this;
+}
+
// Convert a send statement to the backend representation.
Bstatement*
@@ -4421,7 +4468,9 @@
Channel_type* channel_type = this->channel_->type()->channel_type();
Type* element_type = channel_type->element_type();
- Expression* val = Expression::make_cast(element_type, this->val_, loc);
+ Expression* val = Expression::convert_for_assignment(context->gogo(),
+ element_type,
+ this->val_, loc);
bool is_small;
bool can_take_address;
diff -r 47792391c17a go/statements.h
--- a/go/statements.h Wed Dec 17 06:26:14 2014 -0800
+++ b/go/statements.h Thu Dec 18 19:29:08 2014 -0800
@@ -704,6 +704,9 @@
void
do_check_types(Gogo*);
+ Statement*
+ do_flatten(Gogo*, Named_object*, Block*, Statement_inserter*);
+
Bstatement*
do_get_backend(Translate_context*);
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2014-12-19 4:06 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-12-19 4:11 Go patch committed: Don't evaluate interface conversions multiple times Ian Lance Taylor
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).