Skip to content

Commit

Permalink
compiler: Flatten erroneous subtrees into errors.
Browse files Browse the repository at this point in the history
Between the lowering and flattening passes of the compiler, there are
several passes that modify the lowered Go parse tree and as errors are
discovered, several nodes transform into error nodes.  However, for a
higher level node such as a construction expression, the erroneous
nodes in the subtrees might not propagate their error.  The flatten
phase for a node now looks for errors in the subtree and flattens the
node into an error node if any are found.

Fixes golang/go#11559, golang/go#11536, golang/go#11558.

Change-Id: I9a99d5a2f8ab396ca71787c2bd2af3b1f7ef625a
Reviewed-on: https://go-review.googlesource.com/13097
Reviewed-by: Ian Lance Taylor <[email protected]>
  • Loading branch information
Chris Manghane authored and ianlancetaylor committed Aug 13, 2015
1 parent 5fc38e7 commit fc9da31
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 18 deletions.
163 changes: 145 additions & 18 deletions go/expressions.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3101,6 +3101,12 @@ Expression*
Type_conversion_expression::do_flatten(Gogo*, Named_object*,
Statement_inserter* inserter)
{
if (this->type()->is_error_type() || this->expr_->is_error_expression())
{
go_assert(saw_errors());
return Expression::make_error(this->location());
}

if (((this->type()->is_string_type()
&& this->expr_->type()->is_slice_type())
|| this->expr_->type()->interface_type() != NULL)
Expand Down Expand Up @@ -3585,8 +3591,13 @@ Expression*
Unary_expression::do_flatten(Gogo* gogo, Named_object*,
Statement_inserter* inserter)
{
if (this->is_error_expression() || this->expr_->is_error_expression())
return Expression::make_error(this->location());
if (this->is_error_expression()
|| this->expr_->is_error_expression()
|| this->expr_->type()->is_error_type())
{
go_assert(saw_errors());
return Expression::make_error(this->location());
}

Location location = this->location();
if (this->op_ == OPERATOR_MULT
Expand Down Expand Up @@ -5062,10 +5073,16 @@ Expression*
Binary_expression::do_flatten(Gogo* gogo, Named_object*,
Statement_inserter* inserter)
{
if (this->classification() == EXPRESSION_ERROR)
return this;

Location loc = this->location();
if (this->left_->type()->is_error_type()
|| this->right_->type()->is_error_type()
|| this->left_->is_error_expression()
|| this->right_->is_error_expression())
{
go_assert(saw_errors());
return Expression::make_error(loc);
}

Temporary_statement* temp;
if (this->left_->type()->is_string_type()
&& this->op_ == OPERATOR_PLUS)
Expand Down Expand Up @@ -6806,6 +6823,11 @@ Builtin_call_expression::do_flatten(Gogo*, Named_object*,
Statement_inserter* inserter)
{
Location loc = this->location();
if (this->is_erroneous_call())
{
go_assert(saw_errors());
return Expression::make_error(loc);
}

switch (this->code_)
{
Expand Down Expand Up @@ -8733,8 +8755,11 @@ Expression*
Call_expression::do_flatten(Gogo* gogo, Named_object*,
Statement_inserter* inserter)
{
if (this->classification() == EXPRESSION_ERROR)
return this;
if (this->is_erroneous_call())
{
go_assert(saw_errors());
return Expression::make_error(this->location());
}

if (this->is_flattened_)
return this;
Expand Down Expand Up @@ -8902,6 +8927,27 @@ Call_expression::issue_error()
}
}

// Whether or not this call contains errors, either in the call or the
// arguments to the call.

bool
Call_expression::is_erroneous_call()
{
if (this->is_error_expression() || this->fn()->is_error_expression())
return true;

if (this->args() == NULL)
return false;
for (Expression_list::iterator pa = this->args()->begin();
pa != this->args()->end();
++pa)
{
if ((*pa)->type()->is_error_type() || (*pa)->is_error_expression())
return true;
}
return false;
}

// Get the type.

Type*
Expand Down Expand Up @@ -9848,30 +9894,47 @@ Array_index_expression::do_flatten(Gogo*, Named_object*,
Statement_inserter* inserter)
{
Location loc = this->location();
Expression* array = this->array_;
Expression* start = this->start_;
Expression* end = this->end_;
Expression* cap = this->cap_;
if (array->is_error_expression()
|| array->type()->is_error_type()
|| start->is_error_expression()
|| start->type()->is_error_type()
|| (end != NULL
&& (end->is_error_expression() || end->type()->is_error_type()))
|| (cap != NULL
&& (cap->is_error_expression() || cap->type()->is_error_type())))
{
go_assert(saw_errors());
return Expression::make_error(loc);
}

Temporary_statement* temp;
if (this->array_->type()->is_slice_type() && !this->array_->is_variable())
if (array->type()->is_slice_type() && !array->is_variable())
{
temp = Statement::make_temporary(NULL, this->array_, loc);
temp = Statement::make_temporary(NULL, array, loc);
inserter->insert(temp);
this->array_ = Expression::make_temporary_reference(temp, loc);
}
if (!this->start_->is_variable())
if (!start->is_variable())
{
temp = Statement::make_temporary(NULL, this->start_, loc);
temp = Statement::make_temporary(NULL, start, loc);
inserter->insert(temp);
this->start_ = Expression::make_temporary_reference(temp, loc);
}
if (this->end_ != NULL
&& !this->end_->is_nil_expression()
&& !this->end_->is_variable())
if (end != NULL
&& !end->is_nil_expression()
&& !end->is_variable())
{
temp = Statement::make_temporary(NULL, this->end_, loc);
temp = Statement::make_temporary(NULL, end, loc);
inserter->insert(temp);
this->end_ = Expression::make_temporary_reference(temp, loc);
}
if (this->cap_ != NULL && !this->cap_->is_variable())
if (cap!= NULL && !cap->is_variable())
{
temp = Statement::make_temporary(NULL, this->cap_, loc);
temp = Statement::make_temporary(NULL, cap, loc);
inserter->insert(temp);
this->cap_ = Expression::make_temporary_reference(temp, loc);
}
Expand Down Expand Up @@ -10179,8 +10242,22 @@ Expression*
String_index_expression::do_flatten(Gogo*, Named_object*,
Statement_inserter* inserter)
{
Temporary_statement* temp;
Location loc = this->location();
Expression* string = this->string_;
Expression* start = this->start_;
Expression* end = this->end_;
if (string->is_error_expression()
|| string->type()->is_error_type()
|| start->is_error_expression()
|| start->type()->is_error_type()
|| (end != NULL
&& (end->is_error_expression() || end->type()->is_error_type())))
{
go_assert(saw_errors());
return Expression::make_error(loc);
}

Temporary_statement* temp;
if (!this->string_->is_variable())
{
temp = Statement::make_temporary(NULL, this->string_, loc);
Expand Down Expand Up @@ -10419,6 +10496,14 @@ Map_index_expression::do_flatten(Gogo* gogo, Named_object*,
{
Location loc = this->location();
Map_type* mt = this->get_map_type();
if (this->index()->is_error_expression()
|| this->index()->type()->is_error_type()
|| mt->is_error_type())
{
go_assert(saw_errors());
return Expression::make_error(loc);
}

if (!Type::are_identical(mt->key_type(), this->index_->type(), false, NULL))
{
if (this->index_->type()->interface_type() != NULL
Expand All @@ -10443,6 +10528,9 @@ Map_index_expression::do_flatten(Gogo* gogo, Named_object*,

if (this->value_pointer_ == NULL)
this->get_value_pointer(this->is_lvalue_);
if (this->value_pointer_->is_error_expression()
|| this->value_pointer_->type()->is_error_type())
return Expression::make_error(loc);
if (!this->value_pointer_->is_variable())
{
Temporary_statement* temp =
Expand Down Expand Up @@ -10819,6 +10907,13 @@ Expression*
Interface_field_reference_expression::do_flatten(Gogo*, Named_object*,
Statement_inserter* inserter)
{
if (this->expr_->is_error_expression()
|| this->expr_->type()->is_error_type())
{
go_assert(saw_errors());
return Expression::make_error(this->location());
}

if (!this->expr_->is_variable())
{
Temporary_statement* temp =
Expand Down Expand Up @@ -11598,6 +11693,11 @@ Struct_construction_expression::do_flatten(Gogo*, Named_object*,
{
if (*pv != NULL)
{
if ((*pv)->is_error_expression() || (*pv)->type()->is_error_type())
{
go_assert(saw_errors());
return Expression::make_error(loc);
}
if (!(*pv)->is_variable())
{
Temporary_statement* temp =
Expand Down Expand Up @@ -11809,6 +11909,11 @@ Array_construction_expression::do_flatten(Gogo*, Named_object*,
{
if (*pv != NULL)
{
if ((*pv)->is_error_expression() || (*pv)->type()->is_error_type())
{
go_assert(saw_errors());
return Expression::make_error(loc);
}
if (!(*pv)->is_variable())
{
Temporary_statement* temp =
Expand Down Expand Up @@ -12124,6 +12229,11 @@ Map_construction_expression::do_flatten(Gogo* gogo, Named_object*,
{
Expression_list* key_value_pair = new Expression_list();
Expression* key = *pv;
if (key->is_error_expression() || key->type()->is_error_type())
{
go_assert(saw_errors());
return Expression::make_error(loc);
}
if (key->type()->interface_type() != NULL && !key->is_variable())
{
Temporary_statement* temp =
Expand All @@ -12135,6 +12245,11 @@ Map_construction_expression::do_flatten(Gogo* gogo, Named_object*,

++pv;
Expression* val = *pv;
if (val->is_error_expression() || val->type()->is_error_type())
{
go_assert(saw_errors());
return Expression::make_error(loc);
}
if (val->type()->interface_type() != NULL && !val->is_variable())
{
Temporary_statement* temp =
Expand Down Expand Up @@ -13103,6 +13218,13 @@ Expression*
Type_guard_expression::do_flatten(Gogo*, Named_object*,
Statement_inserter* inserter)
{
if (this->expr_->is_error_expression()
|| this->expr_->type()->is_error_type())
{
go_assert(saw_errors());
return Expression::make_error(this->location());
}

if (!this->expr_->is_variable())
{
Temporary_statement* temp = Statement::make_temporary(NULL, this->expr_,
Expand Down Expand Up @@ -13297,6 +13419,11 @@ Receive_expression::do_flatten(Gogo*, Named_object*,
go_assert(saw_errors());
return this;
}
else if (this->channel_->is_error_expression())
{
go_assert(saw_errors());
return Expression::make_error(this->location());
}

Type* element_type = channel_type->element_type();
if (this->temp_receiver_ == NULL)
Expand Down
5 changes: 5 additions & 0 deletions go/expressions.h
Original file line number Diff line number Diff line change
Expand Up @@ -1958,6 +1958,11 @@ class Call_expression : public Expression
bool
issue_error();

// Whether or not this call contains errors, either in the call or the
// arguments to the call.
bool
is_erroneous_call();

// Whether this call returns multiple results that are used as an
// multi-valued argument.
bool
Expand Down
32 changes: 32 additions & 0 deletions go/statements.cc
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,14 @@ Statement*
Variable_declaration_statement::do_flatten(Gogo* gogo, Named_object* function,
Block*, Statement_inserter* inserter)
{
Variable* var = this->var_->var_value();
if (var->type()->is_error_type()
|| (var->init() != NULL
&& var->init()->is_error_expression()))
{
go_assert(saw_errors());
return Statement::make_error_statement(this->location());
}
this->var_->var_value()->flatten_init_expression(gogo, function, inserter);
return this;
}
Expand Down Expand Up @@ -437,6 +445,14 @@ Statement*
Temporary_statement::do_flatten(Gogo*, Named_object*, Block*,
Statement_inserter* inserter)
{
if (this->type()->is_error_type()
|| (this->init_ != NULL
&& this->init_->is_error_expression()))
{
go_assert(saw_errors());
return Statement::make_error_statement(this->location());
}

if (this->type_ != NULL
&& this->init_ != NULL
&& !Type::are_identical(this->type_, this->init_->type(), false, NULL)
Expand Down Expand Up @@ -610,6 +626,15 @@ Statement*
Assignment_statement::do_flatten(Gogo*, Named_object*, Block*,
Statement_inserter* inserter)
{
if (this->lhs_->is_error_expression()
|| this->lhs_->type()->is_error_type()
|| this->rhs_->is_error_expression()
|| this->rhs_->type()->is_error_type())
{
go_assert(saw_errors());
return Statement::make_error_statement(this->location());
}

if (!this->lhs_->is_sink_expression()
&& !Type::are_identical(this->lhs_->type(), this->rhs_->type(),
false, NULL)
Expand Down Expand Up @@ -4397,6 +4422,13 @@ Statement*
Send_statement::do_flatten(Gogo*, Named_object*, Block*,
Statement_inserter* inserter)
{
if (this->channel_->is_error_expression()
|| this->channel_->type()->is_error_type())
{
go_assert(saw_errors());
return Statement::make_error_statement(this->location());
}

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
Expand Down

0 comments on commit fc9da31

Please sign in to comment.