mirror of
https://github.com/golang/go
synced 2024-11-26 04:37:59 -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 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);
|
|||||||
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:
|
|||||||
}
|
}
|
||||||
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[])
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
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**);
|
|||||||
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)
|
|||||||
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;
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -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
|
|||||||
|
|
||||||
func a(b int) int64 {
|
func a(b int) int64 {
|
||||||
b // ERROR "not used"
|
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