diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2015-03-06 00:27:32 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2015-03-06 00:27:32 +0000 |
commit | aeb41dc5b633a87d0ebe69ed1ce0b7f0af24fa3e (patch) | |
tree | 046f5dcdb454e83a54280fc6fa5bd40a8f864333 /gcc/go/gofrontend/parse.cc | |
parent | c4571e0e36c9ff80c29ad562c779be2e8fa1b6b2 (diff) | |
download | gcc-aeb41dc5b633a87d0ebe69ed1ce0b7f0af24fa3e.zip gcc-aeb41dc5b633a87d0ebe69ed1ce0b7f0af24fa3e.tar.gz gcc-aeb41dc5b633a87d0ebe69ed1ce0b7f0af24fa3e.tar.bz2 |
compiler: Do not declare type switch variable outside case statements.
For expressions containing a TypeSwitchGuard with a short variable
declaration e.g. var := x.(type), the spec says that var is declared
at the beginning of the implicit block for each in each clause.
Previously, var was declared in the block for the switch statement
and each implicit block, which led to errors if the type case clause
referenced a type with a similar name as the declared variable.
Fixes golang/go#10047.
From-SVN: r221230
Diffstat (limited to 'gcc/go/gofrontend/parse.cc')
-rw-r--r-- | gcc/go/gofrontend/parse.cc | 104 |
1 files changed, 57 insertions, 47 deletions
diff --git a/gcc/go/gofrontend/parse.cc b/gcc/go/gofrontend/parse.cc index 29edba7..496ab41 100644 --- a/gcc/go/gofrontend/parse.cc +++ b/gcc/go/gofrontend/parse.cc @@ -50,8 +50,7 @@ Parse::Parse(Lex* lex, Gogo* gogo) break_stack_(NULL), continue_stack_(NULL), iota_(0), - enclosing_vars_(), - type_switch_vars_() + enclosing_vars_() { } @@ -4596,32 +4595,33 @@ Statement* Parse::type_switch_body(Label* label, const Type_switch& type_switch, Location location) { - Named_object* switch_no = NULL; - if (!type_switch.name.empty()) - { - if (Gogo::is_sink_name(type_switch.name)) - error_at(type_switch.location, - "no new variables on left side of %<:=%>"); + Expression* init = type_switch.expr; + std::string var_name = type_switch.name; + if (!var_name.empty()) + { + if (Gogo::is_sink_name(var_name)) + { + error_at(type_switch.location, + "no new variables on left side of %<:=%>"); + var_name.clear(); + } else { - Variable* switch_var = new Variable(NULL, type_switch.expr, false, - false, false, - type_switch.location); - switch_no = this->gogo_->add_variable(type_switch.name, switch_var); + Location loc = type_switch.location; + Temporary_statement* switch_temp = + Statement::make_temporary(NULL, init, loc); + this->gogo_->add_statement(switch_temp); + init = Expression::make_temporary_reference(switch_temp, loc); } } Type_switch_statement* statement = - Statement::make_type_switch_statement(switch_no, - (switch_no == NULL - ? type_switch.expr - : NULL), - location); - + Statement::make_type_switch_statement(var_name, init, location); this->push_break_statement(statement, label); Type_case_clauses* case_clauses = new Type_case_clauses(); bool saw_default = false; + std::vector<Named_object*> implicit_vars; while (!this->peek_token()->is_op(OPERATOR_RCURLY)) { if (this->peek_token()->is_eof()) @@ -4629,7 +4629,8 @@ Parse::type_switch_body(Label* label, const Type_switch& type_switch, error_at(this->location(), "missing %<}%>"); return NULL; } - this->type_case_clause(switch_no, case_clauses, &saw_default); + this->type_case_clause(var_name, init, case_clauses, &saw_default, + &implicit_vars); } this->advance_token(); @@ -4637,14 +4638,36 @@ Parse::type_switch_body(Label* label, const Type_switch& type_switch, this->pop_break_statement(); + // If there is a type switch variable implicitly declared in each case clause, + // check that it is used in at least one of the cases. + if (!var_name.empty()) + { + bool used = false; + for (std::vector<Named_object*>::iterator p = implicit_vars.begin(); + p != implicit_vars.end(); + ++p) + { + if ((*p)->var_value()->is_used()) + { + used = true; + break; + } + } + if (!used) + error_at(type_switch.location, "%qs declared and not used", + Gogo::message_name(var_name).c_str()); + } return statement; } // TypeCaseClause = TypeSwitchCase ":" [ StatementList ] . +// IMPLICIT_VARS is the list of variables implicitly declared for each type +// case if there is a type switch variable declared. void -Parse::type_case_clause(Named_object* switch_no, Type_case_clauses* clauses, - bool* saw_default) +Parse::type_case_clause(const std::string& var_name, Expression* init, + Type_case_clauses* clauses, bool* saw_default, + std::vector<Named_object*>* implicit_vars) { Location location = this->location(); @@ -4661,24 +4684,21 @@ Parse::type_case_clause(Named_object* switch_no, Type_case_clauses* clauses, if (this->statement_list_may_start_here()) { this->gogo_->start_block(this->location()); - if (switch_no != NULL && types.size() == 1) + if (!var_name.empty()) { - Type* type = types.front(); - Expression* init = Expression::make_var_reference(switch_no, - location); - init = Expression::make_type_guard(init, type, location); - Variable* v = new Variable(type, init, false, false, false, - location); - v->set_is_type_switch_var(); - Named_object* no = this->gogo_->add_variable(switch_no->name(), v); + Type* type = NULL; + Location var_loc = init->location(); + if (types.size() == 1) + { + type = types.front(); + init = Expression::make_type_guard(init, type, location); + } - // We don't want to issue an error if the compiler - // introduced special variable is not used. Instead we want - // to issue an error if the variable defined by the switch - // is not used. That is handled via type_switch_vars_ and - // Parse::mark_var_used. + Variable* v = new Variable(type, init, false, false, false, + var_loc); v->set_is_used(); - this->type_switch_vars_[no] = switch_no; + v->set_is_type_switch_var(); + implicit_vars->push_back(this->gogo_->add_variable(var_name, v)); } this->statement_list(); statements = this->gogo_->finish_block(this->location()); @@ -5752,15 +5772,5 @@ void Parse::mark_var_used(Named_object* no) { if (no->is_variable()) - { - no->var_value()->set_is_used(); - - // When a type switch uses := to define a variable, then for - // each case with a single type we introduce a new variable with - // the appropriate type. When we do, if the newly introduced - // variable is used, then the type switch variable is used. - Type_switch_vars::iterator p = this->type_switch_vars_.find(no); - if (p != this->type_switch_vars_.end()) - p->second->var_value()->set_is_used(); - } + no->var_value()->set_is_used(); } |