// fix up pc
pcloc = 0;
for(pl=plist; pl!=nil; pl=pl->link) {
+ if(isblank(pl->name))
+ continue;
for(p=pl->firstpc; p!=P; p=p->link) {
p->loc = pcloc;
if(p->as != ADATA && p->as != AGLOBL)
// put out functions
for(pl=plist; pl!=nil; pl=pl->link) {
+ if(isblank(pl->name))
+ continue;
if(debug['S']) {
s = S;
p->to.offset = to->loc;
}
+Prog*
+unpatch(Prog *p)
+{
+ Prog *q;
+
+ if(p->to.type != D_BRANCH)
+ fatal("unpatch: not a branch");
+ if(p->to.branch == P)
+ fatal("unpatch: not patched");
+ q = p->to.branch;
+ p->to.branch = P;
+ p->to.offset = 0;
+ return q;
+}
+
/*
* start a new Prog list.
*/
// fix up pc
pcloc = 0;
for(pl=plist; pl!=nil; pl=pl->link) {
+ if(isblank(pl->name))
+ continue;
for(p=pl->firstpc; p!=P; p=p->link) {
p->loc = pcloc;
if(p->as != ADATA && p->as != AGLOBL)
// put out functions
for(pl=plist; pl!=nil; pl=pl->link) {
+ if(isblank(pl->name))
+ continue;
if(debug['S']) {
s = S;
p->to.offset = to->loc;
}
+Prog*
+unpatch(Prog *p)
+{
+ Prog *q;
+
+ if(p->to.type != D_BRANCH)
+ fatal("unpatch: not a branch");
+ q = p->to.branch;
+ p->to.branch = P;
+ p->to.offset = 0;
+ return q;
+}
+
/*
* start a new Prog list.
*/
// fix up pc
pcloc = 0;
for(pl=plist; pl!=nil; pl=pl->link) {
+ if(isblank(pl->name))
+ continue;
for(p=pl->firstpc; p!=P; p=p->link) {
p->loc = pcloc;
if(p->as != ADATA && p->as != AGLOBL)
// put out functions
for(pl=plist; pl!=nil; pl=pl->link) {
+ if(isblank(pl->name))
+ continue;
if(debug['S']) {
s = S;
p->to.offset = to->loc;
}
+Prog*
+unpatch(Prog *p)
+{
+ Prog *q;
+
+ if(p->to.type != D_BRANCH)
+ fatal("unpatch: not a branch");
+ if(p->to.branch == P)
+ fatal("unpatch: not patched");
+ q = p->to.branch;
+ p->to.branch = P;
+ p->to.offset = 0;
+ return q;
+}
+
/*
* start a new Prog list.
*/
Sym *d;
d = mal(sizeof(*d));
+ d->lastlineno = lineno;
d->link = dclstack;
dclstack = d;
return d;
popdcl(void)
{
Sym *d, *s;
+ int lno;
// if(dflag())
// print("revert\n");
if(d->name == nil)
break;
s = pkglookup(d->name, d->pkg);
+ lno = s->lastlineno;
dcopy(s, d);
+ d->lastlineno = lno;
if(dflag())
print("\t%L pop %S %p\n", lineno, s, s->def);
}
void
poptodcl(void)
{
- Sym *d, *s;
-
- for(d=dclstack; d!=S; d=d->link) {
- if(d->name == nil)
- break;
- s = pkglookup(d->name, d->pkg);
- dcopy(s, d);
- if(dflag())
- print("\t%L pop %S\n", lineno, s);
- }
- if(d == S)
- fatal("poptodcl: no mark");
- dclstack = d;
+ // pop the old marker and push a new one
+ // (cannot reuse the existing one)
+ // because we use the markers to identify blocks
+ // for the goto restriction checks.
+ popdcl();
+ markdcl();
}
void
stksize = 0;
dclcontext = PAUTO;
funcdepth = n->funcdepth + 1;
- hasgoto = 0;
compile(n);
- if(hasgoto)
- clearstk();
curfn = nil;
funcdepth = 0;
dclcontext = PEXTERN;
static void cgen_dcl(Node *n);
static void cgen_proc(Node *n, int proc);
+static void checkgoto(Node*, Node*);
+
+static Label *labellist;
+static Label *lastlabel;
Node*
sysfunc(char *name)
lastlabel = L;
}
-static void
-newlab(int op, Node *nlab, Node *stmt)
+static Label*
+newlab(Node *n)
{
- Label *lab;
Sym *s;
- int32 lno;
+ Label *lab;
+
+ s = n->left->sym;
+ if((lab = s->label) == L) {
+ lab = mal(sizeof(*lab));
+ if(lastlabel == nil)
+ labellist = lab;
+ else
+ lastlabel->link = lab;
+ lastlabel = lab;
+ lab->sym = s;
+ s->label = lab;
+ }
- s = nlab->left->sym;
- lno = nlab->left->lineno;
-
- lab = mal(sizeof(*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;
- }
+ if(n->op == OLABEL) {
+ if(lab->def != N)
+ yyerror("label %S already defined at %L", s, lab->def->lineno);
+ else
+ lab->def = n;
+ } else
+ lab->use = list(lab->use, n);
+
+ return lab;
}
void
checklabels(void)
{
- Label *l;
- Sym *s;
- int lno;
+ Label *lab;
+ NodeList *l;
- lno = lineno;
-
- // resolve goto using syms
- for(l=labellist; l!=L; l=l->link) {
- switch(l->op) {
- case OGOTO:
- s = l->sym;
- 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;
+ for(lab=labellist; lab!=L; lab=lab->link) {
+ if(lab->def == N) {
+ for(l=lab->use; l; l=l->next)
+ yyerrorl(l->n->lineno, "label %S not defined", lab->sym);
+ continue;
}
+ if(lab->use == nil && !lab->used) {
+ yyerrorl(lab->def->lineno, "label %S defined and not used", lab->sym);
+ continue;
+ }
+ if(lab->gotopc != P)
+ fatal("label %S never resolved", lab->sym);
+ for(l=lab->use; l; l=l->next)
+ checkgoto(l->n, lab->def);
}
-
- // 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);
+}
+
+static void
+checkgoto(Node *from, Node *to)
+{
+ int nf, nt;
+ Sym *block, *dcl, *fs, *ts;
+ int lno;
+
+ if(from->sym == to->sym)
+ return;
+
+ nf = 0;
+ for(fs=from->sym; fs; fs=fs->link)
+ nf++;
+ nt = 0;
+ for(fs=to->sym; fs; fs=fs->link)
+ nt++;
+ fs = from->sym;
+ for(; nf > nt; nf--)
+ fs = fs->link;
+ if(fs != to->sym) {
+ lno = lineno;
+ setlineno(from);
+
+ // decide what to complain about.
+ // prefer to complain about 'into block' over declarations,
+ // so scan backward to find most recent block or else dcl.
+ block = S;
+ dcl = S;
+ ts = to->sym;
+ for(; nt > nf; nt--) {
+ if(ts->pkg == nil)
+ block = ts;
+ else
+ dcl = ts;
+ ts = ts->link;
}
+ while(ts != fs) {
+ if(ts->pkg == nil)
+ block = ts;
+ else
+ dcl = ts;
+ ts = ts->link;
+ fs = fs->link;
+ }
+
+ if(block)
+ yyerror("goto %S jumps into block starting at %L", from->left->sym, block->lastlineno);
+ else
+ yyerror("goto %S jumps over declaration of %S at %L", from->left->sym, dcl, dcl->lastlineno);
+ lineno = lno;
}
-
- lineno = lno;
+}
+
+static Label*
+stmtlabel(Node *n)
+{
+ Label *lab;
+
+ if(n->sym != S)
+ if((lab = n->sym->label) != L)
+ if(lab->def != N)
+ if(lab->def->right == n)
+ return lab;
+ return L;
}
/*
break;
case OEMPTY:
- // insert no-op so that
- // L:; for { }
- // does not treat L as a label for the loop.
- if(lastlabel != L && lastlabel->label == p3)
- gused(N);
break;
case OBLOCK:
break;
case OLABEL:
- newlab(OLABEL, n, n->right);
+ lab = newlab(n);
+
+ // if there are pending gotos, resolve them all to the current pc.
+ for(p1=lab->gotopc; p1; p1=p2) {
+ p2 = unpatch(p1);
+ patch(p1, pc);
+ }
+ lab->gotopc = P;
+ if(lab->labelpc == P)
+ lab->labelpc = pc;
+
+ if(n->right) {
+ switch(n->right->op) {
+ case OFOR:
+ case OSWITCH:
+ case OSELECT:
+ // so stmtlabel can find the label
+ n->right->sym = lab->sym;
+ }
+ }
break;
case OGOTO:
- hasgoto = 1;
- newlab(OGOTO, n, N);
- gjmp(P);
+ // if label is defined, emit jump to it.
+ // otherwise save list of pending gotos in lab->gotopc.
+ // the list is linked through the normal jump target field
+ // to avoid a second list. (the jumps are actually still
+ // valid code, since they're just going to another goto
+ // to the same label. we'll unwind it when we learn the pc
+ // of the label in the OLABEL case above.)
+ lab = newlab(n);
+ if(lab->labelpc != P)
+ gjmp(lab->labelpc);
+ else
+ lab->gotopc = gjmp(lab->gotopc);
break;
case OBREAK:
continpc = pc;
// define break and continue labels
- if((lab = lastlabel) != L && lab->label == p3 && lab->op == OLABEL && lab->stmt == n) {
+ if((lab = stmtlabel(n)) != L) {
lab->breakpc = breakpc;
lab->continpc = continpc;
- } else
- lab = L;
-
+ }
gen(n->nincr); // contin: incr
patch(p1, pc); // test:
bgen(n->ntest, 0, breakpc); // if(!test) goto break
breakpc = gjmp(P); // break: goto done
// define break label
- if((lab = lastlabel) != L && lab->label == p3 && lab->op == OLABEL && lab->stmt == n)
+ if((lab = stmtlabel(n)) != L)
lab->breakpc = breakpc;
- else
- lab = L;
patch(p1, pc); // test:
genlist(n->nbody); // switch(test) body
breakpc = gjmp(P); // break: goto done
// define break label
- if((lab = lastlabel) != L && lab->label == p3 && lab->op == OLABEL && lab->stmt == n)
+ if((lab = stmtlabel(n)) != L)
lab->breakpc = breakpc;
- else
- lab = L;
patch(p1, pc); // test:
genlist(n->nbody); // select() body
int32 iota;
};
#define N ((Node*)0)
+EXTERN int32 walkgen;
struct NodeList
{
struct Label
{
- uchar op; // OGOTO/OLABEL
uchar used;
Sym* sym;
- Node* stmt;
- Prog* label; // pointer to code
+ Node* def;
+ NodeList* use;
+ Label* link;
+
+ // for use during gen
+ Prog* gotopc; // pointer to unresolved gotos
+ Prog* labelpc; // 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
* of the compilers arrays.
EXTERN char* outfile;
EXTERN Biobuf* bout;
EXTERN int nerrors;
+EXTERN int nsavederrors;
EXTERN int nsyntaxerrors;
EXTERN int safemode;
EXTERN char namebuf[NSYMB];
void allocparams(void);
void cgen_as(Node *nl, Node *nr);
void cgen_callmeth(Node *n, int proc);
-void checklabels(void);
void clearlabels(void);
+void checklabels(void);
int dotoffset(Node *n, int *oary, Node **nn);
void gen(Node *n);
void genlist(NodeList *l);
void* remal(void *p, int32 on, int32 n);
Sym* restrictlookup(char *name, Pkg *pkg);
Node* safeexpr(Node *n, NodeList **init);
+void saveerrors(void);
Node* cheapexpr(Node *n, NodeList **init);
int32 setlineno(Node *n);
void setmaxarg(Type *t);
Node* nodarg(Type*, int);
void nopout(Prog*);
void patch(Prog*, Prog*);
+Prog* unpatch(Prog*);
void zfile(Biobuf *b, char *p, int n);
void zhist(Biobuf *b, int line, vlong offset);
void zname(Biobuf *b, Sym *s, int t);
void data(void);
void text(void);
-EXTERN int hasgoto;
-void clearstk(void);
%type <node> switch_stmt uexpr
%type <node> xfndcl typedcl
-%type <list> xdcl fnbody fnres switch_body loop_body dcl_name_list
+%type <list> xdcl fnbody fnres loop_body dcl_name_list
%type <list> new_name_list expr_list keyval_list braced_keyval_list expr_or_type_list xdcl_list
%type <list> oexpr_list caseblock_list stmt_list oarg_type_list_ocomma arg_type_list
%type <list> interfacedcl_list vardcl vardcl_list structdcl structdcl_list
// will be converted to OCASE
// right will point to next case
// done in casebody()
- poptodcl();
+ markdcl();
$$ = nod(OXCASE, N, N);
$$->list = $2;
if(typesw != N && typesw->right != N && (n=typesw->right->left) != N) {
// will be converted to OCASE
// right will point to next case
// done in casebody()
- poptodcl();
+ markdcl();
$$ = nod(OXCASE, N, N);
if($2->next == nil)
n = nod(OAS, $2->n, $4);
// will be converted to OCASE
// right will point to next case
// done in casebody()
- poptodcl();
+ markdcl();
$$ = nod(OXCASE, N, N);
$$->list = list1(colas($2, list1($4)));
}
{
Node *n;
- poptodcl();
+ markdcl();
$$ = nod(OXCASE, N, N);
if(typesw != N && typesw->right != N && (n=typesw->right->left) != N) {
// type switch - declare variable
popdcl();
}
-switch_body:
- LBODY
- {
- markdcl();
- }
- caseblock_list '}'
- {
- $$ = $3;
- popdcl();
- }
-
caseblock:
case
{
yyerror("missing statement after label");
$$ = $1;
$$->nbody = $3;
+ popdcl();
}
caseblock_list:
n = N;
typesw = nod(OXXX, typesw, n);
}
- switch_body
+ LBODY caseblock_list '}'
{
$$ = $3;
$$->op = OSWITCH;
- $$->list = $5;
+ $$->list = $6;
typesw = typesw->left;
popdcl();
}
select_stmt:
LSELECT
{
- markdcl();
typesw = nod(OXXX, typesw, N);
}
- switch_body
+ LBODY caseblock_list '}'
{
$$ = nod(OSELECT, N, N);
- $$->list = $3;
+ $$->list = $4;
typesw = typesw->left;
- popdcl();
}
/*
$$ = $1;
$$->nelse = list1($3);
}
-| labelname ':' stmt
+| labelname ':'
+ {
+ $1 = nod(OLABEL, $1, N);
+ $1->sym = dclstack; // context, for goto restrictions
+ }
+ stmt
{
NodeList *l;
- l = list1(nod(OLABEL, $1, $3));
- if($3)
- l = list(l, $3);
+ $1->right = $4;
+ l = list1($1);
+ if($4)
+ l = list(l, $4);
$$ = liststmt(l);
}
| LFALL
| LGOTO new_name
{
$$ = nod(OGOTO, $2, N);
+ $$->sym = dclstack; // context, for goto restrictions
}
| LRETURN oexpr_list
{
// in the program, don't bother complaining
// about the seg fault too; let the user clean up
// the code and try again.
- if(nerrors > 0)
+ if(nsavederrors + nerrors > 0)
errorexit();
fatal("fault");
}
for(l=xtop; l; l=l->next)
if(l->n->op == ODCLFUNC) {
curfn = l->n;
+ saveerrors();
typechecklist(l->n->nbody, Etop);
+ if(nerrors != 0)
+ l->n->nbody = nil; // type errors; do not compile
}
curfn = nil;
if(l->n->op == ODCLFUNC)
funccompile(l->n, 0);
- if(nerrors == 0)
+ if(nsavederrors+nerrors == 0)
fninit(xtop);
while(closures) {
if(l->n->op == ONAME)
typecheck(&l->n, Erv);
- if(nerrors)
+ if(nerrors+nsavederrors)
errorexit();
dumpobj();
- if(nerrors)
+ if(nerrors+nsavederrors)
errorexit();
flusherrors();
return 0;
}
+void
+saveerrors(void)
+{
+ nsavederrors += nerrors;
+ nerrors = 0;
+}
+
static int
arsize(Biobuf *b, char *name)
{
if(fn->nbody == nil)
return;
+ saveerrors();
+
// set up domain for labels
clearlabels();
hasdefer = 0;
walk(curfn);
- if(nerrors != 0 || isblank(curfn->nname))
+ if(nerrors != 0)
goto ret;
allocparams();
setlineno(curfn);
nodconst(&nod1, types[TINT32], 0);
- ptxt = gins(ATEXT, curfn->nname, &nod1);
+ ptxt = gins(ATEXT, isblank(curfn->nname) ? N : curfn->nname, &nod1);
afunclit(&ptxt->from);
ginit();
default:
fatal("typecheckdef %O", n->op);
+ case OGOTO:
+ case OLABEL:
+ // not really syms
+ break;
+
case OLITERAL:
if(n->ntype != N) {
typecheck(&n->ntype, Etype);
if(n->defn == N) {
if(n->etype != 0) // like OPRINTN
break;
- if(nerrors > 0) {
+ if(nsavederrors+nerrors > 0) {
// Can have undefined variables in x := foo
// that make x have an n->ndefn == nil.
// If there are other errors anyway, don't
const i int = 2
const j float64 = 5
-func main() { println(a, b, c, d, e, f, g) }
L1: // ERROR "statement"
default:
// correct since no semicolon is required before a '}'
- L2: // GCCGO_ERROR "not used"
+ L2: // ERROR "not used"
}
}
-// $G $D/$F.go && $L $F.$A && ./$A.out || echo BUG: bug344
+// 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
i := 42
a := []*int{&i, &i, &i, &i}
x := a[0]
- goto start
+ goto start // ERROR "goto start jumps into block"
+ z := 1
+ _ = z
for _, x = range a {
start:
fmt.Sprint(*x)