mirror of
https://github.com/golang/go
synced 2024-11-26 02:07:57 -07:00
cmd/gc: implement new return requirements
Fixes #65. R=ken2 CC=golang-dev https://golang.org/cl/7441049
This commit is contained in:
parent
9905cec0dc
commit
ecab408c42
@ -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);
|
||||
|
||||
/*
|
||||
|
@ -536,7 +536,10 @@ compound_stmt:
|
||||
}
|
||||
stmt_list '}'
|
||||
{
|
||||
$$ = liststmt($3);
|
||||
if($3 == nil)
|
||||
$$ = nod(OEMPTY, N, N);
|
||||
else
|
||||
$$ = liststmt($3);
|
||||
popdcl();
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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
@ -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");
|
||||
|
@ -11,4 +11,5 @@ package main
|
||||
|
||||
func a(b int) int64 {
|
||||
b // ERROR "not used"
|
||||
return 0
|
||||
}
|
||||
|
1453
test/return.go
Normal file
1453
test/return.go
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user