gc: diagnose unused labels
R=ken2
CC=golang-dev
https://golang.org/cl/4287047
diff --git a/src/cmd/5g/ggen.c b/src/cmd/5g/ggen.c
index 182d7f1..7197709 100644
--- a/src/cmd/5g/ggen.c
+++ b/src/cmd/5g/ggen.c
@@ -32,7 +32,7 @@
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 d9fa179..8d89fb1 100644
--- a/src/cmd/6g/ggen.c
+++ b/src/cmd/6g/ggen.c
@@ -32,7 +32,7 @@
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 4dcbd44..8db5524 100644
--- a/src/cmd/8g/ggen.c
+++ b/src/cmd/8g/ggen.c
@@ -32,7 +32,7 @@
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 cbcdcbf..3089a23 100644
--- a/src/cmd/gc/dcl.c
+++ b/src/cmd/gc/dcl.c
@@ -22,7 +22,6 @@
/*
* 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 04af5a7..8ad6c43 100644
--- a/src/cmd/gc/gen.c
+++ b/src/cmd/gc/gen.c
@@ -64,62 +64,83 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
patch(breakpc, pc); // done:
continpc = scontin;
breakpc = sbreak;
+ if(lab) {
+ lab->breakpc = P;
+ lab->continpc = P;
+ }
break;
case OIF:
@@ -274,13 +299,17 @@
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 @@
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 b071eb2..39c316f 100644
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -138,6 +138,7 @@
typedef struct Node Node;
typedef struct NodeList NodeList;
typedef struct Type Type;
+typedef struct Label Label;
struct Type
{
@@ -302,11 +303,14 @@
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 @@
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 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 0000000..ab23123
--- /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 0000000..bba63f2
--- /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
+ }
+ }
+}