diff --git a/src/cmd/5g/ggen.c b/src/cmd/5g/ggen.c index 182d7f147e..7197709d4d 100644 --- a/src/cmd/5g/ggen.c +++ b/src/cmd/5g/ggen.c @@ -32,7 +32,7 @@ compile(Node *fn) return; // set up domain for labels - labellist = L; + clearlabels(); lno = setlineno(fn); diff --git a/src/cmd/6g/ggen.c b/src/cmd/6g/ggen.c index d9fa1793c6..8d89fb164e 100644 --- a/src/cmd/6g/ggen.c +++ b/src/cmd/6g/ggen.c @@ -32,7 +32,7 @@ compile(Node *fn) return; // set up domain for labels - labellist = L; + clearlabels(); lno = setlineno(fn); diff --git a/src/cmd/8g/ggen.c b/src/cmd/8g/ggen.c index 4dcbd44895..8db5524934 100644 --- a/src/cmd/8g/ggen.c +++ b/src/cmd/8g/ggen.c @@ -32,7 +32,7 @@ compile(Node *fn) return; // set up domain for labels - labellist = L; + clearlabels(); lno = setlineno(fn); diff --git a/src/cmd/gc/dcl.c b/src/cmd/gc/dcl.c index cbcdcbf8c7..3089a23b0d 100644 --- a/src/cmd/gc/dcl.c +++ b/src/cmd/gc/dcl.c @@ -22,7 +22,6 @@ dflag(void) /* * declaration stack & operations */ -static Sym* dclstack; static void dcopy(Sym *a, Sym *b) diff --git a/src/cmd/gc/gen.c b/src/cmd/gc/gen.c index 04af5a7bb1..8ad6c437de 100644 --- a/src/cmd/gc/gen.c +++ b/src/cmd/gc/gen.c @@ -64,62 +64,83 @@ allocparams(void) lineno = lno; } +void +clearlabels(void) +{ + Label *l; + + for(l=labellist; l!=L; l=l->link) + l->sym->label = L; + + labellist = L; + lastlabel = L; +} + static void -newlab(int op, Sym *s, Node *stmt) +newlab(int op, Node *nlab, Node *stmt) { Label *lab; + Sym *s; + int32 lno; + + s = nlab->left->sym; + lno = nlab->left->lineno; lab = mal(sizeof(*lab)); - lab->link = labellist; - labellist = lab; + if(lastlabel == nil) + labellist = lab; + else + lastlabel->link = lab; + lastlabel = lab; + lab->lineno = lno; lab->sym = s; lab->op = op; lab->label = pc; lab->stmt = stmt; + if(op == OLABEL) { + if(s->label != L) { + lineno = lno; + yyerror("label %S already defined at %L", s, s->label->lineno); + } else + s->label = lab; + } } void checklabels(void) { - Label *l, *m; + Label *l; Sym *s; + int lno; -// // print the label list -// for(l=labellist; l!=L; l=l->link) { -// print("lab %O %S\n", l->op, l->sym); -// } - + lno = lineno; + + // resolve goto using syms for(l=labellist; l!=L; l=l->link) { - switch(l->op) { - case OLABEL: - // these are definitions - + switch(l->op) { + case OGOTO: s = l->sym; - for(m=labellist; m!=L; m=m->link) { - if(m->sym != s) - continue; - switch(m->op) { - case OLABEL: - // these are definitions - - // look for redefinitions - if(l != m) - yyerror("label %S redefined", s); - break; - case OGOTO: - // these are references - - // patch to definition - patch(m->label, l->label); - m->sym = S; // mark done - break; - } + if(s->label == L) { + lineno = l->lineno; + yyerror("label %S not defined", s); + break; } + s->label->used = 1; + patch(l->label, s->label->label); + break; } } - - // diagnostic for all undefined references - for(l=labellist; l!=L; l=l->link) - if(l->op == OGOTO && l->sym != S) - yyerror("label %S not defined", l->sym); + + // diagnose unused labels + for(l=labellist; l!=L; l=l->link) { + if(l->op == OLABEL && !l->used) { + lineno = l->lineno; + yyerror("label %S defined and not used", l->sym); + } + } + + lineno = lno; } /* @@ -171,7 +192,7 @@ gen(Node *n) // insert no-op so that // L:; for { } // does not treat L as a label for the loop. - if(labellist && labellist->label == p3) + if(lastlabel != L && lastlabel->label == p3) gused(N); break; @@ -180,26 +201,27 @@ gen(Node *n) break; case OLABEL: - newlab(OLABEL, n->left->sym, n->right); + newlab(OLABEL, n, n->right); break; case OGOTO: - newlab(OGOTO, n->left->sym, N); + newlab(OGOTO, n, N); gjmp(P); break; case OBREAK: if(n->left != N) { - for(lab=labellist; lab!=L; lab=lab->link) { - if(lab->sym == n->left->sym) { - if(lab->breakpc == P) - yyerror("invalid break label %S", n->left->sym); - gjmp(lab->breakpc); - goto donebreak; - } - } - if(lab == L) + lab = n->left->sym->label; + if(lab == L) { yyerror("break label not defined: %S", n->left->sym); + break; + } + lab->used = 1; + if(lab->breakpc == P) { + yyerror("invalid break label %S", n->left->sym); + break; + } + gjmp(lab->breakpc); break; } if(breakpc == P) { @@ -207,30 +229,28 @@ gen(Node *n) break; } gjmp(breakpc); - donebreak: break; case OCONTINUE: if(n->left != N) { - for(lab=labellist; lab!=L; lab=lab->link) { - if(lab->sym == n->left->sym) { - if(lab->continpc == P) - yyerror("invalid continue label %S", n->left->sym); - gjmp(lab->continpc); - goto donecont; - } - } - if(lab == L) + lab = n->left->sym->label; + if(lab == L) { yyerror("continue label not defined: %S", n->left->sym); + break; + } + lab->used = 1; + if(lab->continpc == P) { + yyerror("invalid continue label %S", n->left->sym); + break; + } + gjmp(lab->continpc); break; } - if(continpc == P) { yyerror("continue is not in a loop"); break; } gjmp(continpc); - donecont: break; case OFOR: @@ -241,10 +261,11 @@ gen(Node *n) continpc = pc; // define break and continue labels - if((lab = labellist) != L && lab->label == p3 && lab->op == OLABEL && lab->stmt == n) { + if((lab = lastlabel) != L && lab->label == p3 && lab->op == OLABEL && lab->stmt == n) { lab->breakpc = breakpc; lab->continpc = continpc; - } + } else + lab = L; gen(n->nincr); // contin: incr patch(p1, pc); // test: @@ -254,6 +275,10 @@ gen(Node *n) patch(breakpc, pc); // done: continpc = scontin; breakpc = sbreak; + if(lab) { + lab->breakpc = P; + lab->continpc = P; + } break; case OIF: @@ -274,13 +299,17 @@ gen(Node *n) breakpc = gjmp(P); // break: goto done // define break label - if((lab = labellist) != L && lab->label == p3 && lab->op == OLABEL && lab->stmt == n) + if((lab = lastlabel) != L && lab->label == p3 && lab->op == OLABEL && lab->stmt == n) lab->breakpc = breakpc; + else + lab = L; patch(p1, pc); // test: genlist(n->nbody); // switch(test) body patch(breakpc, pc); // done: breakpc = sbreak; + if(lab != L) + lab->breakpc = P; break; case OSELECT: @@ -289,13 +318,17 @@ gen(Node *n) breakpc = gjmp(P); // break: goto done // define break label - if((lab = labellist) != L && lab->label == p3 && lab->op == OLABEL && lab->stmt == n) + if((lab = lastlabel) != L && lab->label == p3 && lab->op == OLABEL && lab->stmt == n) lab->breakpc = breakpc; + else + lab = L; patch(p1, pc); // test: genlist(n->nbody); // select() body patch(breakpc, pc); // done: breakpc = sbreak; + if(lab != L) + lab->breakpc = P; break; case OASOP: diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h index b071eb2273..39c316f2e5 100644 --- a/src/cmd/gc/go.h +++ b/src/cmd/gc/go.h @@ -138,6 +138,7 @@ typedef struct Sym Sym; typedef struct Node Node; typedef struct NodeList NodeList; typedef struct Type Type; +typedef struct Label Label; struct Type { @@ -302,11 +303,14 @@ struct Sym Pkg* pkg; char* name; // variable name Node* def; // definition: ONAME OTYPE OPACK or OLITERAL + Label* label; // corresponding label (ephemeral) int32 block; // blocknumber to catch redeclaration int32 lastlineno; // last declaration for diagnostic }; #define S ((Sym*)0) +EXTERN Sym* dclstack; + struct Pkg { char* name; @@ -619,20 +623,22 @@ struct Magic typedef struct Prog Prog; -typedef struct Label Label; struct Label { uchar op; // OGOTO/OLABEL + uchar used; Sym* sym; Node* stmt; Prog* label; // pointer to code Prog* breakpc; // pointer to code Prog* continpc; // pointer to code Label* link; + int32 lineno; }; #define L ((Label*)0) EXTERN Label* labellist; +EXTERN Label* lastlabel; /* * note this is the runtime representation @@ -900,6 +906,7 @@ void allocparams(void); void cgen_as(Node *nl, Node *nr); void cgen_callmeth(Node *n, int proc); void checklabels(void); +void clearlabels(void); int dotoffset(Node *n, int *oary, Node **nn); void gen(Node *n); void genlist(NodeList *l); diff --git a/test/label.go b/test/label.go new file mode 100644 index 0000000000..ab231232a8 --- /dev/null +++ b/test/label.go @@ -0,0 +1,60 @@ +// errchk $G -e $D/$F.go + +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Pass 1 label errors. + +package main + +var x int + +func f() { +L1: // ERROR "label L1 defined and not used" + for { + } +L2: // ERROR "label L2 defined and not used" + select { + } +L3: // ERROR "label L3 defined and not used" + switch { + } +L4: // ERROR "label L4 defined and not used" + if true { + } +L5: // ERROR "label L5 defined and not used" + f() +L6: + f() +L6: // ERROR "label L6 already defined at" + f() + if x == 20 { + goto L6 + } + +L7: + for { + break L7 + } + +L8: + for { + if x == 21 { + continue L8 + } + } + +L9: + switch { + case true: + break L9 + defalt: // ERROR "label defalt defined and not used" + } + +L10: + select { + default: + break L10 + } +} diff --git a/test/label1.go b/test/label1.go new file mode 100644 index 0000000000..bba63f23d2 --- /dev/null +++ b/test/label1.go @@ -0,0 +1,85 @@ +// errchk $G -e $D/$F.go + +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Pass 2 label errors. + +package main + +var x int + +func f() { +L1: + for { + if x == 0 { + break L1 + } + if x == 1 { + continue L1 + } + goto L1 + } + +L2: + select { + default: + if x == 0 { + break L2 + } + if x == 1 { + continue L2 // ERROR "invalid continue label L2" + } + goto L2 + } + +L3: + switch { + case x > 10: + if x == 11 { + break L3 + } + if x == 12 { + continue L3 // ERROR "invalid continue label L3" + } + goto L3 + } + +L4: + if true { + if x == 13 { + break L4 // ERROR "invalid break label L4" + } + if x == 14 { + continue L4 // ERROR "invalid continue label L4" + } + if x == 15 { + goto L4 + } + } + +L5: + f() + if x == 16 { + break L5 // ERROR "invalid break label L5" + } + if x == 17 { + continue L5 // ERROR "invalid continue label L5" + } + if x == 18 { + goto L5 + } + + for { + if x == 19 { + break L1 // ERROR "invalid break label L1" + } + if x == 20 { + continue L1 // ERROR "invalid continue label L1" + } + if x == 21 { + goto L1 + } + } +}