cmd/gc: implement new return requirements

Fixes #65.

R=ken2
CC=golang-dev
https://golang.org/cl/7441049
This commit is contained in:
Russ Cox 2013-03-04 17:02:04 -05:00
parent 9905cec0dc
commit ecab408c42
9 changed files with 1862 additions and 291 deletions

View file

@ -268,6 +268,7 @@ struct Node
uchar addrtaken; // address taken, even if not moved to heap
uchar dupok; // duplicate definitions ok (for func)
schar likely; // likeliness of if statement
uchar hasbreak; // has break statement
// most nodes
Type* type;
@ -1363,6 +1364,7 @@ Node* typecheck(Node **np, int top);
void typechecklist(NodeList *l, int top);
Node* typecheckdef(Node *n);
void copytype(Node *n, Type *t);
void checkreturn(Node*);
void queuemethod(Node *n);
/*

View file

@ -536,7 +536,10 @@ compound_stmt:
}
stmt_list '}'
{
$$ = liststmt($3);
if($3 == nil)
$$ = nod(OEMPTY, N, N);
else
$$ = liststmt($3);
popdcl();
}

View file

@ -376,6 +376,7 @@ main(int argc, char *argv[])
curfn = l->n;
saveerrors();
typechecklist(l->n->nbody, Etop);
checkreturn(l->n);
if(nerrors != 0)
l->n->nbody = nil; // type errors; do not compile
}

View file

@ -3144,3 +3144,148 @@ checkmake(Type *t, char *arg, Node *n)
}
return 0;
}
static void markbreaklist(NodeList*, Node*);
static void
markbreak(Node *n, Node *implicit)
{
Label *lab;
if(n == N)
return;
switch(n->op) {
case OBREAK:
if(n->left == N) {
if(implicit)
implicit->hasbreak = 1;
} else {
lab = n->left->sym->label;
if(lab != L)
lab->def->hasbreak = 1;
}
break;
case OFOR:
case OSWITCH:
case OTYPESW:
case OSELECT:
case ORANGE:
implicit = n;
// fall through
default:
markbreak(n->left, implicit);
markbreak(n->right, implicit);
markbreak(n->ntest, implicit);
markbreak(n->nincr, implicit);
markbreaklist(n->ninit, implicit);
markbreaklist(n->nbody, implicit);
markbreaklist(n->nelse, implicit);
markbreaklist(n->list, implicit);
markbreaklist(n->rlist, implicit);
break;
}
}
static void
markbreaklist(NodeList *l, Node *implicit)
{
Node *n;
Label *lab;
for(; l; l=l->next) {
n = l->n;
if(n->op == OLABEL && l->next && n->defn == l->next->n) {
switch(n->defn->op) {
case OFOR:
case OSWITCH:
case OTYPESW:
case OSELECT:
case ORANGE:
lab = mal(sizeof *lab);
lab->def = n->defn;
n->left->sym->label = lab;
markbreak(n->defn, n->defn);
n->left->sym->label = L;
l = l->next;
continue;
}
}
markbreak(n, implicit);
}
}
static int
isterminating(NodeList *l, int top)
{
int def;
Node *n;
if(l == nil)
return 0;
if(top) {
while(l->next && l->n->op != OLABEL)
l = l->next;
markbreaklist(l, nil);
}
while(l->next)
l = l->next;
n = l->n;
if(n == N)
return 0;
switch(n->op) {
// NOTE: OLABEL is treated as a separate statement,
// not a separate prefix, so skipping to the last statement
// in the block handles the labeled statement case by
// skipping over the label. No case OLABEL here.
case OBLOCK:
return isterminating(n->list, 0);
case OGOTO:
case ORETURN:
case OPANIC:
case OXFALL:
return 1;
case OFOR:
if(n->ntest != N)
return 0;
if(n->hasbreak)
return 0;
return 1;
case OIF:
return isterminating(n->nbody, 0) && isterminating(n->nelse, 0);
case OSWITCH:
case OTYPESW:
case OSELECT:
if(n->hasbreak)
return 0;
def = 0;
for(l=n->list; l; l=l->next) {
if(!isterminating(l->n->nbody, 0))
return 0;
if(l->n->list == nil) // default
def = 1;
}
if(n->op != OSELECT && !def)
return 0;
return 1;
}
return 0;
}
void
checkreturn(Node *fn)
{
if(fn->type->outtuple && fn->nbody != nil)
if(!isterminating(fn->nbody, 1))
yyerrorl(fn->endlineno, "missing return at end of function");
}

View file

@ -29,40 +29,6 @@ static void walkdiv(Node**, NodeList**);
static int bounded(Node*, int64);
static Mpint mpzero;
// can this code branch reach the end
// without an unconditional RETURN
// this is hard, so it is conservative
static int
walkret(NodeList *l)
{
Node *n;
loop:
while(l && l->next)
l = l->next;
if(l == nil)
return 1;
// at this point, we have the last
// statement of the function
n = l->n;
switch(n->op) {
case OBLOCK:
l = n->list;
goto loop;
case OGOTO:
case ORETURN:
case OPANIC:
return 0;
break;
}
// all other statements
// will flow to the end
return 1;
}
void
walk(Node *fn)
{
@ -76,9 +42,6 @@ walk(Node *fn)
snprint(s, sizeof(s), "\nbefore %S", curfn->nname->sym);
dumplist(s, curfn->nbody);
}
if(curfn->type->outtuple)
if(walkret(curfn->nbody))
yyerror("function ends without a return statement");
lno = lineno;

File diff suppressed because it is too large Load diff

View file

@ -6,12 +6,12 @@
package main
func f() int { // ERROR "return|control"
func f() int { // GCCGO_ERROR "control"
if false {
return 0;
}
// we should not be able to return successfully w/o a return statement
}
} // GC_ERROR "return"
func main() {
print(f(), "\n");

View file

@ -11,4 +11,5 @@ package main
func a(b int) int64 {
b // ERROR "not used"
return 0
}

1453
test/return.go Normal file

File diff suppressed because it is too large Load diff