Implement goto restrictions.

From-SVN: r179018
This commit is contained in:
Ian Lance Taylor 2011-09-20 21:00:07 +00:00
parent b432106bc0
commit 6675c41604
8 changed files with 280 additions and 63 deletions

View File

@ -857,7 +857,7 @@ Gogo::add_label_definition(const std::string& label_name,
{
go_assert(!this->functions_.empty());
Function* func = this->functions_.back().function->func_value();
Label* label = func->add_label_definition(label_name, location);
Label* label = func->add_label_definition(this, label_name, location);
this->add_statement(Statement::make_label_statement(label, location));
return label;
}
@ -865,11 +865,21 @@ Gogo::add_label_definition(const std::string& label_name,
// Add a label reference.
Label*
Gogo::add_label_reference(const std::string& label_name)
Gogo::add_label_reference(const std::string& label_name,
source_location location, bool issue_goto_errors)
{
go_assert(!this->functions_.empty());
Function* func = this->functions_.back().function->func_value();
return func->add_label_reference(label_name);
return func->add_label_reference(this, label_name, location,
issue_goto_errors);
}
// Return the current binding state.
Bindings_snapshot*
Gogo::bindings_snapshot(source_location location)
{
return new Bindings_snapshot(this->current_block(), location);
}
// Add a statement.
@ -2843,30 +2853,24 @@ Function::is_method() const
// Add a label definition.
Label*
Function::add_label_definition(const std::string& label_name,
Function::add_label_definition(Gogo* gogo, const std::string& label_name,
source_location location)
{
Label* lnull = NULL;
std::pair<Labels::iterator, bool> ins =
this->labels_.insert(std::make_pair(label_name, lnull));
Label* label;
if (ins.second)
{
// This is a new label.
Label* label = new Label(label_name);
label->define(location);
label = new Label(label_name);
ins.first->second = label;
return label;
}
else
{
// The label was already in the hash table.
Label* label = ins.first->second;
if (!label->is_defined())
{
label->define(location);
return label;
}
else
label = ins.first->second;
if (label->is_defined())
{
error_at(location, "label %qs already defined",
Gogo::message_name(label_name).c_str());
@ -2875,31 +2879,55 @@ Function::add_label_definition(const std::string& label_name,
return new Label(label_name);
}
}
label->define(location, gogo->bindings_snapshot(location));
// Issue any errors appropriate for any previous goto's to this
// label.
const std::vector<Bindings_snapshot*>& refs(label->refs());
for (std::vector<Bindings_snapshot*>::const_iterator p = refs.begin();
p != refs.end();
++p)
(*p)->check_goto_to(gogo->current_block());
label->clear_refs();
return label;
}
// Add a reference to a label.
Label*
Function::add_label_reference(const std::string& label_name)
Function::add_label_reference(Gogo* gogo, const std::string& label_name,
source_location location, bool issue_goto_errors)
{
Label* lnull = NULL;
std::pair<Labels::iterator, bool> ins =
this->labels_.insert(std::make_pair(label_name, lnull));
Label* label;
if (!ins.second)
{
// The label was already in the hash table.
Label* label = ins.first->second;
label->set_is_used();
return label;
label = ins.first->second;
}
else
{
go_assert(ins.first->second == NULL);
Label* label = new Label(label_name);
label = new Label(label_name);
ins.first->second = label;
label->set_is_used();
return label;
}
label->set_is_used();
if (issue_goto_errors)
{
Bindings_snapshot* snapshot = label->snapshot();
if (snapshot != NULL)
snapshot->check_goto_from(gogo->current_block(), location);
else
label->add_snapshot_ref(gogo->bindings_snapshot(location));
}
return label;
}
// Warn about labels that are defined but not used.
@ -3407,6 +3435,92 @@ Block::get_backend(Translate_context* context)
return ret;
}
// Class Bindings_snapshot.
Bindings_snapshot::Bindings_snapshot(const Block* b, source_location location)
: block_(b), counts_(), location_(location)
{
while (b != NULL)
{
this->counts_.push_back(b->bindings()->size_definitions());
b = b->enclosing();
}
}
// Report errors appropriate for a goto from B to this.
void
Bindings_snapshot::check_goto_from(const Block* b, source_location loc)
{
size_t dummy;
if (!this->check_goto_block(loc, b, this->block_, &dummy))
return;
this->check_goto_defs(loc, this->block_,
this->block_->bindings()->size_definitions(),
this->counts_[0]);
}
// Report errors appropriate for a goto from this to B.
void
Bindings_snapshot::check_goto_to(const Block* b)
{
size_t index;
if (!this->check_goto_block(this->location_, this->block_, b, &index))
return;
this->check_goto_defs(this->location_, b, this->counts_[index],
b->bindings()->size_definitions());
}
// Report errors appropriate for a goto at LOC from BFROM to BTO.
// Return true if all is well, false if we reported an error. If this
// returns true, it sets *PINDEX to the number of blocks BTO is above
// BFROM.
bool
Bindings_snapshot::check_goto_block(source_location loc, const Block* bfrom,
const Block* bto, size_t* pindex)
{
// It is an error if BTO is not either BFROM or above BFROM.
size_t index = 0;
for (const Block* pb = bfrom; pb != bto; pb = pb->enclosing(), ++index)
{
if (pb == NULL)
{
error_at(loc, "goto jumps into block");
inform(bto->start_location(), "goto target block starts here");
return false;
}
}
*pindex = index;
return true;
}
// Report errors appropriate for a goto at LOC ending at BLOCK, where
// CFROM is the number of names defined at the point of the goto and
// CTO is the number of names defined at the point of the label.
void
Bindings_snapshot::check_goto_defs(source_location loc, const Block* block,
size_t cfrom, size_t cto)
{
if (cfrom < cto)
{
Bindings::const_definitions_iterator p =
block->bindings()->begin_definitions();
for (size_t i = 0; i < cfrom; ++i)
{
go_assert(p != block->bindings()->end_definitions());
++p;
}
go_assert(p != block->bindings()->end_definitions());
std::string n = (*p)->message_name();
error_at(loc, "goto jumps over declaration of %qs", n.c_str());
inform((*p)->location(), "%qs defined here", n.c_str());
}
}
// Class Variable.
Variable::Variable(Type* type, Expression* init, bool is_global,
@ -4698,6 +4812,18 @@ Bindings::traverse(Traverse* traverse, bool is_global)
// Class Label.
// Clear any references to this label.
void
Label::clear_refs()
{
for (std::vector<Bindings_snapshot*>::iterator p = this->refs_.begin();
p != this->refs_.end();
++p)
delete *p;
this->refs_.clear();
}
// Get the backend representation for a label.
Blabel*

View File

@ -22,6 +22,7 @@ class Temporary_statement;
class Block;
class Function;
class Bindings;
class Bindings_snapshot;
class Package;
class Variable;
class Pointer_type;
@ -246,6 +247,10 @@ class Gogo
Named_object*
current_function() const;
// Return the current block.
Block*
current_block();
// Start a new block. This is not initially associated with a
// function.
void
@ -269,9 +274,16 @@ class Gogo
Label*
add_label_definition(const std::string&, source_location);
// Add a label reference.
// Add a label reference. ISSUE_GOTO_ERRORS is true if we should
// report errors for a goto from the current location to the label
// location.
Label*
add_label_reference(const std::string&);
add_label_reference(const std::string&, source_location,
bool issue_goto_errors);
// Return a snapshot of the current binding state.
Bindings_snapshot*
bindings_snapshot(source_location);
// Add a statement to the current block.
void
@ -551,10 +563,6 @@ class Gogo
const Bindings*
current_bindings() const;
// Return the current block.
Block*
current_block();
// Get the name of the magic initialization function.
const std::string&
get_init_fn_name();
@ -833,11 +841,14 @@ class Function
// Add a label definition to the function.
Label*
add_label_definition(const std::string& label_name, source_location);
add_label_definition(Gogo*, const std::string& label_name, source_location);
// Add a label reference to a function.
// Add a label reference to a function. ISSUE_GOTO_ERRORS is true
// if we should report errors for a goto from the current location
// to the label location.
Label*
add_label_reference(const std::string& label_name);
add_label_reference(Gogo*, const std::string& label_name,
source_location, bool issue_goto_errors);
// Warn about labels that are defined but not used.
void
@ -980,6 +991,40 @@ class Function
bool has_recover_thunk_;
};
// A snapshot of the current binding state.
class Bindings_snapshot
{
public:
Bindings_snapshot(const Block*, source_location);
// Report any errors appropriate for a goto from the current binding
// state of B to this one.
void
check_goto_from(const Block* b, source_location);
// Report any errors appropriate for a goto from this binding state
// to the current state of B.
void
check_goto_to(const Block* b);
private:
bool
check_goto_block(source_location, const Block*, const Block*, size_t*);
void
check_goto_defs(source_location, const Block*, size_t, size_t);
// The current block.
const Block* block_;
// The number of names currently defined in each open block.
// Element 0 is this->block_, element 1 is
// this->block_->enclosing(), etc.
std::vector<size_t> counts_;
// The location where this snapshot was taken.
source_location location_;
};
// A function declaration.
class Function_declaration
@ -2108,7 +2153,8 @@ class Label
{
public:
Label(const std::string& name)
: name_(name), location_(0), is_used_(false), blabel_(NULL)
: name_(name), location_(0), snapshot_(NULL), refs_(), is_used_(false),
blabel_(NULL)
{ }
// Return the label's name.
@ -2136,12 +2182,36 @@ class Label
location() const
{ return this->location_; }
// Define the label at LOCATION.
// Return the bindings snapshot.
Bindings_snapshot*
snapshot() const
{ return this->snapshot_; }
// Add a snapshot of a goto which refers to this label.
void
define(source_location location)
add_snapshot_ref(Bindings_snapshot* snapshot)
{
go_assert(this->location_ == 0);
this->refs_.push_back(snapshot);
}
// Return the list of snapshots of goto statements which refer to
// this label.
const std::vector<Bindings_snapshot*>&
refs() const
{ return this->refs_; }
// Clear the references.
void
clear_refs();
// Define the label at LOCATION with the given bindings snapshot.
void
define(source_location location, Bindings_snapshot* snapshot)
{
go_assert(this->location_ == 0 && this->snapshot_ == NULL);
this->location_ = location;
this->snapshot_ = snapshot;
}
// Return the backend representation for this label.
@ -2160,6 +2230,11 @@ class Label
// The location of the definition. This is 0 if the label has not
// yet been defined.
source_location location_;
// A snapshot of the set of bindings defined at this label, used to
// issue errors about invalid goto statements.
Bindings_snapshot* snapshot_;
// A list of snapshots of goto statements which refer to this label.
std::vector<Bindings_snapshot*> refs_;
// Whether the label has been used.
bool is_used_;
// The backend representation.

View File

@ -3813,7 +3813,8 @@ Parse::return_stat()
this->gogo_->add_statement(Statement::make_return_statement(vals, location));
}
// IfStmt = "if" [ SimpleStmt ";" ] Expression Block [ "else" Statement ] .
// IfStmt = "if" [ SimpleStmt ";" ] Expression Block
// [ "else" ( IfStmt | Block ) ] .
void
Parse::if_stat()
@ -3883,10 +3884,17 @@ Parse::if_stat()
Block* else_block = NULL;
if (this->peek_token()->is_keyword(KEYWORD_ELSE))
{
this->advance_token();
// We create a block to gather the statement.
this->gogo_->start_block(this->location());
this->statement(NULL);
const Token* token = this->advance_token();
if (token->is_keyword(KEYWORD_IF))
this->if_stat();
else if (token->is_op(OPERATOR_LCURLY))
this->block();
else
{
error_at(this->location(), "expected %<if%> or %<{%>");
this->statement(NULL);
}
else_block = this->gogo_->finish_block(this->location());
}
@ -4914,7 +4922,7 @@ Parse::break_stat()
{
// If there is a label with this name, mark it as used to
// avoid a useless error about an unused label.
this->gogo_->add_label_reference(token->identifier());
this->gogo_->add_label_reference(token->identifier(), 0, false);
error_at(token->location(), "invalid break label %qs",
Gogo::message_name(token->identifier()).c_str());
@ -4969,7 +4977,7 @@ Parse::continue_stat()
{
// If there is a label with this name, mark it as used to
// avoid a useless error about an unused label.
this->gogo_->add_label_reference(token->identifier());
this->gogo_->add_label_reference(token->identifier(), 0, false);
error_at(token->location(), "invalid continue label %qs",
Gogo::message_name(token->identifier()).c_str());
@ -5003,7 +5011,8 @@ Parse::goto_stat()
error_at(this->location(), "expected label for goto");
else
{
Label* label = this->gogo_->add_label_reference(token->identifier());
Label* label = this->gogo_->add_label_reference(token->identifier(),
location, true);
Statement* s = Statement::make_goto_statement(label, location);
this->gogo_->add_statement(s);
this->advance_token();

View File

@ -2291,7 +2291,7 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name)
Label* retaddr_label = NULL;
if (may_call_recover)
{
retaddr_label = gogo->add_label_reference("retaddr");
retaddr_label = gogo->add_label_reference("retaddr", location, false);
Expression* arg = Expression::make_label_addr(retaddr_label, location);
Expression* call = Runtime::make_call(Runtime::SET_DEFER_RETADDR,
location, 1, arg);

View File

@ -10,14 +10,14 @@ func main() {
if true {
} else {
L1:
goto L1
}
if true {
} else {
goto L2
L2:
main()
}
goto L1
goto L2
}
/*

View File

@ -14,6 +14,9 @@ L:
break L
}
panic("BUG: not reached - break")
if false {
goto L1
}
}
L2:
@ -23,11 +26,8 @@ L2:
continue L2
}
panic("BUG: not reached - continue")
}
if false {
goto L1
}
if false {
goto L3
if false {
goto L3
}
}
}

View File

@ -53,25 +53,28 @@ func main() {
count = 0
if true {
count = count + 1
} else
} else {
count = count - 1
}
assertequal(count, 1, "if else true")
count = 0
if false {
count = count + 1
} else
} else {
count = count - 1
}
assertequal(count, -1, "if else false")
count = 0
if t:=1; false {
if t := 1; false {
count = count + 1
_ = t
t := 7
_ = t
} else
} else {
count = count - t
}
assertequal(count, -1, "if else false var")
count = 0
@ -80,8 +83,9 @@ func main() {
count = count + 1
t := 7
_ = t
} else
} else {
count = count - t
}
_ = t
assertequal(count, -1, "if else false var outside")
}

View File

@ -231,6 +231,7 @@ var zeroSysProcAttr SysProcAttr
func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err int) {
var p [2]int
var n Ssize_t
var r1 int
var err1 uintptr
var wstatus WaitStatus
@ -283,20 +284,14 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err int) {
// Kick off child.
pid, err = forkAndExecInChild(argv0p, argvp, envvp, chroot, dir, attr, sys, p[1])
if err != 0 {
error:
if p[0] >= 0 {
Close(p[0])
Close(p[1])
}
ForkLock.Unlock()
return 0, err
goto error
}
ForkLock.Unlock()
// Read child error status from pipe.
Close(p[1])
n := libc_read(p[0], (*byte)(unsafe.Pointer(&err1)),
Size_t(unsafe.Sizeof(err1)))
n = libc_read(p[0], (*byte)(unsafe.Pointer(&err1)),
Size_t(unsafe.Sizeof(err1)))
err = 0
if n < 0 {
err = GetErrno()
@ -321,6 +316,14 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err int) {
// Read got EOF, so pipe closed on exec, so exec succeeded.
return pid, 0
error:
if p[0] >= 0 {
Close(p[0])
Close(p[1])
}
ForkLock.Unlock()
return 0, err
}
// Combination of fork and exec, careful to be thread safe.