Commit ecab408c authored by Russ Cox's avatar Russ Cox

cmd/gc: implement new return requirements

Fixes #65.

R=ken2
CC=golang-dev
https://golang.org/cl/7441049
parent 9905cec0
...@@ -268,6 +268,7 @@ struct Node ...@@ -268,6 +268,7 @@ struct Node
uchar addrtaken; // address taken, even if not moved to heap uchar addrtaken; // address taken, even if not moved to heap
uchar dupok; // duplicate definitions ok (for func) uchar dupok; // duplicate definitions ok (for func)
schar likely; // likeliness of if statement schar likely; // likeliness of if statement
uchar hasbreak; // has break statement
// most nodes // most nodes
Type* type; Type* type;
...@@ -1363,6 +1364,7 @@ Node* typecheck(Node **np, int top); ...@@ -1363,6 +1364,7 @@ Node* typecheck(Node **np, int top);
void typechecklist(NodeList *l, int top); void typechecklist(NodeList *l, int top);
Node* typecheckdef(Node *n); Node* typecheckdef(Node *n);
void copytype(Node *n, Type *t); void copytype(Node *n, Type *t);
void checkreturn(Node*);
void queuemethod(Node *n); void queuemethod(Node *n);
/* /*
......
...@@ -536,7 +536,10 @@ compound_stmt: ...@@ -536,7 +536,10 @@ compound_stmt:
} }
stmt_list '}' stmt_list '}'
{ {
$$ = liststmt($3); if($3 == nil)
$$ = nod(OEMPTY, N, N);
else
$$ = liststmt($3);
popdcl(); popdcl();
} }
......
...@@ -376,6 +376,7 @@ main(int argc, char *argv[]) ...@@ -376,6 +376,7 @@ main(int argc, char *argv[])
curfn = l->n; curfn = l->n;
saveerrors(); saveerrors();
typechecklist(l->n->nbody, Etop); typechecklist(l->n->nbody, Etop);
checkreturn(l->n);
if(nerrors != 0) if(nerrors != 0)
l->n->nbody = nil; // type errors; do not compile l->n->nbody = nil; // type errors; do not compile
} }
......
...@@ -3144,3 +3144,148 @@ checkmake(Type *t, char *arg, Node *n) ...@@ -3144,3 +3144,148 @@ checkmake(Type *t, char *arg, Node *n)
} }
return 0; 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");
}
...@@ -29,40 +29,6 @@ static void walkdiv(Node**, NodeList**); ...@@ -29,40 +29,6 @@ static void walkdiv(Node**, NodeList**);
static int bounded(Node*, int64); static int bounded(Node*, int64);
static Mpint mpzero; 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 void
walk(Node *fn) walk(Node *fn)
{ {
...@@ -76,9 +42,6 @@ walk(Node *fn) ...@@ -76,9 +42,6 @@ walk(Node *fn)
snprint(s, sizeof(s), "\nbefore %S", curfn->nname->sym); snprint(s, sizeof(s), "\nbefore %S", curfn->nname->sym);
dumplist(s, curfn->nbody); dumplist(s, curfn->nbody);
} }
if(curfn->type->outtuple)
if(walkret(curfn->nbody))
yyerror("function ends without a return statement");
lno = lineno; lno = lineno;
......
This diff is collapsed.
...@@ -6,12 +6,12 @@ ...@@ -6,12 +6,12 @@
package main package main
func f() int { // ERROR "return|control" func f() int { // GCCGO_ERROR "control"
if false { if false {
return 0; return 0;
} }
// we should not be able to return successfully w/o a return statement // we should not be able to return successfully w/o a return statement
} } // GC_ERROR "return"
func main() { func main() {
print(f(), "\n"); print(f(), "\n");
......
...@@ -11,4 +11,5 @@ package main ...@@ -11,4 +11,5 @@ package main
func a(b int) int64 { func a(b int) int64 {
b // ERROR "not used" b // ERROR "not used"
return 0
} }
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment