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
+		}
+	}
+}