summaryrefslogtreecommitdiff
path: root/src/cmd/gc
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/gc')
-rw-r--r--src/cmd/gc/Makefile71
-rw-r--r--src/cmd/gc/align.c653
-rwxr-xr-xsrc/cmd/gc/bisonerrors124
-rw-r--r--src/cmd/gc/bits.c161
-rw-r--r--src/cmd/gc/builtin.c.boot114
-rw-r--r--src/cmd/gc/closure.c252
-rw-r--r--src/cmd/gc/const.c1299
-rw-r--r--src/cmd/gc/cplx.c479
-rw-r--r--src/cmd/gc/dcl.c1254
-rw-r--r--src/cmd/gc/doc.go55
-rw-r--r--src/cmd/gc/export.c428
-rw-r--r--src/cmd/gc/gen.c790
-rw-r--r--src/cmd/gc/go.errors70
-rw-r--r--src/cmd/gc/go.h1279
-rw-r--r--src/cmd/gc/go.y1985
-rw-r--r--src/cmd/gc/init.c195
-rw-r--r--src/cmd/gc/lex.c1956
-rw-r--r--src/cmd/gc/md5.c290
-rw-r--r--src/cmd/gc/md5.h16
-rwxr-xr-xsrc/cmd/gc/mkbuiltin31
-rw-r--r--src/cmd/gc/mkbuiltin1.c84
-rwxr-xr-xsrc/cmd/gc/mkopnames24
-rw-r--r--src/cmd/gc/mparith1.c509
-rw-r--r--src/cmd/gc/mparith2.c683
-rw-r--r--src/cmd/gc/mparith3.c313
-rw-r--r--src/cmd/gc/obj.c301
-rw-r--r--src/cmd/gc/pgen.c210
-rw-r--r--src/cmd/gc/print.c480
-rw-r--r--src/cmd/gc/range.c256
-rw-r--r--src/cmd/gc/reflect.c939
-rw-r--r--src/cmd/gc/runtime.go130
-rw-r--r--src/cmd/gc/select.c351
-rw-r--r--src/cmd/gc/sinit.c971
-rw-r--r--src/cmd/gc/subr.c3885
-rw-r--r--src/cmd/gc/swt.c896
-rw-r--r--src/cmd/gc/typecheck.c2827
-rw-r--r--src/cmd/gc/unsafe.c97
-rw-r--r--src/cmd/gc/unsafe.go22
-rw-r--r--src/cmd/gc/walk.c2166
39 files changed, 26646 insertions, 0 deletions
diff --git a/src/cmd/gc/Makefile b/src/cmd/gc/Makefile
new file mode 100644
index 000000000..0af7659e4
--- /dev/null
+++ b/src/cmd/gc/Makefile
@@ -0,0 +1,71 @@
+# Copyright 2009 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.
+
+include ../../Make.inc
+O:=$(HOST_O)
+
+LIB=gc.a
+
+HFILES=\
+ go.h\
+ y.tab.h\
+ md5.h\
+
+YFILES=\
+ go.y\
+
+OFILES=\
+ align.$O\
+ bits.$O\
+ builtin.$O\
+ closure.$O\
+ const.$O\
+ dcl.$O\
+ export.$O\
+ gen.$O\
+ init.$O\
+ lex.$O\
+ md5.$O\
+ mparith1.$O\
+ mparith2.$O\
+ mparith3.$O\
+ obj.$O\
+ print.$O\
+ range.$O\
+ reflect.$O\
+ select.$O\
+ sinit.$O\
+ subr.$O\
+ swt.$O\
+ typecheck.$O\
+ unsafe.$O\
+ walk.$O\
+ y1.tab.$O\
+
+NOINSTALL=1
+include ../../Make.clib
+
+install: $(LIB)
+
+y1.tab.c: y.tab.c # make yystate global, yytname mutable
+ cat y.tab.c | sed '/ int yystate;/d; s/int yychar;/int yychar, yystate;/; s/static const char \*const yytname/const char *yytname/; s/char const \*yymsgp/char *yymsgp/' >y1.tab.c
+
+yerr.h: bisonerrors go.errors y.tab.h # y.tab.h rule generates y.output too
+ awk -f bisonerrors y.output go.errors >yerr.h
+
+subr.$O: yerr.h
+
+builtin.c: builtin.c.boot
+ cp builtin.c.boot builtin.c
+
+subr.$O: opnames.h
+
+opnames.h: mkopnames go.h
+ ./mkopnames go.h >opnames.h
+
+CLEANFILES+=*.[568] [568].out y1.tab.c yerr.h mkbuiltin1 builtin.c _builtin.c opnames.h
+
+mkbuiltin1: mkbuiltin1.$O
+ $(HOST_LD) -o $@ mkbuiltin1.$O -L"$(GOROOT)"/lib -lbio -l9 -lm $(HOST_LDFLAGS)
+
diff --git a/src/cmd/gc/align.c b/src/cmd/gc/align.c
new file mode 100644
index 000000000..14c1c4a8d
--- /dev/null
+++ b/src/cmd/gc/align.c
@@ -0,0 +1,653 @@
+// Copyright 2009 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.
+
+#include "go.h"
+
+/*
+ * machine size and rounding
+ * alignment is dictated around
+ * the size of a pointer, set in betypeinit
+ * (see ../6g/galign.c).
+ */
+
+static int defercalc;
+
+uint32
+rnd(uint32 o, uint32 r)
+{
+ if(r < 1 || r > 8 || (r&(r-1)) != 0)
+ fatal("rnd");
+ return (o+r-1)&~(r-1);
+}
+
+static void
+offmod(Type *t)
+{
+ Type *f;
+ int32 o;
+
+ o = 0;
+ for(f=t->type; f!=T; f=f->down) {
+ if(f->etype != TFIELD)
+ fatal("offmod: not TFIELD: %lT", f);
+ f->width = o;
+ o += widthptr;
+ if(o >= MAXWIDTH) {
+ yyerror("interface too large");
+ o = widthptr;
+ }
+ }
+}
+
+static vlong
+widstruct(Type *errtype, Type *t, vlong o, int flag)
+{
+ Type *f;
+ int32 w, maxalign;
+
+ maxalign = flag;
+ if(maxalign < 1)
+ maxalign = 1;
+ for(f=t->type; f!=T; f=f->down) {
+ if(f->etype != TFIELD)
+ fatal("widstruct: not TFIELD: %lT", f);
+ dowidth(f->type);
+ if(f->type->align > maxalign)
+ maxalign = f->type->align;
+ if(f->type->width < 0)
+ fatal("invalid width %lld", f->type->width);
+ w = f->type->width;
+ if(f->type->align > 0)
+ o = rnd(o, f->type->align);
+ f->width = o; // really offset for TFIELD
+ if(f->nname != N) {
+ // this same stackparam logic is in addrescapes
+ // in typecheck.c. usually addrescapes runs after
+ // widstruct, in which case we could drop this,
+ // but function closure functions are the exception.
+ if(f->nname->stackparam) {
+ f->nname->stackparam->xoffset = o;
+ f->nname->xoffset = 0;
+ } else
+ f->nname->xoffset = o;
+ }
+ o += w;
+ if(o >= MAXWIDTH) {
+ yyerror("type %lT too large", errtype);
+ o = 8; // small but nonzero
+ }
+ }
+ // final width is rounded
+ if(flag)
+ o = rnd(o, maxalign);
+ t->align = maxalign;
+
+ // type width only includes back to first field's offset
+ if(t->type == T)
+ t->width = 0;
+ else
+ t->width = o - t->type->width;
+ return o;
+}
+
+void
+dowidth(Type *t)
+{
+ int32 et;
+ int64 w;
+ int lno;
+ Type *t1;
+
+ if(widthptr == 0)
+ fatal("dowidth without betypeinit");
+
+ if(t == T)
+ return;
+
+ if(t->width > 0)
+ return;
+
+ if(t->width == -2) {
+ lno = lineno;
+ lineno = t->lineno;
+ yyerror("invalid recursive type %T", t);
+ t->width = 0;
+ lineno = lno;
+ return;
+ }
+
+ // defer checkwidth calls until after we're done
+ defercalc++;
+
+ lno = lineno;
+ lineno = t->lineno;
+ t->width = -2;
+ t->align = 0;
+
+ et = t->etype;
+ switch(et) {
+ case TFUNC:
+ case TCHAN:
+ case TMAP:
+ case TSTRING:
+ break;
+
+ default:
+ /* simtype == 0 during bootstrap */
+ if(simtype[t->etype] != 0)
+ et = simtype[t->etype];
+ break;
+ }
+
+ w = 0;
+ switch(et) {
+ default:
+ fatal("dowidth: unknown type: %T", t);
+ break;
+
+ /* compiler-specific stuff */
+ case TINT8:
+ case TUINT8:
+ case TBOOL: // bool is int8
+ w = 1;
+ break;
+ case TINT16:
+ case TUINT16:
+ w = 2;
+ break;
+ case TINT32:
+ case TUINT32:
+ case TFLOAT32:
+ w = 4;
+ break;
+ case TINT64:
+ case TUINT64:
+ case TFLOAT64:
+ case TCOMPLEX64:
+ w = 8;
+ t->align = widthptr;
+ break;
+ case TCOMPLEX128:
+ w = 16;
+ t->align = widthptr;
+ break;
+ case TPTR32:
+ w = 4;
+ checkwidth(t->type);
+ break;
+ case TPTR64:
+ w = 8;
+ checkwidth(t->type);
+ break;
+ case TUNSAFEPTR:
+ w = widthptr;
+ break;
+ case TINTER: // implemented as 2 pointers
+ w = 2*widthptr;
+ t->align = widthptr;
+ offmod(t);
+ break;
+ case TCHAN: // implemented as pointer
+ w = widthptr;
+ checkwidth(t->type);
+
+ // make fake type to check later to
+ // trigger channel argument check.
+ t1 = typ(TCHANARGS);
+ t1->type = t;
+ checkwidth(t1);
+ break;
+ case TCHANARGS:
+ t1 = t->type;
+ dowidth(t->type); // just in case
+ if(t1->type->width >= (1<<16))
+ yyerror("channel element type too large (>64kB)");
+ t->width = 1;
+ break;
+ case TMAP: // implemented as pointer
+ w = widthptr;
+ checkwidth(t->type);
+ checkwidth(t->down);
+ break;
+ case TFORW: // should have been filled in
+ yyerror("invalid recursive type %T", t);
+ w = 1; // anything will do
+ break;
+ case TANY:
+ // dummy type; should be replaced before use.
+ if(!debug['A'])
+ fatal("dowidth any");
+ w = 1; // anything will do
+ break;
+ case TSTRING:
+ if(sizeof_String == 0)
+ fatal("early dowidth string");
+ w = sizeof_String;
+ t->align = widthptr;
+ break;
+ case TARRAY:
+ if(t->type == T)
+ break;
+ if(t->bound >= 0) {
+ uint64 cap;
+
+ dowidth(t->type);
+ if(t->type->width != 0) {
+ cap = (MAXWIDTH-1) / t->type->width;
+ if(t->bound > cap)
+ yyerror("type %lT larger than address space", t);
+ }
+ w = t->bound * t->type->width;
+ t->align = t->type->align;
+ }
+ else if(t->bound == -1) {
+ w = sizeof_Array;
+ checkwidth(t->type);
+ t->align = widthptr;
+ }
+ else if(t->bound == -100)
+ yyerror("use of [...] array outside of array literal");
+ else
+ fatal("dowidth %T", t); // probably [...]T
+ break;
+
+ case TSTRUCT:
+ if(t->funarg)
+ fatal("dowidth fn struct %T", t);
+ w = widstruct(t, t, 0, 1);
+ break;
+
+ case TFUNC:
+ // make fake type to check later to
+ // trigger function argument computation.
+ t1 = typ(TFUNCARGS);
+ t1->type = t;
+ checkwidth(t1);
+
+ // width of func type is pointer
+ w = widthptr;
+ break;
+
+ case TFUNCARGS:
+ // function is 3 cated structures;
+ // compute their widths as side-effect.
+ t1 = t->type;
+ w = widstruct(t->type, *getthis(t1), 0, 0);
+ w = widstruct(t->type, *getinarg(t1), w, widthptr);
+ w = widstruct(t->type, *getoutarg(t1), w, widthptr);
+ t1->argwid = w;
+ if(w%widthptr)
+ warn("bad type %T %d\n", t1, w);
+ t->align = 1;
+ break;
+ }
+
+ t->width = w;
+ if(t->align == 0) {
+ if(w > 8 || (w&(w-1)) != 0)
+ fatal("invalid alignment for %T", t);
+ t->align = w;
+ }
+ lineno = lno;
+
+ if(defercalc == 1)
+ resumecheckwidth();
+ else
+ --defercalc;
+}
+
+/*
+ * when a type's width should be known, we call checkwidth
+ * to compute it. during a declaration like
+ *
+ * type T *struct { next T }
+ *
+ * it is necessary to defer the calculation of the struct width
+ * until after T has been initialized to be a pointer to that struct.
+ * similarly, during import processing structs may be used
+ * before their definition. in those situations, calling
+ * defercheckwidth() stops width calculations until
+ * resumecheckwidth() is called, at which point all the
+ * checkwidths that were deferred are executed.
+ * dowidth should only be called when the type's size
+ * is needed immediately. checkwidth makes sure the
+ * size is evaluated eventually.
+ */
+typedef struct TypeList TypeList;
+struct TypeList {
+ Type *t;
+ TypeList *next;
+};
+
+static TypeList *tlfree;
+static TypeList *tlq;
+
+void
+checkwidth(Type *t)
+{
+ TypeList *l;
+
+ if(t == T)
+ return;
+
+ // function arg structs should not be checked
+ // outside of the enclosing function.
+ if(t->funarg)
+ fatal("checkwidth %T", t);
+
+ if(!defercalc) {
+ dowidth(t);
+ return;
+ }
+ if(t->deferwidth)
+ return;
+ t->deferwidth = 1;
+
+ l = tlfree;
+ if(l != nil)
+ tlfree = l->next;
+ else
+ l = mal(sizeof *l);
+
+ l->t = t;
+ l->next = tlq;
+ tlq = l;
+}
+
+void
+defercheckwidth(void)
+{
+ // we get out of sync on syntax errors, so don't be pedantic.
+ if(defercalc && nerrors == 0)
+ fatal("defercheckwidth");
+ defercalc = 1;
+}
+
+void
+resumecheckwidth(void)
+{
+ TypeList *l;
+
+ if(!defercalc)
+ fatal("resumecheckwidth");
+ for(l = tlq; l != nil; l = tlq) {
+ l->t->deferwidth = 0;
+ tlq = l->next;
+ dowidth(l->t);
+ l->next = tlfree;
+ tlfree = l;
+ }
+ defercalc = 0;
+}
+
+void
+typeinit(void)
+{
+ int i, etype, sameas;
+ Type *t;
+ Sym *s, *s1;
+
+ if(widthptr == 0)
+ fatal("typeinit before betypeinit");
+
+ for(i=0; i<NTYPE; i++)
+ simtype[i] = i;
+
+ types[TPTR32] = typ(TPTR32);
+ dowidth(types[TPTR32]);
+
+ types[TPTR64] = typ(TPTR64);
+ dowidth(types[TPTR64]);
+
+ t = typ(TUNSAFEPTR);
+ types[TUNSAFEPTR] = t;
+ t->sym = pkglookup("Pointer", unsafepkg);
+ t->sym->def = typenod(t);
+
+ dowidth(types[TUNSAFEPTR]);
+
+ tptr = TPTR32;
+ if(widthptr == 8)
+ tptr = TPTR64;
+
+ for(i=TINT8; i<=TUINT64; i++)
+ isint[i] = 1;
+ isint[TINT] = 1;
+ isint[TUINT] = 1;
+ isint[TUINTPTR] = 1;
+
+ isfloat[TFLOAT32] = 1;
+ isfloat[TFLOAT64] = 1;
+
+ iscomplex[TCOMPLEX64] = 1;
+ iscomplex[TCOMPLEX128] = 1;
+
+ isptr[TPTR32] = 1;
+ isptr[TPTR64] = 1;
+
+ isforw[TFORW] = 1;
+
+ issigned[TINT] = 1;
+ issigned[TINT8] = 1;
+ issigned[TINT16] = 1;
+ issigned[TINT32] = 1;
+ issigned[TINT64] = 1;
+
+ /*
+ * initialize okfor
+ */
+ for(i=0; i<NTYPE; i++) {
+ if(isint[i] || i == TIDEAL) {
+ okforeq[i] = 1;
+ okforcmp[i] = 1;
+ okforarith[i] = 1;
+ okforadd[i] = 1;
+ okforand[i] = 1;
+ okforconst[i] = 1;
+ issimple[i] = 1;
+ minintval[i] = mal(sizeof(*minintval[i]));
+ maxintval[i] = mal(sizeof(*maxintval[i]));
+ }
+ if(isfloat[i]) {
+ okforeq[i] = 1;
+ okforcmp[i] = 1;
+ okforadd[i] = 1;
+ okforarith[i] = 1;
+ okforconst[i] = 1;
+ issimple[i] = 1;
+ minfltval[i] = mal(sizeof(*minfltval[i]));
+ maxfltval[i] = mal(sizeof(*maxfltval[i]));
+ }
+ if(iscomplex[i]) {
+ okforeq[i] = 1;
+ okforadd[i] = 1;
+ okforarith[i] = 1;
+ okforconst[i] = 1;
+ issimple[i] = 1;
+ }
+ }
+
+ issimple[TBOOL] = 1;
+
+ okforadd[TSTRING] = 1;
+
+ okforbool[TBOOL] = 1;
+
+ okforcap[TARRAY] = 1;
+ okforcap[TCHAN] = 1;
+
+ okforconst[TBOOL] = 1;
+ okforconst[TSTRING] = 1;
+
+ okforlen[TARRAY] = 1;
+ okforlen[TCHAN] = 1;
+ okforlen[TMAP] = 1;
+ okforlen[TSTRING] = 1;
+
+ okforeq[TPTR32] = 1;
+ okforeq[TPTR64] = 1;
+ okforeq[TUNSAFEPTR] = 1;
+ okforeq[TINTER] = 1;
+ okforeq[TMAP] = 1;
+ okforeq[TCHAN] = 1;
+ okforeq[TFUNC] = 1;
+ okforeq[TSTRING] = 1;
+ okforeq[TBOOL] = 1;
+ okforeq[TARRAY] = 1; // refined in typecheck
+
+ okforcmp[TSTRING] = 1;
+
+ for(i=0; i<nelem(okfor); i++)
+ okfor[i] = okfornone;
+
+ // binary
+ okfor[OADD] = okforadd;
+ okfor[OAND] = okforand;
+ okfor[OANDAND] = okforbool;
+ okfor[OANDNOT] = okforand;
+ okfor[ODIV] = okforarith;
+ okfor[OEQ] = okforeq;
+ okfor[OGE] = okforcmp;
+ okfor[OGT] = okforcmp;
+ okfor[OLE] = okforcmp;
+ okfor[OLT] = okforcmp;
+ okfor[OMOD] = okforand;
+ okfor[OMUL] = okforarith;
+ okfor[ONE] = okforeq;
+ okfor[OOR] = okforand;
+ okfor[OOROR] = okforbool;
+ okfor[OSUB] = okforarith;
+ okfor[OXOR] = okforand;
+ okfor[OLSH] = okforand;
+ okfor[ORSH] = okforand;
+
+ // unary
+ okfor[OCOM] = okforand;
+ okfor[OMINUS] = okforarith;
+ okfor[ONOT] = okforbool;
+ okfor[OPLUS] = okforarith;
+
+ // special
+ okfor[OCAP] = okforcap;
+ okfor[OLEN] = okforlen;
+
+ // comparison
+ iscmp[OLT] = 1;
+ iscmp[OGT] = 1;
+ iscmp[OGE] = 1;
+ iscmp[OLE] = 1;
+ iscmp[OEQ] = 1;
+ iscmp[ONE] = 1;
+
+ mpatofix(maxintval[TINT8], "0x7f");
+ mpatofix(minintval[TINT8], "-0x80");
+ mpatofix(maxintval[TINT16], "0x7fff");
+ mpatofix(minintval[TINT16], "-0x8000");
+ mpatofix(maxintval[TINT32], "0x7fffffff");
+ mpatofix(minintval[TINT32], "-0x80000000");
+ mpatofix(maxintval[TINT64], "0x7fffffffffffffff");
+ mpatofix(minintval[TINT64], "-0x8000000000000000");
+
+ mpatofix(maxintval[TUINT8], "0xff");
+ mpatofix(maxintval[TUINT16], "0xffff");
+ mpatofix(maxintval[TUINT32], "0xffffffff");
+ mpatofix(maxintval[TUINT64], "0xffffffffffffffff");
+
+ /* f is valid float if min < f < max. (min and max are not themselves valid.) */
+ mpatoflt(maxfltval[TFLOAT32], "33554431p103"); /* 2^24-1 p (127-23) + 1/2 ulp*/
+ mpatoflt(minfltval[TFLOAT32], "-33554431p103");
+ mpatoflt(maxfltval[TFLOAT64], "18014398509481983p970"); /* 2^53-1 p (1023-52) + 1/2 ulp */
+ mpatoflt(minfltval[TFLOAT64], "-18014398509481983p970");
+
+ maxfltval[TCOMPLEX64] = maxfltval[TFLOAT32];
+ minfltval[TCOMPLEX64] = minfltval[TFLOAT32];
+ maxfltval[TCOMPLEX128] = maxfltval[TFLOAT64];
+ minfltval[TCOMPLEX128] = minfltval[TFLOAT64];
+
+ /* for walk to use in error messages */
+ types[TFUNC] = functype(N, nil, nil);
+
+ /* types used in front end */
+ // types[TNIL] got set early in lexinit
+ types[TIDEAL] = typ(TIDEAL);
+ types[TINTER] = typ(TINTER);
+
+ /* simple aliases */
+ simtype[TMAP] = tptr;
+ simtype[TCHAN] = tptr;
+ simtype[TFUNC] = tptr;
+ simtype[TUNSAFEPTR] = tptr;
+
+ /* pick up the backend typedefs */
+ for(i=0; typedefs[i].name; i++) {
+ s = lookup(typedefs[i].name);
+ s1 = pkglookup(typedefs[i].name, builtinpkg);
+
+ etype = typedefs[i].etype;
+ if(etype < 0 || etype >= nelem(types))
+ fatal("typeinit: %s bad etype", s->name);
+ sameas = typedefs[i].sameas;
+ if(sameas < 0 || sameas >= nelem(types))
+ fatal("typeinit: %s bad sameas", s->name);
+ simtype[etype] = sameas;
+ minfltval[etype] = minfltval[sameas];
+ maxfltval[etype] = maxfltval[sameas];
+ minintval[etype] = minintval[sameas];
+ maxintval[etype] = maxintval[sameas];
+
+ t = types[etype];
+ if(t != T)
+ fatal("typeinit: %s already defined", s->name);
+
+ t = typ(etype);
+ t->sym = s;
+
+ dowidth(t);
+ types[etype] = t;
+ s1->def = typenod(t);
+ }
+
+ Array_array = rnd(0, widthptr);
+ Array_nel = rnd(Array_array+widthptr, types[TUINT32]->width);
+ Array_cap = rnd(Array_nel+types[TUINT32]->width, types[TUINT32]->width);
+ sizeof_Array = rnd(Array_cap+types[TUINT32]->width, widthptr);
+
+ // string is same as slice wo the cap
+ sizeof_String = rnd(Array_nel+types[TUINT32]->width, widthptr);
+
+ dowidth(types[TSTRING]);
+ dowidth(idealstring);
+}
+
+/*
+ * compute total size of f's in/out arguments.
+ */
+int
+argsize(Type *t)
+{
+ Iter save;
+ Type *fp;
+ int w, x;
+
+ w = 0;
+
+ fp = structfirst(&save, getoutarg(t));
+ while(fp != T) {
+ x = fp->width + fp->type->width;
+ if(x > w)
+ w = x;
+ fp = structnext(&save);
+ }
+
+ fp = funcfirst(&save, t);
+ while(fp != T) {
+ x = fp->width + fp->type->width;
+ if(x > w)
+ w = x;
+ fp = funcnext(&save);
+ }
+
+ w = (w+widthptr-1) & ~(widthptr-1);
+ return w;
+}
diff --git a/src/cmd/gc/bisonerrors b/src/cmd/gc/bisonerrors
new file mode 100755
index 000000000..5110f5350
--- /dev/null
+++ b/src/cmd/gc/bisonerrors
@@ -0,0 +1,124 @@
+#!/usr/bin/awk -f
+# Copyright 2010 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.
+
+# This program implements the core idea from
+#
+# Clinton L. Jeffery, Generating LR syntax error messages from examples,
+# ACM TOPLAS 25(5) (September 2003). http://doi.acm.org/10.1145/937563.937566
+#
+# It reads Bison's summary of a grammar followed by a file
+# like go.errors, replacing lines beginning with % by the
+# yystate and yychar that will be active when an error happens
+# while parsing that line.
+#
+# Unlike the system described in the paper, the lines in go.errors
+# give grammar symbol name lists, not actual program fragments.
+# This is a little less programmer-friendly but doesn't require being
+# able to run the text through lex.c.
+
+BEGIN{
+ bison = 1
+ grammar = 0
+ states = 0
+}
+
+# In Grammar section of y.output,
+# record lhs and length of rhs for each rule.
+bison && /^Grammar/ { grammar = 1 }
+bison && /^(Terminals|state 0)/ { grammar = 0 }
+grammar && NF>0 {
+ if($2 != "|") {
+ r = $2
+ sub(/:$/, "", r)
+ }
+ rulelhs[$1] = r
+ rulesize[$1] = NF-2
+ if(rulesize[$1] == 3 && $3 $4 $5 == "/*empty*/") {
+ rulesize[$1] = 0
+ }
+}
+
+# In state dumps, record shift/reduce actions.
+bison && /^state 0/ { grammar = 0; states = 1 }
+
+states && /^state / { state = $2 }
+states { statetext[state] = statetext[state] $0 "\n" }
+
+states && / shift, and go to state/ {
+ n = nshift[state]++
+ shift[state,n] = $7
+ shifttoken[state,n] = $1
+ next
+}
+states && / go to state/ {
+ n = nshift[state]++
+ shift[state,n] = $5
+ shifttoken[state,n] = $1
+ next
+}
+states && / reduce using rule/ {
+ n = nreduce[state]++
+ reduce[state,n] = $5
+ reducetoken[state,n] = $1
+ next
+}
+
+# First // comment marks the beginning of the pattern file.
+/^\/\// { bison = 0; grammar = 0; state = 0 }
+bison { next }
+
+# Treat % as first field on line as introducing a pattern (token sequence).
+# Run it through the LR machine and print the induced "yystate, yychar,"
+# at the point where the error happens.
+$1 == "%" {
+ nstack = 0
+ state = 0
+ f = 2
+ tok = ""
+ for(;;) {
+ if(tok == "" && f <= NF) {
+ tok = $f
+ f++
+ }
+ found = 0
+ for(j=0; j<nshift[state]; j++) {
+ if(shifttoken[state,j] == tok) {
+ # print "SHIFT " tok " " state " -> " shift[state,j]
+ stack[nstack++] = state
+ state = shift[state,j]
+ found = 1
+ tok = ""
+ break
+ }
+ }
+ if(found)
+ continue
+ for(j=0; j<nreduce[state]; j++) {
+ if(reducetoken[state,j] == tok || reducetoken[state,j] == "$default") {
+ stack[nstack++] = state
+ rule = reduce[state,j]
+ nstack -= rulesize[rule]
+ state = stack[--nstack]
+ lhs = rulelhs[rule]
+ if(tok != "")
+ --f
+ tok = rulelhs[rule]
+ # print "REDUCE " nstack " " state " " tok " rule " rule " size " rulesize[rule]
+ found = 1
+ break
+ }
+ }
+ if(found)
+ continue
+
+ # No shift or reduce applied - found the error.
+ printf("\t%s, %s,\n", state, tok);
+ break
+ }
+ next
+}
+
+# Print other lines verbatim.
+{print}
diff --git a/src/cmd/gc/bits.c b/src/cmd/gc/bits.c
new file mode 100644
index 000000000..7188ac411
--- /dev/null
+++ b/src/cmd/gc/bits.c
@@ -0,0 +1,161 @@
+// Inferno utils/cc/bits.c
+// http://code.google.com/p/inferno-os/source/browse/utils/cc/bits.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+// Portions Copyright © 1997-1999 Vita Nuova Limited
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+// Portions Copyright © 2004,2006 Bruce Ellis
+// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#include "go.h"
+
+/*
+Bits
+bor(Bits a, Bits b)
+{
+ Bits c;
+ int i;
+
+ for(i=0; i<BITS; i++)
+ c.b[i] = a.b[i] | b.b[i];
+ return c;
+}
+
+Bits
+band(Bits a, Bits b)
+{
+ Bits c;
+ int i;
+
+ for(i=0; i<BITS; i++)
+ c.b[i] = a.b[i] & b.b[i];
+ return c;
+}
+
+Bits
+bnot(Bits a)
+{
+ Bits c;
+ int i;
+
+ for(i=0; i<BITS; i++)
+ c.b[i] = ~a.b[i];
+ return c;
+}
+*/
+
+int
+bany(Bits *a)
+{
+ int i;
+
+ for(i=0; i<BITS; i++)
+ if(a->b[i])
+ return 1;
+ return 0;
+}
+
+/*
+int
+beq(Bits a, Bits b)
+{
+ int i;
+
+ for(i=0; i<BITS; i++)
+ if(a.b[i] != b.b[i])
+ return 0;
+ return 1;
+}
+*/
+
+int
+bnum(Bits a)
+{
+ int i;
+ int32 b;
+
+ for(i=0; i<BITS; i++)
+ if(b = a.b[i])
+ return 32*i + bitno(b);
+ fatal("bad in bnum");
+ return 0;
+}
+
+Bits
+blsh(uint n)
+{
+ Bits c;
+
+ c = zbits;
+ c.b[n/32] = 1L << (n%32);
+ return c;
+}
+
+/*
+int
+bset(Bits a, uint n)
+{
+ if(a.b[n/32] & (1L << (n%32)))
+ return 1;
+ return 0;
+}
+*/
+
+int
+bitno(int32 b)
+{
+ int i;
+
+ for(i=0; i<32; i++)
+ if(b & (1L<<i))
+ return i;
+ fatal("bad in bitno");
+ return 0;
+}
+
+int
+Qconv(Fmt *fp)
+{
+ Bits bits;
+ int i, first;
+
+ first = 1;
+ bits = va_arg(fp->args, Bits);
+ while(bany(&bits)) {
+ i = bnum(bits);
+ if(first)
+ first = 0;
+ else
+ fmtprint(fp, " ");
+ if(var[i].sym == S)
+ fmtprint(fp, "$%lld", var[i].offset);
+ else {
+ fmtprint(fp, var[i].sym->name);
+ if(var[i].offset != 0)
+ fmtprint(fp, "%+d", var[i].offset);
+ }
+ bits.b[i/32] &= ~(1L << (i%32));
+ }
+ return 0;
+}
diff --git a/src/cmd/gc/builtin.c.boot b/src/cmd/gc/builtin.c.boot
new file mode 100644
index 000000000..190c56008
--- /dev/null
+++ b/src/cmd/gc/builtin.c.boot
@@ -0,0 +1,114 @@
+char *runtimeimport =
+ "package runtime\n"
+ "import runtime \"runtime\"\n"
+ "func \"\".new (? int32) *any\n"
+ "func \"\".panicindex ()\n"
+ "func \"\".panicslice ()\n"
+ "func \"\".throwreturn ()\n"
+ "func \"\".throwinit ()\n"
+ "func \"\".panicwrap (? string, ? string, ? string)\n"
+ "func \"\".panic (? interface { })\n"
+ "func \"\".recover (? *int32) interface { }\n"
+ "func \"\".printbool (? bool)\n"
+ "func \"\".printfloat (? float64)\n"
+ "func \"\".printint (? int64)\n"
+ "func \"\".printuint (? uint64)\n"
+ "func \"\".printcomplex (? complex128)\n"
+ "func \"\".printstring (? string)\n"
+ "func \"\".printpointer (? any)\n"
+ "func \"\".printiface (? any)\n"
+ "func \"\".printeface (? any)\n"
+ "func \"\".printslice (? any)\n"
+ "func \"\".printnl ()\n"
+ "func \"\".printsp ()\n"
+ "func \"\".goprintf ()\n"
+ "func \"\".concatstring ()\n"
+ "func \"\".append ()\n"
+ "func \"\".appendslice (typ *uint8, x any, y []any) any\n"
+ "func \"\".cmpstring (? string, ? string) int\n"
+ "func \"\".slicestring (? string, ? int, ? int) string\n"
+ "func \"\".slicestring1 (? string, ? int) string\n"
+ "func \"\".intstring (? int64) string\n"
+ "func \"\".slicebytetostring (? []uint8) string\n"
+ "func \"\".sliceinttostring (? []int) string\n"
+ "func \"\".stringtoslicebyte (? string) []uint8\n"
+ "func \"\".stringtosliceint (? string) []int\n"
+ "func \"\".stringiter (? string, ? int) int\n"
+ "func \"\".stringiter2 (? string, ? int) (retk int, retv int)\n"
+ "func \"\".slicecopy (to any, fr any, wid uint32) int\n"
+ "func \"\".slicestringcopy (to any, fr any) int\n"
+ "func \"\".convI2E (elem any) any\n"
+ "func \"\".convI2I (typ *uint8, elem any) any\n"
+ "func \"\".convT2E (typ *uint8, elem any) any\n"
+ "func \"\".convT2I (typ *uint8, typ2 *uint8, elem any) any\n"
+ "func \"\".assertE2E (typ *uint8, iface any) any\n"
+ "func \"\".assertE2E2 (typ *uint8, iface any) (ret any, ok bool)\n"
+ "func \"\".assertE2I (typ *uint8, iface any) any\n"
+ "func \"\".assertE2I2 (typ *uint8, iface any) (ret any, ok bool)\n"
+ "func \"\".assertE2T (typ *uint8, iface any) any\n"
+ "func \"\".assertE2T2 (typ *uint8, iface any) (ret any, ok bool)\n"
+ "func \"\".assertI2E (typ *uint8, iface any) any\n"
+ "func \"\".assertI2E2 (typ *uint8, iface any) (ret any, ok bool)\n"
+ "func \"\".assertI2I (typ *uint8, iface any) any\n"
+ "func \"\".assertI2I2 (typ *uint8, iface any) (ret any, ok bool)\n"
+ "func \"\".assertI2T (typ *uint8, iface any) any\n"
+ "func \"\".assertI2T2 (typ *uint8, iface any) (ret any, ok bool)\n"
+ "func \"\".ifaceeq (i1 any, i2 any) bool\n"
+ "func \"\".efaceeq (i1 any, i2 any) bool\n"
+ "func \"\".ifacethash (i1 any) uint32\n"
+ "func \"\".efacethash (i1 any) uint32\n"
+ "func \"\".makemap (mapType *uint8, hint int64) map[any] any\n"
+ "func \"\".mapaccess1 (mapType *uint8, hmap map[any] any, key any) any\n"
+ "func \"\".mapaccess2 (mapType *uint8, hmap map[any] any, key any) (val any, pres bool)\n"
+ "func \"\".mapassign1 (mapType *uint8, hmap map[any] any, key any, val any)\n"
+ "func \"\".mapassign2 (mapType *uint8, hmap map[any] any, key any, val any, pres bool)\n"
+ "func \"\".mapiterinit (mapType *uint8, hmap map[any] any, hiter *any)\n"
+ "func \"\".mapiternext (hiter *any)\n"
+ "func \"\".mapiter1 (hiter *any) any\n"
+ "func \"\".mapiter2 (hiter *any) (key any, val any)\n"
+ "func \"\".makechan (chanType *uint8, hint int64) chan any\n"
+ "func \"\".chanrecv1 (chanType *uint8, hchan <-chan any) any\n"
+ "func \"\".chanrecv2 (chanType *uint8, hchan <-chan any) (elem any, received bool)\n"
+ "func \"\".chansend1 (chanType *uint8, hchan chan<- any, elem any)\n"
+ "func \"\".closechan (hchan any)\n"
+ "func \"\".selectnbsend (chanType *uint8, hchan chan<- any, elem any) bool\n"
+ "func \"\".selectnbrecv (chanType *uint8, elem *any, hchan <-chan any) bool\n"
+ "func \"\".selectnbrecv2 (chanType *uint8, elem *any, received *bool, hchan <-chan any) bool\n"
+ "func \"\".newselect (size int) *uint8\n"
+ "func \"\".selectsend (sel *uint8, hchan chan<- any, elem *any) bool\n"
+ "func \"\".selectrecv (sel *uint8, hchan <-chan any, elem *any) bool\n"
+ "func \"\".selectrecv2 (sel *uint8, hchan <-chan any, elem *any, received *bool) bool\n"
+ "func \"\".selectdefault (sel *uint8) bool\n"
+ "func \"\".selectgo (sel *uint8)\n"
+ "func \"\".block ()\n"
+ "func \"\".makeslice (typ *uint8, nel int64, cap int64) []any\n"
+ "func \"\".growslice (typ *uint8, old []any, n int64) []any\n"
+ "func \"\".sliceslice1 (old []any, lb uint64, width uint64) []any\n"
+ "func \"\".sliceslice (old []any, lb uint64, hb uint64, width uint64) []any\n"
+ "func \"\".slicearray (old *any, nel uint64, lb uint64, hb uint64, width uint64) []any\n"
+ "func \"\".closure ()\n"
+ "func \"\".int64div (? int64, ? int64) int64\n"
+ "func \"\".uint64div (? uint64, ? uint64) uint64\n"
+ "func \"\".int64mod (? int64, ? int64) int64\n"
+ "func \"\".uint64mod (? uint64, ? uint64) uint64\n"
+ "func \"\".float64toint64 (? float64) int64\n"
+ "func \"\".float64touint64 (? float64) uint64\n"
+ "func \"\".int64tofloat64 (? int64) float64\n"
+ "func \"\".uint64tofloat64 (? uint64) float64\n"
+ "func \"\".complex128div (num complex128, den complex128) complex128\n"
+ "\n"
+ "$$\n";
+char *unsafeimport =
+ "package unsafe\n"
+ "import runtime \"runtime\"\n"
+ "type \"\".Pointer uintptr\n"
+ "func \"\".Offsetof (? any) uintptr\n"
+ "func \"\".Sizeof (? any) uintptr\n"
+ "func \"\".Alignof (? any) uintptr\n"
+ "func \"\".Typeof (i interface { }) interface { }\n"
+ "func \"\".Reflect (i interface { }) (typ interface { }, addr \"\".Pointer)\n"
+ "func \"\".Unreflect (typ interface { }, addr \"\".Pointer) interface { }\n"
+ "func \"\".New (typ interface { }) \"\".Pointer\n"
+ "func \"\".NewArray (typ interface { }, n int) \"\".Pointer\n"
+ "\n"
+ "$$\n";
diff --git a/src/cmd/gc/closure.c b/src/cmd/gc/closure.c
new file mode 100644
index 000000000..1261eefb7
--- /dev/null
+++ b/src/cmd/gc/closure.c
@@ -0,0 +1,252 @@
+// Copyright 2009 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.
+
+/*
+ * function literals aka closures
+ */
+
+#include "go.h"
+
+void
+closurehdr(Node *ntype)
+{
+ Node *n, *name, *a;
+ NodeList *l;
+
+ n = nod(OCLOSURE, N, N);
+ n->ntype = ntype;
+ n->funcdepth = funcdepth;
+
+ funchdr(n);
+
+ // steal ntype's argument names and
+ // leave a fresh copy in their place.
+ // references to these variables need to
+ // refer to the variables in the external
+ // function declared below; see walkclosure.
+ n->list = ntype->list;
+ n->rlist = ntype->rlist;
+ ntype->list = nil;
+ ntype->rlist = nil;
+ for(l=n->list; l; l=l->next) {
+ name = l->n->left;
+ if(name)
+ name = newname(name->sym);
+ a = nod(ODCLFIELD, name, l->n->right);
+ a->isddd = l->n->isddd;
+ if(name)
+ name->isddd = a->isddd;
+ ntype->list = list(ntype->list, a);
+ }
+ for(l=n->rlist; l; l=l->next) {
+ name = l->n->left;
+ if(name)
+ name = newname(name->sym);
+ ntype->rlist = list(ntype->rlist, nod(ODCLFIELD, name, l->n->right));
+ }
+}
+
+Node*
+closurebody(NodeList *body)
+{
+ Node *func, *v;
+ NodeList *l;
+
+ if(body == nil)
+ body = list1(nod(OEMPTY, N, N));
+
+ func = curfn;
+ l = func->dcl;
+ func->nbody = body;
+ funcbody(func);
+
+ // closure-specific variables are hanging off the
+ // ordinary ones in the symbol table; see oldname.
+ // unhook them.
+ // make the list of pointers for the closure call.
+ for(l=func->cvars; l; l=l->next) {
+ v = l->n;
+ v->closure->closure = v->outer;
+ v->heapaddr = nod(OADDR, oldname(v->sym), N);
+ }
+
+ return func;
+}
+
+void
+typecheckclosure(Node *func, int top)
+{
+ Node *oldfn;
+ NodeList *l;
+ Node *v;
+
+ oldfn = curfn;
+ typecheck(&func->ntype, Etype);
+ func->type = func->ntype->type;
+ if(curfn == nil) {
+ xtop = list(xtop, func);
+ return;
+ }
+
+ if(func->type != T) {
+ curfn = func;
+ typechecklist(func->nbody, Etop);
+ curfn = oldfn;
+ }
+
+ // type check the & of closed variables outside the closure,
+ // so that the outer frame also grabs them and knows they
+ // escape.
+ func->enter = nil;
+ for(l=func->cvars; l; l=l->next) {
+ v = l->n;
+ if(v->type == T) {
+ // if v->type is nil, it means v looked like it was
+ // going to be used in the closure but wasn't.
+ // this happens because when parsing a, b, c := f()
+ // the a, b, c gets parsed as references to older
+ // a, b, c before the parser figures out this is a
+ // declaration.
+ v->op = 0;
+ continue;
+ }
+ // For a closure that is called in place, but not
+ // inside a go statement, avoid moving variables to the heap.
+ if ((top & (Ecall|Eproc)) == Ecall)
+ v->heapaddr->etype = 1;
+ typecheck(&v->heapaddr, Erv);
+ func->enter = list(func->enter, v->heapaddr);
+ v->heapaddr = N;
+ }
+}
+
+static Node*
+makeclosure(Node *func, NodeList **init, int nowrap)
+{
+ Node *xtype, *v, *addr, *xfunc;
+ NodeList *l;
+ static int closgen;
+ char *p;
+
+ /*
+ * wrap body in external function
+ * with extra closure parameters.
+ */
+ xtype = nod(OTFUNC, N, N);
+
+ // each closure variable has a corresponding
+ // address parameter.
+ for(l=func->cvars; l; l=l->next) {
+ v = l->n;
+ if(v->op == 0)
+ continue;
+ addr = nod(ONAME, N, N);
+ p = smprint("&%s", v->sym->name);
+ addr->sym = lookup(p);
+ free(p);
+ addr->ntype = nod(OIND, typenod(v->type), N);
+ addr->class = PPARAM;
+ addr->addable = 1;
+ addr->ullman = 1;
+
+ v->heapaddr = addr;
+
+ xtype->list = list(xtype->list, nod(ODCLFIELD, addr, addr->ntype));
+ }
+
+ // then a dummy arg where the closure's caller pc sits
+ if (!nowrap)
+ xtype->list = list(xtype->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR])));
+
+ // then the function arguments
+ xtype->list = concat(xtype->list, func->list);
+ xtype->rlist = concat(xtype->rlist, func->rlist);
+
+ // create the function
+ xfunc = nod(ODCLFUNC, N, N);
+ snprint(namebuf, sizeof namebuf, "_func_%.3d", ++closgen);
+ xfunc->nname = newname(lookup(namebuf));
+ xfunc->nname->ntype = xtype;
+ xfunc->nname->defn = xfunc;
+ declare(xfunc->nname, PFUNC);
+ xfunc->nname->funcdepth = func->funcdepth;
+ xfunc->funcdepth = func->funcdepth;
+ xfunc->nbody = func->nbody;
+ xfunc->dcl = func->dcl;
+ if(xfunc->nbody == nil)
+ fatal("empty body - won't generate any code");
+ typecheck(&xfunc, Etop);
+ closures = list(closures, xfunc);
+
+ return xfunc;
+}
+
+Node*
+walkclosure(Node *func, NodeList **init)
+{
+ int narg;
+ Node *xtype, *xfunc, *call, *clos;
+ NodeList *l, *in;
+
+ /*
+ * wrap body in external function
+ * with extra closure parameters.
+ */
+
+ // create the function
+ xfunc = makeclosure(func, init, 0);
+ xtype = xfunc->nname->ntype;
+
+ // prepare call of sys.closure that turns external func into func literal value.
+ clos = syslook("closure", 1);
+ clos->type = T;
+ clos->ntype = nod(OTFUNC, N, N);
+ in = list1(nod(ODCLFIELD, N, typenod(types[TINT]))); // siz
+ in = list(in, nod(ODCLFIELD, N, xtype));
+ narg = 0;
+ for(l=func->cvars; l; l=l->next) {
+ if(l->n->op == 0)
+ continue;
+ narg++;
+ in = list(in, nod(ODCLFIELD, N, l->n->heapaddr->ntype));
+ }
+ clos->ntype->list = in;
+ clos->ntype->rlist = list1(nod(ODCLFIELD, N, typenod(func->type)));
+ typecheck(&clos, Erv);
+
+ call = nod(OCALL, clos, N);
+ if(narg*widthptr > 100)
+ yyerror("closure needs too many variables; runtime will reject it");
+ in = list1(nodintconst(narg*widthptr));
+ in = list(in, xfunc->nname);
+ in = concat(in, func->enter);
+ call->list = in;
+
+ typecheck(&call, Erv);
+ walkexpr(&call, init);
+ return call;
+}
+
+// Special case for closures that get called in place.
+// Optimize runtime.closure(X, __func__xxxx_, .... ) away
+// to __func__xxxx_(Y ....).
+// On entry, expect n->op == OCALL, n->left->op == OCLOSURE.
+void
+walkcallclosure(Node *n, NodeList **init)
+{
+ if (n->op != OCALLFUNC || n->left->op != OCLOSURE) {
+ dump("walkcallclosure", n);
+ fatal("abuse of walkcallclosure");
+ }
+
+ // New arg list for n. First the closure-args
+ // and then the original parameter list.
+ n->list = concat(n->left->enter, n->list);
+ n->left = makeclosure(n->left, init, 1)->nname;
+ dowidth(n->left->type);
+ n->type = getoutargx(n->left->type);
+ // for a single valued function, pull the field type out of the struct
+ if (n->type && n->type->type && !n->type->type->down)
+ n->type = n->type->type->type;
+}
diff --git a/src/cmd/gc/const.c b/src/cmd/gc/const.c
new file mode 100644
index 000000000..36a64cb97
--- /dev/null
+++ b/src/cmd/gc/const.c
@@ -0,0 +1,1299 @@
+// Copyright 2009 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.
+
+#include "go.h"
+#define TUP(x,y) (((x)<<16)|(y))
+
+static Val tocplx(Val);
+static Val toflt(Val);
+static Val tostr(Val);
+static Val copyval(Val);
+static void cmplxmpy(Mpcplx*, Mpcplx*);
+static void cmplxdiv(Mpcplx*, Mpcplx*);
+
+/*
+ * truncate float literal fv to 32-bit or 64-bit precision
+ * according to type; return truncated value.
+ */
+Mpflt*
+truncfltlit(Mpflt *oldv, Type *t)
+{
+ double d;
+ float f;
+ Mpflt *fv;
+
+ if(t == T)
+ return oldv;
+
+ fv = mal(sizeof *fv);
+ *fv = *oldv;
+
+ // convert large precision literal floating
+ // into limited precision (float64 or float32)
+ // botch -- this assumes that compiler fp
+ // has same precision as runtime fp
+ switch(t->etype) {
+ case TFLOAT64:
+ d = mpgetflt(fv);
+ mpmovecflt(fv, d);
+ break;
+
+ case TFLOAT32:
+ d = mpgetflt(fv);
+ f = d;
+ d = f;
+ mpmovecflt(fv, d);
+ break;
+ }
+ return fv;
+}
+
+/*
+ * convert n, if literal, to type t.
+ * implicit conversion.
+ */
+void
+convlit(Node **np, Type *t)
+{
+ convlit1(np, t, 0);
+}
+
+/*
+ * convert n, if literal, to type t.
+ * return a new node if necessary
+ * (if n is a named constant, can't edit n->type directly).
+ */
+void
+convlit1(Node **np, Type *t, int explicit)
+{
+ int ct, et;
+ Node *n, *nn;
+
+ n = *np;
+ if(n == N || t == T || n->type == T || isideal(t) || n->type == t)
+ return;
+ if(!explicit && !isideal(n->type))
+ return;
+
+ if(n->op == OLITERAL) {
+ nn = nod(OXXX, N, N);
+ *nn = *n;
+ n = nn;
+ *np = n;
+ }
+
+ switch(n->op) {
+ default:
+ if(n->type->etype == TIDEAL) {
+ convlit(&n->left, t);
+ convlit(&n->right, t);
+ n->type = t;
+ }
+ return;
+ case OLITERAL:
+ // target is invalid type for a constant? leave alone.
+ if(!okforconst[t->etype] && n->type->etype != TNIL) {
+ defaultlit(&n, T);
+ *np = n;
+ return;
+ }
+ break;
+ case OLSH:
+ case ORSH:
+ convlit1(&n->left, t, explicit && isideal(n->left->type));
+ t = n->left->type;
+ if(t != T && t->etype == TIDEAL && n->val.ctype != CTINT)
+ n->val = toint(n->val);
+ if(t != T && !isint[t->etype]) {
+ yyerror("invalid operation: %#N (shift of type %T)", n, t);
+ t = T;
+ }
+ n->type = t;
+ return;
+ }
+
+ // avoided repeated calculations, errors
+ if(eqtype(n->type, t))
+ return;
+
+ ct = consttype(n);
+ if(ct < 0)
+ goto bad;
+
+ et = t->etype;
+ if(et == TINTER) {
+ if(ct == CTNIL && n->type == types[TNIL]) {
+ n->type = t;
+ return;
+ }
+ defaultlit(np, T);
+ return;
+ }
+
+ switch(ct) {
+ default:
+ goto bad;
+
+ case CTNIL:
+ switch(et) {
+ default:
+ n->type = T;
+ goto bad;
+
+ case TSTRING:
+ // let normal conversion code handle it
+ return;
+
+ case TARRAY:
+ if(!isslice(t))
+ goto bad;
+ break;
+
+ case TPTR32:
+ case TPTR64:
+ case TINTER:
+ case TMAP:
+ case TCHAN:
+ case TFUNC:
+ case TUNSAFEPTR:
+ break;
+ }
+ break;
+
+ case CTSTR:
+ case CTBOOL:
+ if(et != n->type->etype)
+ goto bad;
+ break;
+
+ case CTINT:
+ case CTFLT:
+ case CTCPLX:
+ ct = n->val.ctype;
+ if(isint[et]) {
+ switch(ct) {
+ default:
+ goto bad;
+ case CTCPLX:
+ case CTFLT:
+ n->val = toint(n->val);
+ // flowthrough
+ case CTINT:
+ overflow(n->val, t);
+ break;
+ }
+ } else
+ if(isfloat[et]) {
+ switch(ct) {
+ default:
+ goto bad;
+ case CTCPLX:
+ case CTINT:
+ n->val = toflt(n->val);
+ // flowthrough
+ case CTFLT:
+ overflow(n->val, t);
+ n->val.u.fval = truncfltlit(n->val.u.fval, t);
+ break;
+ }
+ } else
+ if(iscomplex[et]) {
+ switch(ct) {
+ default:
+ goto bad;
+ case CTFLT:
+ case CTINT:
+ n->val = tocplx(n->val);
+ break;
+ case CTCPLX:
+ overflow(n->val, t);
+ break;
+ }
+ } else
+ if(et == TSTRING && ct == CTINT && explicit)
+ n->val = tostr(n->val);
+ else
+ goto bad;
+ break;
+ }
+ n->type = t;
+ return;
+
+bad:
+ if(!n->diag) {
+ yyerror("cannot convert %#N to type %T", n, t);
+ n->diag = 1;
+ }
+ if(isideal(n->type)) {
+ defaultlit(&n, T);
+ *np = n;
+ }
+ return;
+}
+
+static Val
+copyval(Val v)
+{
+ Mpint *i;
+ Mpflt *f;
+ Mpcplx *c;
+
+ switch(v.ctype) {
+ case CTINT:
+ i = mal(sizeof(*i));
+ mpmovefixfix(i, v.u.xval);
+ v.u.xval = i;
+ break;
+ case CTFLT:
+ f = mal(sizeof(*f));
+ mpmovefltflt(f, v.u.fval);
+ v.u.fval = f;
+ break;
+ case CTCPLX:
+ c = mal(sizeof(*c));
+ mpmovefltflt(&c->real, &v.u.cval->real);
+ mpmovefltflt(&c->imag, &v.u.cval->imag);
+ v.u.cval = c;
+ break;
+ }
+ return v;
+}
+
+static Val
+tocplx(Val v)
+{
+ Mpcplx *c;
+
+ switch(v.ctype) {
+ case CTINT:
+ c = mal(sizeof(*c));
+ mpmovefixflt(&c->real, v.u.xval);
+ mpmovecflt(&c->imag, 0.0);
+ v.ctype = CTCPLX;
+ v.u.cval = c;
+ break;
+ case CTFLT:
+ c = mal(sizeof(*c));
+ mpmovefltflt(&c->real, v.u.fval);
+ mpmovecflt(&c->imag, 0.0);
+ v.ctype = CTCPLX;
+ v.u.cval = c;
+ break;
+ }
+ return v;
+}
+
+static Val
+toflt(Val v)
+{
+ Mpflt *f;
+
+ switch(v.ctype) {
+ case CTINT:
+ f = mal(sizeof(*f));
+ mpmovefixflt(f, v.u.xval);
+ v.ctype = CTFLT;
+ v.u.fval = f;
+ break;
+ case CTCPLX:
+ f = mal(sizeof(*f));
+ mpmovefltflt(f, &v.u.cval->real);
+ if(mpcmpfltc(&v.u.cval->imag, 0) != 0)
+ yyerror("constant %#F%+#Fi truncated to real", &v.u.cval->real, &v.u.cval->imag);
+ v.ctype = CTFLT;
+ v.u.fval = f;
+ break;
+ }
+ return v;
+}
+
+Val
+toint(Val v)
+{
+ Mpint *i;
+
+ switch(v.ctype) {
+ case CTFLT:
+ i = mal(sizeof(*i));
+ if(mpmovefltfix(i, v.u.fval) < 0)
+ yyerror("constant %#F truncated to integer", v.u.fval);
+ v.ctype = CTINT;
+ v.u.xval = i;
+ break;
+ case CTCPLX:
+ i = mal(sizeof(*i));
+ if(mpmovefltfix(i, &v.u.cval->real) < 0)
+ yyerror("constant %#F%+#Fi truncated to integer", &v.u.cval->real, &v.u.cval->imag);
+ if(mpcmpfltc(&v.u.cval->imag, 0) != 0)
+ yyerror("constant %#F%+#Fi truncated to real", &v.u.cval->real, &v.u.cval->imag);
+ v.ctype = CTINT;
+ v.u.xval = i;
+ break;
+ }
+ return v;
+}
+
+void
+overflow(Val v, Type *t)
+{
+ // v has already been converted
+ // to appropriate form for t.
+ if(t == T || t->etype == TIDEAL)
+ return;
+ switch(v.ctype) {
+ case CTINT:
+ if(!isint[t->etype])
+ fatal("overflow: %T integer constant", t);
+ if(mpcmpfixfix(v.u.xval, minintval[t->etype]) < 0 ||
+ mpcmpfixfix(v.u.xval, maxintval[t->etype]) > 0)
+ yyerror("constant %B overflows %T", v.u.xval, t);
+ break;
+ case CTFLT:
+ if(!isfloat[t->etype])
+ fatal("overflow: %T floating-point constant", t);
+ if(mpcmpfltflt(v.u.fval, minfltval[t->etype]) <= 0 ||
+ mpcmpfltflt(v.u.fval, maxfltval[t->etype]) >= 0)
+ yyerror("constant %#F overflows %T", v.u.fval, t);
+ break;
+ case CTCPLX:
+ if(!iscomplex[t->etype])
+ fatal("overflow: %T complex constant", t);
+ if(mpcmpfltflt(&v.u.cval->real, minfltval[t->etype]) <= 0 ||
+ mpcmpfltflt(&v.u.cval->real, maxfltval[t->etype]) >= 0 ||
+ mpcmpfltflt(&v.u.cval->imag, minfltval[t->etype]) <= 0 ||
+ mpcmpfltflt(&v.u.cval->imag, maxfltval[t->etype]) >= 0)
+ yyerror("constant %#F overflows %T", v.u.fval, t);
+ break;
+ }
+}
+
+static Val
+tostr(Val v)
+{
+ Rune rune;
+ int l;
+ Strlit *s;
+
+ switch(v.ctype) {
+ case CTINT:
+ if(mpcmpfixfix(v.u.xval, minintval[TINT]) < 0 ||
+ mpcmpfixfix(v.u.xval, maxintval[TINT]) > 0)
+ yyerror("overflow in int -> string");
+ rune = mpgetfix(v.u.xval);
+ l = runelen(rune);
+ s = mal(sizeof(*s)+l);
+ s->len = l;
+ runetochar((char*)s->s, &rune);
+ memset(&v, 0, sizeof v);
+ v.ctype = CTSTR;
+ v.u.sval = s;
+ break;
+
+ case CTFLT:
+ yyerror("no float -> string");
+
+ case CTNIL:
+ memset(&v, 0, sizeof v);
+ v.ctype = CTSTR;
+ v.u.sval = mal(sizeof *s);
+ break;
+ }
+ return v;
+}
+
+int
+consttype(Node *n)
+{
+ if(n == N || n->op != OLITERAL)
+ return -1;
+ return n->val.ctype;
+}
+
+int
+isconst(Node *n, int ct)
+{
+ return consttype(n) == ct;
+}
+
+/*
+ * if n is constant, rewrite as OLITERAL node.
+ */
+void
+evconst(Node *n)
+{
+ Node *nl, *nr;
+ int32 len;
+ Strlit *str;
+ int wl, wr, lno, et;
+ Val v, rv;
+ Mpint b;
+
+ // pick off just the opcodes that can be
+ // constant evaluated.
+ switch(n->op) {
+ default:
+ return;
+ case OADD:
+ case OADDSTR:
+ case OAND:
+ case OANDAND:
+ case OANDNOT:
+ case OARRAYBYTESTR:
+ case OCOM:
+ case ODIV:
+ case OEQ:
+ case OGE:
+ case OGT:
+ case OLE:
+ case OLSH:
+ case OLT:
+ case OMINUS:
+ case OMOD:
+ case OMUL:
+ case ONE:
+ case ONOT:
+ case OOR:
+ case OOROR:
+ case OPLUS:
+ case ORSH:
+ case OSUB:
+ case OXOR:
+ break;
+ case OCONV:
+ if(n->type == T)
+ return;
+ if(!okforconst[n->type->etype] && n->type->etype != TNIL)
+ return;
+ break;
+ }
+
+ nl = n->left;
+ if(nl == N || nl->type == T)
+ return;
+ if(consttype(nl) < 0)
+ return;
+ wl = nl->type->etype;
+ if(isint[wl] || isfloat[wl] || iscomplex[wl])
+ wl = TIDEAL;
+
+ nr = n->right;
+ if(nr == N)
+ goto unary;
+ if(nr->type == T)
+ return;
+ if(consttype(nr) < 0)
+ return;
+ wr = nr->type->etype;
+ if(isint[wr] || isfloat[wr] || iscomplex[wr])
+ wr = TIDEAL;
+
+ // check for compatible general types (numeric, string, etc)
+ if(wl != wr)
+ goto illegal;
+
+ // check for compatible types.
+ switch(n->op) {
+ default:
+ // ideal const mixes with anything but otherwise must match.
+ if(nl->type->etype != TIDEAL) {
+ defaultlit(&nr, nl->type);
+ n->right = nr;
+ }
+ if(nr->type->etype != TIDEAL) {
+ defaultlit(&nl, nr->type);
+ n->left = nl;
+ }
+ if(nl->type->etype != nr->type->etype)
+ goto illegal;
+ break;
+
+ case OLSH:
+ case ORSH:
+ // right must be unsigned.
+ // left can be ideal.
+ defaultlit(&nr, types[TUINT]);
+ n->right = nr;
+ if(nr->type && (issigned[nr->type->etype] || !isint[nr->type->etype]))
+ goto illegal;
+ nl->val = toint(nl->val);
+ nr->val = toint(nr->val);
+ break;
+ }
+
+ // copy numeric value to avoid modifying
+ // n->left, in case someone still refers to it (e.g. iota).
+ v = nl->val;
+ if(wl == TIDEAL)
+ v = copyval(v);
+
+ rv = nr->val;
+
+ // convert to common ideal
+ if(v.ctype == CTCPLX || rv.ctype == CTCPLX) {
+ v = tocplx(v);
+ rv = tocplx(rv);
+ }
+ if(v.ctype == CTFLT || rv.ctype == CTFLT) {
+ v = toflt(v);
+ rv = toflt(rv);
+ }
+ if(v.ctype != rv.ctype) {
+ // Use of undefined name as constant?
+ if((v.ctype == 0 || rv.ctype == 0) && nerrors > 0)
+ return;
+ fatal("constant type mismatch %T(%d) %T(%d)", nl->type, v.ctype, nr->type, rv.ctype);
+ }
+
+ // run op
+ switch(TUP(n->op, v.ctype)) {
+ default:
+ illegal:
+ if(!n->diag) {
+ yyerror("illegal constant expression: %T %O %T",
+ nl->type, n->op, nr->type);
+ n->diag = 1;
+ }
+ return;
+
+ case TUP(OADD, CTINT):
+ mpaddfixfix(v.u.xval, rv.u.xval);
+ break;
+ case TUP(OSUB, CTINT):
+ mpsubfixfix(v.u.xval, rv.u.xval);
+ break;
+ case TUP(OMUL, CTINT):
+ mpmulfixfix(v.u.xval, rv.u.xval);
+ break;
+ case TUP(ODIV, CTINT):
+ if(mpcmpfixc(rv.u.xval, 0) == 0) {
+ yyerror("division by zero");
+ mpmovecfix(v.u.xval, 1);
+ break;
+ }
+ mpdivfixfix(v.u.xval, rv.u.xval);
+ break;
+ case TUP(OMOD, CTINT):
+ if(mpcmpfixc(rv.u.xval, 0) == 0) {
+ yyerror("division by zero");
+ mpmovecfix(v.u.xval, 1);
+ break;
+ }
+ mpmodfixfix(v.u.xval, rv.u.xval);
+ break;
+
+ case TUP(OLSH, CTINT):
+ mplshfixfix(v.u.xval, rv.u.xval);
+ break;
+ case TUP(ORSH, CTINT):
+ mprshfixfix(v.u.xval, rv.u.xval);
+ break;
+ case TUP(OOR, CTINT):
+ mporfixfix(v.u.xval, rv.u.xval);
+ break;
+ case TUP(OAND, CTINT):
+ mpandfixfix(v.u.xval, rv.u.xval);
+ break;
+ case TUP(OANDNOT, CTINT):
+ mpandnotfixfix(v.u.xval, rv.u.xval);
+ break;
+ case TUP(OXOR, CTINT):
+ mpxorfixfix(v.u.xval, rv.u.xval);
+ break;
+
+ case TUP(OADD, CTFLT):
+ mpaddfltflt(v.u.fval, rv.u.fval);
+ break;
+ case TUP(OSUB, CTFLT):
+ mpsubfltflt(v.u.fval, rv.u.fval);
+ break;
+ case TUP(OMUL, CTFLT):
+ mpmulfltflt(v.u.fval, rv.u.fval);
+ break;
+ case TUP(ODIV, CTFLT):
+ if(mpcmpfltc(rv.u.fval, 0) == 0) {
+ yyerror("division by zero");
+ mpmovecflt(v.u.fval, 1.0);
+ break;
+ }
+ mpdivfltflt(v.u.fval, rv.u.fval);
+ break;
+
+ case TUP(OADD, CTCPLX):
+ mpaddfltflt(&v.u.cval->real, &rv.u.cval->real);
+ mpaddfltflt(&v.u.cval->imag, &rv.u.cval->imag);
+ break;
+ case TUP(OSUB, CTCPLX):
+ mpsubfltflt(&v.u.cval->real, &rv.u.cval->real);
+ mpsubfltflt(&v.u.cval->imag, &rv.u.cval->imag);
+ break;
+ case TUP(OMUL, CTCPLX):
+ cmplxmpy(v.u.cval, rv.u.cval);
+ break;
+ case TUP(ODIV, CTCPLX):
+ if(mpcmpfltc(&rv.u.cval->real, 0) == 0 &&
+ mpcmpfltc(&rv.u.cval->imag, 0) == 0) {
+ yyerror("complex division by zero");
+ mpmovecflt(&rv.u.cval->real, 1.0);
+ mpmovecflt(&rv.u.cval->imag, 0.0);
+ break;
+ }
+ cmplxdiv(v.u.cval, rv.u.cval);
+ break;
+
+ case TUP(OEQ, CTNIL):
+ goto settrue;
+ case TUP(ONE, CTNIL):
+ goto setfalse;
+
+ case TUP(OEQ, CTINT):
+ if(mpcmpfixfix(v.u.xval, rv.u.xval) == 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(ONE, CTINT):
+ if(mpcmpfixfix(v.u.xval, rv.u.xval) != 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(OLT, CTINT):
+ if(mpcmpfixfix(v.u.xval, rv.u.xval) < 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(OLE, CTINT):
+ if(mpcmpfixfix(v.u.xval, rv.u.xval) <= 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(OGE, CTINT):
+ if(mpcmpfixfix(v.u.xval, rv.u.xval) >= 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(OGT, CTINT):
+ if(mpcmpfixfix(v.u.xval, rv.u.xval) > 0)
+ goto settrue;
+ goto setfalse;
+
+ case TUP(OEQ, CTFLT):
+ if(mpcmpfltflt(v.u.fval, rv.u.fval) == 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(ONE, CTFLT):
+ if(mpcmpfltflt(v.u.fval, rv.u.fval) != 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(OLT, CTFLT):
+ if(mpcmpfltflt(v.u.fval, rv.u.fval) < 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(OLE, CTFLT):
+ if(mpcmpfltflt(v.u.fval, rv.u.fval) <= 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(OGE, CTFLT):
+ if(mpcmpfltflt(v.u.fval, rv.u.fval) >= 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(OGT, CTFLT):
+ if(mpcmpfltflt(v.u.fval, rv.u.fval) > 0)
+ goto settrue;
+ goto setfalse;
+
+ case TUP(OEQ, CTCPLX):
+ if(mpcmpfltflt(&v.u.cval->real, &rv.u.cval->real) == 0 &&
+ mpcmpfltflt(&v.u.cval->imag, &rv.u.cval->imag) == 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(ONE, CTCPLX):
+ if(mpcmpfltflt(&v.u.cval->real, &rv.u.cval->real) != 0 ||
+ mpcmpfltflt(&v.u.cval->imag, &rv.u.cval->imag) != 0)
+ goto settrue;
+ goto setfalse;
+
+ case TUP(OEQ, CTSTR):
+ if(cmpslit(nl, nr) == 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(ONE, CTSTR):
+ if(cmpslit(nl, nr) != 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(OLT, CTSTR):
+ if(cmpslit(nl, nr) < 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(OLE, CTSTR):
+ if(cmpslit(nl, nr) <= 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(OGE, CTSTR):
+ if(cmpslit(nl, nr) >= 0l)
+ goto settrue;
+ goto setfalse;
+ case TUP(OGT, CTSTR):
+ if(cmpslit(nl, nr) > 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(OADDSTR, CTSTR):
+ len = v.u.sval->len + rv.u.sval->len;
+ str = mal(sizeof(*str) + len);
+ str->len = len;
+ memcpy(str->s, v.u.sval->s, v.u.sval->len);
+ memcpy(str->s+v.u.sval->len, rv.u.sval->s, rv.u.sval->len);
+ str->len = len;
+ v.u.sval = str;
+ break;
+
+ case TUP(OOROR, CTBOOL):
+ if(v.u.bval || rv.u.bval)
+ goto settrue;
+ goto setfalse;
+ case TUP(OANDAND, CTBOOL):
+ if(v.u.bval && rv.u.bval)
+ goto settrue;
+ goto setfalse;
+ case TUP(OEQ, CTBOOL):
+ if(v.u.bval == rv.u.bval)
+ goto settrue;
+ goto setfalse;
+ case TUP(ONE, CTBOOL):
+ if(v.u.bval != rv.u.bval)
+ goto settrue;
+ goto setfalse;
+ }
+ goto ret;
+
+unary:
+ // copy numeric value to avoid modifying
+ // nl, in case someone still refers to it (e.g. iota).
+ v = nl->val;
+ if(wl == TIDEAL)
+ v = copyval(v);
+
+ switch(TUP(n->op, v.ctype)) {
+ default:
+ if(!n->diag) {
+ yyerror("illegal constant expression %O %T", n->op, nl->type);
+ n->diag = 1;
+ }
+ return;
+
+ case TUP(OCONV, CTNIL):
+ case TUP(OARRAYBYTESTR, CTNIL):
+ if(n->type->etype == TSTRING) {
+ v = tostr(v);
+ nl->type = n->type;
+ break;
+ }
+ // fall through
+ case TUP(OCONV, CTINT):
+ case TUP(OCONV, CTFLT):
+ case TUP(OCONV, CTSTR):
+ convlit1(&nl, n->type, 1);
+ break;
+
+ case TUP(OPLUS, CTINT):
+ break;
+ case TUP(OMINUS, CTINT):
+ mpnegfix(v.u.xval);
+ break;
+ case TUP(OCOM, CTINT):
+ et = Txxx;
+ if(nl->type != T)
+ et = nl->type->etype;
+
+ // calculate the mask in b
+ // result will be (a ^ mask)
+ switch(et) {
+ default:
+ // signed guys change sign
+ mpmovecfix(&b, -1);
+ break;
+
+ case TUINT8:
+ case TUINT16:
+ case TUINT32:
+ case TUINT64:
+ case TUINT:
+ case TUINTPTR:
+ // unsigned guys invert their bits
+ mpmovefixfix(&b, maxintval[et]);
+ break;
+ }
+ mpxorfixfix(v.u.xval, &b);
+ break;
+
+ case TUP(OPLUS, CTFLT):
+ break;
+ case TUP(OMINUS, CTFLT):
+ mpnegflt(v.u.fval);
+ break;
+
+ case TUP(OPLUS, CTCPLX):
+ break;
+ case TUP(OMINUS, CTCPLX):
+ mpnegflt(&v.u.cval->real);
+ mpnegflt(&v.u.cval->imag);
+ break;
+
+ case TUP(ONOT, CTBOOL):
+ if(!v.u.bval)
+ goto settrue;
+ goto setfalse;
+ }
+
+ret:
+ // rewrite n in place.
+ *n = *nl;
+ n->val = v;
+
+ // check range.
+ lno = setlineno(n);
+ overflow(v, n->type);
+ lineno = lno;
+
+ // truncate precision for non-ideal float.
+ if(v.ctype == CTFLT && n->type->etype != TIDEAL)
+ n->val.u.fval = truncfltlit(v.u.fval, n->type);
+ return;
+
+settrue:
+ *n = *nodbool(1);
+ return;
+
+setfalse:
+ *n = *nodbool(0);
+ return;
+}
+
+Node*
+nodlit(Val v)
+{
+ Node *n;
+
+ n = nod(OLITERAL, N, N);
+ n->val = v;
+ switch(v.ctype) {
+ default:
+ fatal("nodlit ctype %d", v.ctype);
+ case CTSTR:
+ n->type = idealstring;
+ break;
+ case CTBOOL:
+ n->type = idealbool;
+ break;
+ case CTINT:
+ case CTFLT:
+ case CTCPLX:
+ n->type = types[TIDEAL];
+ break;
+ case CTNIL:
+ n->type = types[TNIL];
+ break;
+ }
+ return n;
+}
+
+Node*
+nodcplxlit(Val r, Val i)
+{
+ Node *n;
+ Mpcplx *c;
+
+ r = toflt(r);
+ i = toflt(i);
+
+ c = mal(sizeof(*c));
+ n = nod(OLITERAL, N, N);
+ n->type = types[TIDEAL];
+ n->val.u.cval = c;
+ n->val.ctype = CTCPLX;
+
+ if(r.ctype != CTFLT || i.ctype != CTFLT)
+ fatal("nodcplxlit ctype %d/%d", r.ctype, i.ctype);
+
+ mpmovefltflt(&c->real, r.u.fval);
+ mpmovefltflt(&c->imag, i.u.fval);
+ return n;
+}
+
+// TODO(rsc): combine with convlit
+void
+defaultlit(Node **np, Type *t)
+{
+ int lno;
+ Node *n, *nn;
+
+ n = *np;
+ if(n == N || !isideal(n->type))
+ return;
+
+ switch(n->op) {
+ case OLITERAL:
+ nn = nod(OXXX, N, N);
+ *nn = *n;
+ n = nn;
+ *np = n;
+ break;
+ case OLSH:
+ case ORSH:
+ defaultlit(&n->left, t);
+ t = n->left->type;
+ if(t != T && !isint[t->etype]) {
+ yyerror("invalid operation: %#N (shift of type %T)", n, t);
+ t = T;
+ }
+ n->type = t;
+ return;
+ default:
+ if(n->left == N) {
+ dump("defaultlit", n);
+ fatal("defaultlit");
+ }
+ // n is ideal, so left and right must both be ideal.
+ // n has not been computed as a constant value,
+ // so either left or right must not be constant.
+ // The only 'ideal' non-constant expressions are shifts. Ugh.
+ // If one of these is a shift and the other is not, use that type.
+ // When compiling x := 1<<i + 3.14, this means we try to push
+ // the float64 down into the 1<<i, producing the correct error
+ // (cannot shift float64).
+ if(t == T && (n->right->op == OLSH || n->right->op == ORSH)) {
+ defaultlit(&n->left, T);
+ defaultlit(&n->right, n->left->type);
+ } else if(t == T && (n->left->op == OLSH || n->left->op == ORSH)) {
+ defaultlit(&n->right, T);
+ defaultlit(&n->left, n->right->type);
+ } else {
+ defaultlit(&n->left, t);
+ defaultlit(&n->right, t);
+ }
+ if(n->type == idealbool || n->type == idealstring)
+ n->type = types[n->type->etype];
+ else
+ n->type = n->left->type;
+ return;
+ }
+
+ lno = setlineno(n);
+ switch(n->val.ctype) {
+ default:
+ if(t != T) {
+ convlit(np, t);
+ break;
+ }
+ if(n->val.ctype == CTNIL) {
+ lineno = lno;
+ yyerror("use of untyped nil");
+ n->type = T;
+ break;
+ }
+ if(n->val.ctype == CTSTR) {
+ n->type = types[TSTRING];
+ break;
+ }
+ yyerror("defaultlit: unknown literal: %#N", n);
+ break;
+ case CTBOOL:
+ n->type = types[TBOOL];
+ if(t != T && t->etype == TBOOL)
+ n->type = t;
+ break;
+ case CTINT:
+ n->type = types[TINT];
+ goto num;
+ case CTFLT:
+ n->type = types[TFLOAT64];
+ goto num;
+ case CTCPLX:
+ n->type = types[TCOMPLEX128];
+ goto num;
+ num:
+ if(t != T) {
+ if(isint[t->etype]) {
+ n->type = t;
+ n->val = toint(n->val);
+ }
+ else
+ if(isfloat[t->etype]) {
+ n->type = t;
+ n->val = toflt(n->val);
+ }
+ else
+ if(iscomplex[t->etype]) {
+ n->type = t;
+ n->val = tocplx(n->val);
+ }
+ }
+ overflow(n->val, n->type);
+ break;
+ }
+ lineno = lno;
+}
+
+/*
+ * defaultlit on both nodes simultaneously;
+ * if they're both ideal going in they better
+ * get the same type going out.
+ * force means must assign concrete (non-ideal) type.
+ */
+void
+defaultlit2(Node **lp, Node **rp, int force)
+{
+ Node *l, *r;
+
+ l = *lp;
+ r = *rp;
+ if(l->type == T || r->type == T)
+ return;
+ if(!isideal(l->type)) {
+ convlit(rp, l->type);
+ return;
+ }
+ if(!isideal(r->type)) {
+ convlit(lp, r->type);
+ return;
+ }
+ if(!force)
+ return;
+ if(isconst(l, CTCPLX) || isconst(r, CTCPLX)) {
+ convlit(lp, types[TCOMPLEX128]);
+ convlit(rp, types[TCOMPLEX128]);
+ return;
+ }
+ if(isconst(l, CTFLT) || isconst(r, CTFLT)) {
+ convlit(lp, types[TFLOAT64]);
+ convlit(rp, types[TFLOAT64]);
+ return;
+ }
+ convlit(lp, types[TINT]);
+ convlit(rp, types[TINT]);
+}
+
+int
+cmpslit(Node *l, Node *r)
+{
+ int32 l1, l2, i, m;
+ uchar *s1, *s2;
+
+ l1 = l->val.u.sval->len;
+ l2 = r->val.u.sval->len;
+ s1 = (uchar*)l->val.u.sval->s;
+ s2 = (uchar*)r->val.u.sval->s;
+
+ m = l1;
+ if(l2 < m)
+ m = l2;
+
+ for(i=0; i<m; i++) {
+ if(s1[i] == s2[i])
+ continue;
+ if(s1[i] > s2[i])
+ return +1;
+ return -1;
+ }
+ if(l1 == l2)
+ return 0;
+ if(l1 > l2)
+ return +1;
+ return -1;
+}
+
+int
+smallintconst(Node *n)
+{
+ if(n->op == OLITERAL && n->type != T)
+ switch(simtype[n->type->etype]) {
+ case TINT8:
+ case TUINT8:
+ case TINT16:
+ case TUINT16:
+ case TINT32:
+ case TUINT32:
+ case TBOOL:
+ case TPTR32:
+ return 1;
+ case TINT64:
+ case TUINT64:
+ if(mpcmpfixfix(n->val.u.xval, minintval[TINT32]) < 0
+ || mpcmpfixfix(n->val.u.xval, maxintval[TINT32]) > 0)
+ break;
+ return 1;
+ }
+ return 0;
+}
+
+long
+nonnegconst(Node *n)
+{
+ if(n->op == OLITERAL && n->type != T)
+ switch(simtype[n->type->etype]) {
+ case TINT8:
+ case TUINT8:
+ case TINT16:
+ case TUINT16:
+ case TINT32:
+ case TUINT32:
+ case TINT64:
+ case TUINT64:
+ case TIDEAL:
+ // check negative and 2^31
+ if(mpcmpfixfix(n->val.u.xval, minintval[TUINT32]) < 0
+ || mpcmpfixfix(n->val.u.xval, maxintval[TINT32]) > 0)
+ break;
+ return mpgetfix(n->val.u.xval);
+ }
+ return -1;
+}
+
+/*
+ * convert x to type et and back to int64
+ * for sign extension and truncation.
+ */
+static int64
+iconv(int64 x, int et)
+{
+ switch(et) {
+ case TINT8:
+ x = (int8)x;
+ break;
+ case TUINT8:
+ x = (uint8)x;
+ break;
+ case TINT16:
+ x = (int16)x;
+ break;
+ case TUINT16:
+ x = (uint64)x;
+ break;
+ case TINT32:
+ x = (int32)x;
+ break;
+ case TUINT32:
+ x = (uint32)x;
+ break;
+ case TINT64:
+ case TUINT64:
+ break;
+ }
+ return x;
+}
+
+/*
+ * convert constant val to type t; leave in con.
+ * for back end.
+ */
+void
+convconst(Node *con, Type *t, Val *val)
+{
+ int64 i;
+ int tt;
+
+ tt = simsimtype(t);
+
+ // copy the constant for conversion
+ nodconst(con, types[TINT8], 0);
+ con->type = t;
+ con->val = *val;
+
+ if(isint[tt]) {
+ con->val.ctype = CTINT;
+ con->val.u.xval = mal(sizeof *con->val.u.xval);
+ switch(val->ctype) {
+ default:
+ fatal("convconst ctype=%d %lT", val->ctype, t);
+ case CTINT:
+ i = mpgetfix(val->u.xval);
+ break;
+ case CTBOOL:
+ i = val->u.bval;
+ break;
+ case CTNIL:
+ i = 0;
+ break;
+ }
+ i = iconv(i, tt);
+ mpmovecfix(con->val.u.xval, i);
+ return;
+ }
+
+ if(isfloat[tt]) {
+ con->val = toflt(con->val);
+ if(con->val.ctype != CTFLT)
+ fatal("convconst ctype=%d %T", con->val.ctype, t);
+ if(tt == TFLOAT32)
+ con->val.u.fval = truncfltlit(con->val.u.fval, t);
+ return;
+ }
+
+ if(iscomplex[tt]) {
+ con->val = tocplx(con->val);
+ if(tt == TCOMPLEX64) {
+ con->val.u.cval->real = *truncfltlit(&con->val.u.cval->real, types[TFLOAT32]);
+ con->val.u.cval->imag = *truncfltlit(&con->val.u.cval->imag, types[TFLOAT32]);
+ }
+ return;
+ }
+
+ fatal("convconst %lT constant", t);
+
+}
+
+// complex multiply v *= rv
+// (a, b) * (c, d) = (a*c - b*d, b*c + a*d)
+static void
+cmplxmpy(Mpcplx *v, Mpcplx *rv)
+{
+ Mpflt ac, bd, bc, ad;
+
+ mpmovefltflt(&ac, &v->real);
+ mpmulfltflt(&ac, &rv->real); // ac
+
+ mpmovefltflt(&bd, &v->imag);
+ mpmulfltflt(&bd, &rv->imag); // bd
+
+ mpmovefltflt(&bc, &v->imag);
+ mpmulfltflt(&bc, &rv->real); // bc
+
+ mpmovefltflt(&ad, &v->real);
+ mpmulfltflt(&ad, &rv->imag); // ad
+
+ mpmovefltflt(&v->real, &ac);
+ mpsubfltflt(&v->real, &bd); // ac-bd
+
+ mpmovefltflt(&v->imag, &bc);
+ mpaddfltflt(&v->imag, &ad); // bc+ad
+}
+
+// complex divide v /= rv
+// (a, b) / (c, d) = ((a*c + b*d), (b*c - a*d))/(c*c + d*d)
+static void
+cmplxdiv(Mpcplx *v, Mpcplx *rv)
+{
+ Mpflt ac, bd, bc, ad, cc_plus_dd;
+
+ mpmovefltflt(&cc_plus_dd, &rv->real);
+ mpmulfltflt(&cc_plus_dd, &rv->real); // cc
+
+ mpmovefltflt(&ac, &rv->imag);
+ mpmulfltflt(&ac, &rv->imag); // dd
+
+ mpaddfltflt(&cc_plus_dd, &ac); // cc+dd
+
+ mpmovefltflt(&ac, &v->real);
+ mpmulfltflt(&ac, &rv->real); // ac
+
+ mpmovefltflt(&bd, &v->imag);
+ mpmulfltflt(&bd, &rv->imag); // bd
+
+ mpmovefltflt(&bc, &v->imag);
+ mpmulfltflt(&bc, &rv->real); // bc
+
+ mpmovefltflt(&ad, &v->real);
+ mpmulfltflt(&ad, &rv->imag); // ad
+
+ mpmovefltflt(&v->real, &ac);
+ mpaddfltflt(&v->real, &bd); // ac+bd
+ mpdivfltflt(&v->real, &cc_plus_dd); // (ac+bd)/(cc+dd)
+
+ mpmovefltflt(&v->imag, &bc);
+ mpsubfltflt(&v->imag, &ad); // bc-ad
+ mpdivfltflt(&v->imag, &cc_plus_dd); // (bc+ad)/(cc+dd)
+}
diff --git a/src/cmd/gc/cplx.c b/src/cmd/gc/cplx.c
new file mode 100644
index 000000000..890cf7f10
--- /dev/null
+++ b/src/cmd/gc/cplx.c
@@ -0,0 +1,479 @@
+// Copyright 2009 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.
+
+#include "gg.h"
+
+static void subnode(Node *nr, Node *ni, Node *nc);
+static void minus(Node *nl, Node *res);
+ void complexminus(Node*, Node*);
+ void complexadd(int op, Node*, Node*, Node*);
+ void complexmul(Node*, Node*, Node*);
+
+#define CASE(a,b) (((a)<<16)|((b)<<0))
+
+static int
+overlap(Node *f, Node *t)
+{
+ // check whether f and t could be overlapping stack references.
+ // not exact, because it's hard to check for the stack register
+ // in portable code. close enough: worst case we will allocate
+ // an extra temporary and the registerizer will clean it up.
+ return f->op == OINDREG &&
+ t->op == OINDREG &&
+ f->xoffset+f->type->width >= t->xoffset &&
+ t->xoffset+t->type->width >= f->xoffset;
+}
+
+/*
+ * generate:
+ * res = n;
+ * simplifies and calls gmove.
+ */
+void
+complexmove(Node *f, Node *t)
+{
+ int ft, tt;
+ Node n1, n2, n3, n4;
+
+ if(debug['g']) {
+ dump("\ncomplexmove-f", f);
+ dump("complexmove-t", t);
+ }
+
+ if(!t->addable)
+ fatal("complexmove: to not addable");
+
+ ft = simsimtype(f->type);
+ tt = simsimtype(t->type);
+ switch(CASE(ft,tt)) {
+
+ default:
+ fatal("complexmove: unknown conversion: %T -> %T\n",
+ f->type, t->type);
+
+ case CASE(TCOMPLEX64,TCOMPLEX64):
+ case CASE(TCOMPLEX64,TCOMPLEX128):
+ case CASE(TCOMPLEX128,TCOMPLEX64):
+ case CASE(TCOMPLEX128,TCOMPLEX128):
+ // complex to complex move/convert.
+ // make f addable.
+ // also use temporary if possible stack overlap.
+ if(!f->addable || overlap(f, t)) {
+ tempname(&n1, f->type);
+ complexmove(f, &n1);
+ f = &n1;
+ }
+
+ subnode(&n1, &n2, f);
+ subnode(&n3, &n4, t);
+
+ cgen(&n1, &n3);
+ cgen(&n2, &n4);
+ break;
+ }
+}
+
+int
+complexop(Node *n, Node *res)
+{
+ if(n != N && n->type != T)
+ if(iscomplex[n->type->etype]) {
+ goto maybe;
+ }
+ if(res != N && res->type != T)
+ if(iscomplex[res->type->etype]) {
+ goto maybe;
+ }
+
+ if(n->op == OREAL || n->op == OIMAG)
+ goto yes;
+
+ goto no;
+
+maybe:
+ switch(n->op) {
+ case OCONV: // implemented ops
+ case OADD:
+ case OSUB:
+ case OMUL:
+ case OMINUS:
+ case OCOMPLEX:
+ case OREAL:
+ case OIMAG:
+ goto yes;
+
+ case ODOT:
+ case ODOTPTR:
+ case OINDEX:
+ case OIND:
+ case ONAME:
+ goto yes;
+ }
+
+no:
+//dump("\ncomplex-no", n);
+ return 0;
+yes:
+//dump("\ncomplex-yes", n);
+ return 1;
+}
+
+void
+complexgen(Node *n, Node *res)
+{
+ Node *nl, *nr;
+ Node tnl, tnr;
+ Node n1, n2, tmp;
+ int tl, tr;
+
+ if(debug['g']) {
+ dump("\ncomplexgen-n", n);
+ dump("complexgen-res", res);
+ }
+
+ // pick off float/complex opcodes
+ switch(n->op) {
+ case OCOMPLEX:
+ if(res->addable) {
+ subnode(&n1, &n2, res);
+ tempname(&tmp, n1.type);
+ cgen(n->left, &tmp);
+ cgen(n->right, &n2);
+ cgen(&tmp, &n1);
+ return;
+ }
+ break;
+
+ case OREAL:
+ case OIMAG:
+ nl = n->left;
+ if(!nl->addable) {
+ tempname(&tmp, nl->type);
+ complexgen(nl, &tmp);
+ nl = &tmp;
+ }
+ subnode(&n1, &n2, nl);
+ if(n->op == OREAL) {
+ cgen(&n1, res);
+ return;
+ }
+ cgen(&n2, res);
+ return;
+ }
+
+ // perform conversion from n to res
+ tl = simsimtype(res->type);
+ tl = cplxsubtype(tl);
+ tr = simsimtype(n->type);
+ tr = cplxsubtype(tr);
+ if(tl != tr) {
+ if(!n->addable) {
+ tempname(&n1, n->type);
+ complexmove(n, &n1);
+ n = &n1;
+ }
+ complexmove(n, res);
+ return;
+ }
+
+ if(!res->addable) {
+ igen(res, &n1, N);
+ cgen(n, &n1);
+ regfree(&n1);
+ return;
+ }
+ if(n->addable) {
+ complexmove(n, res);
+ return;
+ }
+
+ switch(n->op) {
+ default:
+ dump("complexgen: unknown op", n);
+ fatal("complexgen: unknown op %O", n->op);
+
+ case ODOT:
+ case ODOTPTR:
+ case OINDEX:
+ case OIND:
+ case ONAME: // PHEAP or PPARAMREF var
+ case OCALLFUNC:
+ igen(n, &n1, res);
+ complexmove(&n1, res);
+ regfree(&n1);
+ return;
+
+ case OCONV:
+ case OADD:
+ case OSUB:
+ case OMUL:
+ case OMINUS:
+ case OCOMPLEX:
+ case OREAL:
+ case OIMAG:
+ break;
+ }
+
+ nl = n->left;
+ if(nl == N)
+ return;
+ nr = n->right;
+
+ // make both sides addable in ullman order
+ if(nr != N) {
+ if(nl->ullman > nr->ullman && !nl->addable) {
+ tempname(&tnl, nl->type);
+ cgen(nl, &tnl);
+ nl = &tnl;
+ }
+ if(!nr->addable) {
+ tempname(&tnr, nr->type);
+ cgen(nr, &tnr);
+ nr = &tnr;
+ }
+ }
+ if(!nl->addable) {
+ tempname(&tnl, nl->type);
+ cgen(nl, &tnl);
+ nl = &tnl;
+ }
+
+ switch(n->op) {
+ default:
+ fatal("complexgen: unknown op %O", n->op);
+ break;
+
+ case OCONV:
+ complexmove(nl, res);
+ break;
+
+ case OMINUS:
+ complexminus(nl, res);
+ break;
+
+ case OADD:
+ case OSUB:
+ complexadd(n->op, nl, nr, res);
+ break;
+
+ case OMUL:
+ complexmul(nl, nr, res);
+ break;
+ }
+}
+
+void
+complexbool(int op, Node *nl, Node *nr, int true, Prog *to)
+{
+ Node tnl, tnr;
+ Node n1, n2, n3, n4;
+ Node na, nb, nc;
+
+ // make both sides addable in ullman order
+ if(nr != N) {
+ if(nl->ullman > nr->ullman && !nl->addable) {
+ tempname(&tnl, nl->type);
+ cgen(nl, &tnl);
+ nl = &tnl;
+ }
+ if(!nr->addable) {
+ tempname(&tnr, nr->type);
+ cgen(nr, &tnr);
+ nr = &tnr;
+ }
+ }
+ if(!nl->addable) {
+ tempname(&tnl, nl->type);
+ cgen(nl, &tnl);
+ nl = &tnl;
+ }
+
+ // build tree
+ // real(l) == real(r) && imag(l) == imag(r)
+
+ subnode(&n1, &n2, nl);
+ subnode(&n3, &n4, nr);
+
+ memset(&na, 0, sizeof(na));
+ na.op = OANDAND;
+ na.left = &nb;
+ na.right = &nc;
+ na.type = types[TBOOL];
+
+ memset(&nb, 0, sizeof(na));
+ nb.op = OEQ;
+ nb.left = &n1;
+ nb.right = &n3;
+ nb.type = types[TBOOL];
+
+ memset(&nc, 0, sizeof(na));
+ nc.op = OEQ;
+ nc.left = &n2;
+ nc.right = &n4;
+ nc.type = types[TBOOL];
+
+ if(op == ONE)
+ true = !true;
+
+ bgen(&na, true, to);
+}
+
+void
+nodfconst(Node *n, Type *t, Mpflt* fval)
+{
+ memset(n, 0, sizeof(*n));
+ n->op = OLITERAL;
+ n->addable = 1;
+ ullmancalc(n);
+ n->val.u.fval = fval;
+ n->val.ctype = CTFLT;
+ n->type = t;
+
+ if(!isfloat[t->etype])
+ fatal("nodfconst: bad type %T", t);
+}
+
+// break addable nc-complex into nr-real and ni-imaginary
+static void
+subnode(Node *nr, Node *ni, Node *nc)
+{
+ int tc;
+ Type *t;
+
+ if(!nc->addable)
+ fatal("subnode not addable");
+
+ tc = simsimtype(nc->type);
+ tc = cplxsubtype(tc);
+ t = types[tc];
+
+ if(nc->op == OLITERAL) {
+ nodfconst(nr, t, &nc->val.u.cval->real);
+ nodfconst(ni, t, &nc->val.u.cval->imag);
+ return;
+ }
+
+ *nr = *nc;
+ nr->type = t;
+
+ *ni = *nc;
+ ni->type = t;
+ ni->xoffset += t->width;
+}
+
+// generate code res = -nl
+static void
+minus(Node *nl, Node *res)
+{
+ Node ra;
+
+ memset(&ra, 0, sizeof(ra));
+ ra.op = OMINUS;
+ ra.left = nl;
+ ra.type = nl->type;
+ cgen(&ra, res);
+}
+
+// build and execute tree
+// real(res) = -real(nl)
+// imag(res) = -imag(nl)
+void
+complexminus(Node *nl, Node *res)
+{
+ Node n1, n2, n5, n6;
+
+ subnode(&n1, &n2, nl);
+ subnode(&n5, &n6, res);
+
+ minus(&n1, &n5);
+ minus(&n2, &n6);
+}
+
+
+// build and execute tree
+// real(res) = real(nl) op real(nr)
+// imag(res) = imag(nl) op imag(nr)
+void
+complexadd(int op, Node *nl, Node *nr, Node *res)
+{
+ Node n1, n2, n3, n4, n5, n6;
+ Node ra;
+
+ subnode(&n1, &n2, nl);
+ subnode(&n3, &n4, nr);
+ subnode(&n5, &n6, res);
+
+ memset(&ra, 0, sizeof(ra));
+ ra.op = op;
+ ra.left = &n1;
+ ra.right = &n3;
+ ra.type = n1.type;
+ cgen(&ra, &n5);
+
+ memset(&ra, 0, sizeof(ra));
+ ra.op = op;
+ ra.left = &n2;
+ ra.right = &n4;
+ ra.type = n2.type;
+ cgen(&ra, &n6);
+}
+
+// build and execute tree
+// tmp = real(nl)*real(nr) - imag(nl)*imag(nr)
+// imag(res) = real(nl)*imag(nr) + imag(nl)*real(nr)
+// real(res) = tmp
+void
+complexmul(Node *nl, Node *nr, Node *res)
+{
+ Node n1, n2, n3, n4, n5, n6;
+ Node rm1, rm2, ra, tmp;
+
+ subnode(&n1, &n2, nl);
+ subnode(&n3, &n4, nr);
+ subnode(&n5, &n6, res);
+ tempname(&tmp, n5.type);
+
+ // real part -> tmp
+ memset(&rm1, 0, sizeof(ra));
+ rm1.op = OMUL;
+ rm1.left = &n1;
+ rm1.right = &n3;
+ rm1.type = n1.type;
+
+ memset(&rm2, 0, sizeof(ra));
+ rm2.op = OMUL;
+ rm2.left = &n2;
+ rm2.right = &n4;
+ rm2.type = n2.type;
+
+ memset(&ra, 0, sizeof(ra));
+ ra.op = OSUB;
+ ra.left = &rm1;
+ ra.right = &rm2;
+ ra.type = rm1.type;
+ cgen(&ra, &tmp);
+
+ // imag part
+ memset(&rm1, 0, sizeof(ra));
+ rm1.op = OMUL;
+ rm1.left = &n1;
+ rm1.right = &n4;
+ rm1.type = n1.type;
+
+ memset(&rm2, 0, sizeof(ra));
+ rm2.op = OMUL;
+ rm2.left = &n2;
+ rm2.right = &n3;
+ rm2.type = n2.type;
+
+ memset(&ra, 0, sizeof(ra));
+ ra.op = OADD;
+ ra.left = &rm1;
+ ra.right = &rm2;
+ ra.type = rm1.type;
+ cgen(&ra, &n6);
+
+ // tmp ->real part
+ cgen(&tmp, &n5);
+}
diff --git a/src/cmd/gc/dcl.c b/src/cmd/gc/dcl.c
new file mode 100644
index 000000000..5bfeeb97a
--- /dev/null
+++ b/src/cmd/gc/dcl.c
@@ -0,0 +1,1254 @@
+// Copyright 2009 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.
+
+#include "go.h"
+#include "y.tab.h"
+
+static void funcargs(Node*);
+
+static int
+dflag(void)
+{
+ if(!debug['d'])
+ return 0;
+ if(debug['y'])
+ return 1;
+ if(incannedimport)
+ return 0;
+ return 1;
+}
+
+/*
+ * declaration stack & operations
+ */
+
+static void
+dcopy(Sym *a, Sym *b)
+{
+ a->pkg = b->pkg;
+ a->name = b->name;
+ a->def = b->def;
+ a->block = b->block;
+ a->lastlineno = b->lastlineno;
+}
+
+static Sym*
+push(void)
+{
+ Sym *d;
+
+ d = mal(sizeof(*d));
+ d->lastlineno = lineno;
+ d->link = dclstack;
+ dclstack = d;
+ return d;
+}
+
+static Sym*
+pushdcl(Sym *s)
+{
+ Sym *d;
+
+ d = push();
+ dcopy(d, s);
+ if(dflag())
+ print("\t%L push %S %p\n", lineno, s, s->def);
+ return d;
+}
+
+void
+popdcl(void)
+{
+ Sym *d, *s;
+ int lno;
+
+// if(dflag())
+// print("revert\n");
+
+ for(d=dclstack; d!=S; d=d->link) {
+ 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);
+ }
+ if(d == S)
+ fatal("popdcl: no mark");
+ dclstack = d->link;
+ block = d->block;
+}
+
+void
+poptodcl(void)
+{
+ // 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
+markdcl(void)
+{
+ Sym *d;
+
+ d = push();
+ d->name = nil; // used as a mark in fifo
+ d->block = block;
+
+ blockgen++;
+ block = blockgen;
+
+// if(dflag())
+// print("markdcl\n");
+}
+
+void
+dumpdcl(char *st)
+{
+ Sym *s, *d;
+ int i;
+
+ i = 0;
+ for(d=dclstack; d!=S; d=d->link) {
+ i++;
+ print(" %.2d %p", i, d);
+ if(d->name == nil) {
+ print("\n");
+ continue;
+ }
+ print(" '%s'", d->name);
+ s = pkglookup(d->name, d->pkg);
+ print(" %lS\n", s);
+ }
+}
+
+void
+testdclstack(void)
+{
+ Sym *d;
+
+ for(d=dclstack; d!=S; d=d->link) {
+ if(d->name == nil) {
+ yyerror("mark left on the stack");
+ continue;
+ }
+ }
+}
+
+void
+redeclare(Sym *s, char *where)
+{
+ if(s->lastlineno == 0)
+ yyerror("%S redeclared %s\n"
+ "\tprevious declaration during import",
+ s, where);
+ else
+ yyerror("%S redeclared %s\n"
+ "\tprevious declaration at %L",
+ s, where, s->lastlineno);
+}
+
+/*
+ * declare individual names - var, typ, const
+ */
+void
+declare(Node *n, int ctxt)
+{
+ Sym *s;
+ int gen;
+ static int typegen, vargen;
+
+ if(isblank(n))
+ return;
+
+ n->lineno = parserline();
+ s = n->sym;
+ gen = 0;
+ if(ctxt == PEXTERN) {
+ externdcl = list(externdcl, n);
+ if(dflag())
+ print("\t%L global decl %S %p\n", lineno, s, n);
+ } else {
+ if(curfn == nil && ctxt == PAUTO)
+ fatal("automatic outside function");
+ if(curfn != nil)
+ curfn->dcl = list(curfn->dcl, n);
+ if(n->op == OTYPE)
+ gen = ++typegen;
+ else if(n->op == ONAME)
+ gen = ++vargen;
+ pushdcl(s);
+ n->curfn = curfn;
+ }
+ if(ctxt == PAUTO)
+ n->xoffset = BADWIDTH;
+
+ if(s->block == block)
+ redeclare(s, "in this block");
+
+ s->block = block;
+ s->lastlineno = parserline();
+ s->def = n;
+ n->vargen = gen;
+ n->funcdepth = funcdepth;
+ n->class = ctxt;
+
+ autoexport(n, ctxt);
+}
+
+void
+addvar(Node *n, Type *t, int ctxt)
+{
+ if(n==N || n->sym == S || (n->op != ONAME && n->op != ONONAME) || t == T)
+ fatal("addvar: n=%N t=%T nil", n, t);
+
+ n->op = ONAME;
+ declare(n, ctxt);
+ n->type = t;
+}
+
+/*
+ * declare variables from grammar
+ * new_name_list (type | [type] = expr_list)
+ */
+NodeList*
+variter(NodeList *vl, Node *t, NodeList *el)
+{
+ int doexpr;
+ Node *v, *e, *as2;
+ NodeList *init;
+
+ init = nil;
+ doexpr = el != nil;
+
+ if(count(el) == 1 && count(vl) > 1) {
+ e = el->n;
+ as2 = nod(OAS2, N, N);
+ as2->list = vl;
+ as2->rlist = list1(e);
+ for(; vl; vl=vl->next) {
+ v = vl->n;
+ v->op = ONAME;
+ declare(v, dclcontext);
+ v->ntype = t;
+ v->defn = as2;
+ if(funcdepth > 0)
+ init = list(init, nod(ODCL, v, N));
+ }
+ return list(init, as2);
+ }
+
+ for(; vl; vl=vl->next) {
+ if(doexpr) {
+ if(el == nil) {
+ yyerror("missing expr in var dcl");
+ break;
+ }
+ e = el->n;
+ el = el->next;
+ } else
+ e = N;
+
+ v = vl->n;
+ v->op = ONAME;
+ declare(v, dclcontext);
+ v->ntype = t;
+
+ if(e != N || funcdepth > 0 || isblank(v)) {
+ if(funcdepth > 0)
+ init = list(init, nod(ODCL, v, N));
+ e = nod(OAS, v, e);
+ init = list(init, e);
+ if(e->right != N)
+ v->defn = e;
+ }
+ }
+ if(el != nil)
+ yyerror("extra expr in var dcl");
+ return init;
+}
+
+/*
+ * declare constants from grammar
+ * new_name_list [[type] = expr_list]
+ */
+NodeList*
+constiter(NodeList *vl, Node *t, NodeList *cl)
+{
+ Node *v, *c;
+ NodeList *vv;
+
+ vv = nil;
+ if(cl == nil) {
+ if(t != N)
+ yyerror("constdcl cannot have type without expr");
+ cl = lastconst;
+ t = lasttype;
+ } else {
+ lastconst = cl;
+ lasttype = t;
+ }
+ cl = listtreecopy(cl);
+
+ for(; vl; vl=vl->next) {
+ if(cl == nil) {
+ yyerror("missing expr in const dcl");
+ break;
+ }
+ c = cl->n;
+ cl = cl->next;
+
+ v = vl->n;
+ v->op = OLITERAL;
+ declare(v, dclcontext);
+
+ v->ntype = t;
+ v->defn = c;
+
+ vv = list(vv, nod(ODCLCONST, v, N));
+ }
+ if(cl != nil)
+ yyerror("extra expr in const dcl");
+ iota += 1;
+ return vv;
+}
+
+/*
+ * this generates a new name node,
+ * typically for labels or other one-off names.
+ */
+Node*
+newname(Sym *s)
+{
+ Node *n;
+
+ if(s == S)
+ fatal("newname nil");
+
+ n = nod(ONAME, N, N);
+ n->sym = s;
+ n->type = T;
+ n->addable = 1;
+ n->ullman = 1;
+ n->xoffset = 0;
+ return n;
+}
+
+/*
+ * this generates a new name node for a name
+ * being declared.
+ */
+Node*
+dclname(Sym *s)
+{
+ Node *n;
+
+ n = newname(s);
+ n->op = ONONAME; // caller will correct it
+ return n;
+}
+
+Node*
+typenod(Type *t)
+{
+ // if we copied another type with *t = *u
+ // then t->nod might be out of date, so
+ // check t->nod->type too
+ if(t->nod == N || t->nod->type != t) {
+ t->nod = nod(OTYPE, N, N);
+ t->nod->type = t;
+ t->nod->sym = t->sym;
+ }
+ return t->nod;
+}
+
+
+/*
+ * this will return an old name
+ * that has already been pushed on the
+ * declaration list. a diagnostic is
+ * generated if no name has been defined.
+ */
+Node*
+oldname(Sym *s)
+{
+ Node *n;
+ Node *c;
+
+ n = s->def;
+ if(n == N) {
+ // maybe a top-level name will come along
+ // to give this a definition later.
+ // walkdef will check s->def again once
+ // all the input source has been processed.
+ n = newname(s);
+ n->op = ONONAME;
+ n->iota = iota; // save current iota value in const declarations
+ }
+ if(curfn != nil && n->funcdepth > 0 && n->funcdepth != funcdepth && n->op == ONAME) {
+ // inner func is referring to var in outer func.
+ //
+ // TODO(rsc): If there is an outer variable x and we
+ // are parsing x := 5 inside the closure, until we get to
+ // the := it looks like a reference to the outer x so we'll
+ // make x a closure variable unnecessarily.
+ if(n->closure == N || n->closure->funcdepth != funcdepth) {
+ // create new closure var.
+ c = nod(ONAME, N, N);
+ c->sym = s;
+ c->class = PPARAMREF;
+ c->isddd = n->isddd;
+ c->defn = n;
+ c->addable = 0;
+ c->ullman = 2;
+ c->funcdepth = funcdepth;
+ c->outer = n->closure;
+ n->closure = c;
+ c->closure = n;
+ c->xoffset = 0;
+ curfn->cvars = list(curfn->cvars, c);
+ }
+ // return ref to closure var, not original
+ return n->closure;
+ }
+ return n;
+}
+
+/*
+ * same for types
+ */
+Type*
+newtype(Sym *s)
+{
+ Type *t;
+
+ t = typ(TFORW);
+ t->sym = s;
+ t->type = T;
+ return t;
+}
+
+
+/*
+ * := declarations
+ */
+
+static int
+colasname(Node *n)
+{
+ switch(n->op) {
+ case ONAME:
+ case ONONAME:
+ case OPACK:
+ case OTYPE:
+ case OLITERAL:
+ return n->sym != S;
+ }
+ return 0;
+}
+
+void
+colasdefn(NodeList *left, Node *defn)
+{
+ int nnew, nerr;
+ NodeList *l;
+ Node *n;
+
+ nnew = 0;
+ nerr = 0;
+ for(l=left; l; l=l->next) {
+ n = l->n;
+ if(isblank(n))
+ continue;
+ if(!colasname(n)) {
+ yyerror("non-name %#N on left side of :=", n);
+ nerr++;
+ continue;
+ }
+ if(n->sym->block == block)
+ continue;
+
+ nnew++;
+ n = newname(n->sym);
+ declare(n, dclcontext);
+ n->defn = defn;
+ defn->ninit = list(defn->ninit, nod(ODCL, n, N));
+ l->n = n;
+ }
+ if(nnew == 0 && nerr == 0)
+ yyerror("no new variables on left side of :=");
+}
+
+Node*
+colas(NodeList *left, NodeList *right)
+{
+ Node *as;
+
+ as = nod(OAS2, N, N);
+ as->list = left;
+ as->rlist = right;
+ as->colas = 1;
+ colasdefn(left, as);
+
+ // make the tree prettier; not necessary
+ if(count(left) == 1 && count(right) == 1) {
+ as->left = as->list->n;
+ as->right = as->rlist->n;
+ as->list = nil;
+ as->rlist = nil;
+ as->op = OAS;
+ }
+
+ return as;
+}
+
+/*
+ * declare the arguments in an
+ * interface field declaration.
+ */
+void
+ifacedcl(Node *n)
+{
+ if(n->op != ODCLFIELD || n->right == N)
+ fatal("ifacedcl");
+
+ dclcontext = PAUTO;
+ markdcl();
+ funcdepth++;
+ n->outer = curfn;
+ curfn = n;
+ funcargs(n->right);
+
+ // funcbody is normally called after the parser has
+ // seen the body of a function but since an interface
+ // field declaration does not have a body, we must
+ // call it now to pop the current declaration context.
+ funcbody(n);
+}
+
+/*
+ * declare the function proper
+ * and declare the arguments.
+ * called in extern-declaration context
+ * returns in auto-declaration context.
+ */
+void
+funchdr(Node *n)
+{
+
+ if(n->nname != N) {
+ n->nname->op = ONAME;
+ declare(n->nname, PFUNC);
+ n->nname->defn = n;
+ }
+
+ // change the declaration context from extern to auto
+ if(funcdepth == 0 && dclcontext != PEXTERN)
+ fatal("funchdr: dclcontext");
+
+ dclcontext = PAUTO;
+ markdcl();
+ funcdepth++;
+
+ n->outer = curfn;
+ curfn = n;
+ if(n->nname)
+ funcargs(n->nname->ntype);
+ else
+ funcargs(n->ntype);
+}
+
+static void
+funcargs(Node *nt)
+{
+ Node *n;
+ NodeList *l;
+ int gen;
+
+ if(nt->op != OTFUNC)
+ fatal("funcargs %O", nt->op);
+
+ // declare the receiver and in arguments.
+ // no n->defn because type checking of func header
+ // will fill in the types before we can demand them.
+ if(nt->left != N) {
+ n = nt->left;
+ if(n->op != ODCLFIELD)
+ fatal("funcargs1 %O", n->op);
+ if(n->left != N) {
+ n->left->op = ONAME;
+ n->left->ntype = n->right;
+ declare(n->left, PPARAM);
+ }
+ }
+ for(l=nt->list; l; l=l->next) {
+ n = l->n;
+ if(n->op != ODCLFIELD)
+ fatal("funcargs2 %O", n->op);
+ if(n->left != N) {
+ n->left->op = ONAME;
+ n->left->ntype = n->right;
+ declare(n->left, PPARAM);
+ }
+ }
+
+ // declare the out arguments.
+ gen = 0;
+ for(l=nt->rlist; l; l=l->next) {
+ n = l->n;
+ if(n->op != ODCLFIELD)
+ fatal("funcargs3 %O", n->op);
+ if(n->left != N) {
+ n->left->op = ONAME;
+ n->left->ntype = n->right;
+ if(isblank(n->left)) {
+ // Give it a name so we can assign to it during return.
+ snprint(namebuf, sizeof(namebuf), ".anon%d", gen++);
+ n->left->sym = lookup(namebuf);
+ }
+ declare(n->left, PPARAMOUT);
+ }
+ }
+}
+
+/*
+ * finish the body.
+ * called in auto-declaration context.
+ * returns in extern-declaration context.
+ */
+void
+funcbody(Node *n)
+{
+ // change the declaration context from auto to extern
+ if(dclcontext != PAUTO)
+ fatal("funcbody: dclcontext");
+ popdcl();
+ funcdepth--;
+ curfn = n->outer;
+ n->outer = N;
+ if(funcdepth == 0)
+ dclcontext = PEXTERN;
+}
+
+/*
+ * new type being defined with name s.
+ */
+Node*
+typedcl0(Sym *s)
+{
+ Node *n;
+
+ n = dclname(s);
+ n->op = OTYPE;
+ declare(n, dclcontext);
+ return n;
+}
+
+/*
+ * node n, which was returned by typedcl0
+ * is being declared to have uncompiled type t.
+ * return the ODCLTYPE node to use.
+ */
+Node*
+typedcl1(Node *n, Node *t, int local)
+{
+ n->ntype = t;
+ n->local = local;
+ return nod(ODCLTYPE, n, N);
+}
+
+/*
+ * typedcl1 but during imports
+ */
+void
+typedcl2(Type *pt, Type *t)
+{
+ Node *n;
+
+ // override declaration in unsafe.go for Pointer.
+ // there is no way in Go code to define unsafe.Pointer
+ // so we have to supply it.
+ if(incannedimport &&
+ strcmp(importpkg->name, "unsafe") == 0 &&
+ strcmp(pt->nod->sym->name, "Pointer") == 0) {
+ t = types[TUNSAFEPTR];
+ }
+
+ if(pt->etype == TFORW)
+ goto ok;
+ if(!eqtype(pt->orig, t))
+ yyerror("inconsistent definition for type %S during import\n\t%lT\n\t%lT", pt->sym, pt->orig, t);
+ return;
+
+ok:
+ n = pt->nod;
+ copytype(pt->nod, t);
+ // unzero nod
+ pt->nod = n;
+
+ pt->sym->lastlineno = parserline();
+ declare(n, PEXTERN);
+
+ checkwidth(pt);
+}
+
+/*
+ * structs, functions, and methods.
+ * they don't belong here, but where do they belong?
+ */
+
+
+/*
+ * turn a parsed struct into a type
+ */
+static Type**
+stotype(NodeList *l, int et, Type **t, int funarg)
+{
+ Type *f, *t1, *t2, **t0;
+ Strlit *note;
+ int lno;
+ Node *n, *left;
+ char *what;
+
+ t0 = t;
+ lno = lineno;
+ what = "field";
+ if(et == TINTER)
+ what = "method";
+
+ for(; l; l=l->next) {
+ n = l->n;
+ lineno = n->lineno;
+ note = nil;
+
+ if(n->op != ODCLFIELD)
+ fatal("stotype: oops %N\n", n);
+ left = n->left;
+ if(funarg && isblank(left))
+ left = N;
+ if(n->right != N) {
+ if(et == TINTER && left != N) {
+ // queue resolution of method type for later.
+ // right now all we need is the name list.
+ // avoids cycles for recursive interface types.
+ n->type = typ(TINTERMETH);
+ n->type->nname = n->right;
+ n->right = N;
+ left->type = n->type;
+ queuemethod(n);
+ } else {
+ typecheck(&n->right, Etype);
+ n->type = n->right->type;
+ if(n->type == T)
+ continue;
+ if(left != N)
+ left->type = n->type;
+ n->right = N;
+ if(n->embedded && n->type != T) {
+ t1 = n->type;
+ if(t1->sym == S && isptr[t1->etype]) {
+ t1 = t1->type;
+ if(t1->etype == TINTER)
+ yyerror("embedded type cannot be a pointer to interface");
+ }
+ if(isptr[t1->etype])
+ yyerror("embedded type cannot be a pointer");
+ else if(t1->etype == TFORW && t1->embedlineno == 0)
+ t1->embedlineno = lineno;
+ }
+ }
+ }
+
+ if(n->type == T) {
+ // assume error already printed
+ continue;
+ }
+
+ switch(n->val.ctype) {
+ case CTSTR:
+ if(et != TSTRUCT)
+ yyerror("interface method cannot have annotation");
+ note = n->val.u.sval;
+ break;
+ default:
+ if(et != TSTRUCT)
+ yyerror("interface method cannot have annotation");
+ else
+ yyerror("field annotation must be string");
+ case CTxxx:
+ note = nil;
+ break;
+ }
+
+ if(et == TINTER && left == N) {
+ // embedded interface - inline the methods
+ if(n->type->etype != TINTER) {
+ if(n->type->etype == TFORW)
+ yyerror("interface type loop involving %T", n->type);
+ else
+ yyerror("interface contains embedded non-interface %T", n->type);
+ continue;
+ }
+ for(t1=n->type->type; t1!=T; t1=t1->down) {
+ f = typ(TFIELD);
+ f->type = t1->type;
+ f->width = BADWIDTH;
+ f->nname = newname(t1->sym);
+ f->sym = t1->sym;
+ for(t2=*t0; t2!=T; t2=t2->down) {
+ if(t2->sym == f->sym) {
+ yyerror("duplicate method %s", t2->sym->name);
+ break;
+ }
+ }
+ *t = f;
+ t = &f->down;
+ }
+ continue;
+ }
+
+ f = typ(TFIELD);
+ f->type = n->type;
+ f->note = note;
+ f->width = BADWIDTH;
+ f->isddd = n->isddd;
+
+ if(left != N && left->op == ONAME) {
+ f->nname = left;
+ f->embedded = n->embedded;
+ f->sym = f->nname->sym;
+ if(importpkg && !exportname(f->sym->name))
+ f->sym = pkglookup(f->sym->name, structpkg);
+ if(f->sym && !isblank(f->nname)) {
+ for(t1=*t0; t1!=T; t1=t1->down) {
+ if(t1->sym == f->sym) {
+ yyerror("duplicate %s %s", what, t1->sym->name);
+ break;
+ }
+ }
+ }
+ }
+
+ *t = f;
+ t = &f->down;
+ }
+
+ *t = T;
+ lineno = lno;
+ return t;
+}
+
+Type*
+dostruct(NodeList *l, int et)
+{
+ Type *t;
+ int funarg;
+
+ /*
+ * convert a parsed id/type list into
+ * a type for struct/interface/arglist
+ */
+
+ funarg = 0;
+ if(et == TFUNC) {
+ funarg = 1;
+ et = TSTRUCT;
+ }
+ t = typ(et);
+ t->funarg = funarg;
+ stotype(l, et, &t->type, funarg);
+ if(t->type == T && l != nil) {
+ t->broke = 1;
+ return t;
+ }
+ if(et == TINTER)
+ t = sortinter(t);
+ if(!funarg)
+ checkwidth(t);
+ return t;
+}
+
+
+Node*
+embedded(Sym *s)
+{
+ Node *n;
+ char *name;
+
+ // Names sometimes have disambiguation junk
+ // appended after a center dot. Discard it when
+ // making the name for the embedded struct field.
+ enum { CenterDot = 0xB7 };
+ name = s->name;
+ if(utfrune(s->name, CenterDot)) {
+ name = strdup(s->name);
+ *utfrune(name, CenterDot) = 0;
+ }
+
+ n = newname(lookup(name));
+ n = nod(ODCLFIELD, n, oldname(s));
+ n->embedded = 1;
+ return n;
+}
+
+/*
+ * check that the list of declarations is either all anonymous or all named
+ */
+
+static Node*
+findtype(NodeList *l)
+{
+ for(; l; l=l->next)
+ if(l->n->op == OKEY)
+ return l->n->right;
+ return N;
+}
+
+NodeList*
+checkarglist(NodeList *all, int input)
+{
+ int named;
+ Node *n, *t, *nextt;
+ NodeList *l;
+
+ named = 0;
+ for(l=all; l; l=l->next) {
+ if(l->n->op == OKEY) {
+ named = 1;
+ break;
+ }
+ }
+ if(named) {
+ n = N;
+ for(l=all; l; l=l->next) {
+ n = l->n;
+ if(n->op != OKEY && n->sym == S) {
+ yyerror("mixed named and unnamed function parameters");
+ break;
+ }
+ }
+ if(l == nil && n != N && n->op != OKEY)
+ yyerror("final function parameter must have type");
+ }
+
+ nextt = nil;
+ for(l=all; l; l=l->next) {
+ // can cache result from findtype to avoid
+ // quadratic behavior here, but unlikely to matter.
+ n = l->n;
+ if(named) {
+ if(n->op == OKEY) {
+ t = n->right;
+ n = n->left;
+ nextt = nil;
+ } else {
+ if(nextt == nil)
+ nextt = findtype(l);
+ t = nextt;
+ }
+ } else {
+ t = n;
+ n = N;
+ }
+ if(n != N && n->sym == S) {
+ t = n;
+ n = N;
+ }
+ if(n != N)
+ n = newname(n->sym);
+ n = nod(ODCLFIELD, n, t);
+ if(n->right != N && n->right->op == ODDD) {
+ if(!input)
+ yyerror("cannot use ... in output argument list");
+ else if(l->next != nil)
+ yyerror("can only use ... as final argument in list");
+ n->right->op = OTARRAY;
+ n->right->right = n->right->left;
+ n->right->left = N;
+ n->isddd = 1;
+ if(n->left != N)
+ n->left->isddd = 1;
+ }
+ l->n = n;
+ }
+ return all;
+}
+
+
+Node*
+fakethis(void)
+{
+ Node *n;
+
+ n = nod(ODCLFIELD, N, typenod(ptrto(typ(TSTRUCT))));
+ return n;
+}
+
+/*
+ * Is this field a method on an interface?
+ * Those methods have an anonymous
+ * *struct{} as the receiver.
+ * (See fakethis above.)
+ */
+int
+isifacemethod(Type *f)
+{
+ Type *rcvr;
+ Type *t;
+
+ rcvr = getthisx(f)->type;
+ if(rcvr->sym != S)
+ return 0;
+ t = rcvr->type;
+ if(!isptr[t->etype])
+ return 0;
+ t = t->type;
+ if(t->sym != S || t->etype != TSTRUCT || t->type != T)
+ return 0;
+ return 1;
+}
+
+/*
+ * turn a parsed function declaration
+ * into a type
+ */
+Type*
+functype(Node *this, NodeList *in, NodeList *out)
+{
+ Type *t;
+ NodeList *rcvr;
+
+ t = typ(TFUNC);
+
+ rcvr = nil;
+ if(this)
+ rcvr = list1(this);
+ t->type = dostruct(rcvr, TFUNC);
+ t->type->down = dostruct(out, TFUNC);
+ t->type->down->down = dostruct(in, TFUNC);
+
+ if(this)
+ t->thistuple = 1;
+ t->outtuple = count(out);
+ t->intuple = count(in);
+ t->outnamed = t->outtuple > 0 && out->n->left != N;
+
+ return t;
+}
+
+Sym*
+methodsym(Sym *nsym, Type *t0, int iface)
+{
+ Sym *s;
+ char *p;
+ Type *t;
+ char *suffix;
+
+ t = t0;
+ if(t == T)
+ goto bad;
+ s = t->sym;
+ if(s == S) {
+ if(!isptr[t->etype])
+ goto bad;
+ t = t->type;
+ if(t == T)
+ goto bad;
+ s = t->sym;
+ if(s == S)
+ goto bad;
+ }
+
+ // if t0 == *t and t0 has a sym,
+ // we want to see *t, not t0, in the method name.
+ if(t != t0 && t0->sym)
+ t0 = ptrto(t);
+
+ suffix = "";
+ if(iface) {
+ dowidth(t0);
+ if(t0->width < types[tptr]->width)
+ suffix = "·i";
+ }
+ if(t0->sym == S && isptr[t0->etype])
+ p = smprint("(%#hT).%s%s", t0, nsym->name, suffix);
+ else
+ p = smprint("%#hT.%s%s", t0, nsym->name, suffix);
+ s = pkglookup(p, s->pkg);
+ free(p);
+ return s;
+
+bad:
+ yyerror("illegal receiver type: %T", t0);
+ return S;
+}
+
+Node*
+methodname(Node *n, Type *t)
+{
+ Sym *s;
+
+ s = methodsym(n->sym, t, 0);
+ if(s == S)
+ return n;
+ return newname(s);
+}
+
+Node*
+methodname1(Node *n, Node *t)
+{
+ char *star;
+ char *p;
+
+ star = nil;
+ if(t->op == OIND) {
+ star = "*";
+ t = t->left;
+ }
+ if(t->sym == S || isblank(n))
+ return newname(n->sym);
+ if(star)
+ p = smprint("(%s%S).%S", star, t->sym, n->sym);
+ else
+ p = smprint("%S.%S", t->sym, n->sym);
+ n = newname(pkglookup(p, t->sym->pkg));
+ free(p);
+ return n;
+}
+
+/*
+ * add a method, declared as a function,
+ * n is fieldname, pa is base type, t is function type
+ */
+void
+addmethod(Sym *sf, Type *t, int local)
+{
+ Type *f, *d, *pa;
+ Node *n;
+
+ pa = nil;
+
+ // get field sym
+ if(sf == S)
+ fatal("no method symbol");
+
+ // get parent type sym
+ pa = getthisx(t)->type; // ptr to this structure
+ if(pa == T) {
+ yyerror("missing receiver");
+ return;
+ }
+
+ pa = pa->type;
+ f = methtype(pa);
+ if(f == T) {
+ t = pa;
+ if(t != T) {
+ if(isptr[t->etype]) {
+ if(t->sym != S) {
+ yyerror("invalid receiver type %T (%T is a pointer type)", pa, t);
+ return;
+ }
+ t = t->type;
+ }
+ }
+ if(t != T) {
+ if(t->sym == S) {
+ yyerror("invalid receiver type %T (%T is an unnamed type)", pa, t);
+ return;
+ }
+ if(isptr[t->etype]) {
+ yyerror("invalid receiver type %T (%T is a pointer type)", pa, t);
+ return;
+ }
+ if(t->etype == TINTER) {
+ yyerror("invalid receiver type %T (%T is an interface type)", pa, t);
+ return;
+ }
+ }
+ // Should have picked off all the reasons above,
+ // but just in case, fall back to generic error.
+ yyerror("invalid receiver type %T", pa);
+ return;
+ }
+
+ pa = f;
+ if(importpkg && !exportname(sf->name))
+ sf = pkglookup(sf->name, importpkg);
+
+ n = nod(ODCLFIELD, newname(sf), N);
+ n->type = t;
+
+ d = T; // last found
+ for(f=pa->method; f!=T; f=f->down) {
+ d = f;
+ if(f->etype != TFIELD)
+ fatal("addmethod: not TFIELD: %N", f);
+ if(strcmp(sf->name, f->sym->name) != 0)
+ continue;
+ if(!eqtype(t, f->type))
+ yyerror("method redeclared: %T.%S\n\t%T\n\t%T", pa, sf, f->type, t);
+ return;
+ }
+
+ if(local && !pa->local) {
+ // defining method on non-local type.
+ yyerror("cannot define new methods on non-local type %T", pa);
+ return;
+ }
+
+ if(d == T)
+ stotype(list1(n), 0, &pa->method, 0);
+ else
+ stotype(list1(n), 0, &d->down, 0);
+ return;
+}
+
+void
+funccompile(Node *n, int isclosure)
+{
+ stksize = BADWIDTH;
+ maxarg = 0;
+
+ if(n->type == T) {
+ if(nerrors == 0)
+ fatal("funccompile missing type");
+ return;
+ }
+
+ // assign parameter offsets
+ checkwidth(n->type);
+
+ // record offset to actual frame pointer.
+ // for closure, have to skip over leading pointers and PC slot.
+ nodfp->xoffset = 0;
+ if(isclosure) {
+ NodeList *l;
+ for(l=n->nname->ntype->list; l; l=l->next) {
+ nodfp->xoffset += widthptr;
+ if(l->n->left == N) // found slot for PC
+ break;
+ }
+ }
+
+ if(curfn)
+ fatal("funccompile %S inside %S", n->nname->sym, curfn->nname->sym);
+
+ stksize = 0;
+ dclcontext = PAUTO;
+ funcdepth = n->funcdepth + 1;
+ compile(n);
+ curfn = nil;
+ funcdepth = 0;
+ dclcontext = PEXTERN;
+}
+
+
+
diff --git a/src/cmd/gc/doc.go b/src/cmd/gc/doc.go
new file mode 100644
index 000000000..3fe7fafdd
--- /dev/null
+++ b/src/cmd/gc/doc.go
@@ -0,0 +1,55 @@
+// Copyright 2009 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.
+
+/*
+
+Gc is the generic label for the family of Go compilers
+that function as part of the (modified) Plan 9 tool chain. The C compiler
+documentation at
+
+ http://plan9.bell-labs.com/sys/doc/comp.pdf (Tools overview)
+ http://plan9.bell-labs.com/sys/doc/compiler.pdf (C compiler architecture)
+
+gives the overall design of the tool chain. Aside from a few adapted pieces,
+such as the optimizer, the Go compilers are wholly new programs.
+
+The compiler reads in a set of Go files, typically suffixed ".go". They
+must all be part of one package. The output is a single intermediate file
+representing the "binary assembly" of the compiled package, ready as input
+for the linker (6l, etc.).
+
+The generated files contain type information about the symbols exported by
+the package and about types used by symbols imported by the package from
+other packages. It is therefore not necessary when compiling client C of
+package P to read the files of P's dependencies, only the compiled output
+of P.
+
+Usage:
+ 6g [flags] file...
+The specified files must be Go source files and all part of the same package.
+Substitute 6g with 8g or 5g where appropriate.
+
+Flags:
+ -o file
+ output file, default file.6 for 6g, etc.
+ -e
+ normally the compiler quits after 10 errors; -e prints all errors
+ -L
+ show entire file path when printing line numbers in errors
+ -I dir1 -I dir2
+ add dir1 and dir2 to the list of paths to check for imported packages
+ -N
+ disable optimization
+ -S
+ write assembly language text to standard output
+ -u
+ disallow importing packages not marked as safe
+ -V
+ print the compiler version
+
+There are also a number of debugging flags; run the command with no arguments
+to get a usage message.
+
+*/
+package documentation
diff --git a/src/cmd/gc/export.c b/src/cmd/gc/export.c
new file mode 100644
index 000000000..014f0c5f0
--- /dev/null
+++ b/src/cmd/gc/export.c
@@ -0,0 +1,428 @@
+// Copyright 2009 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.
+
+#include "go.h"
+#include "y.tab.h"
+
+static void dumpsym(Sym*);
+static void dumpexporttype(Sym*);
+static void dumpexportvar(Sym*);
+static void dumpexportconst(Sym*);
+
+void
+exportsym(Node *n)
+{
+ if(n == N || n->sym == S)
+ return;
+ if(n->sym->flags & (SymExport|SymPackage)) {
+ if(n->sym->flags & SymPackage)
+ yyerror("export/package mismatch: %S", n->sym);
+ return;
+ }
+ n->sym->flags |= SymExport;
+
+ exportlist = list(exportlist, n);
+}
+
+static void
+packagesym(Node *n)
+{
+ if(n == N || n->sym == S)
+ return;
+ if(n->sym->flags & (SymExport|SymPackage)) {
+ if(n->sym->flags & SymExport)
+ yyerror("export/package mismatch: %S", n->sym);
+ return;
+ }
+ n->sym->flags |= SymPackage;
+
+ exportlist = list(exportlist, n);
+}
+
+int
+exportname(char *s)
+{
+ Rune r;
+
+ if((uchar)s[0] < Runeself)
+ return 'A' <= s[0] && s[0] <= 'Z';
+ chartorune(&r, s);
+ return isupperrune(r);
+}
+
+static int
+initname(char *s)
+{
+ return strcmp(s, "init") == 0;
+}
+
+void
+autoexport(Node *n, int ctxt)
+{
+ if(n == N || n->sym == S)
+ return;
+ if((ctxt != PEXTERN && ctxt != PFUNC) || dclcontext != PEXTERN)
+ return;
+ if(n->ntype && n->ntype->op == OTFUNC && n->ntype->left) // method
+ return;
+ if(exportname(n->sym->name) || initname(n->sym->name))
+ exportsym(n);
+ else
+ packagesym(n);
+}
+
+static void
+dumppkg(Pkg *p)
+{
+ char *suffix;
+
+ if(p == nil || p == localpkg || p->exported)
+ return;
+ p->exported = 1;
+ suffix = "";
+ if(!p->direct)
+ suffix = " // indirect";
+ Bprint(bout, "\timport %s \"%Z\"%s\n", p->name, p->path, suffix);
+}
+
+static void
+dumpprereq(Type *t)
+{
+ if(t == T)
+ return;
+
+ if(t->printed || t == types[t->etype])
+ return;
+ t->printed = 1;
+
+ if(t->sym != S) {
+ dumppkg(t->sym->pkg);
+ if(t->etype != TFIELD)
+ dumpsym(t->sym);
+ }
+ dumpprereq(t->type);
+ dumpprereq(t->down);
+}
+
+static void
+dumpexportconst(Sym *s)
+{
+ Node *n;
+ Type *t;
+
+ n = s->def;
+ typecheck(&n, Erv);
+ if(n == N || n->op != OLITERAL)
+ fatal("dumpexportconst: oconst nil: %S", s);
+
+ t = n->type; // may or may not be specified
+ if(t != T)
+ dumpprereq(t);
+
+ Bprint(bout, "\t");
+ Bprint(bout, "const %#S", s);
+ if(t != T && !isideal(t))
+ Bprint(bout, " %#T", t);
+ Bprint(bout, " = ");
+
+ switch(n->val.ctype) {
+ default:
+ fatal("dumpexportconst: unknown ctype: %S %d", s, n->val.ctype);
+ case CTINT:
+ Bprint(bout, "%B\n", n->val.u.xval);
+ break;
+ case CTBOOL:
+ if(n->val.u.bval)
+ Bprint(bout, "true\n");
+ else
+ Bprint(bout, "false\n");
+ break;
+ case CTFLT:
+ Bprint(bout, "%F\n", n->val.u.fval);
+ break;
+ case CTCPLX:
+ Bprint(bout, "(%F+%F)\n", &n->val.u.cval->real, &n->val.u.cval->imag);
+ break;
+ case CTSTR:
+ Bprint(bout, "\"%Z\"\n", n->val.u.sval);
+ break;
+ }
+}
+
+static void
+dumpexportvar(Sym *s)
+{
+ Node *n;
+ Type *t;
+
+ n = s->def;
+ typecheck(&n, Erv);
+ if(n == N || n->type == T) {
+ yyerror("variable exported but not defined: %S", s);
+ return;
+ }
+
+ t = n->type;
+ dumpprereq(t);
+
+ Bprint(bout, "\t");
+ if(t->etype == TFUNC && n->class == PFUNC)
+ Bprint(bout, "func %#S %#hhT", s, t);
+ else
+ Bprint(bout, "var %#S %#T", s, t);
+ Bprint(bout, "\n");
+}
+
+static void
+dumpexporttype(Sym *s)
+{
+ Type *t;
+
+ t = s->def->type;
+ dumpprereq(t);
+ Bprint(bout, "\t");
+ switch (t->etype) {
+ case TFORW:
+ yyerror("export of incomplete type %T", t);
+ return;
+ }
+ if(Bprint(bout, "type %#T %l#T\n", t, t) < 0)
+ fatal("Bprint failed for %T", t);
+}
+
+static int
+methcmp(const void *va, const void *vb)
+{
+ Type *a, *b;
+
+ a = *(Type**)va;
+ b = *(Type**)vb;
+ return strcmp(a->sym->name, b->sym->name);
+}
+
+static void
+dumpsym(Sym *s)
+{
+ Type *f, *t;
+ Type **m;
+ int i, n;
+
+ if(s->flags & SymExported)
+ return;
+ s->flags |= SymExported;
+
+ if(s->def == N) {
+ yyerror("unknown export symbol: %S", s);
+ return;
+ }
+
+ dumppkg(s->pkg);
+
+ switch(s->def->op) {
+ default:
+ yyerror("unexpected export symbol: %O %S", s->def->op, s);
+ break;
+ case OLITERAL:
+ dumpexportconst(s);
+ break;
+ case OTYPE:
+ t = s->def->type;
+ n = 0;
+ for(f=t->method; f!=T; f=f->down) {
+ dumpprereq(f);
+ n++;
+ }
+ m = mal(n*sizeof m[0]);
+ i = 0;
+ for(f=t->method; f!=T; f=f->down)
+ m[i++] = f;
+ qsort(m, n, sizeof m[0], methcmp);
+
+ dumpexporttype(s);
+ for(i=0; i<n; i++) {
+ f = m[i];
+ Bprint(bout, "\tfunc (%#T) %hS %#hhT\n",
+ f->type->type->type, f->sym, f->type);
+ }
+ break;
+ case ONAME:
+ dumpexportvar(s);
+ break;
+ }
+}
+
+static void
+dumptype(Type *t)
+{
+ // no need to re-dump type if already exported
+ if(t->printed)
+ return;
+
+ // no need to dump type if it's not ours (was imported)
+ if(t->sym != S && t->sym->def == typenod(t) && !t->local)
+ return;
+
+ Bprint(bout, "type %#T %l#T\n", t, t);
+}
+
+void
+dumpexport(void)
+{
+ NodeList *l;
+ int32 i, lno;
+ Pkg *p;
+
+ lno = lineno;
+
+ packagequotes = 1;
+ Bprint(bout, "\n$$ // exports\n");
+
+ Bprint(bout, " package %s", localpkg->name);
+ if(safemode)
+ Bprint(bout, " safe");
+ Bprint(bout, "\n");
+
+ for(i=0; i<nelem(phash); i++)
+ for(p=phash[i]; p; p=p->link)
+ if(p->direct)
+ dumppkg(p);
+
+ for(l=exportlist; l; l=l->next) {
+ lineno = l->n->lineno;
+ dumpsym(l->n->sym);
+ }
+
+ Bprint(bout, "\n$$ // local types\n");
+
+ for(l=typelist; l; l=l->next) {
+ lineno = l->n->lineno;
+ dumptype(l->n->type);
+ }
+
+ Bprint(bout, "\n$$\n");
+ packagequotes = 0;
+
+ lineno = lno;
+}
+
+/*
+ * import
+ */
+
+/*
+ * return the sym for ss, which should match lexical
+ */
+Sym*
+importsym(Sym *s, int op)
+{
+ if(s->def != N && s->def->op != op)
+ redeclare(s, "during import");
+
+ // mark the symbol so it is not reexported
+ if(s->def == N) {
+ if(exportname(s->name) || initname(s->name))
+ s->flags |= SymExport;
+ else
+ s->flags |= SymPackage; // package scope
+ }
+ return s;
+}
+
+/*
+ * return the type pkg.name, forward declaring if needed
+ */
+Type*
+pkgtype(Sym *s)
+{
+ Type *t;
+
+ importsym(s, OTYPE);
+ if(s->def == N || s->def->op != OTYPE) {
+ t = typ(TFORW);
+ t->sym = s;
+ s->def = typenod(t);
+ }
+ if(s->def->type == T)
+ yyerror("pkgtype %lS", s);
+ return s->def->type;
+}
+
+static int
+mypackage(Sym *s)
+{
+ // we import all definitions for runtime.
+ // lowercase ones can only be used by the compiler.
+ return s->pkg == localpkg || s->pkg == runtimepkg;
+}
+
+void
+importconst(Sym *s, Type *t, Node *n)
+{
+ Node *n1;
+
+ if(!exportname(s->name) && !mypackage(s))
+ return;
+ importsym(s, OLITERAL);
+ convlit(&n, t);
+ if(s->def != N) {
+ // TODO: check if already the same.
+ return;
+ }
+
+ if(n->op != OLITERAL) {
+ yyerror("expression must be a constant");
+ return;
+ }
+ if(n->sym != S) {
+ n1 = nod(OXXX, N, N);
+ *n1 = *n;
+ n = n1;
+ }
+ n->sym = s;
+ declare(n, PEXTERN);
+
+ if(debug['E'])
+ print("import const %S\n", s);
+}
+
+void
+importvar(Sym *s, Type *t, int ctxt)
+{
+ Node *n;
+
+ if(!exportname(s->name) && !initname(s->name) && !mypackage(s))
+ return;
+
+ importsym(s, ONAME);
+ if(s->def != N && s->def->op == ONAME) {
+ if(eqtype(t, s->def->type))
+ return;
+ yyerror("inconsistent definition for var %S during import\n\t%T\n\t%T",
+ s, s->def->type, t);
+ }
+ n = newname(s);
+ n->type = t;
+ declare(n, ctxt);
+
+ if(debug['E'])
+ print("import var %S %lT\n", s, t);
+}
+
+void
+importtype(Type *pt, Type *t)
+{
+ if(pt != T && t != T)
+ typedcl2(pt, t);
+
+ if(debug['E'])
+ print("import type %T %lT\n", pt, t);
+}
+
+void
+importmethod(Sym *s, Type *t)
+{
+ checkwidth(t);
+ addmethod(s, t, 0);
+}
+
diff --git a/src/cmd/gc/gen.c b/src/cmd/gc/gen.c
new file mode 100644
index 000000000..cb66921ba
--- /dev/null
+++ b/src/cmd/gc/gen.c
@@ -0,0 +1,790 @@
+// Copyright 2009 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.
+
+/*
+ * portable half of code generator.
+ * mainly statements and control flow.
+ */
+
+#include "go.h"
+
+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)
+{
+ Node *n;
+
+ n = newname(pkglookup(name, runtimepkg));
+ n->class = PFUNC;
+ return n;
+}
+
+void
+allocparams(void)
+{
+ NodeList *l;
+ Node *n;
+ uint32 w;
+ Sym *s;
+ int lno;
+
+ if(stksize < 0)
+ fatal("allocparams not during code generation");
+
+ /*
+ * allocate (set xoffset) the stack
+ * slots for all automatics.
+ * allocated starting at -w down.
+ */
+ lno = lineno;
+ for(l=curfn->dcl; l; l=l->next) {
+ n = l->n;
+ if(n->op == ONAME && n->class == PHEAP-1) {
+ // heap address variable; finish the job
+ // started in addrescapes.
+ s = n->sym;
+ tempname(n, n->type);
+ n->sym = s;
+ }
+ if(n->op != ONAME || n->class != PAUTO)
+ continue;
+ if (n->xoffset != BADWIDTH)
+ continue;
+ if(n->type == T)
+ continue;
+ dowidth(n->type);
+ w = n->type->width;
+ if(w >= MAXWIDTH)
+ fatal("bad width");
+ stksize += w;
+ stksize = rnd(stksize, n->type->align);
+ if(thechar == '5')
+ stksize = rnd(stksize, widthptr);
+ n->xoffset = -stksize;
+ }
+ lineno = lno;
+}
+
+void
+clearlabels(void)
+{
+ Label *l;
+
+ for(l=labellist; l!=L; l=l->link)
+ l->sym->label = L;
+
+ labellist = L;
+ lastlabel = L;
+}
+
+static Label*
+newlab(Node *n)
+{
+ Sym *s;
+ 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;
+ }
+
+ 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 *lab;
+ NodeList *l;
+
+ 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);
+ }
+}
+
+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;
+ }
+}
+
+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;
+}
+
+/*
+ * compile statements
+ */
+void
+genlist(NodeList *l)
+{
+ for(; l; l=l->next)
+ gen(l->n);
+}
+
+void
+gen(Node *n)
+{
+ int32 lno;
+ Prog *scontin, *sbreak;
+ Prog *p1, *p2, *p3;
+ Label *lab;
+ int32 wasregalloc;
+
+ lno = setlineno(n);
+ wasregalloc = anyregalloc();
+
+ if(n == N)
+ goto ret;
+
+ p3 = pc; // save pc for loop labels
+ if(n->ninit)
+ genlist(n->ninit);
+
+ setlineno(n);
+
+ switch(n->op) {
+ default:
+ fatal("gen: unknown op %N", n);
+ break;
+
+ case OCASE:
+ case OFALL:
+ case OXCASE:
+ case OXFALL:
+ case ODCLCONST:
+ case ODCLFUNC:
+ case ODCLTYPE:
+ break;
+
+ case OEMPTY:
+ break;
+
+ case OBLOCK:
+ genlist(n->list);
+ break;
+
+ case OLABEL:
+ 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:
+ // 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:
+ if(n->left != N) {
+ 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) {
+ yyerror("break is not in a loop");
+ break;
+ }
+ gjmp(breakpc);
+ break;
+
+ case OCONTINUE:
+ if(n->left != N) {
+ 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);
+ break;
+
+ case OFOR:
+ sbreak = breakpc;
+ p1 = gjmp(P); // goto test
+ breakpc = gjmp(P); // break: goto done
+ scontin = continpc;
+ continpc = pc;
+
+ // define break and continue labels
+ if((lab = stmtlabel(n)) != L) {
+ lab->breakpc = breakpc;
+ lab->continpc = continpc;
+ }
+ gen(n->nincr); // contin: incr
+ patch(p1, pc); // test:
+ bgen(n->ntest, 0, breakpc); // if(!test) goto break
+ genlist(n->nbody); // body
+ gjmp(continpc);
+ patch(breakpc, pc); // done:
+ continpc = scontin;
+ breakpc = sbreak;
+ if(lab) {
+ lab->breakpc = P;
+ lab->continpc = P;
+ }
+ break;
+
+ case OIF:
+ p1 = gjmp(P); // goto test
+ p2 = gjmp(P); // p2: goto else
+ patch(p1, pc); // test:
+ bgen(n->ntest, 0, p2); // if(!test) goto p2
+ genlist(n->nbody); // then
+ p3 = gjmp(P); // goto done
+ patch(p2, pc); // else:
+ genlist(n->nelse); // else
+ patch(p3, pc); // done:
+ break;
+
+ case OSWITCH:
+ sbreak = breakpc;
+ p1 = gjmp(P); // goto test
+ breakpc = gjmp(P); // break: goto done
+
+ // define break label
+ if((lab = stmtlabel(n)) != L)
+ lab->breakpc = breakpc;
+
+ 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:
+ sbreak = breakpc;
+ p1 = gjmp(P); // goto test
+ breakpc = gjmp(P); // break: goto done
+
+ // define break label
+ if((lab = stmtlabel(n)) != L)
+ lab->breakpc = breakpc;
+
+ patch(p1, pc); // test:
+ genlist(n->nbody); // select() body
+ patch(breakpc, pc); // done:
+ breakpc = sbreak;
+ if(lab != L)
+ lab->breakpc = P;
+ break;
+
+ case OASOP:
+ cgen_asop(n);
+ break;
+
+ case ODCL:
+ cgen_dcl(n->left);
+ break;
+
+ case OAS:
+ if(gen_as_init(n))
+ break;
+ cgen_as(n->left, n->right);
+ break;
+
+ case OCALLMETH:
+ cgen_callmeth(n, 0);
+ break;
+
+ case OCALLINTER:
+ cgen_callinter(n, N, 0);
+ break;
+
+ case OCALLFUNC:
+ cgen_call(n, 0);
+ break;
+
+ case OPROC:
+ cgen_proc(n, 1);
+ break;
+
+ case ODEFER:
+ cgen_proc(n, 2);
+ break;
+
+ case ORETURN:
+ cgen_ret(n);
+ break;
+ }
+
+ret:
+ if(anyregalloc() != wasregalloc) {
+ dump("node", n);
+ fatal("registers left allocated");
+ }
+
+ lineno = lno;
+}
+
+/*
+ * generate call to non-interface method
+ * proc=0 normal call
+ * proc=1 goroutine run in new proc
+ * proc=2 defer call save away stack
+ */
+void
+cgen_callmeth(Node *n, int proc)
+{
+ Node *l;
+
+ // generate a rewrite for method call
+ // (p.f)(...) goes to (f)(p,...)
+
+ l = n->left;
+ if(l->op != ODOTMETH)
+ fatal("cgen_callmeth: not dotmethod: %N");
+
+ n->op = OCALLFUNC;
+ n->left = n->left->right;
+ n->left->type = l->type;
+
+ if(n->left->op == ONAME)
+ n->left->class = PFUNC;
+ cgen_call(n, proc);
+}
+
+/*
+ * generate code to start new proc running call n.
+ */
+void
+cgen_proc(Node *n, int proc)
+{
+ switch(n->left->op) {
+ default:
+ fatal("cgen_proc: unknown call %O", n->left->op);
+
+ case OCALLMETH:
+ cgen_callmeth(n->left, proc);
+ break;
+
+ case OCALLINTER:
+ cgen_callinter(n->left, N, proc);
+ break;
+
+ case OCALLFUNC:
+ cgen_call(n->left, proc);
+ break;
+ }
+
+}
+
+/*
+ * generate declaration.
+ * nothing to do for on-stack automatics,
+ * but might have to allocate heap copy
+ * for escaped variables.
+ */
+static void
+cgen_dcl(Node *n)
+{
+ if(debug['g'])
+ dump("\ncgen-dcl", n);
+ if(n->op != ONAME) {
+ dump("cgen_dcl", n);
+ fatal("cgen_dcl");
+ }
+ if(!(n->class & PHEAP))
+ return;
+ if(n->alloc == nil)
+ n->alloc = callnew(n->type);
+ cgen_as(n->heapaddr, n->alloc);
+}
+
+/*
+ * generate discard of value
+ */
+static void
+cgen_discard(Node *nr)
+{
+ Node tmp;
+
+ if(nr == N)
+ return;
+
+ switch(nr->op) {
+ case ONAME:
+ if(!(nr->class & PHEAP) && nr->class != PEXTERN && nr->class != PFUNC && nr->class != PPARAMREF)
+ gused(nr);
+ break;
+
+ // unary
+ case OADD:
+ case OAND:
+ case ODIV:
+ case OEQ:
+ case OGE:
+ case OGT:
+ case OLE:
+ case OLSH:
+ case OLT:
+ case OMOD:
+ case OMUL:
+ case ONE:
+ case OOR:
+ case ORSH:
+ case OSUB:
+ case OXOR:
+ cgen_discard(nr->left);
+ cgen_discard(nr->right);
+ break;
+
+ // binary
+ case OCAP:
+ case OCOM:
+ case OLEN:
+ case OMINUS:
+ case ONOT:
+ case OPLUS:
+ cgen_discard(nr->left);
+ break;
+
+ // special enough to just evaluate
+ default:
+ tempname(&tmp, nr->type);
+ cgen_as(&tmp, nr);
+ gused(&tmp);
+ }
+}
+
+/*
+ * generate assignment:
+ * nl = nr
+ * nr == N means zero nl.
+ */
+void
+cgen_as(Node *nl, Node *nr)
+{
+ Node nc;
+ Type *tl;
+ int iszer;
+
+ if(nl == N)
+ return;
+
+ if(debug['g']) {
+ dump("cgen_as", nl);
+ dump("cgen_as = ", nr);
+ }
+
+ if(isblank(nl)) {
+ cgen_discard(nr);
+ return;
+ }
+
+ iszer = 0;
+ if(nr == N || isnil(nr)) {
+ // externals and heaps should already be clear
+ if(nr == N) {
+ if(nl->class == PEXTERN)
+ return;
+ if(nl->class & PHEAP)
+ return;
+ }
+
+ tl = nl->type;
+ if(tl == T)
+ return;
+ if(isfat(tl)) {
+ clearfat(nl);
+ goto ret;
+ }
+
+ /* invent a "zero" for the rhs */
+ iszer = 1;
+ nr = &nc;
+ memset(nr, 0, sizeof(*nr));
+ switch(simtype[tl->etype]) {
+ default:
+ fatal("cgen_as: tl %T", tl);
+ break;
+
+ case TINT8:
+ case TUINT8:
+ case TINT16:
+ case TUINT16:
+ case TINT32:
+ case TUINT32:
+ case TINT64:
+ case TUINT64:
+ nr->val.u.xval = mal(sizeof(*nr->val.u.xval));
+ mpmovecfix(nr->val.u.xval, 0);
+ nr->val.ctype = CTINT;
+ break;
+
+ case TFLOAT32:
+ case TFLOAT64:
+ nr->val.u.fval = mal(sizeof(*nr->val.u.fval));
+ mpmovecflt(nr->val.u.fval, 0.0);
+ nr->val.ctype = CTFLT;
+ break;
+
+ case TBOOL:
+ nr->val.u.bval = 0;
+ nr->val.ctype = CTBOOL;
+ break;
+
+ case TPTR32:
+ case TPTR64:
+ nr->val.ctype = CTNIL;
+ break;
+
+ case TCOMPLEX64:
+ case TCOMPLEX128:
+ nr->val.u.cval = mal(sizeof(*nr->val.u.cval));
+ mpmovecflt(&nr->val.u.cval->real, 0.0);
+ mpmovecflt(&nr->val.u.cval->imag, 0.0);
+ break;
+ }
+ nr->op = OLITERAL;
+ nr->type = tl;
+ nr->addable = 1;
+ ullmancalc(nr);
+ }
+
+ tl = nl->type;
+ if(tl == T)
+ return;
+
+ cgen(nr, nl);
+ if(iszer && nl->addable)
+ gused(nl);
+
+ret:
+ ;
+}
+
+/*
+ * gather series of offsets
+ * >=0 is direct addressed field
+ * <0 is pointer to next field (+1)
+ */
+int
+dotoffset(Node *n, int *oary, Node **nn)
+{
+ int i;
+
+ switch(n->op) {
+ case ODOT:
+ if(n->xoffset == BADWIDTH) {
+ dump("bad width in dotoffset", n);
+ fatal("bad width in dotoffset");
+ }
+ i = dotoffset(n->left, oary, nn);
+ if(i > 0) {
+ if(oary[i-1] >= 0)
+ oary[i-1] += n->xoffset;
+ else
+ oary[i-1] -= n->xoffset;
+ break;
+ }
+ if(i < 10)
+ oary[i++] = n->xoffset;
+ break;
+
+ case ODOTPTR:
+ if(n->xoffset == BADWIDTH) {
+ dump("bad width in dotoffset", n);
+ fatal("bad width in dotoffset");
+ }
+ i = dotoffset(n->left, oary, nn);
+ if(i < 10)
+ oary[i++] = -(n->xoffset+1);
+ break;
+
+ default:
+ *nn = n;
+ return 0;
+ }
+ if(i >= 10)
+ *nn = N;
+ return i;
+}
+
+/*
+ * make a new off the books
+ */
+void
+tempname(Node *nn, Type *t)
+{
+ Node *n;
+ Sym *s;
+ uint32 w;
+
+ if(stksize < 0)
+ fatal("tempname not during code generation");
+
+ if (curfn == N)
+ fatal("no curfn for tempname");
+
+ if(t == T) {
+ yyerror("tempname called with nil type");
+ t = types[TINT32];
+ }
+
+ // give each tmp a different name so that there
+ // a chance to registerizer them
+ snprint(namebuf, sizeof(namebuf), "autotmp_%.4d", statuniqgen);
+ statuniqgen++;
+ s = lookup(namebuf);
+ n = nod(ONAME, N, N);
+ n->sym = s;
+ n->type = t;
+ n->class = PAUTO;
+ n->addable = 1;
+ n->ullman = 1;
+ n->noescape = 1;
+ n->curfn = curfn;
+ curfn->dcl = list(curfn->dcl, n);
+
+ dowidth(t);
+ w = t->width;
+ stksize += w;
+ stksize = rnd(stksize, t->align);
+ if(thechar == '5')
+ stksize = rnd(stksize, widthptr);
+ n->xoffset = -stksize;
+
+ // print("\ttmpname (%d): %N\n", stksize, n);
+
+ *nn = *n;
+}
diff --git a/src/cmd/gc/go.errors b/src/cmd/gc/go.errors
new file mode 100644
index 000000000..b5af4678c
--- /dev/null
+++ b/src/cmd/gc/go.errors
@@ -0,0 +1,70 @@
+// Copyright 2010 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.
+
+// Example-based syntax error messages.
+// See bisonerrors, Makefile, go.y.
+
+static struct {
+ int yystate;
+ int yychar;
+ char *msg;
+} yymsg[] = {
+ // Each line of the form % token list
+ // is converted by bisonerrors into the yystate and yychar caused
+ // by that token list.
+
+ % loadsys package LIMPORT '(' LLITERAL import_package import_there ','
+ "unexpected comma during import block",
+
+ % loadsys package imports LFUNC LNAME '(' ')' '{' LIF if_header ';'
+ "unexpected semicolon or newline before {",
+
+ % loadsys package imports LFUNC LNAME '(' ')' '{' LSWITCH if_header ';'
+ "unexpected semicolon or newline before {",
+
+ % loadsys package imports LFUNC LNAME '(' ')' '{' LFOR for_header ';'
+ "unexpected semicolon or newline before {",
+
+ % loadsys package imports LFUNC LNAME '(' ')' '{' LFOR ';' LBODY
+ "unexpected semicolon or newline before {",
+
+ % loadsys package imports LFUNC LNAME '(' ')' ';' '{'
+ "unexpected semicolon or newline before {",
+
+ % loadsys package imports LTYPE LNAME ';'
+ "unexpected semicolon or newline in type declaration",
+
+ % loadsys package imports LCHAN '}'
+ "unexpected } in channel type",
+
+ % loadsys package imports LCHAN ')'
+ "unexpected ) in channel type",
+
+ % loadsys package imports LCHAN ','
+ "unexpected comma in channel type",
+
+ % loadsys package imports LFUNC LNAME '(' ')' '{' if_stmt ';' LELSE
+ "unexpected semicolon or newline before else",
+
+ % loadsys package imports LTYPE LNAME LINTERFACE '{' LNAME ',' LNAME
+ "name list not allowed in interface type",
+
+ % loadsys package imports LFUNC LNAME '(' ')' '{' LFOR LVAR LNAME '=' LNAME
+ "var declaration not allowed in for initializer",
+
+ % loadsys package imports LVAR LNAME '[' ']' LNAME '{'
+ "unexpected { at end of statement",
+
+ % loadsys package imports LFUNC LNAME '(' ')' '{' LVAR LNAME '[' ']' LNAME '{'
+ "unexpected { at end of statement",
+
+ % loadsys package imports LFUNC LNAME '(' ')' '{' LDEFER LNAME ';'
+ "argument to go/defer must be function call",
+
+ % loadsys package imports LVAR LNAME '=' LNAME '{' LNAME ';'
+ "need trailing comma before newline in composite literal",
+
+ % loadsys package imports LFUNC LNAME '(' ')' '{' LFUNC LNAME
+ "nested func not allowed",
+};
diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h
new file mode 100644
index 000000000..da0fb5146
--- /dev/null
+++ b/src/cmd/gc/go.h
@@ -0,0 +1,1279 @@
+// Copyright 2009 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.
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+#undef OAPPEND
+
+// avoid <ctype.h>
+#undef isblank
+#define isblank goisblank
+
+#ifndef EXTERN
+#define EXTERN extern
+#endif
+
+#undef BUFSIZ
+
+enum
+{
+ NHUNK = 50000,
+ BUFSIZ = 8192,
+ NSYMB = 500,
+ NHASH = 1024,
+ STRINGSZ = 200,
+ YYMAXDEPTH = 500,
+ MAXALIGN = 7,
+ UINF = 100,
+ HISTSZ = 10,
+
+ PRIME1 = 3,
+
+ AUNK = 100,
+
+ // these values are known by runtime
+ AMEM = 0,
+ ANOEQ,
+ ASTRING,
+ AINTER,
+ ANILINTER,
+ ASLICE,
+ AMEM8,
+ AMEM16,
+ AMEM32,
+ AMEM64,
+ AMEM128,
+ ANOEQ8,
+ ANOEQ16,
+ ANOEQ32,
+ ANOEQ64,
+ ANOEQ128,
+
+ BADWIDTH = -1000000000,
+};
+
+extern vlong MAXWIDTH;
+
+/*
+ * note this is the representation
+ * of the compilers string literals,
+ * it is not the runtime representation
+ */
+typedef struct Strlit Strlit;
+struct Strlit
+{
+ int32 len;
+ char s[3]; // variable
+};
+
+/*
+ * note this is the runtime representation
+ * of hashmap iterator. it is probably
+ * insafe to use it this way, but it puts
+ * all the changes in one place.
+ * only flag is referenced from go.
+ * actual placement does not matter as long
+ * as the size is >= actual size.
+ */
+typedef struct Hiter Hiter;
+struct Hiter
+{
+ uchar data[8]; // return val from next
+ int32 elemsize; // size of elements in table */
+ int32 changes; // number of changes observed last time */
+ int32 i; // stack pointer in subtable_state */
+ uchar last[8]; // last hash value returned */
+ uchar h[8]; // the hash table */
+ struct
+ {
+ uchar sub[8]; // pointer into subtable */
+ uchar start[8]; // pointer into start of subtable */
+ uchar end[8]; // pointer into end of subtable */
+ uchar pad[8];
+ } sub[4];
+};
+
+enum
+{
+ Mpscale = 29, // safely smaller than bits in a long
+ Mpprec = 16, // Mpscale*Mpprec is max number of bits
+ Mpnorm = Mpprec - 1, // significant words in a normalized float
+ Mpbase = 1L << Mpscale,
+ Mpsign = Mpbase >> 1,
+ Mpmask = Mpbase - 1,
+ Mpdebug = 0,
+};
+
+typedef struct Mpint Mpint;
+struct Mpint
+{
+ long a[Mpprec];
+ uchar neg;
+ uchar ovf;
+};
+
+typedef struct Mpflt Mpflt;
+struct Mpflt
+{
+ Mpint val;
+ short exp;
+};
+
+typedef struct Mpcplx Mpcplx;
+struct Mpcplx
+{
+ Mpflt real;
+ Mpflt imag;
+};
+
+typedef struct Val Val;
+struct Val
+{
+ short ctype;
+ union
+ {
+ short reg; // OREGISTER
+ short bval; // bool value CTBOOL
+ Mpint* xval; // int CTINT
+ Mpflt* fval; // float CTFLT
+ Mpcplx* cval; // float CTCPLX
+ Strlit* sval; // string CTSTR
+ } u;
+};
+
+typedef struct Pkg Pkg;
+typedef struct Sym Sym;
+typedef struct Node Node;
+typedef struct NodeList NodeList;
+typedef struct Type Type;
+typedef struct Label Label;
+
+struct Type
+{
+ uchar etype;
+ uchar chan;
+ uchar recur; // to detect loops
+ uchar trecur; // to detect loops
+ uchar printed;
+ uchar embedded; // TFIELD embedded type
+ uchar siggen;
+ uchar funarg;
+ uchar copyany;
+ uchar local; // created in this file
+ uchar deferwidth;
+ uchar broke;
+ uchar isddd; // TFIELD is ... argument
+ uchar align;
+
+ Node* nod; // canonical OTYPE node
+ Type* orig; // original type (type literal or predefined type)
+ int lineno;
+
+ // TFUNCT
+ uchar thistuple;
+ uchar outtuple;
+ uchar intuple;
+ uchar outnamed;
+
+ Type* method;
+ Type* xmethod;
+
+ Sym* sym;
+ int32 vargen; // unique name for OTYPE/ONAME
+
+ Node* nname;
+ vlong argwid;
+
+ // most nodes
+ Type* type;
+ vlong width; // offset in TFIELD, width in all others
+
+ // TFIELD
+ Type* down; // also used in TMAP
+ Strlit* note; // literal string annotation
+
+ // TARRAY
+ int32 bound; // negative is dynamic array
+
+ int32 maplineno; // first use of TFORW as map key
+ int32 embedlineno; // first use of TFORW as embedded type
+};
+#define T ((Type*)0)
+
+struct Node
+{
+ uchar op;
+ uchar ullman; // sethi/ullman number
+ uchar addable; // type of addressability - 0 is not addressable
+ uchar trecur; // to detect loops
+ uchar etype; // op for OASOP, etype for OTYPE, exclam for export
+ uchar class; // PPARAM, PAUTO, PEXTERN, etc
+ uchar method; // OCALLMETH name
+ uchar embedded; // ODCLFIELD embedded type
+ uchar colas; // OAS resulting from :=
+ uchar diag; // already printed error about this
+ uchar noescape; // ONAME never move to heap
+ uchar funcdepth;
+ uchar builtin; // built-in name, like len or close
+ uchar walkdef;
+ uchar typecheck;
+ uchar local;
+ uchar initorder;
+ uchar dodata; // compile literal assignment as data statement
+ uchar used;
+ uchar isddd;
+ uchar pun; // don't registerize variable ONAME
+ uchar readonly;
+ uchar implicit; // don't show in printout
+
+ // most nodes
+ Node* left;
+ Node* right;
+ Type* type;
+ Type* realtype; // as determined by typecheck
+ NodeList* list;
+ NodeList* rlist;
+ Node* orig; // original form, for printing, and tracking copies of ONAMEs
+
+ // for-body
+ NodeList* ninit;
+ Node* ntest;
+ Node* nincr;
+ NodeList* nbody;
+
+ // if-body
+ NodeList* nelse;
+
+ // cases
+ Node* ncase;
+
+ // func
+ Node* nname;
+ Node* shortname;
+ NodeList* enter;
+ NodeList* exit;
+ NodeList* cvars; // closure params
+ NodeList* dcl; // autodcl for this func/closure
+
+ // OLITERAL/OREGISTER
+ Val val;
+
+ // ONAME
+ Node* ntype;
+ Node* defn;
+ Node* pack; // real package for import . names
+ Node* curfn; // function for local variables
+
+ // ONAME func param with PHEAP
+ Node* heapaddr; // temp holding heap address of param
+ Node* stackparam; // OPARAM node referring to stack copy of param
+ Node* alloc; // allocation call
+
+ // ONAME closure param with PPARAMREF
+ Node* outer; // outer PPARAMREF in nested closure
+ Node* closure; // ONAME/PHEAP <-> ONAME/PPARAMREF
+
+ // OPACK
+ Pkg* pkg;
+
+ Sym* sym; // various
+ int32 vargen; // unique name for OTYPE/ONAME
+ int32 lineno;
+ int32 endlineno;
+ vlong xoffset;
+ int32 stkdelta; // offset added by stack frame compaction phase.
+ int32 ostk;
+ int32 iota;
+};
+#define N ((Node*)0)
+EXTERN int32 walkgen;
+
+struct NodeList
+{
+ Node* n;
+ NodeList* next;
+ NodeList* end;
+};
+
+enum
+{
+ SymExport = 1<<0,
+ SymPackage = 1<<1,
+ SymExported = 1<<2,
+ SymUniq = 1<<3,
+ SymSiggen = 1<<4,
+};
+
+struct Sym
+{
+ ushort lexical;
+ uchar flags;
+ uchar sym; // huffman encoding in object file
+ Sym* link;
+ int32 npkg; // number of imported packages with this name
+
+ // saved and restored by dcopy
+ 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;
+ Strlit* path;
+ Sym* pathsym;
+ char* prefix;
+ Pkg* link;
+ char exported; // import line written in export data
+ char direct; // imported directly
+};
+
+typedef struct Iter Iter;
+struct Iter
+{
+ int done;
+ Type* tfunc;
+ Type* t;
+ Node** an;
+ Node* n;
+};
+
+typedef struct Hist Hist;
+struct Hist
+{
+ Hist* link;
+ char* name;
+ int32 line;
+ int32 offset;
+};
+#define H ((Hist*)0)
+
+enum
+{
+ OXXX,
+
+ // names
+ ONAME,
+ ONONAME,
+ OTYPE,
+ OPACK,
+ OLITERAL,
+
+ // exprs
+ OADD, OSUB, OOR, OXOR, OADDSTR,
+ OADDR,
+ OANDAND,
+ OAPPEND,
+ OARRAY,
+ OARRAYBYTESTR, OARRAYRUNESTR,
+ OSTRARRAYBYTE, OSTRARRAYRUNE,
+ OAS, OAS2, OAS2MAPW, OAS2FUNC, OAS2RECV, OAS2MAPR, OAS2DOTTYPE, OASOP,
+ OBAD,
+ OCALL, OCALLFUNC, OCALLMETH, OCALLINTER,
+ OCAP,
+ OCLOSE,
+ OCLOSURE,
+ OCMPIFACE, OCMPSTR,
+ OCOMPLIT, OMAPLIT, OSTRUCTLIT, OARRAYLIT,
+ OCONV, OCONVIFACE, OCONVNOP,
+ OCOPY,
+ ODCL, ODCLFUNC, ODCLFIELD, ODCLCONST, ODCLTYPE,
+ ODOT, ODOTPTR, ODOTMETH, ODOTINTER, OXDOT,
+ ODOTTYPE,
+ ODOTTYPE2,
+ OEQ, ONE, OLT, OLE, OGE, OGT,
+ OIND,
+ OINDEX, OINDEXMAP,
+ OKEY, OPARAM,
+ OLEN,
+ OMAKE, OMAKECHAN, OMAKEMAP, OMAKESLICE,
+ OHMUL, ORRC, OLRC, // high-mul and rotate-carry
+ OMUL, ODIV, OMOD, OLSH, ORSH, OAND, OANDNOT,
+ ONEW,
+ ONOT, OCOM, OPLUS, OMINUS,
+ OOROR,
+ OPANIC, OPRINT, OPRINTN,
+ OPAREN,
+ OSEND,
+ OSLICE, OSLICEARR, OSLICESTR,
+ ORECOVER,
+ ORECV,
+ ORUNESTR,
+ OSELRECV,
+ OSELRECV2,
+ OIOTA,
+ OREAL, OIMAG, OCOMPLEX,
+
+ // stmts
+ OBLOCK,
+ OBREAK,
+ OCASE, OXCASE,
+ OCONTINUE,
+ ODEFER,
+ OEMPTY,
+ OFALL, OXFALL,
+ OFOR,
+ OGOTO,
+ OIF,
+ OLABEL,
+ OPROC,
+ ORANGE,
+ ORETURN,
+ OSELECT,
+ OSWITCH,
+ OTYPESW, // l = r.(type)
+
+ // types
+ OTCHAN,
+ OTMAP,
+ OTSTRUCT,
+ OTINTER,
+ OTFUNC,
+ OTARRAY,
+ OTPAREN,
+
+ // misc
+ ODDD,
+
+ // for back ends
+ OCMP, ODEC, OEXTEND, OINC, OREGISTER, OINDREG,
+
+ OEND,
+};
+enum
+{
+ Txxx, // 0
+
+ TINT8, TUINT8, // 1
+ TINT16, TUINT16,
+ TINT32, TUINT32,
+ TINT64, TUINT64,
+ TINT, TUINT, TUINTPTR,
+
+ TCOMPLEX64, // 12
+ TCOMPLEX128,
+
+ TFLOAT32, // 14
+ TFLOAT64,
+
+ TBOOL, // 16
+
+ TPTR32, TPTR64, // 17
+
+ TFUNC, // 19
+ TARRAY,
+ T_old_DARRAY,
+ TSTRUCT, // 22
+ TCHAN,
+ TMAP,
+ TINTER, // 25
+ TFORW,
+ TFIELD,
+ TANY,
+ TSTRING,
+ TUNSAFEPTR,
+
+ // pseudo-types for literals
+ TIDEAL, // 31
+ TNIL,
+ TBLANK,
+
+ // pseudo-type for frame layout
+ TFUNCARGS,
+ TCHANARGS,
+ TINTERMETH,
+
+ NTYPE,
+};
+enum
+{
+ CTxxx,
+
+ CTINT,
+ CTFLT,
+ CTCPLX,
+ CTSTR,
+ CTBOOL,
+ CTNIL,
+};
+
+enum
+{
+ /* types of channel */
+ /* must match ../../pkg/nreflect/type.go:/Chandir */
+ Cxxx,
+ Crecv = 1<<0,
+ Csend = 1<<1,
+ Cboth = Crecv | Csend,
+};
+
+enum
+{
+ Pxxx,
+
+ PEXTERN, // declaration context
+ PAUTO,
+ PPARAM,
+ PPARAMOUT,
+ PPARAMREF, // param passed by reference
+ PFUNC,
+
+ PHEAP = 1<<7,
+};
+
+enum
+{
+ Etop = 1<<1, // evaluated at statement level
+ Erv = 1<<2, // evaluated in value context
+ Etype = 1<<3,
+ Ecall = 1<<4, // call-only expressions are ok
+ Efnstruct = 1<<5, // multivalue function returns are ok
+ Eiota = 1<<6, // iota is ok
+ Easgn = 1<<7, // assigning to expression
+ Eindir = 1<<8, // indirecting through expression
+ Eaddr = 1<<9, // taking address of expression
+ Eproc = 1<<10, // inside a go statement
+ Ecomplit = 1<<11, // type in composite literal
+};
+
+#define BITS 5
+#define NVAR (BITS*sizeof(uint32)*8)
+
+typedef struct Bits Bits;
+struct Bits
+{
+ uint32 b[BITS];
+};
+
+EXTERN Bits zbits;
+
+typedef struct Var Var;
+struct Var
+{
+ vlong offset;
+ Sym* sym;
+ Sym* gotype;
+ Node* node;
+ int width;
+ char name;
+ char etype;
+ char addr;
+};
+
+EXTERN Var var[NVAR];
+
+typedef struct Typedef Typedef;
+struct Typedef
+{
+ char* name;
+ int etype;
+ int sameas;
+};
+
+extern Typedef typedefs[];
+
+typedef struct Sig Sig;
+struct Sig
+{
+ char* name;
+ Pkg* pkg;
+ Sym* isym;
+ Sym* tsym;
+ Type* type;
+ Type* mtype;
+ int32 offset;
+ Sig* link;
+};
+
+typedef struct Io Io;
+struct Io
+{
+ char* infile;
+ Biobuf* bin;
+ int32 ilineno;
+ int nlsemi;
+ int eofnl;
+ int peekc;
+ int peekc1; // second peekc for ...
+ char* cp; // used for content when bin==nil
+ int importsafe;
+};
+
+typedef struct Dlist Dlist;
+struct Dlist
+{
+ Type* field;
+};
+
+typedef struct Idir Idir;
+struct Idir
+{
+ Idir* link;
+ char* dir;
+};
+
+/*
+ * argument passing to/from
+ * smagic and umagic
+ */
+typedef struct Magic Magic;
+struct Magic
+{
+ int w; // input for both - width
+ int s; // output for both - shift
+ int bad; // output for both - unexpected failure
+
+ // magic multiplier for signed literal divisors
+ int64 sd; // input - literal divisor
+ int64 sm; // output - multiplier
+
+ // magic multiplier for unsigned literal divisors
+ uint64 ud; // input - literal divisor
+ uint64 um; // output - multiplier
+ int ua; // output - adder
+};
+
+typedef struct Prog Prog;
+
+struct Label
+{
+ uchar used;
+ Sym* sym;
+ 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
+};
+#define L ((Label*)0)
+
+/*
+ * note this is the runtime representation
+ * of the compilers arrays.
+ *
+ * typedef struct
+ * { // must not move anything
+ * uchar array[8]; // pointer to data
+ * uchar nel[4]; // number of elements
+ * uchar cap[4]; // allocated number of elements
+ * } Array;
+ */
+EXTERN int Array_array; // runtime offsetof(Array,array) - same for String
+EXTERN int Array_nel; // runtime offsetof(Array,nel) - same for String
+EXTERN int Array_cap; // runtime offsetof(Array,cap)
+EXTERN int sizeof_Array; // runtime sizeof(Array)
+
+
+/*
+ * note this is the runtime representation
+ * of the compilers strings.
+ *
+ * typedef struct
+ * { // must not move anything
+ * uchar array[8]; // pointer to data
+ * uchar nel[4]; // number of elements
+ * } String;
+ */
+EXTERN int sizeof_String; // runtime sizeof(String)
+
+EXTERN Dlist dotlist[10]; // size is max depth of embeddeds
+
+EXTERN Io curio;
+EXTERN Io pushedio;
+EXTERN int32 lexlineno;
+EXTERN int32 lineno;
+EXTERN int32 prevlineno;
+EXTERN char* pathname;
+EXTERN Hist* hist;
+EXTERN Hist* ehist;
+
+EXTERN char* infile;
+EXTERN char* outfile;
+EXTERN Biobuf* bout;
+EXTERN int nerrors;
+EXTERN int nsavederrors;
+EXTERN int nsyntaxerrors;
+EXTERN int safemode;
+EXTERN char namebuf[NSYMB];
+EXTERN char lexbuf[NSYMB];
+EXTERN char litbuf[NSYMB];
+EXTERN char debug[256];
+EXTERN Sym* hash[NHASH];
+EXTERN Sym* importmyname; // my name for package
+EXTERN Pkg* localpkg; // package being compiled
+EXTERN Pkg* importpkg; // package being imported
+EXTERN Pkg* structpkg; // package that declared struct, during import
+EXTERN Pkg* builtinpkg; // fake package for builtins
+EXTERN Pkg* gostringpkg; // fake pkg for Go strings
+EXTERN Pkg* runtimepkg; // package runtime
+EXTERN Pkg* stringpkg; // fake package for C strings
+EXTERN Pkg* typepkg; // fake package for runtime type info
+EXTERN Pkg* unsafepkg; // package unsafe
+EXTERN Pkg* phash[128];
+EXTERN int tptr; // either TPTR32 or TPTR64
+extern char* runtimeimport;
+extern char* unsafeimport;
+EXTERN Idir* idirs;
+
+EXTERN Type* types[NTYPE];
+EXTERN Type* idealstring;
+EXTERN Type* idealbool;
+EXTERN uchar simtype[NTYPE];
+EXTERN uchar isptr[NTYPE];
+EXTERN uchar isforw[NTYPE];
+EXTERN uchar isint[NTYPE];
+EXTERN uchar isfloat[NTYPE];
+EXTERN uchar iscomplex[NTYPE];
+EXTERN uchar issigned[NTYPE];
+EXTERN uchar issimple[NTYPE];
+
+EXTERN uchar okforeq[NTYPE];
+EXTERN uchar okforadd[NTYPE];
+EXTERN uchar okforand[NTYPE];
+EXTERN uchar okfornone[NTYPE];
+EXTERN uchar okforcmp[NTYPE];
+EXTERN uchar okforbool[NTYPE];
+EXTERN uchar okforcap[NTYPE];
+EXTERN uchar okforlen[NTYPE];
+EXTERN uchar okforarith[NTYPE];
+EXTERN uchar okforconst[NTYPE];
+EXTERN uchar* okfor[OEND];
+EXTERN uchar iscmp[OEND];
+
+EXTERN Mpint* minintval[NTYPE];
+EXTERN Mpint* maxintval[NTYPE];
+EXTERN Mpflt* minfltval[NTYPE];
+EXTERN Mpflt* maxfltval[NTYPE];
+
+EXTERN NodeList* xtop;
+EXTERN NodeList* externdcl;
+EXTERN NodeList* closures;
+EXTERN NodeList* exportlist;
+EXTERN NodeList* typelist;
+EXTERN int dclcontext; // PEXTERN/PAUTO
+EXTERN int incannedimport;
+EXTERN int statuniqgen; // name generator for static temps
+EXTERN int loophack;
+
+EXTERN int32 iota;
+EXTERN NodeList* lastconst;
+EXTERN Node* lasttype;
+EXTERN int32 maxarg;
+EXTERN int32 stksize; // stack size for current frame
+EXTERN int32 blockgen; // max block number
+EXTERN int32 block; // current block number
+EXTERN int hasdefer; // flag that curfn has defer statetment
+
+EXTERN Node* curfn;
+
+EXTERN int widthptr;
+
+EXTERN Node* typesw;
+EXTERN Node* nblank;
+
+extern int thechar;
+extern char* thestring;
+EXTERN char* hunk;
+EXTERN int32 nhunk;
+EXTERN int32 thunk;
+
+EXTERN int exporting;
+EXTERN int erroring;
+EXTERN int noargnames;
+
+EXTERN int funcdepth;
+EXTERN int typecheckok;
+EXTERN int packagequotes;
+EXTERN int longsymnames;
+EXTERN int compiling_runtime;
+
+/*
+ * y.tab.c
+ */
+int yyparse(void);
+
+/*
+ * align.c
+ */
+int argsize(Type *t);
+void checkwidth(Type *t);
+void defercheckwidth(void);
+void dowidth(Type *t);
+void resumecheckwidth(void);
+uint32 rnd(uint32 o, uint32 r);
+void typeinit(void);
+
+/*
+ * bits.c
+ */
+int Qconv(Fmt *fp);
+Bits band(Bits a, Bits b);
+int bany(Bits *a);
+int beq(Bits a, Bits b);
+int bitno(int32 b);
+Bits blsh(uint n);
+Bits bnot(Bits a);
+int bnum(Bits a);
+Bits bor(Bits a, Bits b);
+int bset(Bits a, uint n);
+
+/*
+ * closure.c
+ */
+Node* closurebody(NodeList *body);
+void closurehdr(Node *ntype);
+void typecheckclosure(Node *func, int top);
+Node* walkclosure(Node *func, NodeList **init);
+void walkcallclosure(Node *n, NodeList **init);
+
+/*
+ * const.c
+ */
+int cmpslit(Node *l, Node *r);
+int consttype(Node *n);
+void convconst(Node *con, Type *t, Val *val);
+void convlit(Node **np, Type *t);
+void convlit1(Node **np, Type *t, int explicit);
+void defaultlit(Node **np, Type *t);
+void defaultlit2(Node **lp, Node **rp, int force);
+void evconst(Node *n);
+int isconst(Node *n, int ct);
+Node* nodcplxlit(Val r, Val i);
+Node* nodlit(Val v);
+long nonnegconst(Node *n);
+void overflow(Val v, Type *t);
+int smallintconst(Node *n);
+Val toint(Val v);
+Mpflt* truncfltlit(Mpflt *oldv, Type *t);
+
+/*
+ * cplx.c
+ */
+void complexadd(int op, Node *nl, Node *nr, Node *res);
+void complexbool(int op, Node *nl, Node *nr, int true, Prog *to);
+void complexgen(Node *n, Node *res);
+void complexminus(Node *nl, Node *res);
+void complexmove(Node *f, Node *t);
+void complexmul(Node *nl, Node *nr, Node *res);
+int complexop(Node *n, Node *res);
+void nodfconst(Node *n, Type *t, Mpflt* fval);
+
+/*
+ * dcl.c
+ */
+void addmethod(Sym *sf, Type *t, int local);
+void addvar(Node *n, Type *t, int ctxt);
+NodeList* checkarglist(NodeList *all, int input);
+Node* colas(NodeList *left, NodeList *right);
+void colasdefn(NodeList *left, Node *defn);
+NodeList* constiter(NodeList *vl, Node *t, NodeList *cl);
+Node* dclname(Sym *s);
+void declare(Node *n, int ctxt);
+Type* dostruct(NodeList *l, int et);
+void dumpdcl(char *st);
+Node* embedded(Sym *s);
+Node* fakethis(void);
+void funcbody(Node *n);
+void funccompile(Node *n, int isclosure);
+void funchdr(Node *n);
+Type* functype(Node *this, NodeList *in, NodeList *out);
+void ifacedcl(Node *n);
+int isifacemethod(Type *f);
+void markdcl(void);
+Node* methodname(Node *n, Type *t);
+Node* methodname1(Node *n, Node *t);
+Sym* methodsym(Sym *nsym, Type *t0, int iface);
+Node* newname(Sym *s);
+Type* newtype(Sym *s);
+Node* oldname(Sym *s);
+void popdcl(void);
+void poptodcl(void);
+void redeclare(Sym *s, char *where);
+void testdclstack(void);
+Node* typedcl0(Sym *s);
+Node* typedcl1(Node *n, Node *t, int local);
+void typedcl2(Type *pt, Type *t);
+Node* typenod(Type *t);
+NodeList* variter(NodeList *vl, Node *t, NodeList *el);
+
+/*
+ * export.c
+ */
+void autoexport(Node *n, int ctxt);
+void dumpexport(void);
+int exportname(char *s);
+void exportsym(Node *n);
+void importconst(Sym *s, Type *t, Node *n);
+void importmethod(Sym *s, Type *t);
+Sym* importsym(Sym *s, int op);
+void importtype(Type *pt, Type *t);
+void importvar(Sym *s, Type *t, int ctxt);
+Type* pkgtype(Sym *s);
+
+/*
+ * gen.c
+ */
+void allocparams(void);
+void cgen_as(Node *nl, Node *nr);
+void cgen_callmeth(Node *n, int proc);
+void clearlabels(void);
+void checklabels(void);
+int dotoffset(Node *n, int *oary, Node **nn);
+void gen(Node *n);
+void genlist(NodeList *l);
+Node* sysfunc(char *name);
+void tempname(Node *n, Type *t);
+
+/*
+ * init.c
+ */
+void fninit(NodeList *n);
+Node* renameinit(Node *n);
+
+/*
+ * lex.c
+ */
+void cannedimports(char *file, char *cp);
+void importfile(Val *f, int line);
+char* lexname(int lex);
+void mkpackage(char* pkgname);
+void unimportfile(void);
+int32 yylex(void);
+extern int windows;
+extern int yylast;
+extern int yyprev;
+
+/*
+ * mparith1.c
+ */
+int Bconv(Fmt *fp);
+int Fconv(Fmt *fp);
+void mpaddcfix(Mpint *a, vlong c);
+void mpaddcflt(Mpflt *a, double c);
+void mpatofix(Mpint *a, char *as);
+void mpatoflt(Mpflt *a, char *as);
+int mpcmpfixc(Mpint *b, vlong c);
+int mpcmpfixfix(Mpint *a, Mpint *b);
+int mpcmpfixflt(Mpint *a, Mpflt *b);
+int mpcmpfltc(Mpflt *b, double c);
+int mpcmpfltfix(Mpflt *a, Mpint *b);
+int mpcmpfltflt(Mpflt *a, Mpflt *b);
+void mpcomfix(Mpint *a);
+void mpdivfixfix(Mpint *a, Mpint *b);
+void mpmodfixfix(Mpint *a, Mpint *b);
+void mpmovefixfix(Mpint *a, Mpint *b);
+void mpmovefixflt(Mpflt *a, Mpint *b);
+int mpmovefltfix(Mpint *a, Mpflt *b);
+void mpmovefltflt(Mpflt *a, Mpflt *b);
+void mpmulcfix(Mpint *a, vlong c);
+void mpmulcflt(Mpflt *a, double c);
+void mpsubfixfix(Mpint *a, Mpint *b);
+void mpsubfltflt(Mpflt *a, Mpflt *b);
+
+/*
+ * mparith2.c
+ */
+void mpaddfixfix(Mpint *a, Mpint *b);
+void mpandfixfix(Mpint *a, Mpint *b);
+void mpandnotfixfix(Mpint *a, Mpint *b);
+void mpdivfract(Mpint *a, Mpint *b);
+void mpdivmodfixfix(Mpint *q, Mpint *r, Mpint *n, Mpint *d);
+vlong mpgetfix(Mpint *a);
+void mplshfixfix(Mpint *a, Mpint *b);
+void mpmovecfix(Mpint *a, vlong c);
+void mpmulfixfix(Mpint *a, Mpint *b);
+void mpmulfract(Mpint *a, Mpint *b);
+void mpnegfix(Mpint *a);
+void mporfixfix(Mpint *a, Mpint *b);
+void mprshfixfix(Mpint *a, Mpint *b);
+void mpshiftfix(Mpint *a, int s);
+int mptestfix(Mpint *a);
+void mpxorfixfix(Mpint *a, Mpint *b);
+
+/*
+ * mparith3.c
+ */
+void mpaddfltflt(Mpflt *a, Mpflt *b);
+void mpdivfltflt(Mpflt *a, Mpflt *b);
+double mpgetflt(Mpflt *a);
+void mpmovecflt(Mpflt *a, double c);
+void mpmulfltflt(Mpflt *a, Mpflt *b);
+void mpnegflt(Mpflt *a);
+void mpnorm(Mpflt *a);
+int mptestflt(Mpflt *a);
+int sigfig(Mpflt *a);
+
+/*
+ * obj.c
+ */
+void Bputname(Biobuf *b, Sym *s);
+int duint16(Sym *s, int off, uint16 v);
+int duint32(Sym *s, int off, uint32 v);
+int duint64(Sym *s, int off, uint64 v);
+int duint8(Sym *s, int off, uint8 v);
+int duintptr(Sym *s, int off, uint64 v);
+int dsname(Sym *s, int off, char *dat, int ndat);
+void dumpobj(void);
+void ieeedtod(uint64 *ieee, double native);
+Sym* stringsym(char*, int);
+
+/*
+ * print.c
+ */
+void exprfmt(Fmt *f, Node *n, int prec);
+void exprlistfmt(Fmt *f, NodeList *l);
+
+/*
+ * range.c
+ */
+void typecheckrange(Node *n);
+void walkrange(Node *n);
+
+/*
+ * reflect.c
+ */
+void dumptypestructs(void);
+Type* methodfunc(Type *f, Type*);
+Node* typename(Type *t);
+Sym* typesym(Type *t);
+
+/*
+ * select.c
+ */
+void typecheckselect(Node *sel);
+void walkselect(Node *sel);
+
+/*
+ * sinit.c
+ */
+void anylit(int, Node *n, Node *var, NodeList **init);
+int gen_as_init(Node *n);
+NodeList* initfix(NodeList *l);
+int oaslit(Node *n, NodeList **init);
+int stataddr(Node *nam, Node *n);
+
+/*
+ * subr.c
+ */
+int Econv(Fmt *fp);
+int Jconv(Fmt *fp);
+int Lconv(Fmt *fp);
+int Nconv(Fmt *fp);
+int Oconv(Fmt *fp);
+int Sconv(Fmt *fp);
+int Tconv(Fmt *fp);
+int Tpretty(Fmt *fp, Type *t);
+int Zconv(Fmt *fp);
+Node* adddot(Node *n);
+int adddot1(Sym *s, Type *t, int d, Type **save, int ignorecase);
+Type* aindex(Node *b, Type *t);
+int algtype(Type *t);
+void argtype(Node *on, Type *t);
+Node* assignconv(Node *n, Type *t, char *context);
+int assignop(Type *src, Type *dst, char **why);
+void badtype(int o, Type *tl, Type *tr);
+int brcom(int a);
+int brrev(int a);
+NodeList* concat(NodeList *a, NodeList *b);
+int convertop(Type *src, Type *dst, char **why);
+int count(NodeList *l);
+int cplxsubtype(int et);
+void dump(char *s, Node *n);
+void dumplist(char *s, NodeList *l);
+int eqtype(Type *t1, Type *t2);
+int eqtypenoname(Type *t1, Type *t2);
+void errorexit(void);
+void expandmeth(Sym *s, Type *t);
+void fatal(char *fmt, ...);
+void flusherrors(void);
+void frame(int context);
+Type* funcfirst(Iter *s, Type *t);
+Type* funcnext(Iter *s);
+void genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface);
+Type** getinarg(Type *t);
+Type* getinargx(Type *t);
+Type** getoutarg(Type *t);
+Type* getoutargx(Type *t);
+Type** getthis(Type *t);
+Type* getthisx(Type *t);
+int implements(Type *t, Type *iface, Type **missing, Type **have, int *ptr);
+void importdot(Pkg *opkg, Node *pack);
+int is64(Type *t);
+int isblank(Node *n);
+int isfixedarray(Type *t);
+int isideal(Type *t);
+int isinter(Type *t);
+int isnil(Node *n);
+int isnilinter(Type *t);
+int isptrto(Type *t, int et);
+int isslice(Type *t);
+int istype(Type *t, int et);
+void linehist(char *file, int32 off, int relative);
+NodeList* list(NodeList *l, Node *n);
+NodeList* list1(Node *n);
+void listsort(NodeList**, int(*f)(Node*, Node*));
+Node* liststmt(NodeList *l);
+NodeList* listtreecopy(NodeList *l);
+Sym* lookup(char *name);
+void* mal(int32 n);
+Type* maptype(Type *key, Type *val);
+Type* methtype(Type *t);
+Pkg* mkpkg(Strlit *path);
+Sym* ngotype(Node *n);
+int noconv(Type *t1, Type *t2);
+Node* nod(int op, Node *nleft, Node *nright);
+Node* nodbool(int b);
+void nodconst(Node *n, Type *t, int64 v);
+Node* nodintconst(int64 v);
+Node* nodfltconst(Mpflt *v);
+Node* nodnil(void);
+int parserline(void);
+Sym* pkglookup(char *name, Pkg *pkg);
+int powtwo(Node *n);
+Type* ptrto(Type *t);
+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);
+Node* localexpr(Node *n, Type *t, NodeList **init);
+int32 setlineno(Node *n);
+void setmaxarg(Type *t);
+Type* shallow(Type *t);
+int simsimtype(Type *t);
+void smagic(Magic *m);
+Type* sortinter(Type *t);
+uint32 stringhash(char *p);
+Strlit* strlit(char *s);
+int structcount(Type *t);
+Type* structfirst(Iter *s, Type **nn);
+Type* structnext(Iter *s);
+Node* syslook(char *name, int copy);
+Type* tounsigned(Type *t);
+Node* treecopy(Node *n);
+Type* typ(int et);
+uint32 typehash(Type *t);
+void ullmancalc(Node *n);
+void umagic(Magic *m);
+void warn(char *fmt, ...);
+void yyerror(char *fmt, ...);
+void yyerrorl(int line, char *fmt, ...);
+
+/*
+ * swt.c
+ */
+void typecheckswitch(Node *n);
+void walkswitch(Node *sw);
+
+/*
+ * typecheck.c
+ */
+int exportassignok(Type *t, char *desc);
+int islvalue(Node *n);
+Node* typecheck(Node **np, int top);
+void typechecklist(NodeList *l, int top);
+Node* typecheckdef(Node *n);
+void resumetypecopy(void);
+void copytype(Node *n, Type *t);
+void defertypecopy(Node *n, Type *t);
+void queuemethod(Node *n);
+
+/*
+ * unsafe.c
+ */
+Node* unsafenmagic(Node *n);
+
+/*
+ * walk.c
+ */
+Node* callnew(Type *t);
+Node* chanfn(char *name, int n, Type *t);
+Node* mkcall(char *name, Type *t, NodeList **init, ...);
+Node* mkcall1(Node *fn, Type *t, NodeList **init, ...);
+int vmatch1(Node *l, Node *r);
+void walk(Node *fn);
+void walkexpr(Node **np, NodeList **init);
+void walkexprlist(NodeList *l, NodeList **init);
+void walkexprlistsafe(NodeList *l, NodeList **init);
+void walkstmt(Node **np);
+void walkstmtlist(NodeList *l);
+
+/*
+ * arch-specific ggen.c/gsubr.c/gobj.c/pgen.c
+ */
+#define P ((Prog*)0)
+
+typedef struct Plist Plist;
+struct Plist
+{
+ Node* name;
+ Prog* firstpc;
+ int recur;
+ Plist* link;
+};
+
+EXTERN Plist* plist;
+EXTERN Plist* plast;
+
+EXTERN Prog* continpc;
+EXTERN Prog* breakpc;
+EXTERN Prog* pc;
+EXTERN Prog* firstpc;
+EXTERN Prog* retpc;
+
+EXTERN Node* nodfp;
+
+int anyregalloc(void);
+void betypeinit(void);
+void bgen(Node *n, int true, Prog *to);
+void cgen(Node*, Node*);
+void cgen_asop(Node *n);
+void cgen_call(Node *n, int proc);
+void cgen_callinter(Node *n, Node *res, int proc);
+void cgen_ret(Node *n);
+void clearfat(Node *n);
+void compile(Node*);
+void defframe(Prog*);
+int dgostringptr(Sym*, int off, char *str);
+int dgostrlitptr(Sym*, int off, Strlit*);
+int dstringptr(Sym *s, int off, char *str);
+int dsymptr(Sym *s, int off, Sym *x, int xoff);
+int duintxx(Sym *s, int off, uint64 v, int wid);
+void dumpdata(void);
+void dumpfuncs(void);
+void fixautoused(Prog*);
+void gdata(Node*, Node*, int);
+void gdatacomplex(Node*, Mpcplx*);
+void gdatastring(Node*, Strlit*);
+void genembedtramp(Type*, Type*, Sym*, int iface);
+void ggloblnod(Node *nam, int32 width);
+void ggloblsym(Sym *s, int32 width, int dupok);
+Prog* gjmp(Prog*);
+void gused(Node*);
+int isfat(Type*);
+void markautoused(Prog*);
+Plist* newplist(void);
+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);
+
diff --git a/src/cmd/gc/go.y b/src/cmd/gc/go.y
new file mode 100644
index 000000000..4c7fe6068
--- /dev/null
+++ b/src/cmd/gc/go.y
@@ -0,0 +1,1985 @@
+// Copyright 2009 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.
+
+/*
+ * Go language grammar.
+ *
+ * The Go semicolon rules are:
+ *
+ * 1. all statements and declarations are terminated by semicolons.
+ * 2. semicolons can be omitted before a closing ) or }.
+ * 3. semicolons are inserted by the lexer before a newline
+ * following a specific list of tokens.
+ *
+ * Rules #1 and #2 are accomplished by writing the lists as
+ * semicolon-separated lists with an optional trailing semicolon.
+ * Rule #3 is implemented in yylex.
+ */
+
+%{
+#include <stdio.h> /* if we don't, bison will, and go.h re-#defines getc */
+#include "go.h"
+
+static void fixlbrace(int);
+%}
+%union {
+ Node* node;
+ NodeList* list;
+ Type* type;
+ Sym* sym;
+ struct Val val;
+ int lint;
+}
+
+// |sed 's/.* //' |9 fmt -l1 |sort |9 fmt -l50 | sed 's/^/%xxx /'
+
+%token <val> LLITERAL
+%token <lint> LASOP
+%token <sym> LBREAK LCASE LCHAN LCOLAS LCONST LCONTINUE LDDD
+%token <sym> LDEFAULT LDEFER LELSE LFALL LFOR LFUNC LGO LGOTO
+%token <sym> LIF LIMPORT LINTERFACE LMAP LNAME
+%token <sym> LPACKAGE LRANGE LRETURN LSELECT LSTRUCT LSWITCH
+%token <sym> LTYPE LVAR
+
+%token LANDAND LANDNOT LBODY LCOMM LDEC LEQ LGE LGT
+%token LIGNORE LINC LLE LLSH LLT LNE LOROR LRSH
+
+%type <lint> lbrace import_here
+%type <sym> sym packname
+%type <val> oliteral
+
+%type <node> stmt ntype
+%type <node> arg_type
+%type <node> case caseblock
+%type <node> compound_stmt dotname embed expr complitexpr
+%type <node> expr_or_type
+%type <node> fndcl fnliteral
+%type <node> for_body for_header for_stmt if_header if_stmt non_dcl_stmt
+%type <node> interfacedcl keyval labelname name
+%type <node> name_or_type non_expr_type
+%type <node> new_name dcl_name oexpr typedclname
+%type <node> onew_name
+%type <node> osimple_stmt pexpr pexpr_no_paren
+%type <node> pseudocall range_stmt select_stmt
+%type <node> simple_stmt
+%type <node> switch_stmt uexpr
+%type <node> xfndcl typedcl
+
+%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
+%type <list> common_dcl constdcl constdcl1 constdcl_list typedcl_list
+
+%type <node> convtype comptype dotdotdot
+%type <node> indcl interfacetype structtype ptrtype
+%type <node> recvchantype non_recvchantype othertype fnret_type fntype
+
+%type <val> hidden_tag
+
+%type <sym> hidden_importsym hidden_pkg_importsym
+
+%type <node> hidden_constant hidden_literal hidden_dcl
+%type <node> hidden_interfacedcl hidden_structdcl hidden_opt_sym
+
+%type <list> hidden_funres
+%type <list> ohidden_funres
+%type <list> hidden_funarg_list ohidden_funarg_list
+%type <list> hidden_interfacedcl_list ohidden_interfacedcl_list
+%type <list> hidden_structdcl_list ohidden_structdcl_list
+
+%type <type> hidden_type hidden_type_misc hidden_pkgtype
+%type <type> hidden_type_func
+%type <type> hidden_type_recv_chan hidden_type_non_recv_chan
+
+%left LCOMM /* outside the usual hierarchy; here for good error messages */
+
+%left LOROR
+%left LANDAND
+%left LEQ LNE LLE LGE LLT LGT
+%left '+' '-' '|' '^'
+%left '*' '/' '%' '&' LLSH LRSH LANDNOT
+
+/*
+ * manual override of shift/reduce conflicts.
+ * the general form is that we assign a precedence
+ * to the token being shifted and then introduce
+ * NotToken with lower precedence or PreferToToken with higher
+ * and annotate the reducing rule accordingly.
+ */
+%left NotPackage
+%left LPACKAGE
+
+%left NotParen
+%left '('
+
+%left ')'
+%left PreferToRightParen
+
+%error-verbose
+
+%%
+file:
+ loadsys
+ package
+ imports
+ xdcl_list
+ {
+ xtop = concat(xtop, $4);
+ }
+
+package:
+ %prec NotPackage
+ {
+ prevlineno = lineno;
+ yyerror("package statement must be first");
+ flusherrors();
+ mkpackage("main");
+ }
+| LPACKAGE sym ';'
+ {
+ mkpackage($2->name);
+ }
+
+/*
+ * this loads the definitions for the low-level runtime functions,
+ * so that the compiler can generate calls to them,
+ * but does not make the name "runtime" visible as a package.
+ */
+loadsys:
+ {
+ importpkg = runtimepkg;
+
+ if(debug['A'])
+ cannedimports("runtime.builtin", "package runtime\n\n$$\n\n");
+ else
+ cannedimports("runtime.builtin", runtimeimport);
+ curio.importsafe = 1;
+ }
+ import_package
+ import_there
+ {
+ importpkg = nil;
+ }
+
+imports:
+| imports import ';'
+
+import:
+ LIMPORT import_stmt
+| LIMPORT '(' import_stmt_list osemi ')'
+| LIMPORT '(' ')'
+
+import_stmt:
+ import_here import_package import_there
+ {
+ Pkg *ipkg;
+ Sym *my;
+ Node *pack;
+
+ ipkg = importpkg;
+ my = importmyname;
+ importpkg = nil;
+ importmyname = S;
+
+ if(my == nil)
+ my = lookup(ipkg->name);
+
+ pack = nod(OPACK, N, N);
+ pack->sym = my;
+ pack->pkg = ipkg;
+ pack->lineno = $1;
+
+ if(my->name[0] == '.') {
+ importdot(ipkg, pack);
+ break;
+ }
+ if(my->name[0] == '_' && my->name[1] == '\0')
+ break;
+ if(my->def) {
+ lineno = $1;
+ redeclare(my, "as imported package name");
+ }
+ my->def = pack;
+ my->lastlineno = $1;
+ my->block = 1; // at top level
+ }
+
+
+import_stmt_list:
+ import_stmt
+| import_stmt_list ';' import_stmt
+
+import_here:
+ LLITERAL
+ {
+ // import with original name
+ $$ = parserline();
+ importmyname = S;
+ importfile(&$1, $$);
+ }
+| sym LLITERAL
+ {
+ // import with given name
+ $$ = parserline();
+ importmyname = $1;
+ importfile(&$2, $$);
+ }
+| '.' LLITERAL
+ {
+ // import into my name space
+ $$ = parserline();
+ importmyname = lookup(".");
+ importfile(&$2, $$);
+ }
+
+import_package:
+ LPACKAGE sym import_safety ';'
+ {
+ if(importpkg->name == nil) {
+ importpkg->name = $2->name;
+ pkglookup($2->name, nil)->npkg++;
+ } else if(strcmp(importpkg->name, $2->name) != 0)
+ yyerror("conflicting names %s and %s for package %Z", importpkg->name, $2->name, importpkg->path);
+ importpkg->direct = 1;
+
+ if(safemode && !curio.importsafe)
+ yyerror("cannot import unsafe package %Z", importpkg->path);
+ }
+
+import_safety:
+| LNAME
+ {
+ if(strcmp($1->name, "safe") == 0)
+ curio.importsafe = 1;
+ }
+
+import_there:
+ {
+ defercheckwidth();
+ }
+ hidden_import_list '$' '$'
+ {
+ resumecheckwidth();
+ unimportfile();
+ }
+
+/*
+ * declarations
+ */
+xdcl:
+ {
+ yyerror("empty top-level declaration");
+ $$ = nil;
+ }
+| common_dcl
+| xfndcl
+ {
+ $$ = list1($1);
+ }
+| non_dcl_stmt
+ {
+ yyerror("non-declaration statement outside function body");
+ $$ = nil;
+ }
+| error
+ {
+ $$ = nil;
+ }
+
+common_dcl:
+ LVAR vardcl
+ {
+ $$ = $2;
+ }
+| LVAR '(' vardcl_list osemi ')'
+ {
+ $$ = $3;
+ }
+| LVAR '(' ')'
+ {
+ $$ = nil;
+ }
+| lconst constdcl
+ {
+ $$ = $2;
+ iota = -100000;
+ lastconst = nil;
+ }
+| lconst '(' constdcl osemi ')'
+ {
+ $$ = $3;
+ iota = -100000;
+ lastconst = nil;
+ }
+| lconst '(' constdcl ';' constdcl_list osemi ')'
+ {
+ $$ = concat($3, $5);
+ iota = -100000;
+ lastconst = nil;
+ }
+| lconst '(' ')'
+ {
+ $$ = nil;
+ iota = -100000;
+ }
+| LTYPE typedcl
+ {
+ $$ = list1($2);
+ }
+| LTYPE '(' typedcl_list osemi ')'
+ {
+ $$ = $3;
+ }
+| LTYPE '(' ')'
+ {
+ $$ = nil;
+ }
+
+lconst:
+ LCONST
+ {
+ iota = 0;
+ }
+
+vardcl:
+ dcl_name_list ntype
+ {
+ $$ = variter($1, $2, nil);
+ }
+| dcl_name_list ntype '=' expr_list
+ {
+ $$ = variter($1, $2, $4);
+ }
+| dcl_name_list '=' expr_list
+ {
+ $$ = variter($1, nil, $3);
+ }
+
+constdcl:
+ dcl_name_list ntype '=' expr_list
+ {
+ $$ = constiter($1, $2, $4);
+ }
+| dcl_name_list '=' expr_list
+ {
+ $$ = constiter($1, N, $3);
+ }
+
+constdcl1:
+ constdcl
+| dcl_name_list ntype
+ {
+ $$ = constiter($1, $2, nil);
+ }
+| dcl_name_list
+ {
+ $$ = constiter($1, N, nil);
+ }
+
+typedclname:
+ sym
+ {
+ // different from dclname because the name
+ // becomes visible right here, not at the end
+ // of the declaration.
+ $$ = typedcl0($1);
+ }
+
+typedcl:
+ typedclname ntype
+ {
+ $$ = typedcl1($1, $2, 1);
+ }
+
+simple_stmt:
+ expr
+ {
+ $$ = $1;
+ }
+| expr LASOP expr
+ {
+ $$ = nod(OASOP, $1, $3);
+ $$->etype = $2; // rathole to pass opcode
+ }
+| expr_list '=' expr_list
+ {
+ if($1->next == nil && $3->next == nil) {
+ // simple
+ $$ = nod(OAS, $1->n, $3->n);
+ break;
+ }
+ // multiple
+ $$ = nod(OAS2, N, N);
+ $$->list = $1;
+ $$->rlist = $3;
+ }
+| expr_list LCOLAS expr_list
+ {
+ if($3->n->op == OTYPESW) {
+ Node *n;
+
+ n = N;
+ if($3->next != nil)
+ yyerror("expr.(type) must be alone in list");
+ if($1->next != nil)
+ yyerror("argument count mismatch: %d = %d", count($1), 1);
+ else if($1->n->op != ONAME && $1->n->op != OTYPE && $1->n->op != ONONAME)
+ yyerror("invalid variable name %#N in type switch", $1->n);
+ else
+ n = $1->n;
+ $$ = nod(OTYPESW, n, $3->n->right);
+ break;
+ }
+ $$ = colas($1, $3);
+ }
+| expr LINC
+ {
+ $$ = nod(OASOP, $1, nodintconst(1));
+ $$->etype = OADD;
+ }
+| expr LDEC
+ {
+ $$ = nod(OASOP, $1, nodintconst(1));
+ $$->etype = OSUB;
+ }
+
+case:
+ LCASE expr_or_type_list ':'
+ {
+ Node *n;
+
+ // will be converted to OCASE
+ // right will point to next case
+ // done in casebody()
+ markdcl();
+ $$ = nod(OXCASE, N, N);
+ $$->list = $2;
+ if(typesw != N && typesw->right != N && (n=typesw->right->left) != N) {
+ // type switch - declare variable
+ n = newname(n->sym);
+ n->used = 1; // TODO(rsc): better job here
+ declare(n, dclcontext);
+ $$->nname = n;
+ }
+ break;
+ }
+| LCASE expr_or_type_list '=' expr ':'
+ {
+ Node *n;
+
+ // will be converted to OCASE
+ // right will point to next case
+ // done in casebody()
+ markdcl();
+ $$ = nod(OXCASE, N, N);
+ if($2->next == nil)
+ n = nod(OAS, $2->n, $4);
+ else {
+ n = nod(OAS2, N, N);
+ n->list = $2;
+ n->rlist = list1($4);
+ }
+ $$->list = list1(n);
+ }
+| LCASE expr_or_type_list LCOLAS expr ':'
+ {
+ // will be converted to OCASE
+ // right will point to next case
+ // done in casebody()
+ markdcl();
+ $$ = nod(OXCASE, N, N);
+ $$->list = list1(colas($2, list1($4)));
+ }
+| LDEFAULT ':'
+ {
+ Node *n;
+
+ markdcl();
+ $$ = nod(OXCASE, N, N);
+ if(typesw != N && typesw->right != N && (n=typesw->right->left) != N) {
+ // type switch - declare variable
+ n = newname(n->sym);
+ n->used = 1; // TODO(rsc): better job here
+ declare(n, dclcontext);
+ $$->nname = n;
+ }
+ }
+
+compound_stmt:
+ '{'
+ {
+ markdcl();
+ }
+ stmt_list '}'
+ {
+ $$ = liststmt($3);
+ popdcl();
+ }
+
+caseblock:
+ case
+ {
+ // If the last token read by the lexer was consumed
+ // as part of the case, clear it (parser has cleared yychar).
+ // If the last token read by the lexer was the lookahead
+ // leave it alone (parser has it cached in yychar).
+ // This is so that the stmt_list action doesn't look at
+ // the case tokens if the stmt_list is empty.
+ yylast = yychar;
+ }
+ stmt_list
+ {
+ int last;
+
+ // This is the only place in the language where a statement
+ // list is not allowed to drop the final semicolon, because
+ // it's the only place where a statement list is not followed
+ // by a closing brace. Handle the error for pedantry.
+
+ // Find the final token of the statement list.
+ // yylast is lookahead; yyprev is last of stmt_list
+ last = yyprev;
+
+ if(last > 0 && last != ';' && yychar != '}')
+ yyerror("missing statement after label");
+ $$ = $1;
+ $$->nbody = $3;
+ popdcl();
+ }
+
+caseblock_list:
+ {
+ $$ = nil;
+ }
+| caseblock_list caseblock
+ {
+ $$ = list($1, $2);
+ }
+
+loop_body:
+ LBODY
+ {
+ markdcl();
+ }
+ stmt_list '}'
+ {
+ $$ = $3;
+ popdcl();
+ }
+
+range_stmt:
+ expr_list '=' LRANGE expr
+ {
+ $$ = nod(ORANGE, N, $4);
+ $$->list = $1;
+ $$->etype = 0; // := flag
+ }
+| expr_list LCOLAS LRANGE expr
+ {
+ $$ = nod(ORANGE, N, $4);
+ $$->list = $1;
+ $$->colas = 1;
+ colasdefn($1, $$);
+ }
+
+for_header:
+ osimple_stmt ';' osimple_stmt ';' osimple_stmt
+ {
+ // init ; test ; incr
+ if($5 != N && $5->colas != 0)
+ yyerror("cannot declare in the for-increment");
+ $$ = nod(OFOR, N, N);
+ if($1 != N)
+ $$->ninit = list1($1);
+ $$->ntest = $3;
+ $$->nincr = $5;
+ }
+| osimple_stmt
+ {
+ // normal test
+ $$ = nod(OFOR, N, N);
+ $$->ntest = $1;
+ }
+| range_stmt
+
+for_body:
+ for_header loop_body
+ {
+ $$ = $1;
+ $$->nbody = concat($$->nbody, $2);
+ }
+
+for_stmt:
+ LFOR
+ {
+ markdcl();
+ }
+ for_body
+ {
+ $$ = $3;
+ popdcl();
+ }
+
+if_header:
+ osimple_stmt
+ {
+ // test
+ $$ = nod(OIF, N, N);
+ $$->ntest = $1;
+ }
+| osimple_stmt ';' osimple_stmt
+ {
+ // init ; test
+ $$ = nod(OIF, N, N);
+ if($1 != N)
+ $$->ninit = list1($1);
+ $$->ntest = $3;
+ }
+
+if_stmt:
+ LIF
+ {
+ markdcl();
+ }
+ if_header
+ {
+ if($3->ntest == N)
+ yyerror("missing condition in if statement");
+ }
+ loop_body
+ {
+ $$ = $3;
+ $$->nbody = $5;
+ // no popdcl; maybe there's an LELSE
+ }
+
+switch_stmt:
+ LSWITCH
+ {
+ markdcl();
+ }
+ if_header
+ {
+ Node *n;
+ n = $3->ntest;
+ if(n != N && n->op != OTYPESW)
+ n = N;
+ typesw = nod(OXXX, typesw, n);
+ }
+ LBODY caseblock_list '}'
+ {
+ $$ = $3;
+ $$->op = OSWITCH;
+ $$->list = $6;
+ typesw = typesw->left;
+ popdcl();
+ }
+
+select_stmt:
+ LSELECT
+ {
+ typesw = nod(OXXX, typesw, N);
+ }
+ LBODY caseblock_list '}'
+ {
+ $$ = nod(OSELECT, N, N);
+ $$->lineno = typesw->lineno;
+ $$->list = $4;
+ typesw = typesw->left;
+ }
+
+/*
+ * expressions
+ */
+expr:
+ uexpr
+| expr LOROR expr
+ {
+ $$ = nod(OOROR, $1, $3);
+ }
+| expr LANDAND expr
+ {
+ $$ = nod(OANDAND, $1, $3);
+ }
+| expr LEQ expr
+ {
+ $$ = nod(OEQ, $1, $3);
+ }
+| expr LNE expr
+ {
+ $$ = nod(ONE, $1, $3);
+ }
+| expr LLT expr
+ {
+ $$ = nod(OLT, $1, $3);
+ }
+| expr LLE expr
+ {
+ $$ = nod(OLE, $1, $3);
+ }
+| expr LGE expr
+ {
+ $$ = nod(OGE, $1, $3);
+ }
+| expr LGT expr
+ {
+ $$ = nod(OGT, $1, $3);
+ }
+| expr '+' expr
+ {
+ $$ = nod(OADD, $1, $3);
+ }
+| expr '-' expr
+ {
+ $$ = nod(OSUB, $1, $3);
+ }
+| expr '|' expr
+ {
+ $$ = nod(OOR, $1, $3);
+ }
+| expr '^' expr
+ {
+ $$ = nod(OXOR, $1, $3);
+ }
+| expr '*' expr
+ {
+ $$ = nod(OMUL, $1, $3);
+ }
+| expr '/' expr
+ {
+ $$ = nod(ODIV, $1, $3);
+ }
+| expr '%' expr
+ {
+ $$ = nod(OMOD, $1, $3);
+ }
+| expr '&' expr
+ {
+ $$ = nod(OAND, $1, $3);
+ }
+| expr LANDNOT expr
+ {
+ $$ = nod(OANDNOT, $1, $3);
+ }
+| expr LLSH expr
+ {
+ $$ = nod(OLSH, $1, $3);
+ }
+| expr LRSH expr
+ {
+ $$ = nod(ORSH, $1, $3);
+ }
+ /* not an expression anymore, but left in so we can give a good error */
+| expr LCOMM expr
+ {
+ $$ = nod(OSEND, $1, $3);
+ }
+
+uexpr:
+ pexpr
+| '*' uexpr
+ {
+ $$ = nod(OIND, $2, N);
+ }
+| '&' uexpr
+ {
+ $$ = nod(OADDR, $2, N);
+ }
+| '+' uexpr
+ {
+ $$ = nod(OPLUS, $2, N);
+ }
+| '-' uexpr
+ {
+ $$ = nod(OMINUS, $2, N);
+ }
+| '!' uexpr
+ {
+ $$ = nod(ONOT, $2, N);
+ }
+| '~' uexpr
+ {
+ yyerror("the bitwise complement operator is ^");
+ $$ = nod(OCOM, $2, N);
+ }
+| '^' uexpr
+ {
+ $$ = nod(OCOM, $2, N);
+ }
+| LCOMM uexpr
+ {
+ $$ = nod(ORECV, $2, N);
+ }
+
+/*
+ * call-like statements that
+ * can be preceded by 'defer' and 'go'
+ */
+pseudocall:
+ pexpr '(' ')'
+ {
+ $$ = nod(OCALL, $1, N);
+ }
+| pexpr '(' expr_or_type_list ocomma ')'
+ {
+ $$ = nod(OCALL, $1, N);
+ $$->list = $3;
+ }
+| pexpr '(' expr_or_type_list LDDD ocomma ')'
+ {
+ $$ = nod(OCALL, $1, N);
+ $$->list = $3;
+ $$->isddd = 1;
+ }
+
+pexpr_no_paren:
+ LLITERAL
+ {
+ $$ = nodlit($1);
+ }
+| name
+| pexpr '.' sym
+ {
+ if($1->op == OPACK) {
+ Sym *s;
+ s = restrictlookup($3->name, $1->pkg);
+ $1->used = 1;
+ $$ = oldname(s);
+ break;
+ }
+ $$ = nod(OXDOT, $1, newname($3));
+ }
+| pexpr '.' '(' expr_or_type ')'
+ {
+ $$ = nod(ODOTTYPE, $1, $4);
+ }
+| pexpr '.' '(' LTYPE ')'
+ {
+ $$ = nod(OTYPESW, N, $1);
+ }
+| pexpr '[' expr ']'
+ {
+ $$ = nod(OINDEX, $1, $3);
+ }
+| pexpr '[' oexpr ':' oexpr ']'
+ {
+ $$ = nod(OSLICE, $1, nod(OKEY, $3, $5));
+ }
+| pseudocall
+| convtype '(' expr ')'
+ {
+ // conversion
+ $$ = nod(OCALL, $1, N);
+ $$->list = list1($3);
+ }
+| comptype lbrace braced_keyval_list '}'
+ {
+ // composite expression
+ $$ = nod(OCOMPLIT, N, $1);
+ $$->list = $3;
+
+ fixlbrace($2);
+ }
+| pexpr_no_paren '{' braced_keyval_list '}'
+ {
+ // composite expression
+ $$ = nod(OCOMPLIT, N, $1);
+ $$->list = $3;
+ }
+| '(' expr_or_type ')' '{' braced_keyval_list '}'
+ {
+ yyerror("cannot parenthesize type in composite literal");
+ // composite expression
+ $$ = nod(OCOMPLIT, N, $2);
+ $$->list = $5;
+ }
+| fnliteral
+
+keyval:
+ expr ':' complitexpr
+ {
+ $$ = nod(OKEY, $1, $3);
+ }
+
+complitexpr:
+ expr
+| '{' braced_keyval_list '}'
+ {
+ $$ = nod(OCOMPLIT, N, N);
+ $$->list = $2;
+ }
+
+pexpr:
+ pexpr_no_paren
+| '(' expr_or_type ')'
+ {
+ $$ = $2;
+
+ // Need to know on lhs of := whether there are ( ).
+ // Don't bother with the OPAREN in other cases:
+ // it's just a waste of memory and time.
+ switch($$->op) {
+ case ONAME:
+ case ONONAME:
+ case OPACK:
+ case OTYPE:
+ case OLITERAL:
+ $$ = nod(OPAREN, $$, N);
+ }
+ }
+
+expr_or_type:
+ expr
+| non_expr_type %prec PreferToRightParen
+
+name_or_type:
+ ntype
+
+lbrace:
+ LBODY
+ {
+ $$ = LBODY;
+ }
+| '{'
+ {
+ $$ = '{';
+ }
+
+/*
+ * names and types
+ * newname is used before declared
+ * oldname is used after declared
+ */
+new_name:
+ sym
+ {
+ $$ = newname($1);
+ }
+
+dcl_name:
+ sym
+ {
+ $$ = dclname($1);
+ }
+
+onew_name:
+ {
+ $$ = N;
+ }
+| new_name
+
+sym:
+ LNAME
+
+name:
+ sym %prec NotParen
+ {
+ $$ = oldname($1);
+ if($$->pack != N)
+ $$->pack->used = 1;
+ }
+
+labelname:
+ new_name
+
+/*
+ * to avoid parsing conflicts, type is split into
+ * channel types
+ * function types
+ * parenthesized types
+ * any other type
+ * the type system makes additional restrictions,
+ * but those are not implemented in the grammar.
+ */
+dotdotdot:
+ LDDD
+ {
+ yyerror("final argument in variadic function missing type");
+ $$ = nod(ODDD, typenod(typ(TINTER)), N);
+ }
+| LDDD ntype
+ {
+ $$ = nod(ODDD, $2, N);
+ }
+
+ntype:
+ recvchantype
+| fntype
+| othertype
+| ptrtype
+| dotname
+| '(' ntype ')'
+ {
+ $$ = nod(OTPAREN, $2, N);
+ }
+
+non_expr_type:
+ recvchantype
+| fntype
+| othertype
+| '*' non_expr_type
+ {
+ $$ = nod(OIND, $2, N);
+ }
+
+non_recvchantype:
+ fntype
+| othertype
+| ptrtype
+| dotname
+| '(' ntype ')'
+ {
+ $$ = nod(OTPAREN, $2, N);
+ }
+
+convtype:
+ fntype
+| othertype
+
+comptype:
+ othertype
+
+fnret_type:
+ recvchantype
+| fntype
+| othertype
+| ptrtype
+| dotname
+
+dotname:
+ name
+| name '.' sym
+ {
+ if($1->op == OPACK) {
+ Sym *s;
+ s = restrictlookup($3->name, $1->pkg);
+ $1->used = 1;
+ $$ = oldname(s);
+ break;
+ }
+ $$ = nod(OXDOT, $1, newname($3));
+ }
+
+othertype:
+ '[' oexpr ']' ntype
+ {
+ $$ = nod(OTARRAY, $2, $4);
+ }
+| '[' LDDD ']' ntype
+ {
+ // array literal of nelem
+ $$ = nod(OTARRAY, nod(ODDD, N, N), $4);
+ }
+| LCHAN non_recvchantype
+ {
+ $$ = nod(OTCHAN, $2, N);
+ $$->etype = Cboth;
+ }
+| LCHAN LCOMM ntype
+ {
+ $$ = nod(OTCHAN, $3, N);
+ $$->etype = Csend;
+ }
+| LMAP '[' ntype ']' ntype
+ {
+ $$ = nod(OTMAP, $3, $5);
+ }
+| structtype
+| interfacetype
+
+ptrtype:
+ '*' ntype
+ {
+ $$ = nod(OIND, $2, N);
+ }
+
+recvchantype:
+ LCOMM LCHAN ntype
+ {
+ $$ = nod(OTCHAN, $3, N);
+ $$->etype = Crecv;
+ }
+
+structtype:
+ LSTRUCT lbrace structdcl_list osemi '}'
+ {
+ $$ = nod(OTSTRUCT, N, N);
+ $$->list = $3;
+ fixlbrace($2);
+ }
+| LSTRUCT lbrace '}'
+ {
+ $$ = nod(OTSTRUCT, N, N);
+ fixlbrace($2);
+ }
+
+interfacetype:
+ LINTERFACE lbrace interfacedcl_list osemi '}'
+ {
+ $$ = nod(OTINTER, N, N);
+ $$->list = $3;
+ fixlbrace($2);
+ }
+| LINTERFACE lbrace '}'
+ {
+ $$ = nod(OTINTER, N, N);
+ fixlbrace($2);
+ }
+
+/*
+ * function stuff
+ * all in one place to show how crappy it all is
+ */
+xfndcl:
+ LFUNC fndcl fnbody
+ {
+ $$ = $2;
+ if($$ == N)
+ break;
+ $$->nbody = $3;
+ $$->endlineno = lineno;
+ funcbody($$);
+ }
+
+fndcl:
+ dcl_name '(' oarg_type_list_ocomma ')' fnres
+ {
+ Node *n;
+
+ $3 = checkarglist($3, 1);
+ $$ = nod(ODCLFUNC, N, N);
+ $$->nname = $1;
+ n = nod(OTFUNC, N, N);
+ n->list = $3;
+ n->rlist = $5;
+ if(strcmp($1->sym->name, "init") == 0) {
+ $$->nname = renameinit($1);
+ if($3 != nil || $5 != nil)
+ yyerror("func init must have no arguments and no return values");
+ }
+ if(strcmp(localpkg->name, "main") == 0 && strcmp($1->sym->name, "main") == 0) {
+ if($3 != nil || $5 != nil)
+ yyerror("func main must have no arguments and no return values");
+ }
+ // TODO: check if nname already has an ntype
+ $$->nname->ntype = n;
+ funchdr($$);
+ }
+| '(' oarg_type_list_ocomma ')' sym '(' oarg_type_list_ocomma ')' fnres
+ {
+ Node *rcvr, *t;
+ Node *name;
+
+ name = newname($4);
+ $2 = checkarglist($2, 0);
+ $6 = checkarglist($6, 1);
+ $$ = N;
+ if($2 == nil) {
+ yyerror("method has no receiver");
+ break;
+ }
+ if($2->next != nil) {
+ yyerror("method has multiple receivers");
+ break;
+ }
+ rcvr = $2->n;
+ if(rcvr->op != ODCLFIELD) {
+ yyerror("bad receiver in method");
+ break;
+ }
+ if(rcvr->right->op == OTPAREN || (rcvr->right->op == OIND && rcvr->right->left->op == OTPAREN))
+ yyerror("cannot parenthesize receiver type");
+
+ $$ = nod(ODCLFUNC, N, N);
+ $$->nname = methodname1(name, rcvr->right);
+ t = nod(OTFUNC, rcvr, N);
+ t->list = $6;
+ t->rlist = $8;
+ $$->nname->ntype = t;
+ $$->shortname = name;
+ funchdr($$);
+ }
+
+fntype:
+ LFUNC '(' oarg_type_list_ocomma ')' fnres
+ {
+ $3 = checkarglist($3, 1);
+ $$ = nod(OTFUNC, N, N);
+ $$->list = $3;
+ $$->rlist = $5;
+ }
+
+fnbody:
+ {
+ $$ = nil;
+ }
+| '{' stmt_list '}'
+ {
+ $$ = $2;
+ if($$ == nil)
+ $$ = list1(nod(OEMPTY, N, N));
+ }
+
+fnres:
+ %prec NotParen
+ {
+ $$ = nil;
+ }
+| fnret_type
+ {
+ $$ = list1(nod(ODCLFIELD, N, $1));
+ }
+| '(' oarg_type_list_ocomma ')'
+ {
+ $2 = checkarglist($2, 0);
+ $$ = $2;
+ }
+
+fnlitdcl:
+ fntype
+ {
+ closurehdr($1);
+ }
+
+fnliteral:
+ fnlitdcl lbrace stmt_list '}'
+ {
+ $$ = closurebody($3);
+ fixlbrace($2);
+ }
+| fnlitdcl error
+ {
+ $$ = closurebody(nil);
+ }
+
+/*
+ * lists of things
+ * note that they are left recursive
+ * to conserve yacc stack. they need to
+ * be reversed to interpret correctly
+ */
+xdcl_list:
+ {
+ $$ = nil;
+ }
+| xdcl_list xdcl ';'
+ {
+ $$ = concat($1, $2);
+ if(nsyntaxerrors == 0)
+ testdclstack();
+ }
+
+vardcl_list:
+ vardcl
+| vardcl_list ';' vardcl
+ {
+ $$ = concat($1, $3);
+ }
+
+constdcl_list:
+ constdcl1
+| constdcl_list ';' constdcl1
+ {
+ $$ = concat($1, $3);
+ }
+
+typedcl_list:
+ typedcl
+ {
+ $$ = list1($1);
+ }
+| typedcl_list ';' typedcl
+ {
+ $$ = list($1, $3);
+ }
+
+structdcl_list:
+ structdcl
+| structdcl_list ';' structdcl
+ {
+ $$ = concat($1, $3);
+ }
+
+interfacedcl_list:
+ interfacedcl
+ {
+ $$ = list1($1);
+ }
+| interfacedcl_list ';' interfacedcl
+ {
+ $$ = list($1, $3);
+ }
+
+structdcl:
+ new_name_list ntype oliteral
+ {
+ NodeList *l;
+
+ for(l=$1; l; l=l->next) {
+ l->n = nod(ODCLFIELD, l->n, $2);
+ l->n->val = $3;
+ }
+ }
+| embed oliteral
+ {
+ $1->val = $2;
+ $$ = list1($1);
+ }
+| '(' embed ')' oliteral
+ {
+ $2->val = $4;
+ $$ = list1($2);
+ yyerror("cannot parenthesize embedded type");
+ }
+| '*' embed oliteral
+ {
+ $2->right = nod(OIND, $2->right, N);
+ $2->val = $3;
+ $$ = list1($2);
+ }
+| '(' '*' embed ')' oliteral
+ {
+ $3->right = nod(OIND, $3->right, N);
+ $3->val = $5;
+ $$ = list1($3);
+ yyerror("cannot parenthesize embedded type");
+ }
+| '*' '(' embed ')' oliteral
+ {
+ $3->right = nod(OIND, $3->right, N);
+ $3->val = $5;
+ $$ = list1($3);
+ yyerror("cannot parenthesize embedded type");
+ }
+
+packname:
+ LNAME
+ {
+ Node *n;
+
+ $$ = $1;
+ n = oldname($1);
+ if(n->pack != N)
+ n->pack->used = 1;
+ }
+| LNAME '.' sym
+ {
+ Pkg *pkg;
+
+ if($1->def == N || $1->def->op != OPACK) {
+ yyerror("%S is not a package", $1);
+ pkg = localpkg;
+ } else {
+ $1->def->used = 1;
+ pkg = $1->def->pkg;
+ }
+ $$ = restrictlookup($3->name, pkg);
+ }
+
+embed:
+ packname
+ {
+ $$ = embedded($1);
+ }
+
+interfacedcl:
+ new_name indcl
+ {
+ $$ = nod(ODCLFIELD, $1, $2);
+ ifacedcl($$);
+ }
+| packname
+ {
+ $$ = nod(ODCLFIELD, N, oldname($1));
+ }
+| '(' packname ')'
+ {
+ $$ = nod(ODCLFIELD, N, oldname($2));
+ yyerror("cannot parenthesize embedded type");
+ }
+
+indcl:
+ '(' oarg_type_list_ocomma ')' fnres
+ {
+ // without func keyword
+ $2 = checkarglist($2, 1);
+ $$ = nod(OTFUNC, fakethis(), N);
+ $$->list = $2;
+ $$->rlist = $4;
+ }
+
+/*
+ * function arguments.
+ */
+arg_type:
+ name_or_type
+| sym name_or_type
+ {
+ $$ = nod(ONONAME, N, N);
+ $$->sym = $1;
+ $$ = nod(OKEY, $$, $2);
+ }
+| sym dotdotdot
+ {
+ $$ = nod(ONONAME, N, N);
+ $$->sym = $1;
+ $$ = nod(OKEY, $$, $2);
+ }
+| dotdotdot
+
+arg_type_list:
+ arg_type
+ {
+ $$ = list1($1);
+ }
+| arg_type_list ',' arg_type
+ {
+ $$ = list($1, $3);
+ }
+
+oarg_type_list_ocomma:
+ {
+ $$ = nil;
+ }
+| arg_type_list ocomma
+ {
+ $$ = $1;
+ }
+
+/*
+ * statement
+ */
+stmt:
+ {
+ $$ = N;
+ }
+| compound_stmt
+| common_dcl
+ {
+ $$ = liststmt($1);
+ }
+| non_dcl_stmt
+| error
+ {
+ $$ = N;
+ }
+
+non_dcl_stmt:
+ simple_stmt
+| for_stmt
+| switch_stmt
+| select_stmt
+| if_stmt
+ {
+ popdcl();
+ $$ = $1;
+ }
+| if_stmt LELSE stmt
+ {
+ if($3->op != OIF && $3->op != OBLOCK)
+ yyerror("missing { } after else");
+
+ popdcl();
+ $$ = $1;
+ $$->nelse = list1($3);
+ }
+| labelname ':'
+ {
+ $1 = nod(OLABEL, $1, N);
+ $1->sym = dclstack; // context, for goto restrictions
+ }
+ stmt
+ {
+ NodeList *l;
+
+ $1->right = $4;
+ l = list1($1);
+ if($4)
+ l = list(l, $4);
+ $$ = liststmt(l);
+ }
+| LFALL
+ {
+ // will be converted to OFALL
+ $$ = nod(OXFALL, N, N);
+ }
+| LBREAK onew_name
+ {
+ $$ = nod(OBREAK, $2, N);
+ }
+| LCONTINUE onew_name
+ {
+ $$ = nod(OCONTINUE, $2, N);
+ }
+| LGO pseudocall
+ {
+ $$ = nod(OPROC, $2, N);
+ }
+| LDEFER pseudocall
+ {
+ $$ = nod(ODEFER, $2, N);
+ }
+| LGOTO new_name
+ {
+ $$ = nod(OGOTO, $2, N);
+ $$->sym = dclstack; // context, for goto restrictions
+ }
+| LRETURN oexpr_list
+ {
+ $$ = nod(ORETURN, N, N);
+ $$->list = $2;
+ }
+
+stmt_list:
+ stmt
+ {
+ $$ = nil;
+ if($1 != N)
+ $$ = list1($1);
+ }
+| stmt_list ';' stmt
+ {
+ $$ = $1;
+ if($3 != N)
+ $$ = list($$, $3);
+ }
+
+new_name_list:
+ new_name
+ {
+ $$ = list1($1);
+ }
+| new_name_list ',' new_name
+ {
+ $$ = list($1, $3);
+ }
+
+dcl_name_list:
+ dcl_name
+ {
+ $$ = list1($1);
+ }
+| dcl_name_list ',' dcl_name
+ {
+ $$ = list($1, $3);
+ }
+
+expr_list:
+ expr
+ {
+ $$ = list1($1);
+ }
+| expr_list ',' expr
+ {
+ $$ = list($1, $3);
+ }
+
+expr_or_type_list:
+ expr_or_type
+ {
+ $$ = list1($1);
+ }
+| expr_or_type_list ',' expr_or_type
+ {
+ $$ = list($1, $3);
+ }
+
+/*
+ * list of combo of keyval and val
+ */
+keyval_list:
+ keyval
+ {
+ $$ = list1($1);
+ }
+| complitexpr
+ {
+ $$ = list1($1);
+ }
+| keyval_list ',' keyval
+ {
+ $$ = list($1, $3);
+ }
+| keyval_list ',' complitexpr
+ {
+ $$ = list($1, $3);
+ }
+
+braced_keyval_list:
+ {
+ $$ = nil;
+ }
+| keyval_list ocomma
+ {
+ $$ = $1;
+ }
+
+/*
+ * optional things
+ */
+osemi:
+| ';'
+
+ocomma:
+| ','
+
+oexpr:
+ {
+ $$ = N;
+ }
+| expr
+
+oexpr_list:
+ {
+ $$ = nil;
+ }
+| expr_list
+
+osimple_stmt:
+ {
+ $$ = N;
+ }
+| simple_stmt
+
+ohidden_funarg_list:
+ {
+ $$ = nil;
+ }
+| hidden_funarg_list
+
+ohidden_structdcl_list:
+ {
+ $$ = nil;
+ }
+| hidden_structdcl_list
+
+ohidden_interfacedcl_list:
+ {
+ $$ = nil;
+ }
+| hidden_interfacedcl_list
+
+oliteral:
+ {
+ $$.ctype = CTxxx;
+ }
+| LLITERAL
+
+/*
+ * import syntax from header of
+ * an output package
+ */
+hidden_import:
+ LIMPORT sym LLITERAL ';'
+ {
+ // Informational: record package name
+ // associated with import path, for use in
+ // human-readable messages.
+ Pkg *p;
+
+ p = mkpkg($3.u.sval);
+ if(p->name == nil) {
+ p->name = $2->name;
+ pkglookup($2->name, nil)->npkg++;
+ } else if(strcmp(p->name, $2->name) != 0)
+ yyerror("conflicting names %s and %s for package %Z", p->name, $2->name, p->path);
+ }
+| LVAR hidden_pkg_importsym hidden_type ';'
+ {
+ importvar($2, $3, PEXTERN);
+ }
+| LCONST hidden_pkg_importsym '=' hidden_constant ';'
+ {
+ importconst($2, types[TIDEAL], $4);
+ }
+| LCONST hidden_pkg_importsym hidden_type '=' hidden_constant ';'
+ {
+ importconst($2, $3, $5);
+ }
+| LTYPE hidden_pkgtype hidden_type ';'
+ {
+ importtype($2, $3);
+ }
+| LFUNC hidden_pkg_importsym '(' ohidden_funarg_list ')' ohidden_funres ';'
+ {
+ importvar($2, functype(N, $4, $6), PFUNC);
+ }
+| LFUNC '(' hidden_funarg_list ')' sym '(' ohidden_funarg_list ')' ohidden_funres ';'
+ {
+ if($3->next != nil || $3->n->op != ODCLFIELD) {
+ yyerror("bad receiver in method");
+ YYERROR;
+ }
+ importmethod($5, functype($3->n, $7, $9));
+ }
+
+hidden_pkgtype:
+ hidden_pkg_importsym
+ {
+ $$ = pkgtype($1);
+ importsym($1, OTYPE);
+ }
+
+hidden_type:
+ hidden_type_misc
+| hidden_type_recv_chan
+| hidden_type_func
+
+hidden_type_non_recv_chan:
+ hidden_type_misc
+| hidden_type_func
+
+hidden_type_misc:
+ hidden_importsym
+ {
+ $$ = pkgtype($1);
+ }
+| LNAME
+ {
+ // predefined name like uint8
+ $1 = pkglookup($1->name, builtinpkg);
+ if($1->def == N || $1->def->op != OTYPE) {
+ yyerror("%s is not a type", $1->name);
+ $$ = T;
+ } else
+ $$ = $1->def->type;
+ }
+| '[' ']' hidden_type
+ {
+ $$ = aindex(N, $3);
+ }
+| '[' LLITERAL ']' hidden_type
+ {
+ $$ = aindex(nodlit($2), $4);
+ }
+| LMAP '[' hidden_type ']' hidden_type
+ {
+ $$ = maptype($3, $5);
+ }
+| LSTRUCT '{' ohidden_structdcl_list '}'
+ {
+ $$ = dostruct($3, TSTRUCT);
+ }
+| LINTERFACE '{' ohidden_interfacedcl_list '}'
+ {
+ $$ = dostruct($3, TINTER);
+ }
+| '*' hidden_type
+ {
+ $$ = ptrto($2);
+ }
+| LCHAN hidden_type_non_recv_chan
+ {
+ $$ = typ(TCHAN);
+ $$->type = $2;
+ $$->chan = Cboth;
+ }
+| LCHAN '(' hidden_type_recv_chan ')'
+ {
+ $$ = typ(TCHAN);
+ $$->type = $3;
+ $$->chan = Cboth;
+ }
+| LCHAN LCOMM hidden_type
+ {
+ $$ = typ(TCHAN);
+ $$->type = $3;
+ $$->chan = Csend;
+ }
+
+hidden_type_recv_chan:
+ LCOMM LCHAN hidden_type
+ {
+ $$ = typ(TCHAN);
+ $$->type = $3;
+ $$->chan = Crecv;
+ }
+
+hidden_type_func:
+ LFUNC '(' ohidden_funarg_list ')' ohidden_funres
+ {
+ $$ = functype(nil, $3, $5);
+ }
+
+hidden_opt_sym:
+ sym
+ {
+ $$ = newname($1);
+ }
+| '?'
+ {
+ $$ = N;
+ }
+
+hidden_dcl:
+ hidden_opt_sym hidden_type hidden_tag
+ {
+ $$ = nod(ODCLFIELD, $1, typenod($2));
+ $$->val = $3;
+ }
+| hidden_opt_sym LDDD hidden_type hidden_tag
+ {
+ Type *t;
+
+ t = typ(TARRAY);
+ t->bound = -1;
+ t->type = $3;
+ $$ = nod(ODCLFIELD, $1, typenod(t));
+ $$->isddd = 1;
+ $$->val = $4;
+ }
+
+hidden_structdcl:
+ sym hidden_type hidden_tag
+ {
+ $$ = nod(ODCLFIELD, newname($1), typenod($2));
+ $$->val = $3;
+ }
+| '?' hidden_type hidden_tag
+ {
+ Sym *s;
+
+ s = $2->sym;
+ if(s == S && isptr[$2->etype])
+ s = $2->type->sym;
+ if(s && s->pkg == builtinpkg)
+ s = lookup(s->name);
+ $$ = embedded(s);
+ $$->right = typenod($2);
+ $$->val = $3;
+ }
+
+hidden_tag:
+ {
+ $$.ctype = CTxxx;
+ }
+| ':' LLITERAL // extra colon avoids conflict with "" looking like beginning of "".typename
+ {
+ $$ = $2;
+ }
+
+hidden_interfacedcl:
+ sym '(' ohidden_funarg_list ')' ohidden_funres
+ {
+ $$ = nod(ODCLFIELD, newname($1), typenod(functype(fakethis(), $3, $5)));
+ }
+| hidden_importsym '(' ohidden_funarg_list ')' ohidden_funres
+ {
+ $$ = nod(ODCLFIELD, newname($1), typenod(functype(fakethis(), $3, $5)));
+ }
+
+ohidden_funres:
+ {
+ $$ = nil;
+ }
+| hidden_funres
+
+hidden_funres:
+ '(' ohidden_funarg_list ')'
+ {
+ $$ = $2;
+ }
+| hidden_type
+ {
+ $$ = list1(nod(ODCLFIELD, N, typenod($1)));
+ }
+
+hidden_literal:
+ LLITERAL
+ {
+ $$ = nodlit($1);
+ }
+| '-' LLITERAL
+ {
+ $$ = nodlit($2);
+ switch($$->val.ctype){
+ case CTINT:
+ mpnegfix($$->val.u.xval);
+ break;
+ case CTFLT:
+ mpnegflt($$->val.u.fval);
+ break;
+ default:
+ yyerror("bad negated constant");
+ }
+ }
+| sym
+ {
+ $$ = oldname(pkglookup($1->name, builtinpkg));
+ if($$->op != OLITERAL)
+ yyerror("bad constant %S", $$->sym);
+ }
+
+hidden_constant:
+ hidden_literal
+| '(' hidden_literal '+' hidden_literal ')'
+ {
+ $$ = nodcplxlit($2->val, $4->val);
+ }
+
+hidden_importsym:
+ LLITERAL '.' sym
+ {
+ Pkg *p;
+
+ if($1.u.sval->len == 0)
+ p = importpkg;
+ else
+ p = mkpkg($1.u.sval);
+ $$ = pkglookup($3->name, p);
+ }
+
+hidden_pkg_importsym:
+ hidden_importsym
+ {
+ $$ = $1;
+ structpkg = $$->pkg;
+ }
+
+hidden_import_list:
+| hidden_import_list hidden_import
+
+hidden_funarg_list:
+ hidden_dcl
+ {
+ $$ = list1($1);
+ }
+| hidden_funarg_list ',' hidden_dcl
+ {
+ $$ = list($1, $3);
+ }
+
+hidden_structdcl_list:
+ hidden_structdcl
+ {
+ $$ = list1($1);
+ }
+| hidden_structdcl_list ';' hidden_structdcl
+ {
+ $$ = list($1, $3);
+ }
+
+hidden_interfacedcl_list:
+ hidden_interfacedcl
+ {
+ $$ = list1($1);
+ }
+| hidden_interfacedcl_list ';' hidden_interfacedcl
+ {
+ $$ = list($1, $3);
+ }
+
+%%
+
+static void
+fixlbrace(int lbr)
+{
+ // If the opening brace was an LBODY,
+ // set up for another one now that we're done.
+ // See comment in lex.c about loophack.
+ if(lbr == LBODY)
+ loophack = 1;
+}
+
diff --git a/src/cmd/gc/init.c b/src/cmd/gc/init.c
new file mode 100644
index 000000000..8818db08c
--- /dev/null
+++ b/src/cmd/gc/init.c
@@ -0,0 +1,195 @@
+// Copyright 2009 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.
+
+#include "go.h"
+
+/*
+ * a function named init is a special case.
+ * it is called by the initialization before
+ * main is run. to make it unique within a
+ * package and also uncallable, the name,
+ * normally "pkg.init", is altered to "pkg.init·1".
+ */
+Node*
+renameinit(Node *n)
+{
+ Sym *s;
+ static int initgen;
+
+ s = n->sym;
+ if(s == S)
+ return n;
+ if(strcmp(s->name, "init") != 0)
+ return n;
+
+ snprint(namebuf, sizeof(namebuf), "init·%d", ++initgen);
+ s = lookup(namebuf);
+ return newname(s);
+}
+
+/*
+ * hand-craft the following initialization code
+ * var initdone· uint8 (1)
+ * func init() (2)
+ * if initdone· != 0 { (3)
+ * if initdone· == 2 (4)
+ * return
+ * throw(); (5)
+ * }
+ * initdone· = 1; (6)
+ * // over all matching imported symbols
+ * <pkg>.init() (7)
+ * { <init stmts> } (8)
+ * init·<n>() // if any (9)
+ * initdone· = 2; (10)
+ * return (11)
+ * }
+ */
+static int
+anyinit(NodeList *n)
+{
+ uint32 h;
+ Sym *s;
+ NodeList *l;
+
+ // are there any interesting init statements
+ for(l=n; l; l=l->next) {
+ switch(l->n->op) {
+ case ODCLFUNC:
+ case ODCLCONST:
+ case ODCLTYPE:
+ case OEMPTY:
+ break;
+ default:
+ return 1;
+ }
+ }
+
+ // is this main
+ if(strcmp(localpkg->name, "main") == 0)
+ return 1;
+
+ // is there an explicit init function
+ snprint(namebuf, sizeof(namebuf), "init·1");
+ s = lookup(namebuf);
+ if(s->def != N)
+ return 1;
+
+ // are there any imported init functions
+ for(h=0; h<NHASH; h++)
+ for(s = hash[h]; s != S; s = s->link) {
+ if(s->name[0] != 'i' || strcmp(s->name, "init") != 0)
+ continue;
+ if(s->def == N)
+ continue;
+ return 1;
+ }
+
+ // then none
+ return 0;
+}
+
+void
+fninit(NodeList *n)
+{
+ int i;
+ Node *gatevar;
+ Node *a, *b, *fn;
+ NodeList *r;
+ uint32 h;
+ Sym *s, *initsym;
+
+ if(debug['A']) {
+ // sys.go or unsafe.go during compiler build
+ return;
+ }
+
+ n = initfix(n);
+ if(!anyinit(n))
+ return;
+
+ r = nil;
+
+ // (1)
+ snprint(namebuf, sizeof(namebuf), "initdone·");
+ gatevar = newname(lookup(namebuf));
+ addvar(gatevar, types[TUINT8], PEXTERN);
+
+ // (2)
+ maxarg = 0;
+ snprint(namebuf, sizeof(namebuf), "init");
+
+ fn = nod(ODCLFUNC, N, N);
+ initsym = lookup(namebuf);
+ fn->nname = newname(initsym);
+ fn->nname->ntype = nod(OTFUNC, N, N);
+ funchdr(fn);
+
+ // (3)
+ a = nod(OIF, N, N);
+ a->ntest = nod(ONE, gatevar, nodintconst(0));
+ r = list(r, a);
+
+ // (4)
+ b = nod(OIF, N, N);
+ b->ntest = nod(OEQ, gatevar, nodintconst(2));
+ b->nbody = list1(nod(ORETURN, N, N));
+ a->nbody = list1(b);
+
+ // (5)
+ b = syslook("throwinit", 0);
+ b = nod(OCALL, b, N);
+ a->nbody = list(a->nbody, b);
+
+ // (6)
+ a = nod(OAS, gatevar, nodintconst(1));
+ r = list(r, a);
+
+ // (7)
+ for(h=0; h<NHASH; h++)
+ for(s = hash[h]; s != S; s = s->link) {
+ if(s->name[0] != 'i' || strcmp(s->name, "init") != 0)
+ continue;
+ if(s->def == N)
+ continue;
+ if(s == initsym)
+ continue;
+
+ // could check that it is fn of no args/returns
+ a = nod(OCALL, s->def, N);
+ r = list(r, a);
+ }
+
+ // (8)
+ r = concat(r, n);
+
+ // (9)
+ // could check that it is fn of no args/returns
+ for(i=1;; i++) {
+ snprint(namebuf, sizeof(namebuf), "init·%d", i);
+ s = lookup(namebuf);
+ if(s->def == N)
+ break;
+ a = nod(OCALL, s->def, N);
+ r = list(r, a);
+ }
+
+ // (10)
+ a = nod(OAS, gatevar, nodintconst(2));
+ r = list(r, a);
+
+ // (11)
+ a = nod(ORETURN, N, N);
+ r = list(r, a);
+ exportsym(fn->nname);
+
+ fn->nbody = r;
+ funcbody(fn);
+
+ curfn = fn;
+ typecheck(&fn, Etop);
+ typechecklist(r, Etop);
+ curfn = nil;
+ funccompile(fn, 0);
+}
diff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c
new file mode 100644
index 000000000..29b6d27ff
--- /dev/null
+++ b/src/cmd/gc/lex.c
@@ -0,0 +1,1956 @@
+// Copyright 2009 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.
+
+#define EXTERN
+#include "go.h"
+#include "y.tab.h"
+#include <ar.h>
+
+#undef getc
+#undef ungetc
+#define getc ccgetc
+#define ungetc ccungetc
+
+extern int yychar;
+int windows;
+int yyprev;
+int yylast;
+
+static void lexinit(void);
+static void lexfini(void);
+static void yytinit(void);
+static int getc(void);
+static void ungetc(int);
+static int32 getr(void);
+static int escchar(int, int*, vlong*);
+static void addidir(char*);
+static int getlinepragma(void);
+static char *goos, *goarch, *goroot;
+
+// Our own isdigit, isspace, isalpha, isalnum that take care
+// of EOF and other out of range arguments.
+static int
+yy_isdigit(int c)
+{
+ return c >= 0 && c <= 0xFF && isdigit(c);
+}
+
+static int
+yy_isspace(int c)
+{
+ return c >= 0 && c <= 0xFF && isspace(c);
+}
+
+static int
+yy_isalpha(int c)
+{
+ return c >= 0 && c <= 0xFF && isalpha(c);
+}
+
+static int
+yy_isalnum(int c)
+{
+ return c >= 0 && c <= 0xFF && isalnum(c);
+}
+
+// Disallow use of isdigit etc.
+#undef isdigit
+#undef isspace
+#undef isalpha
+#undef isalnum
+#define isdigit use_yy_isdigit_instead_of_isdigit
+#define isspace use_yy_isspace_instead_of_isspace
+#define isalpha use_yy_isalpha_instead_of_isalpha
+#define isalnum use_yy_isalnum_instead_of_isalnum
+
+#define DBG if(!debug['x']);else print
+enum
+{
+ EOF = -1,
+};
+
+void
+usage(void)
+{
+ print("gc: usage: %cg [flags] file.go...\n", thechar);
+ print("flags:\n");
+ // -A is allow use of "any" type, for bootstrapping
+ print(" -I DIR search for packages in DIR\n");
+ print(" -d print declarations\n");
+ print(" -e no limit on number of errors printed\n");
+ print(" -f print stack frame structure\n");
+ print(" -h panic on an error\n");
+ print(" -o file specify output file\n");
+ print(" -S print the assembly language\n");
+ print(" -V print the compiler version\n");
+ print(" -u disable package unsafe\n");
+ print(" -w print the parse tree after typing\n");
+ print(" -x print lex tokens\n");
+ exit(0);
+}
+
+void
+fault(int s)
+{
+ // If we've already complained about things
+ // in the program, don't bother complaining
+ // about the seg fault too; let the user clean up
+ // the code and try again.
+ if(nsavederrors + nerrors > 0)
+ errorexit();
+ fatal("fault");
+}
+
+int
+main(int argc, char *argv[])
+{
+ int i, c;
+ NodeList *l;
+ char *p;
+
+ signal(SIGBUS, fault);
+ signal(SIGSEGV, fault);
+
+ localpkg = mkpkg(strlit(""));
+ localpkg->prefix = "\"\"";
+
+ builtinpkg = mkpkg(strlit("go.builtin"));
+
+ gostringpkg = mkpkg(strlit("go.string"));
+ gostringpkg->name = "go.string";
+ gostringpkg->prefix = "go.string"; // not go%2estring
+
+ runtimepkg = mkpkg(strlit("runtime"));
+ runtimepkg->name = "runtime";
+
+ typepkg = mkpkg(strlit("type"));
+ typepkg->name = "type";
+
+ unsafepkg = mkpkg(strlit("unsafe"));
+ unsafepkg->name = "unsafe";
+
+ goroot = getgoroot();
+ goos = getgoos();
+ goarch = thestring;
+
+ outfile = nil;
+ ARGBEGIN {
+ default:
+ c = ARGC();
+ if(c >= 0 && c < sizeof(debug))
+ debug[c]++;
+ break;
+
+ case 'o':
+ outfile = EARGF(usage());
+ break;
+
+ case 'I':
+ addidir(EARGF(usage()));
+ break;
+
+ case 'u':
+ safemode = 1;
+ break;
+
+ case 'V':
+ print("%cg version %s\n", thechar, getgoversion());
+ exit(0);
+ } ARGEND
+
+ if(argc < 1)
+ usage();
+
+ // special flag to detect compilation of package runtime
+ compiling_runtime = debug['+'];
+
+ pathname = mal(1000);
+ if(getwd(pathname, 999) == 0)
+ strcpy(pathname, "/???");
+
+ if(yy_isalpha(pathname[0]) && pathname[1] == ':') {
+ // On Windows.
+ windows = 1;
+
+ // Canonicalize path by converting \ to / (Windows accepts both).
+ for(p=pathname; *p; p++)
+ if(*p == '\\')
+ *p = '/';
+ }
+
+ fmtinstall('O', Oconv); // node opcodes
+ fmtinstall('E', Econv); // etype opcodes
+ fmtinstall('J', Jconv); // all the node flags
+ fmtinstall('S', Sconv); // sym pointer
+ fmtinstall('T', Tconv); // type pointer
+ fmtinstall('N', Nconv); // node pointer
+ fmtinstall('Z', Zconv); // escaped string
+ fmtinstall('L', Lconv); // line number
+ fmtinstall('B', Bconv); // big numbers
+ fmtinstall('F', Fconv); // big float numbers
+
+ betypeinit();
+ if(widthptr == 0)
+ fatal("betypeinit failed");
+
+ lexinit();
+ typeinit();
+ yytinit();
+
+ blockgen = 1;
+ dclcontext = PEXTERN;
+ nerrors = 0;
+ lexlineno = 1;
+
+ for(i=0; i<argc; i++) {
+ infile = argv[i];
+ linehist(infile, 0, 0);
+
+ curio.infile = infile;
+ curio.bin = Bopen(infile, OREAD);
+ if(curio.bin == nil) {
+ print("open %s: %r\n", infile);
+ errorexit();
+ }
+ curio.peekc = 0;
+ curio.peekc1 = 0;
+ curio.nlsemi = 0;
+
+ block = 1;
+ iota = -1000000;
+
+ yyparse();
+ if(nsyntaxerrors != 0)
+ errorexit();
+
+ linehist(nil, 0, 0);
+ if(curio.bin != nil)
+ Bterm(curio.bin);
+ }
+ testdclstack();
+ mkpackage(localpkg->name); // final import not used checks
+ lexfini();
+
+ typecheckok = 1;
+ if(debug['f'])
+ frame(1);
+
+ // Process top-level declarations in four phases.
+ // Phase 1: const, type, and names and types of funcs.
+ // This will gather all the information about types
+ // and methods but doesn't depend on any of it.
+ // Phase 2: Variable assignments.
+ // To check interface assignments, depends on phase 1.
+ // Phase 3: Type check function bodies.
+ // Phase 4: Compile function bodies.
+ defercheckwidth();
+ for(l=xtop; l; l=l->next)
+ if(l->n->op != ODCL && l->n->op != OAS)
+ typecheck(&l->n, Etop);
+ for(l=xtop; l; l=l->next)
+ if(l->n->op == ODCL || l->n->op == OAS)
+ typecheck(&l->n, Etop);
+ resumetypecopy();
+ resumecheckwidth();
+
+ for(l=xtop; l; l=l->next) {
+ if(l->n->op == ODCLFUNC || l->n->op == OCLOSURE) {
+ curfn = l->n;
+ saveerrors();
+ typechecklist(l->n->nbody, Etop);
+ if(nerrors != 0)
+ l->n->nbody = nil; // type errors; do not compile
+ }
+ }
+
+ curfn = nil;
+
+ if(nsavederrors+nerrors)
+ errorexit();
+
+ for(l=xtop; l; l=l->next)
+ if(l->n->op == ODCLFUNC)
+ funccompile(l->n, 0);
+
+ if(nsavederrors+nerrors == 0)
+ fninit(xtop);
+
+ while(closures) {
+ l = closures;
+ closures = nil;
+ for(; l; l=l->next) {
+ funccompile(l->n, 1);
+ }
+ }
+
+ for(l=externdcl; l; l=l->next)
+ if(l->n->op == ONAME)
+ typecheck(&l->n, Erv);
+
+ if(nerrors+nsavederrors)
+ errorexit();
+
+ dumpobj();
+
+ if(nerrors+nsavederrors)
+ errorexit();
+
+ flusherrors();
+ exit(0);
+ return 0;
+}
+
+void
+saveerrors(void)
+{
+ nsavederrors += nerrors;
+ nerrors = 0;
+}
+
+static int
+arsize(Biobuf *b, char *name)
+{
+ struct ar_hdr *a;
+
+ if((a = Brdline(b, '\n')) == nil)
+ return -1;
+ if(Blinelen(b) != sizeof(struct ar_hdr))
+ return -1;
+ if(strncmp(a->name, name, strlen(name)) != 0)
+ return -1;
+ return atoi(a->size);
+}
+
+static int
+skiptopkgdef(Biobuf *b)
+{
+ char *p;
+ int sz;
+
+ /* archive header */
+ if((p = Brdline(b, '\n')) == nil)
+ return 0;
+ if(Blinelen(b) != 8)
+ return 0;
+ if(memcmp(p, "!<arch>\n", 8) != 0)
+ return 0;
+ /* symbol table is first; skip it */
+ sz = arsize(b, "__.SYMDEF");
+ if(sz < 0)
+ return 0;
+ Bseek(b, sz, 1);
+ /* package export block is second */
+ sz = arsize(b, "__.PKGDEF");
+ if(sz <= 0)
+ return 0;
+ return 1;
+}
+
+static void
+addidir(char* dir)
+{
+ Idir** pp;
+
+ if(dir == nil)
+ return;
+
+ for(pp = &idirs; *pp != nil; pp = &(*pp)->link)
+ ;
+ *pp = mal(sizeof(Idir));
+ (*pp)->link = nil;
+ (*pp)->dir = dir;
+}
+
+// is this path a local name? begins with ./ or ../ or /
+static int
+islocalname(Strlit *name)
+{
+ if(!windows && name->len >= 1 && name->s[0] == '/')
+ return 1;
+ if(windows && name->len >= 3 &&
+ yy_isalpha(name->s[0]) && name->s[1] == ':' && name->s[2] == '/')
+ return 1;
+ if(name->len >= 2 && strncmp(name->s, "./", 2) == 0)
+ return 1;
+ if(name->len >= 3 && strncmp(name->s, "../", 3) == 0)
+ return 1;
+ return 0;
+}
+
+static int
+findpkg(Strlit *name)
+{
+ Idir *p;
+ char *q;
+
+ if(islocalname(name)) {
+ if(safemode)
+ return 0;
+ // try .a before .6. important for building libraries:
+ // if there is an array.6 in the array.a library,
+ // want to find all of array.a, not just array.6.
+ snprint(namebuf, sizeof(namebuf), "%Z.a", name);
+ if(access(namebuf, 0) >= 0)
+ return 1;
+ snprint(namebuf, sizeof(namebuf), "%Z.%c", name, thechar);
+ if(access(namebuf, 0) >= 0)
+ return 1;
+ return 0;
+ }
+
+ // local imports should be canonicalized already.
+ // don't want to see "container/../container/vector"
+ // as different from "container/vector".
+ q = mal(name->len+1);
+ memmove(q, name->s, name->len);
+ q[name->len] = '\0';
+ cleanname(q);
+ if(strlen(q) != name->len || memcmp(q, name->s, name->len) != 0) {
+ yyerror("non-canonical import path %Z (should be %s)", name, q);
+ return 0;
+ }
+
+ for(p = idirs; p != nil; p = p->link) {
+ snprint(namebuf, sizeof(namebuf), "%s/%Z.a", p->dir, name);
+ if(access(namebuf, 0) >= 0)
+ return 1;
+ snprint(namebuf, sizeof(namebuf), "%s/%Z.%c", p->dir, name, thechar);
+ if(access(namebuf, 0) >= 0)
+ return 1;
+ }
+ if(goroot != nil) {
+ snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s/%Z.a", goroot, goos, goarch, name);
+ if(access(namebuf, 0) >= 0)
+ return 1;
+ snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s/%Z.%c", goroot, goos, goarch, name, thechar);
+ if(access(namebuf, 0) >= 0)
+ return 1;
+ }
+ return 0;
+}
+
+void
+importfile(Val *f, int line)
+{
+ Biobuf *imp;
+ char *file, *p, *q;
+ int32 c;
+ int len;
+ Strlit *path;
+ char *cleanbuf;
+
+ // TODO(rsc): don't bother reloading imports more than once?
+
+ if(f->ctype != CTSTR) {
+ yyerror("import statement not a string");
+ return;
+ }
+
+ if(strlen(f->u.sval->s) != f->u.sval->len) {
+ yyerror("import path contains NUL");
+ errorexit();
+ }
+
+ // The package name main is no longer reserved,
+ // but we reserve the import path "main" to identify
+ // the main package, just as we reserve the import
+ // path "math" to identify the standard math package.
+ if(strcmp(f->u.sval->s, "main") == 0) {
+ yyerror("cannot import \"main\"");
+ errorexit();
+ }
+
+ if(strcmp(f->u.sval->s, "unsafe") == 0) {
+ if(safemode) {
+ yyerror("cannot import package unsafe");
+ errorexit();
+ }
+ importpkg = mkpkg(f->u.sval);
+ cannedimports("unsafe.6", unsafeimport);
+ return;
+ }
+
+ path = f->u.sval;
+ if(islocalname(path)) {
+ cleanbuf = mal(strlen(pathname) + strlen(path->s) + 2);
+ strcpy(cleanbuf, pathname);
+ strcat(cleanbuf, "/");
+ strcat(cleanbuf, path->s);
+ cleanname(cleanbuf);
+ path = strlit(cleanbuf);
+ }
+
+ if(!findpkg(path)) {
+ yyerror("can't find import: %Z", f->u.sval);
+ errorexit();
+ }
+ importpkg = mkpkg(path);
+
+ imp = Bopen(namebuf, OREAD);
+ if(imp == nil) {
+ yyerror("can't open import: %Z: %r", f->u.sval);
+ errorexit();
+ }
+ file = strdup(namebuf);
+
+ len = strlen(namebuf);
+ if(len > 2 && namebuf[len-2] == '.' && namebuf[len-1] == 'a') {
+ if(!skiptopkgdef(imp)) {
+ yyerror("import %s: not a package file", file);
+ errorexit();
+ }
+ }
+
+ // check object header
+ p = Brdstr(imp, '\n', 1);
+ if(strcmp(p, "empty archive") != 0) {
+ if(strncmp(p, "go object ", 10) != 0) {
+ yyerror("import %s: not a go object file", file);
+ errorexit();
+ }
+ q = smprint("%s %s %s", getgoos(), thestring, getgoversion());
+ if(strcmp(p+10, q) != 0) {
+ yyerror("import %s: object is [%s] expected [%s]", file, p+10, q);
+ errorexit();
+ }
+ free(q);
+ }
+
+ // assume files move (get installed)
+ // so don't record the full path.
+ linehist(file + len - path->len - 2, -1, 1); // acts as #pragma lib
+
+ /*
+ * position the input right
+ * after $$ and return
+ */
+ pushedio = curio;
+ curio.bin = imp;
+ curio.peekc = 0;
+ curio.peekc1 = 0;
+ curio.infile = file;
+ curio.nlsemi = 0;
+ typecheckok = 1;
+
+ for(;;) {
+ c = getc();
+ if(c == EOF)
+ break;
+ if(c != '$')
+ continue;
+ c = getc();
+ if(c == EOF)
+ break;
+ if(c != '$')
+ continue;
+ return;
+ }
+ yyerror("no import in: %Z", f->u.sval);
+ unimportfile();
+}
+
+void
+unimportfile(void)
+{
+ if(curio.bin != nil) {
+ Bterm(curio.bin);
+ curio.bin = nil;
+ } else
+ lexlineno--; // re correct sys.6 line number
+
+ curio = pushedio;
+ pushedio.bin = nil;
+ incannedimport = 0;
+ typecheckok = 0;
+}
+
+void
+cannedimports(char *file, char *cp)
+{
+ lexlineno++; // if sys.6 is included on line 1,
+
+ pushedio = curio;
+ curio.bin = nil;
+ curio.peekc = 0;
+ curio.peekc1 = 0;
+ curio.infile = file;
+ curio.cp = cp;
+ curio.nlsemi = 0;
+ curio.importsafe = 0;
+
+ typecheckok = 1;
+ incannedimport = 1;
+}
+
+static int
+isfrog(int c)
+{
+ // complain about possibly invisible control characters
+ if(c < 0)
+ return 1;
+ if(c < ' ') {
+ if(c == '\n' || c== '\r' || c == '\t') // good white space
+ return 0;
+ return 1;
+ }
+ if(0x7f <= c && c <= 0xa0) // DEL, unicode block including unbreakable space.
+ return 1;
+ return 0;
+}
+
+typedef struct Loophack Loophack;
+struct Loophack {
+ int v;
+ Loophack *next;
+};
+
+static int32
+_yylex(void)
+{
+ int c, c1, clen, escflag, ncp;
+ vlong v;
+ char *cp, *ep;
+ Rune rune;
+ Sym *s;
+ static Loophack *lstk;
+ Loophack *h;
+
+ prevlineno = lineno;
+
+l0:
+ c = getc();
+ if(yy_isspace(c)) {
+ if(c == '\n' && curio.nlsemi) {
+ ungetc(c);
+ DBG("lex: implicit semi\n");
+ return ';';
+ }
+ goto l0;
+ }
+
+ lineno = lexlineno; /* start of token */
+
+ if(c >= Runeself) {
+ /* all multibyte runes are alpha */
+ cp = lexbuf;
+ ep = lexbuf+sizeof lexbuf;
+ goto talph;
+ }
+
+ if(yy_isalpha(c)) {
+ cp = lexbuf;
+ ep = lexbuf+sizeof lexbuf;
+ goto talph;
+ }
+
+ if(yy_isdigit(c))
+ goto tnum;
+
+ switch(c) {
+ case EOF:
+ lineno = prevlineno;
+ ungetc(EOF);
+ return -1;
+
+ case '_':
+ cp = lexbuf;
+ ep = lexbuf+sizeof lexbuf;
+ goto talph;
+
+ case '.':
+ c1 = getc();
+ if(yy_isdigit(c1)) {
+ cp = lexbuf;
+ ep = lexbuf+sizeof lexbuf;
+ *cp++ = c;
+ c = c1;
+ c1 = 0;
+ goto casedot;
+ }
+ if(c1 == '.') {
+ c1 = getc();
+ if(c1 == '.') {
+ c = LDDD;
+ goto lx;
+ }
+ ungetc(c1);
+ c1 = '.';
+ }
+ break;
+
+ case '"':
+ /* "..." */
+ strcpy(lexbuf, "\"<string>\"");
+ cp = mal(8);
+ clen = sizeof(int32);
+ ncp = 8;
+
+ for(;;) {
+ if(clen+UTFmax > ncp) {
+ cp = remal(cp, ncp, ncp);
+ ncp += ncp;
+ }
+ if(escchar('"', &escflag, &v))
+ break;
+ if(v < Runeself || escflag) {
+ cp[clen++] = v;
+ } else {
+ rune = v;
+ c = runelen(rune);
+ runetochar(cp+clen, &rune);
+ clen += c;
+ }
+ }
+ goto strlit;
+
+ case '`':
+ /* `...` */
+ strcpy(lexbuf, "`<string>`");
+ cp = mal(8);
+ clen = sizeof(int32);
+ ncp = 8;
+
+ for(;;) {
+ if(clen+UTFmax > ncp) {
+ cp = remal(cp, ncp, ncp);
+ ncp += ncp;
+ }
+ c = getr();
+ if(c == EOF) {
+ yyerror("eof in string");
+ break;
+ }
+ if(c == '`')
+ break;
+ rune = c;
+ clen += runetochar(cp+clen, &rune);
+ }
+
+ strlit:
+ *(int32*)cp = clen-sizeof(int32); // length
+ do {
+ cp[clen++] = 0;
+ } while(clen & MAXALIGN);
+ yylval.val.u.sval = (Strlit*)cp;
+ yylval.val.ctype = CTSTR;
+ DBG("lex: string literal\n");
+ strcpy(litbuf, "string literal");
+ return LLITERAL;
+
+ case '\'':
+ /* '.' */
+ if(escchar('\'', &escflag, &v)) {
+ yyerror("empty character literal or unescaped ' in character literal");
+ v = '\'';
+ }
+ if(!escchar('\'', &escflag, &v)) {
+ yyerror("missing '");
+ ungetc(v);
+ }
+ yylval.val.u.xval = mal(sizeof(*yylval.val.u.xval));
+ mpmovecfix(yylval.val.u.xval, v);
+ yylval.val.ctype = CTINT;
+ DBG("lex: codepoint literal\n");
+ strcpy(litbuf, "string literal");
+ return LLITERAL;
+
+ case '/':
+ c1 = getc();
+ if(c1 == '*') {
+ int nl;
+
+ nl = 0;
+ for(;;) {
+ c = getr();
+ if(c == '\n')
+ nl = 1;
+ while(c == '*') {
+ c = getr();
+ if(c == '/') {
+ if(nl)
+ ungetc('\n');
+ goto l0;
+ }
+ if(c == '\n')
+ nl = 1;
+ }
+ if(c == EOF) {
+ yyerror("eof in comment");
+ errorexit();
+ }
+ }
+ }
+ if(c1 == '/') {
+ c = getlinepragma();
+ for(;;) {
+ if(c == '\n' || c == EOF) {
+ ungetc(c);
+ goto l0;
+ }
+ c = getr();
+ }
+ }
+ if(c1 == '=') {
+ c = ODIV;
+ goto asop;
+ }
+ break;
+
+ case ':':
+ c1 = getc();
+ if(c1 == '=') {
+ c = LCOLAS;
+ goto lx;
+ }
+ break;
+
+ case '*':
+ c1 = getc();
+ if(c1 == '=') {
+ c = OMUL;
+ goto asop;
+ }
+ break;
+
+ case '%':
+ c1 = getc();
+ if(c1 == '=') {
+ c = OMOD;
+ goto asop;
+ }
+ break;
+
+ case '+':
+ c1 = getc();
+ if(c1 == '+') {
+ c = LINC;
+ goto lx;
+ }
+ if(c1 == '=') {
+ c = OADD;
+ goto asop;
+ }
+ break;
+
+ case '-':
+ c1 = getc();
+ if(c1 == '-') {
+ c = LDEC;
+ goto lx;
+ }
+ if(c1 == '=') {
+ c = OSUB;
+ goto asop;
+ }
+ break;
+
+ case '>':
+ c1 = getc();
+ if(c1 == '>') {
+ c = LRSH;
+ c1 = getc();
+ if(c1 == '=') {
+ c = ORSH;
+ goto asop;
+ }
+ break;
+ }
+ if(c1 == '=') {
+ c = LGE;
+ goto lx;
+ }
+ c = LGT;
+ break;
+
+ case '<':
+ c1 = getc();
+ if(c1 == '<') {
+ c = LLSH;
+ c1 = getc();
+ if(c1 == '=') {
+ c = OLSH;
+ goto asop;
+ }
+ break;
+ }
+ if(c1 == '=') {
+ c = LLE;
+ goto lx;
+ }
+ if(c1 == '-') {
+ c = LCOMM;
+ goto lx;
+ }
+ c = LLT;
+ break;
+
+ case '=':
+ c1 = getc();
+ if(c1 == '=') {
+ c = LEQ;
+ goto lx;
+ }
+ break;
+
+ case '!':
+ c1 = getc();
+ if(c1 == '=') {
+ c = LNE;
+ goto lx;
+ }
+ break;
+
+ case '&':
+ c1 = getc();
+ if(c1 == '&') {
+ c = LANDAND;
+ goto lx;
+ }
+ if(c1 == '^') {
+ c = LANDNOT;
+ c1 = getc();
+ if(c1 == '=') {
+ c = OANDNOT;
+ goto asop;
+ }
+ break;
+ }
+ if(c1 == '=') {
+ c = OAND;
+ goto asop;
+ }
+ break;
+
+ case '|':
+ c1 = getc();
+ if(c1 == '|') {
+ c = LOROR;
+ goto lx;
+ }
+ if(c1 == '=') {
+ c = OOR;
+ goto asop;
+ }
+ break;
+
+ case '^':
+ c1 = getc();
+ if(c1 == '=') {
+ c = OXOR;
+ goto asop;
+ }
+ break;
+
+ /*
+ * clumsy dance:
+ * to implement rule that disallows
+ * if T{1}[0] { ... }
+ * but allows
+ * if (T{1}[0]) { ... }
+ * the block bodies for if/for/switch/select
+ * begin with an LBODY token, not '{'.
+ *
+ * when we see the keyword, the next
+ * non-parenthesized '{' becomes an LBODY.
+ * loophack is normally 0.
+ * a keyword makes it go up to 1.
+ * parens push loophack onto a stack and go back to 0.
+ * a '{' with loophack == 1 becomes LBODY and disables loophack.
+ *
+ * i said it was clumsy.
+ */
+ case '(':
+ case '[':
+ if(loophack || lstk != nil) {
+ h = malloc(sizeof *h);
+ h->v = loophack;
+ h->next = lstk;
+ lstk = h;
+ loophack = 0;
+ }
+ goto lx;
+ case ')':
+ case ']':
+ if(lstk != nil) {
+ h = lstk;
+ loophack = h->v;
+ lstk = h->next;
+ free(h);
+ }
+ goto lx;
+ case '{':
+ if(loophack == 1) {
+ DBG("%L lex: LBODY\n", lexlineno);
+ loophack = 0;
+ return LBODY;
+ }
+ goto lx;
+
+ default:
+ goto lx;
+ }
+ ungetc(c1);
+
+lx:
+ if(c > 0xff)
+ DBG("%L lex: TOKEN %s\n", lexlineno, lexname(c));
+ else
+ DBG("%L lex: TOKEN '%c'\n", lexlineno, c);
+ if(isfrog(c)) {
+ yyerror("illegal character 0x%ux", c);
+ goto l0;
+ }
+ if(importpkg == nil && (c == '#' || c == '$' || c == '?' || c == '@' || c == '\\')) {
+ yyerror("%s: unexpected %c", "syntax error", c);
+ goto l0;
+ }
+ return c;
+
+asop:
+ yylval.lint = c; // rathole to hold which asop
+ DBG("lex: TOKEN ASOP %c\n", c);
+ return LASOP;
+
+talph:
+ /*
+ * cp is set to lexbuf and some
+ * prefix has been stored
+ */
+ for(;;) {
+ if(cp+10 >= ep) {
+ yyerror("identifier too long");
+ errorexit();
+ }
+ if(c >= Runeself) {
+ ungetc(c);
+ rune = getr();
+ // 0xb7 · is used for internal names
+ if(!isalpharune(rune) && !isdigitrune(rune) && (importpkg == nil || rune != 0xb7))
+ yyerror("invalid identifier character 0x%ux", rune);
+ cp += runetochar(cp, &rune);
+ } else if(!yy_isalnum(c) && c != '_')
+ break;
+ else
+ *cp++ = c;
+ c = getc();
+ }
+ *cp = 0;
+ ungetc(c);
+
+ s = lookup(lexbuf);
+ switch(s->lexical) {
+ case LIGNORE:
+ goto l0;
+
+ case LFOR:
+ case LIF:
+ case LSWITCH:
+ case LSELECT:
+ loophack = 1; // see comment about loophack above
+ break;
+ }
+
+ DBG("lex: %S %s\n", s, lexname(s->lexical));
+ yylval.sym = s;
+ return s->lexical;
+
+tnum:
+ c1 = 0;
+ cp = lexbuf;
+ ep = lexbuf+sizeof lexbuf;
+ if(c != '0') {
+ for(;;) {
+ if(cp+10 >= ep) {
+ yyerror("identifier too long");
+ errorexit();
+ }
+ *cp++ = c;
+ c = getc();
+ if(yy_isdigit(c))
+ continue;
+ goto dc;
+ }
+ }
+ *cp++ = c;
+ c = getc();
+ if(c == 'x' || c == 'X') {
+ for(;;) {
+ if(cp+10 >= ep) {
+ yyerror("identifier too long");
+ errorexit();
+ }
+ *cp++ = c;
+ c = getc();
+ if(yy_isdigit(c))
+ continue;
+ if(c >= 'a' && c <= 'f')
+ continue;
+ if(c >= 'A' && c <= 'F')
+ continue;
+ if(cp == lexbuf+2)
+ yyerror("malformed hex constant");
+ goto ncu;
+ }
+ }
+
+ if(c == 'p') // 0p begins floating point zero
+ goto casep;
+
+ c1 = 0;
+ for(;;) {
+ if(cp+10 >= ep) {
+ yyerror("identifier too long");
+ errorexit();
+ }
+ if(!yy_isdigit(c))
+ break;
+ if(c < '0' || c > '7')
+ c1 = 1; // not octal
+ *cp++ = c;
+ c = getc();
+ }
+ if(c == '.')
+ goto casedot;
+ if(c == 'e' || c == 'E')
+ goto casee;
+ if(c == 'i')
+ goto casei;
+ if(c1)
+ yyerror("malformed octal constant");
+ goto ncu;
+
+dc:
+ if(c == '.')
+ goto casedot;
+ if(c == 'e' || c == 'E')
+ goto casee;
+ if(c == 'p' || c == 'P')
+ goto casep;
+ if(c == 'i')
+ goto casei;
+
+ncu:
+ *cp = 0;
+ ungetc(c);
+
+ yylval.val.u.xval = mal(sizeof(*yylval.val.u.xval));
+ mpatofix(yylval.val.u.xval, lexbuf);
+ if(yylval.val.u.xval->ovf) {
+ yyerror("overflow in constant");
+ mpmovecfix(yylval.val.u.xval, 0);
+ }
+ yylval.val.ctype = CTINT;
+ DBG("lex: integer literal\n");
+ strcpy(litbuf, "literal ");
+ strcat(litbuf, lexbuf);
+ return LLITERAL;
+
+casedot:
+ for(;;) {
+ if(cp+10 >= ep) {
+ yyerror("identifier too long");
+ errorexit();
+ }
+ *cp++ = c;
+ c = getc();
+ if(!yy_isdigit(c))
+ break;
+ }
+ if(c == 'i')
+ goto casei;
+ if(c != 'e' && c != 'E')
+ goto caseout;
+
+casee:
+ *cp++ = 'e';
+ c = getc();
+ if(c == '+' || c == '-') {
+ *cp++ = c;
+ c = getc();
+ }
+ if(!yy_isdigit(c))
+ yyerror("malformed fp constant exponent");
+ while(yy_isdigit(c)) {
+ if(cp+10 >= ep) {
+ yyerror("identifier too long");
+ errorexit();
+ }
+ *cp++ = c;
+ c = getc();
+ }
+ if(c == 'i')
+ goto casei;
+ goto caseout;
+
+casep:
+ *cp++ = 'p';
+ c = getc();
+ if(c == '+' || c == '-') {
+ *cp++ = c;
+ c = getc();
+ }
+ if(!yy_isdigit(c))
+ yyerror("malformed fp constant exponent");
+ while(yy_isdigit(c)) {
+ if(cp+10 >= ep) {
+ yyerror("identifier too long");
+ errorexit();
+ }
+ *cp++ = c;
+ c = getc();
+ }
+ if(c == 'i')
+ goto casei;
+ goto caseout;
+
+casei:
+ // imaginary constant
+ *cp = 0;
+ yylval.val.u.cval = mal(sizeof(*yylval.val.u.cval));
+ mpmovecflt(&yylval.val.u.cval->real, 0.0);
+ mpatoflt(&yylval.val.u.cval->imag, lexbuf);
+ if(yylval.val.u.cval->imag.val.ovf) {
+ yyerror("overflow in imaginary constant");
+ mpmovecflt(&yylval.val.u.cval->real, 0.0);
+ }
+ yylval.val.ctype = CTCPLX;
+ DBG("lex: imaginary literal\n");
+ strcpy(litbuf, "literal ");
+ strcat(litbuf, lexbuf);
+ return LLITERAL;
+
+caseout:
+ *cp = 0;
+ ungetc(c);
+
+ yylval.val.u.fval = mal(sizeof(*yylval.val.u.fval));
+ mpatoflt(yylval.val.u.fval, lexbuf);
+ if(yylval.val.u.fval->val.ovf) {
+ yyerror("overflow in float constant");
+ mpmovecflt(yylval.val.u.fval, 0.0);
+ }
+ yylval.val.ctype = CTFLT;
+ DBG("lex: floating literal\n");
+ strcpy(litbuf, "literal ");
+ strcat(litbuf, lexbuf);
+ return LLITERAL;
+}
+
+/*
+ * read and interpret syntax that looks like
+ * //line parse.y:15
+ * as a discontinuity in sequential line numbers.
+ * the next line of input comes from parse.y:15
+ */
+static int
+getlinepragma(void)
+{
+ int i, c, n;
+ char *cp, *ep;
+ Hist *h;
+
+ for(i=0; i<5; i++) {
+ c = getr();
+ if(c != "line "[i])
+ goto out;
+ }
+
+ cp = lexbuf;
+ ep = lexbuf+sizeof(lexbuf)-5;
+ for(;;) {
+ c = getr();
+ if(c == '\n' || c == EOF)
+ goto out;
+ if(c == ' ')
+ continue;
+ if(c == ':')
+ break;
+ if(cp < ep)
+ *cp++ = c;
+ }
+ *cp = 0;
+
+ n = 0;
+ for(;;) {
+ c = getr();
+ if(!yy_isdigit(c))
+ break;
+ n = n*10 + (c-'0');
+ if(n > 1e8) {
+ yyerror("line number out of range");
+ errorexit();
+ }
+ }
+
+ if(c != '\n' || n <= 0)
+ goto out;
+
+ // try to avoid allocating file name over and over
+ for(h=hist; h!=H; h=h->link) {
+ if(h->name != nil && strcmp(h->name, lexbuf) == 0) {
+ linehist(h->name, n, 0);
+ goto out;
+ }
+ }
+ linehist(strdup(lexbuf), n, 0);
+
+out:
+ return c;
+}
+
+int32
+yylex(void)
+{
+ int lx;
+
+ lx = _yylex();
+
+ if(curio.nlsemi && lx == EOF) {
+ // Treat EOF as "end of line" for the purposes
+ // of inserting a semicolon.
+ lx = ';';
+ }
+
+ switch(lx) {
+ case LNAME:
+ case LLITERAL:
+ case LBREAK:
+ case LCONTINUE:
+ case LFALL:
+ case LRETURN:
+ case LINC:
+ case LDEC:
+ case ')':
+ case '}':
+ case ']':
+ curio.nlsemi = 1;
+ break;
+ default:
+ curio.nlsemi = 0;
+ break;
+ }
+
+ // Track last two tokens returned by yylex.
+ yyprev = yylast;
+ yylast = lx;
+ return lx;
+}
+
+static int
+getc(void)
+{
+ int c;
+
+ c = curio.peekc;
+ if(c != 0) {
+ curio.peekc = curio.peekc1;
+ curio.peekc1 = 0;
+ if(c == '\n' && pushedio.bin == nil)
+ lexlineno++;
+ return c;
+ }
+
+ if(curio.bin == nil) {
+ c = *curio.cp & 0xff;
+ if(c != 0)
+ curio.cp++;
+ } else
+ c = Bgetc(curio.bin);
+
+ switch(c) {
+ case 0:
+ if(curio.bin != nil) {
+ yyerror("illegal NUL byte");
+ break;
+ }
+ case EOF:
+ // insert \n at EOF
+ if(curio.eofnl)
+ return EOF;
+ curio.eofnl = 1;
+ c = '\n';
+ case '\n':
+ if(pushedio.bin == nil)
+ lexlineno++;
+ break;
+ }
+ return c;
+}
+
+static void
+ungetc(int c)
+{
+ curio.peekc1 = curio.peekc;
+ curio.peekc = c;
+ if(c == '\n' && pushedio.bin == nil)
+ lexlineno--;
+}
+
+static int32
+getr(void)
+{
+ int c, i;
+ char str[UTFmax+1];
+ Rune rune;
+
+ c = getc();
+ if(c < Runeself)
+ return c;
+ i = 0;
+ str[i++] = c;
+
+loop:
+ c = getc();
+ str[i++] = c;
+ if(!fullrune(str, i))
+ goto loop;
+ c = chartorune(&rune, str);
+ if(rune == Runeerror && c == 1) {
+ lineno = lexlineno;
+ yyerror("illegal UTF-8 sequence");
+ flusherrors();
+ print("\t");
+ for(c=0; c<i; c++)
+ print("%s%.2x", c > 0 ? " " : "", *(uchar*)(str+c));
+ print("\n");
+ }
+ return rune;
+}
+
+static int
+escchar(int e, int *escflg, vlong *val)
+{
+ int i, u, c;
+ vlong l;
+
+ *escflg = 0;
+
+ c = getr();
+ switch(c) {
+ case EOF:
+ yyerror("eof in string");
+ return 1;
+ case '\n':
+ yyerror("newline in string");
+ return 1;
+ case '\\':
+ break;
+ default:
+ if(c == e)
+ return 1;
+ *val = c;
+ return 0;
+ }
+
+ u = 0;
+ c = getr();
+ switch(c) {
+ case 'x':
+ *escflg = 1; // it's a byte
+ i = 2;
+ goto hex;
+
+ case 'u':
+ i = 4;
+ u = 1;
+ goto hex;
+
+ case 'U':
+ i = 8;
+ u = 1;
+ goto hex;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ *escflg = 1; // it's a byte
+ goto oct;
+
+ case 'a': c = '\a'; break;
+ case 'b': c = '\b'; break;
+ case 'f': c = '\f'; break;
+ case 'n': c = '\n'; break;
+ case 'r': c = '\r'; break;
+ case 't': c = '\t'; break;
+ case 'v': c = '\v'; break;
+ case '\\': c = '\\'; break;
+
+ default:
+ if(c != e)
+ yyerror("unknown escape sequence: %c", c);
+ }
+ *val = c;
+ return 0;
+
+hex:
+ l = 0;
+ for(; i>0; i--) {
+ c = getc();
+ if(c >= '0' && c <= '9') {
+ l = l*16 + c-'0';
+ continue;
+ }
+ if(c >= 'a' && c <= 'f') {
+ l = l*16 + c-'a' + 10;
+ continue;
+ }
+ if(c >= 'A' && c <= 'F') {
+ l = l*16 + c-'A' + 10;
+ continue;
+ }
+ yyerror("non-hex character in escape sequence: %c", c);
+ ungetc(c);
+ break;
+ }
+ if(u && (l > Runemax || (0xd800 <= l && l < 0xe000))) {
+ yyerror("invalid Unicode code point in escape sequence: %#llx", l);
+ l = Runeerror;
+ }
+ *val = l;
+ return 0;
+
+oct:
+ l = c - '0';
+ for(i=2; i>0; i--) {
+ c = getc();
+ if(c >= '0' && c <= '7') {
+ l = l*8 + c-'0';
+ continue;
+ }
+ yyerror("non-octal character in escape sequence: %c", c);
+ ungetc(c);
+ }
+ if(l > 255)
+ yyerror("octal escape value > 255: %d", l);
+
+ *val = l;
+ return 0;
+}
+
+static struct
+{
+ char* name;
+ int lexical;
+ int etype;
+ int op;
+} syms[] =
+{
+/* name lexical etype op
+ */
+/* basic types */
+ "int8", LNAME, TINT8, OXXX,
+ "int16", LNAME, TINT16, OXXX,
+ "int32", LNAME, TINT32, OXXX,
+ "int64", LNAME, TINT64, OXXX,
+
+ "uint8", LNAME, TUINT8, OXXX,
+ "uint16", LNAME, TUINT16, OXXX,
+ "uint32", LNAME, TUINT32, OXXX,
+ "uint64", LNAME, TUINT64, OXXX,
+
+ "float32", LNAME, TFLOAT32, OXXX,
+ "float64", LNAME, TFLOAT64, OXXX,
+
+ "complex64", LNAME, TCOMPLEX64, OXXX,
+ "complex128", LNAME, TCOMPLEX128, OXXX,
+
+ "bool", LNAME, TBOOL, OXXX,
+ "byte", LNAME, TUINT8, OXXX,
+ "string", LNAME, TSTRING, OXXX,
+
+ "any", LNAME, TANY, OXXX,
+
+ "break", LBREAK, Txxx, OXXX,
+ "case", LCASE, Txxx, OXXX,
+ "chan", LCHAN, Txxx, OXXX,
+ "const", LCONST, Txxx, OXXX,
+ "continue", LCONTINUE, Txxx, OXXX,
+ "default", LDEFAULT, Txxx, OXXX,
+ "else", LELSE, Txxx, OXXX,
+ "defer", LDEFER, Txxx, OXXX,
+ "fallthrough", LFALL, Txxx, OXXX,
+ "for", LFOR, Txxx, OXXX,
+ "func", LFUNC, Txxx, OXXX,
+ "go", LGO, Txxx, OXXX,
+ "goto", LGOTO, Txxx, OXXX,
+ "if", LIF, Txxx, OXXX,
+ "import", LIMPORT, Txxx, OXXX,
+ "interface", LINTERFACE, Txxx, OXXX,
+ "map", LMAP, Txxx, OXXX,
+ "package", LPACKAGE, Txxx, OXXX,
+ "range", LRANGE, Txxx, OXXX,
+ "return", LRETURN, Txxx, OXXX,
+ "select", LSELECT, Txxx, OXXX,
+ "struct", LSTRUCT, Txxx, OXXX,
+ "switch", LSWITCH, Txxx, OXXX,
+ "type", LTYPE, Txxx, OXXX,
+ "var", LVAR, Txxx, OXXX,
+
+ "append", LNAME, Txxx, OAPPEND,
+ "cap", LNAME, Txxx, OCAP,
+ "close", LNAME, Txxx, OCLOSE,
+ "complex", LNAME, Txxx, OCOMPLEX,
+ "copy", LNAME, Txxx, OCOPY,
+ "imag", LNAME, Txxx, OIMAG,
+ "len", LNAME, Txxx, OLEN,
+ "make", LNAME, Txxx, OMAKE,
+ "new", LNAME, Txxx, ONEW,
+ "panic", LNAME, Txxx, OPANIC,
+ "print", LNAME, Txxx, OPRINT,
+ "println", LNAME, Txxx, OPRINTN,
+ "real", LNAME, Txxx, OREAL,
+ "recover", LNAME, Txxx, ORECOVER,
+
+ "notwithstanding", LIGNORE, Txxx, OXXX,
+ "thetruthofthematter", LIGNORE, Txxx, OXXX,
+ "despiteallobjections", LIGNORE, Txxx, OXXX,
+ "whereas", LIGNORE, Txxx, OXXX,
+ "insofaras", LIGNORE, Txxx, OXXX,
+};
+
+static void
+lexinit(void)
+{
+ int i, lex;
+ Sym *s, *s1;
+ Type *t;
+ int etype;
+
+ /*
+ * initialize basic types array
+ * initialize known symbols
+ */
+ for(i=0; i<nelem(syms); i++) {
+ lex = syms[i].lexical;
+ s = lookup(syms[i].name);
+ s->lexical = lex;
+
+ etype = syms[i].etype;
+ if(etype != Txxx) {
+ if(etype < 0 || etype >= nelem(types))
+ fatal("lexinit: %s bad etype", s->name);
+ t = types[etype];
+ if(t == T) {
+ t = typ(etype);
+ t->sym = s;
+
+ if(etype != TANY && etype != TSTRING)
+ dowidth(t);
+ types[etype] = t;
+ }
+ s1 = pkglookup(syms[i].name, builtinpkg);
+ s1->lexical = LNAME;
+ s1->def = typenod(t);
+ continue;
+ }
+ }
+
+ // logically, the type of a string literal.
+ // types[TSTRING] is the named type string
+ // (the type of x in var x string or var x = "hello").
+ // this is the ideal form
+ // (the type of x in const x = "hello").
+ idealstring = typ(TSTRING);
+ idealbool = typ(TBOOL);
+
+ s = pkglookup("true", builtinpkg);
+ s->def = nodbool(1);
+ s->def->sym = lookup("true");
+ s->def->type = idealbool;
+
+ s = pkglookup("false", builtinpkg);
+ s->def = nodbool(0);
+ s->def->sym = lookup("false");
+ s->def->type = idealbool;
+
+ s = lookup("_");
+ s->block = -100;
+ s->def = nod(ONAME, N, N);
+ s->def->sym = s;
+ types[TBLANK] = typ(TBLANK);
+ s->def->type = types[TBLANK];
+ nblank = s->def;
+}
+
+static void
+lexfini(void)
+{
+ Sym *s;
+ int lex, etype, i;
+ Val v;
+
+ for(i=0; i<nelem(syms); i++) {
+ lex = syms[i].lexical;
+ if(lex != LNAME)
+ continue;
+ s = lookup(syms[i].name);
+ s->lexical = lex;
+
+ etype = syms[i].etype;
+ if(etype != Txxx && (etype != TANY || debug['A']) && s->def == N)
+ s->def = typenod(types[etype]);
+
+ etype = syms[i].op;
+ if(etype != OXXX && s->def == N) {
+ s->def = nod(ONAME, N, N);
+ s->def->sym = s;
+ s->def->etype = etype;
+ s->def->builtin = 1;
+ }
+ }
+
+ for(i=0; typedefs[i].name; i++) {
+ s = lookup(typedefs[i].name);
+ if(s->def == N)
+ s->def = typenod(types[typedefs[i].etype]);
+ }
+
+ // there's only so much table-driven we can handle.
+ // these are special cases.
+ types[TNIL] = typ(TNIL);
+ s = lookup("nil");
+ if(s->def == N) {
+ v.ctype = CTNIL;
+ s->def = nodlit(v);
+ s->def->sym = s;
+ }
+
+ s = lookup("iota");
+ if(s->def == N) {
+ s->def = nod(OIOTA, N, N);
+ s->def->sym = s;
+ }
+
+ s = lookup("true");
+ if(s->def == N) {
+ s->def = nodbool(1);
+ s->def->sym = s;
+ }
+
+ s = lookup("false");
+ if(s->def == N) {
+ s->def = nodbool(0);
+ s->def->sym = s;
+ }
+
+ nodfp = nod(ONAME, N, N);
+ nodfp->noescape = 1;
+ nodfp->type = types[TINT32];
+ nodfp->xoffset = 0;
+ nodfp->class = PPARAM;
+ nodfp->sym = lookup(".fp");
+}
+
+struct
+{
+ int lex;
+ char* name;
+} lexn[] =
+{
+ LANDAND, "ANDAND",
+ LASOP, "ASOP",
+ LBREAK, "BREAK",
+ LCASE, "CASE",
+ LCHAN, "CHAN",
+ LCOLAS, "COLAS",
+ LCONST, "CONST",
+ LCONTINUE, "CONTINUE",
+ LDEC, "DEC",
+ LDEFER, "DEFER",
+ LELSE, "ELSE",
+ LEQ, "EQ",
+ LFALL, "FALL",
+ LFOR, "FOR",
+ LFUNC, "FUNC",
+ LGE, "GE",
+ LGO, "GO",
+ LGOTO, "GOTO",
+ LGT, "GT",
+ LIF, "IF",
+ LIMPORT, "IMPORT",
+ LINC, "INC",
+ LINTERFACE, "INTERFACE",
+ LLE, "LE",
+ LLITERAL, "LITERAL",
+ LLSH, "LSH",
+ LLT, "LT",
+ LMAP, "MAP",
+ LNAME, "NAME",
+ LNE, "NE",
+ LOROR, "OROR",
+ LPACKAGE, "PACKAGE",
+ LRANGE, "RANGE",
+ LRETURN, "RETURN",
+ LRSH, "RSH",
+ LSTRUCT, "STRUCT",
+ LSWITCH, "SWITCH",
+ LTYPE, "TYPE",
+ LVAR, "VAR",
+};
+
+char*
+lexname(int lex)
+{
+ int i;
+ static char buf[100];
+
+ for(i=0; i<nelem(lexn); i++)
+ if(lexn[i].lex == lex)
+ return lexn[i].name;
+ snprint(buf, sizeof(buf), "LEX-%d", lex);
+ return buf;
+}
+
+struct
+{
+ char *have;
+ char *want;
+} yytfix[] =
+{
+ "$end", "EOF",
+ "LLITERAL", "literal",
+ "LASOP", "op=",
+ "LBREAK", "break",
+ "LCASE", "case",
+ "LCOLAS", ":=",
+ "LCONST", "const",
+ "LCONTINUE", "continue",
+ "LDDD", "...",
+ "LDEFAULT", "default",
+ "LDEFER", "defer",
+ "LELSE", "else",
+ "LFALL", "fallthrough",
+ "LFOR", "for",
+ "LFUNC", "func",
+ "LGO", "go",
+ "LGOTO", "goto",
+ "LIF", "if",
+ "LIMPORT", "import",
+ "LINTERFACE", "interface",
+ "LMAP", "map",
+ "LNAME", "name",
+ "LPACKAGE", "package",
+ "LRANGE", "range",
+ "LRETURN", "return",
+ "LSELECT", "select",
+ "LSTRUCT", "struct",
+ "LSWITCH", "switch",
+ "LTYPE", "type",
+ "LVAR", "var",
+ "LANDAND", "&&",
+ "LANDNOT", "&^",
+ "LBODY", "{",
+ "LCOMM", "<-",
+ "LDEC", "--",
+ "LINC", "++",
+ "LEQ", "==",
+ "LGE", ">=",
+ "LGT", ">",
+ "LLE", "<=",
+ "LLT", "<",
+ "LLSH", "<<",
+ "LRSH", ">>",
+ "LOROR", "||",
+ "LNE", "!=",
+
+ // spell out to avoid confusion with punctuation in error messages
+ "';'", "semicolon or newline",
+ "','", "comma",
+};
+
+static void
+yytinit(void)
+{
+ int i, j;
+ extern char *yytname[];
+ char *s, *t;
+
+ for(i=0; yytname[i] != nil; i++) {
+ s = yytname[i];
+
+ if(strcmp(s, "LLITERAL") == 0) {
+ strcpy(litbuf, "literal");
+ yytname[i] = litbuf;
+ goto loop;
+ }
+
+ // apply yytfix if possible
+ for(j=0; j<nelem(yytfix); j++) {
+ if(strcmp(s, yytfix[j].have) == 0) {
+ yytname[i] = yytfix[j].want;
+ goto loop;
+ }
+ }
+
+ // turn 'x' into x.
+ if(s[0] == '\'') {
+ t = strdup(s+1);
+ t[strlen(t)-1] = '\0';
+ yytname[i] = t;
+ }
+ loop:;
+ }
+}
+
+void
+mkpackage(char* pkgname)
+{
+ Sym *s;
+ int32 h;
+ char *p;
+
+ if(localpkg->name == nil) {
+ if(strcmp(pkgname, "_") == 0)
+ yyerror("invalid package name _");
+ localpkg->name = pkgname;
+ } else {
+ if(strcmp(pkgname, localpkg->name) != 0)
+ yyerror("package %s; expected %s", pkgname, localpkg->name);
+ for(h=0; h<NHASH; h++) {
+ for(s = hash[h]; s != S; s = s->link) {
+ if(s->def == N || s->pkg != localpkg)
+ continue;
+ if(s->def->op == OPACK) {
+ // throw away top-level package name leftover
+ // from previous file.
+ // leave s->block set to cause redeclaration
+ // errors if a conflicting top-level name is
+ // introduced by a different file.
+ if(!s->def->used && !nsyntaxerrors)
+ yyerrorl(s->def->lineno, "imported and not used: %Z", s->def->pkg->path);
+ s->def = N;
+ continue;
+ }
+ if(s->def->sym != s) {
+ // throw away top-level name left over
+ // from previous import . "x"
+ if(s->def->pack != N && !s->def->pack->used && !nsyntaxerrors) {
+ yyerrorl(s->def->pack->lineno, "imported and not used: %Z", s->def->pack->pkg->path);
+ s->def->pack->used = 1;
+ }
+ s->def = N;
+ continue;
+ }
+ }
+ }
+ }
+
+ if(outfile == nil) {
+ p = strrchr(infile, '/');
+ if(p == nil)
+ p = infile;
+ else
+ p = p+1;
+ snprint(namebuf, sizeof(namebuf), "%s", p);
+ p = strrchr(namebuf, '.');
+ if(p != nil)
+ *p = 0;
+ outfile = smprint("%s.%c", namebuf, thechar);
+ }
+}
diff --git a/src/cmd/gc/md5.c b/src/cmd/gc/md5.c
new file mode 100644
index 000000000..7cea1a6cf
--- /dev/null
+++ b/src/cmd/gc/md5.c
@@ -0,0 +1,290 @@
+// Copyright 2009 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.
+
+// 64-bit MD5 (does full MD5 but returns 64 bits only).
+// Translation of ../../pkg/crypto/md5/md5*.go.
+
+#include "go.h"
+#include "md5.h"
+
+static int md5block(MD5 *dig, uchar *p, int nn);
+
+enum {
+ _Chunk = 64
+};
+
+#define _Init0 0x67452301
+#define _Init1 0xEFCDAB89
+#define _Init2 0x98BADCFE
+#define _Init3 0x10325476
+
+void
+md5reset(MD5 *d)
+{
+ d->s[0] = _Init0;
+ d->s[1] = _Init1;
+ d->s[2] = _Init2;
+ d->s[3] = _Init3;
+ d->nx = 0;
+ d->len = 0;
+}
+
+void
+md5write(MD5 *d, uchar *p, int nn)
+{
+ int i, n;
+
+ d->len += nn;
+ if(d->nx > 0) {
+ n = nn;
+ if(n > _Chunk - d->nx)
+ n = _Chunk - d->nx;
+ for(i=0; i<n; i++)
+ d->x[d->nx+i] = p[i];
+ d->nx += n;
+ if(d->nx == _Chunk) {
+ md5block(d, d->x, _Chunk);
+ d->nx = 0;
+ }
+ p += n;
+ nn -= n;
+ }
+ n = md5block(d, p, nn);
+ p += n;
+ nn -= n;
+ if(nn > 0) {
+ for(i=0; i<nn; i++)
+ d->x[i] = p[i];
+ d->nx = nn;
+ }
+}
+
+uint64
+md5sum(MD5 *d)
+{
+ uchar tmp[64];
+ int i;
+ uint64 len;
+
+ // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.
+ len = d->len;
+ memset(tmp, 0, sizeof tmp);
+ tmp[0] = 0x80;
+ if(len%64 < 56)
+ md5write(d, tmp, 56-len%64);
+ else
+ md5write(d, tmp, 64+56-len%64);
+
+ // Length in bits.
+ len <<= 3;
+ for(i=0; i<8; i++)
+ tmp[i] = len>>(8*i);
+ md5write(d, tmp, 8);
+
+ if(d->nx != 0)
+ fatal("md5sum");
+
+ return d->s[0] | ((uint64)d->s[1]<<32);
+}
+
+
+// MD5 block step.
+// In its own file so that a faster assembly or C version
+// can be substituted easily.
+
+// table[i] = int((1<<32) * abs(sin(i+1 radians))).
+static uint32 table[64] = {
+ // round 1
+ 0xd76aa478,
+ 0xe8c7b756,
+ 0x242070db,
+ 0xc1bdceee,
+ 0xf57c0faf,
+ 0x4787c62a,
+ 0xa8304613,
+ 0xfd469501,
+ 0x698098d8,
+ 0x8b44f7af,
+ 0xffff5bb1,
+ 0x895cd7be,
+ 0x6b901122,
+ 0xfd987193,
+ 0xa679438e,
+ 0x49b40821,
+
+ // round 2
+ 0xf61e2562,
+ 0xc040b340,
+ 0x265e5a51,
+ 0xe9b6c7aa,
+ 0xd62f105d,
+ 0x2441453,
+ 0xd8a1e681,
+ 0xe7d3fbc8,
+ 0x21e1cde6,
+ 0xc33707d6,
+ 0xf4d50d87,
+ 0x455a14ed,
+ 0xa9e3e905,
+ 0xfcefa3f8,
+ 0x676f02d9,
+ 0x8d2a4c8a,
+
+ // round3
+ 0xfffa3942,
+ 0x8771f681,
+ 0x6d9d6122,
+ 0xfde5380c,
+ 0xa4beea44,
+ 0x4bdecfa9,
+ 0xf6bb4b60,
+ 0xbebfbc70,
+ 0x289b7ec6,
+ 0xeaa127fa,
+ 0xd4ef3085,
+ 0x4881d05,
+ 0xd9d4d039,
+ 0xe6db99e5,
+ 0x1fa27cf8,
+ 0xc4ac5665,
+
+ // round 4
+ 0xf4292244,
+ 0x432aff97,
+ 0xab9423a7,
+ 0xfc93a039,
+ 0x655b59c3,
+ 0x8f0ccc92,
+ 0xffeff47d,
+ 0x85845dd1,
+ 0x6fa87e4f,
+ 0xfe2ce6e0,
+ 0xa3014314,
+ 0x4e0811a1,
+ 0xf7537e82,
+ 0xbd3af235,
+ 0x2ad7d2bb,
+ 0xeb86d391,
+};
+
+static uint32 shift1[] = { 7, 12, 17, 22 };
+static uint32 shift2[] = { 5, 9, 14, 20 };
+static uint32 shift3[] = { 4, 11, 16, 23 };
+static uint32 shift4[] = { 6, 10, 15, 21 };
+
+static int
+md5block(MD5 *dig, uchar *p, int nn)
+{
+ uint32 a, b, c, d, aa, bb, cc, dd;
+ int i, j, n;
+ uint32 X[16];
+
+ a = dig->s[0];
+ b = dig->s[1];
+ c = dig->s[2];
+ d = dig->s[3];
+ n = 0;
+
+ while(nn >= _Chunk) {
+ aa = a;
+ bb = b;
+ cc = c;
+ dd = d;
+
+ for(i=0; i<16; i++) {
+ j = i*4;
+ X[i] = p[j] | (p[j+1]<<8) | (p[j+2]<<16) | (p[j+3]<<24);
+ }
+
+ // Round 1.
+ for(i=0; i<16; i++) {
+ uint32 x, t, s, f;
+ x = i;
+ t = i;
+ s = shift1[i%4];
+ f = ((c ^ d) & b) ^ d;
+ a += f + X[x] + table[t];
+ a = a<<s | a>>(32-s);
+ a += b;
+
+ t = d;
+ d = c;
+ c = b;
+ b = a;
+ a = t;
+ }
+
+ // Round 2.
+ for(i=0; i<16; i++) {
+ uint32 x, t, s, g;
+
+ x = (1+5*i)%16;
+ t = 16+i;
+ s = shift2[i%4];
+ g = ((b ^ c) & d) ^ c;
+ a += g + X[x] + table[t];
+ a = a<<s | a>>(32-s);
+ a += b;
+
+ t = d;
+ d = c;
+ c = b;
+ b = a;
+ a = t;
+ }
+
+ // Round 3.
+ for(i=0; i<16; i++) {
+ uint32 x, t, s, h;
+
+ x = (5+3*i)%16;
+ t = 32+i;
+ s = shift3[i%4];
+ h = b ^ c ^ d;
+ a += h + X[x] + table[t];
+ a = a<<s | a>>(32-s);
+ a += b;
+
+ t = d;
+ d = c;
+ c = b;
+ b = a;
+ a = t;
+ }
+
+ // Round 4.
+ for(i=0; i<16; i++) {
+ uint32 x, s, t, ii;
+
+ x = (7*i)%16;
+ s = shift4[i%4];
+ t = 48+i;
+ ii = c ^ (b | ~d);
+ a += ii + X[x] + table[t];
+ a = a<<s | a>>(32-s);
+ a += b;
+
+ t = d;
+ d = c;
+ c = b;
+ b = a;
+ a = t;
+ }
+
+ a += aa;
+ b += bb;
+ c += cc;
+ d += dd;
+
+ p += _Chunk;
+ n += _Chunk;
+ nn -= _Chunk;
+ }
+
+ dig->s[0] = a;
+ dig->s[1] = b;
+ dig->s[2] = c;
+ dig->s[3] = d;
+ return n;
+}
diff --git a/src/cmd/gc/md5.h b/src/cmd/gc/md5.h
new file mode 100644
index 000000000..f153e30f2
--- /dev/null
+++ b/src/cmd/gc/md5.h
@@ -0,0 +1,16 @@
+// Copyright 2009 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.
+
+typedef struct MD5 MD5;
+struct MD5
+{
+ uint32 s[4];
+ uchar x[64];
+ int nx;
+ uint64 len;
+};
+
+void md5reset(MD5*);
+void md5write(MD5*, uchar*, int);
+uint64 md5sum(MD5*);
diff --git a/src/cmd/gc/mkbuiltin b/src/cmd/gc/mkbuiltin
new file mode 100755
index 000000000..cfd6e59c1
--- /dev/null
+++ b/src/cmd/gc/mkbuiltin
@@ -0,0 +1,31 @@
+#!/bin/sh
+# Copyright 2009 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.
+
+# Generate builtin.c and builtin.c.boot from $* (runtime.go and unsafe.go).
+# Run this after changing runtime.go and unsafe.go
+# or after changing the export metadata format in the compiler.
+# Either way, you need to have a working compiler binary first.
+
+set -e
+
+eval $(gomake --no-print-directory -f ../../Make.inc go-env)
+if [ -z "$GC" ]; then
+ echo 'missing $GC - gomake failed?' 1>&2
+ exit 1
+fi
+
+gomake mkbuiltin1
+rm -f _builtin.c
+for i in runtime unsafe
+do
+ $GC -A $i.go
+ O=$O ./mkbuiltin1 $i >>_builtin.c
+done
+
+# If _builtin.c has changed vs builtin.c.boot,
+# check in the new change.
+cmp -s _builtin.c builtin.c.boot || cp _builtin.c builtin.c.boot
+
+mv _builtin.c builtin.c
diff --git a/src/cmd/gc/mkbuiltin1.c b/src/cmd/gc/mkbuiltin1.c
new file mode 100644
index 000000000..ad83c0346
--- /dev/null
+++ b/src/cmd/gc/mkbuiltin1.c
@@ -0,0 +1,84 @@
+// Copyright 2009 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.
+
+// Compile .go file, import data from .6 file, and generate C string version.
+
+#include <u.h>
+#include <libc.h>
+#include <stdio.h>
+
+void esc(char*);
+
+void
+main(int argc, char **argv)
+{
+ char *name;
+ FILE *fin;
+ char buf[1024], initfunc[1024], *p, *q;
+
+ if(argc != 2) {
+ fprintf(stderr, "usage: mkbuiltin1 sys\n");
+ sysfatal("in file $1.6 s/PACKAGE/$1/\n");
+ }
+
+ name = argv[1];
+ snprintf(initfunc, sizeof(initfunc), "init_%s_function", name);
+
+ snprintf(buf, sizeof(buf), "%s.%s", name, getenv("O"));
+ if((fin = fopen(buf, "r")) == NULL) {
+ sysfatal("open %s: %r\n", buf);
+ }
+
+ // look for $$ that introduces imports
+ while(fgets(buf, sizeof buf, fin) != NULL)
+ if(strstr(buf, "$$"))
+ goto begin;
+ sysfatal("did not find beginning of imports\n");
+
+begin:
+ printf("char *%simport =\n", name);
+
+ // process imports, stopping at $$ that closes them
+ while(fgets(buf, sizeof buf, fin) != NULL) {
+ buf[strlen(buf)-1] = 0; // chop \n
+ if(strstr(buf, "$$"))
+ goto end;
+
+ // chop leading white space
+ for(p=buf; *p==' ' || *p == '\t'; p++)
+ ;
+
+ // cut out decl of init_$1_function - it doesn't exist
+ if(strstr(buf, initfunc))
+ continue;
+
+ // sys.go claims to be in package PACKAGE to avoid
+ // conflicts during "6g sys.go". rename PACKAGE to $2.
+ printf("\t\"");
+ while(q = strstr(p, "PACKAGE")) {
+ *q = 0;
+ esc(p); // up to the substitution
+ printf("%s", name); // the sub name
+ p = q+7; // continue with rest
+ }
+
+ esc(p);
+ printf("\\n\"\n");
+ }
+ sysfatal("did not find end of imports\n");
+
+end:
+ printf("\t\"$$\\n\";\n");
+ exits(0);
+}
+
+void
+esc(char *p)
+{
+ for(; *p; p++) {
+ if(*p == '\\' || *p == '\"')
+ printf("\\");
+ putchar(*p);
+ }
+}
diff --git a/src/cmd/gc/mkopnames b/src/cmd/gc/mkopnames
new file mode 100755
index 000000000..fb2ceec81
--- /dev/null
+++ b/src/cmd/gc/mkopnames
@@ -0,0 +1,24 @@
+#!/bin/sh
+# Copyright 2009 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.
+
+# Disable colored grep if user has it set to --color=always.
+# (Arguably user error.)
+export GREP_OPTIONS=""
+
+echo '// auto generated by mkopnames'
+echo 'static char*'
+echo 'opnames[] = '
+echo '{'
+sed -n '/OXXX/,/OEND/p' go.h |
+ cpp |
+ sed 's!//.*!!; /^#/d' |
+ tr ' ' '\n' |
+ tr -d ' \t,' |
+ grep . |
+ sort |
+ grep -v '^OEND$' |
+ sed 's/O//; s/.*/ [O&] = "&",/'
+echo '};'
+
diff --git a/src/cmd/gc/mparith1.c b/src/cmd/gc/mparith1.c
new file mode 100644
index 000000000..6cd4e2500
--- /dev/null
+++ b/src/cmd/gc/mparith1.c
@@ -0,0 +1,509 @@
+// Copyright 2009 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.
+
+#include "go.h"
+
+/// uses arithmetic
+
+int
+mpcmpfixflt(Mpint *a, Mpflt *b)
+{
+ char buf[500];
+ Mpflt c;
+
+ snprint(buf, sizeof(buf), "%B", a);
+ mpatoflt(&c, buf);
+ return mpcmpfltflt(&c, b);
+}
+
+int
+mpcmpfltfix(Mpflt *a, Mpint *b)
+{
+ char buf[500];
+ Mpflt c;
+
+ snprint(buf, sizeof(buf), "%B", b);
+ mpatoflt(&c, buf);
+ return mpcmpfltflt(a, &c);
+}
+
+int
+mpcmpfixfix(Mpint *a, Mpint *b)
+{
+ Mpint c;
+
+ mpmovefixfix(&c, a);
+ mpsubfixfix(&c, b);
+ return mptestfix(&c);
+}
+
+int
+mpcmpfixc(Mpint *b, vlong c)
+{
+ Mpint a;
+
+ mpmovecfix(&a, c);
+ return mpcmpfixfix(&a, b);
+}
+
+int
+mpcmpfltflt(Mpflt *a, Mpflt *b)
+{
+ Mpflt c;
+
+ mpmovefltflt(&c, a);
+ mpsubfltflt(&c, b);
+ return mptestflt(&c);
+}
+
+int
+mpcmpfltc(Mpflt *b, double c)
+{
+ Mpflt a;
+
+ mpmovecflt(&a, c);
+ return mpcmpfltflt(&a, b);
+}
+
+void
+mpsubfixfix(Mpint *a, Mpint *b)
+{
+ mpnegfix(a);
+ mpaddfixfix(a, b);
+ mpnegfix(a);
+}
+
+void
+mpsubfltflt(Mpflt *a, Mpflt *b)
+{
+ mpnegflt(a);
+ mpaddfltflt(a, b);
+ mpnegflt(a);
+}
+
+void
+mpaddcfix(Mpint *a, vlong c)
+{
+ Mpint b;
+
+ mpmovecfix(&b, c);
+ mpaddfixfix(a, &b);
+}
+
+void
+mpaddcflt(Mpflt *a, double c)
+{
+ Mpflt b;
+
+ mpmovecflt(&b, c);
+ mpaddfltflt(a, &b);
+}
+
+void
+mpmulcfix(Mpint *a, vlong c)
+{
+ Mpint b;
+
+ mpmovecfix(&b, c);
+ mpmulfixfix(a, &b);
+}
+
+void
+mpmulcflt(Mpflt *a, double c)
+{
+ Mpflt b;
+
+ mpmovecflt(&b, c);
+ mpmulfltflt(a, &b);
+}
+
+void
+mpdivfixfix(Mpint *a, Mpint *b)
+{
+ Mpint q, r;
+
+ mpdivmodfixfix(&q, &r, a, b);
+ mpmovefixfix(a, &q);
+}
+
+void
+mpmodfixfix(Mpint *a, Mpint *b)
+{
+ Mpint q, r;
+
+ mpdivmodfixfix(&q, &r, a, b);
+ mpmovefixfix(a, &r);
+}
+
+void
+mpcomfix(Mpint *a)
+{
+ Mpint b;
+
+ mpmovecfix(&b, 1);
+ mpnegfix(a);
+ mpsubfixfix(a, &b);
+}
+
+void
+mpmovefixflt(Mpflt *a, Mpint *b)
+{
+ a->val = *b;
+ a->exp = 0;
+ mpnorm(a);
+}
+
+// convert (truncate) b to a.
+// return -1 (but still convert) if b was non-integer.
+static int
+mpexactfltfix(Mpint *a, Mpflt *b)
+{
+ Mpflt f;
+
+ *a = b->val;
+ mpshiftfix(a, b->exp);
+ if(b->exp < 0) {
+ f.val = *a;
+ f.exp = 0;
+ mpnorm(&f);
+ if(mpcmpfltflt(b, &f) != 0)
+ return -1;
+ }
+ return 0;
+}
+
+int
+mpmovefltfix(Mpint *a, Mpflt *b)
+{
+ Mpflt f;
+ int i;
+
+ if(mpexactfltfix(a, b) == 0)
+ return 0;
+
+ // try rounding down a little
+ f = *b;
+ f.val.a[0] = 0;
+ if(mpexactfltfix(a, &f) == 0)
+ return 0;
+
+ // try rounding up a little
+ for(i=1; i<Mpprec; i++) {
+ f.val.a[i]++;
+ if(f.val.a[i] != Mpbase)
+ break;
+ f.val.a[i] = 0;
+ }
+ mpnorm(&f);
+ if(mpexactfltfix(a, &f) == 0)
+ return 0;
+
+ return -1;
+}
+
+void
+mpmovefixfix(Mpint *a, Mpint *b)
+{
+ *a = *b;
+}
+
+void
+mpmovefltflt(Mpflt *a, Mpflt *b)
+{
+ *a = *b;
+}
+
+static double tab[] = { 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7 };
+static void
+mppow10flt(Mpflt *a, int p)
+{
+ if(p < 0)
+ abort();
+ if(p < nelem(tab)) {
+ mpmovecflt(a, tab[p]);
+ return;
+ }
+ mppow10flt(a, p>>1);
+ mpmulfltflt(a, a);
+ if(p & 1)
+ mpmulcflt(a, 10);
+}
+
+//
+// floating point input
+// required syntax is [+-]d*[.]d*[e[+-]d*]
+//
+void
+mpatoflt(Mpflt *a, char *as)
+{
+ Mpflt b;
+ int dp, c, f, ef, ex, eb;
+ char *s;
+
+ s = as;
+ dp = 0; /* digits after decimal point */
+ f = 0; /* sign */
+ ex = 0; /* exponent */
+ eb = 0; /* binary point */
+
+ mpmovecflt(a, 0.0);
+ for(;;) {
+ switch(c = *s++) {
+ default:
+ goto bad;
+
+ case '-':
+ f = 1;
+
+ case ' ':
+ case '\t':
+ case '+':
+ continue;
+
+ case '.':
+ dp = 1;
+ continue;
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '0':
+ mpmulcflt(a, 10);
+ mpaddcflt(a, c-'0');
+ if(dp)
+ dp++;
+ continue;
+
+ case 'P':
+ case 'p':
+ eb = 1;
+
+ case 'E':
+ case 'e':
+ ex = 0;
+ ef = 0;
+ for(;;) {
+ c = *s++;
+ if(c == '+' || c == ' ' || c == '\t')
+ continue;
+ if(c == '-') {
+ ef = 1;
+ continue;
+ }
+ if(c >= '0' && c <= '9') {
+ ex = ex*10 + (c-'0');
+ if(ex > 1e8) {
+ yyerror("exponent out of range");
+ errorexit();
+ }
+ continue;
+ }
+ break;
+ }
+ if(ef)
+ ex = -ex;
+
+ case 0:
+ break;
+ }
+ break;
+ }
+
+ if(eb) {
+ if(dp)
+ goto bad;
+ a->exp += ex;
+ goto out;
+ }
+
+ if(dp)
+ dp--;
+ if(mpcmpfltc(a, 0.0) != 0) {
+ if(ex >= dp) {
+ mppow10flt(&b, ex-dp);
+ mpmulfltflt(a, &b);
+ } else {
+ mppow10flt(&b, dp-ex);
+ mpdivfltflt(a, &b);
+ }
+ }
+
+out:
+ if(f)
+ mpnegflt(a);
+ return;
+
+bad:
+ yyerror("set ovf in mpatof");
+ mpmovecflt(a, 0.0);
+}
+
+//
+// fixed point input
+// required syntax is [+-][0[x]]d*
+//
+void
+mpatofix(Mpint *a, char *as)
+{
+ int c, f;
+ char *s;
+
+ s = as;
+ f = 0;
+ mpmovecfix(a, 0);
+
+ c = *s++;
+ switch(c) {
+ case '-':
+ f = 1;
+
+ case '+':
+ c = *s++;
+ if(c != '0')
+ break;
+
+ case '0':
+ goto oct;
+ }
+
+ while(c) {
+ if(c >= '0' && c <= '9') {
+ mpmulcfix(a, 10);
+ mpaddcfix(a, c-'0');
+ c = *s++;
+ continue;
+ }
+ goto bad;
+ }
+ goto out;
+
+oct:
+ c = *s++;
+ if(c == 'x' || c == 'X')
+ goto hex;
+ while(c) {
+ if(c >= '0' && c <= '7') {
+ mpmulcfix(a, 8);
+ mpaddcfix(a, c-'0');
+ c = *s++;
+ continue;
+ }
+ goto bad;
+ }
+ goto out;
+
+hex:
+ c = *s++;
+ while(c) {
+ if(c >= '0' && c <= '9') {
+ mpmulcfix(a, 16);
+ mpaddcfix(a, c-'0');
+ c = *s++;
+ continue;
+ }
+ if(c >= 'a' && c <= 'f') {
+ mpmulcfix(a, 16);
+ mpaddcfix(a, c+10-'a');
+ c = *s++;
+ continue;
+ }
+ if(c >= 'A' && c <= 'F') {
+ mpmulcfix(a, 16);
+ mpaddcfix(a, c+10-'A');
+ c = *s++;
+ continue;
+ }
+ goto bad;
+ }
+
+out:
+ if(f)
+ mpnegfix(a);
+ return;
+
+bad:
+ yyerror("set ovf in mpatov: %s", as);
+ mpmovecfix(a, 0);
+}
+
+int
+Bconv(Fmt *fp)
+{
+ char buf[500], *p;
+ Mpint *xval, q, r, ten;
+ int f;
+
+ xval = va_arg(fp->args, Mpint*);
+ mpmovefixfix(&q, xval);
+ f = 0;
+ if(mptestfix(&q) < 0) {
+ f = 1;
+ mpnegfix(&q);
+ }
+ mpmovecfix(&ten, 10);
+
+ p = &buf[sizeof(buf)];
+ *--p = 0;
+ for(;;) {
+ mpdivmodfixfix(&q, &r, &q, &ten);
+ *--p = mpgetfix(&r) + '0';
+ if(mptestfix(&q) <= 0)
+ break;
+ }
+ if(f)
+ *--p = '-';
+ return fmtstrcpy(fp, p);
+}
+
+int
+Fconv(Fmt *fp)
+{
+ char buf[500];
+ Mpflt *fvp, fv;
+ double d;
+
+ fvp = va_arg(fp->args, Mpflt*);
+ if(fp->flags & FmtSharp) {
+ // alternate form - decimal for error messages.
+ // for well in range, convert to double and use print's %g
+ if(-900 < fvp->exp && fvp->exp < 900) {
+ d = mpgetflt(fvp);
+ if(d >= 0 && (fp->flags & FmtSign))
+ fmtprint(fp, "+");
+ return fmtprint(fp, "%g", d);
+ }
+ // TODO(rsc): for well out of range, print
+ // an approximation like 1.234e1000
+ }
+
+ if(sigfig(fvp) == 0) {
+ snprint(buf, sizeof(buf), "0p+0");
+ goto out;
+ }
+ fv = *fvp;
+
+ while(fv.val.a[0] == 0) {
+ mpshiftfix(&fv.val, -Mpscale);
+ fv.exp += Mpscale;
+ }
+ while((fv.val.a[0]&1) == 0) {
+ mpshiftfix(&fv.val, -1);
+ fv.exp += 1;
+ }
+
+ if(fv.exp >= 0) {
+ snprint(buf, sizeof(buf), "%Bp+%d", &fv.val, fv.exp);
+ goto out;
+ }
+ snprint(buf, sizeof(buf), "%Bp-%d", &fv.val, -fv.exp);
+
+out:
+ return fmtstrcpy(fp, buf);
+}
diff --git a/src/cmd/gc/mparith2.c b/src/cmd/gc/mparith2.c
new file mode 100644
index 000000000..403255005
--- /dev/null
+++ b/src/cmd/gc/mparith2.c
@@ -0,0 +1,683 @@
+// Copyright 2009 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.
+
+#include "go.h"
+
+//
+// return the significant
+// words of the argument
+//
+static int
+mplen(Mpint *a)
+{
+ int i, n;
+ long *a1;
+
+ n = -1;
+ a1 = &a->a[0];
+ for(i=0; i<Mpprec; i++) {
+ if(*a1++ != 0)
+ n = i;
+ }
+ return n+1;
+}
+
+//
+// left shift mpint by one
+// ignores sign and overflow
+//
+static void
+mplsh(Mpint *a)
+{
+ long *a1, x;
+ int i, c;
+
+ c = 0;
+ a1 = &a->a[0];
+ for(i=0; i<Mpprec; i++) {
+ x = (*a1 << 1) + c;
+ c = 0;
+ if(x >= Mpbase) {
+ x -= Mpbase;
+ c = 1;
+ }
+ *a1++ = x;
+ }
+}
+
+//
+// left shift mpint by Mpscale
+// ignores sign and overflow
+//
+static void
+mplshw(Mpint *a)
+{
+ long *a1;
+ int i;
+
+ a1 = &a->a[Mpprec-1];
+ for(i=1; i<Mpprec; i++) {
+ a1[0] = a1[-1];
+ a1--;
+ }
+ a1[0] = 0;
+}
+
+//
+// right shift mpint by one
+// ignores sign and overflow
+//
+static void
+mprsh(Mpint *a)
+{
+ long *a1, x, lo;
+ int i, c;
+
+ c = 0;
+ lo = a->a[0] & 1;
+ a1 = &a->a[Mpprec];
+ for(i=0; i<Mpprec; i++) {
+ x = *--a1;
+ *a1 = (x + c) >> 1;
+ c = 0;
+ if(x & 1)
+ c = Mpbase;
+ }
+ if(a->neg && lo != 0)
+ mpaddcfix(a, -1);
+}
+
+//
+// right shift mpint by Mpscale
+// ignores sign and overflow
+//
+static void
+mprshw(Mpint *a)
+{
+ long *a1, lo;
+ int i;
+
+ lo = a->a[0];
+ a1 = &a->a[0];
+ for(i=1; i<Mpprec; i++) {
+ a1[0] = a1[1];
+ a1++;
+ }
+ a1[0] = 0;
+ if(a->neg && lo != 0)
+ mpaddcfix(a, -1);
+}
+
+//
+// return the sign of (abs(a)-abs(b))
+//
+static int
+mpcmp(Mpint *a, Mpint *b)
+{
+ long x, *a1, *b1;
+ int i;
+
+ if(a->ovf || b->ovf) {
+ yyerror("ovf in cmp");
+ return 0;
+ }
+
+ a1 = &a->a[0] + Mpprec;
+ b1 = &b->a[0] + Mpprec;
+
+ for(i=0; i<Mpprec; i++) {
+ x = *--a1 - *--b1;
+ if(x > 0)
+ return +1;
+ if(x < 0)
+ return -1;
+ }
+ return 0;
+}
+
+//
+// negate a
+// ignore sign and ovf
+//
+static void
+mpneg(Mpint *a)
+{
+ long x, *a1;
+ int i, c;
+
+ a1 = &a->a[0];
+ c = 0;
+ for(i=0; i<Mpprec; i++) {
+ x = -*a1 -c;
+ c = 0;
+ if(x < 0) {
+ x += Mpbase;
+ c = 1;
+ }
+ *a1++ = x;
+ }
+}
+
+// shift left by s (or right by -s)
+void
+mpshiftfix(Mpint *a, int s)
+{
+ if(s >= 0) {
+ while(s >= Mpscale) {
+ mplshw(a);
+ s -= Mpscale;
+ }
+ while(s > 0) {
+ mplsh(a);
+ s--;
+ }
+ } else {
+ s = -s;
+ while(s >= Mpscale) {
+ mprshw(a);
+ s -= Mpscale;
+ }
+ while(s > 0) {
+ mprsh(a);
+ s--;
+ }
+ }
+}
+
+/// implements fix arihmetic
+
+void
+mpaddfixfix(Mpint *a, Mpint *b)
+{
+ int i, c;
+ long x, *a1, *b1;
+
+ if(a->ovf || b->ovf) {
+ yyerror("ovf in mpaddxx");
+ a->ovf = 1;
+ return;
+ }
+
+ c = 0;
+ a1 = &a->a[0];
+ b1 = &b->a[0];
+ if(a->neg != b->neg)
+ goto sub;
+
+ // perform a+b
+ for(i=0; i<Mpprec; i++) {
+ x = *a1 + *b1++ + c;
+ c = 0;
+ if(x >= Mpbase) {
+ x -= Mpbase;
+ c = 1;
+ }
+ *a1++ = x;
+ }
+ a->ovf = c;
+ if(a->ovf)
+ yyerror("set ovf in mpaddxx");
+
+ return;
+
+sub:
+ // perform a-b
+ switch(mpcmp(a, b)) {
+ case 0:
+ mpmovecfix(a, 0);
+ break;
+
+ case 1:
+ for(i=0; i<Mpprec; i++) {
+ x = *a1 - *b1++ - c;
+ c = 0;
+ if(x < 0) {
+ x += Mpbase;
+ c = 1;
+ }
+ *a1++ = x;
+ }
+ break;
+
+ case -1:
+ a->neg ^= 1;
+ for(i=0; i<Mpprec; i++) {
+ x = *b1++ - *a1 - c;
+ c = 0;
+ if(x < 0) {
+ x += Mpbase;
+ c = 1;
+ }
+ *a1++ = x;
+ }
+ break;
+ }
+}
+
+void
+mpmulfixfix(Mpint *a, Mpint *b)
+{
+
+ int i, j, na, nb;
+ long *a1, x;
+ Mpint s, q;
+
+ if(a->ovf || b->ovf) {
+ yyerror("ovf in mpmulfixfix");
+ a->ovf = 1;
+ return;
+ }
+
+ // pick the smaller
+ // to test for bits
+ na = mplen(a);
+ nb = mplen(b);
+ if(na > nb) {
+ mpmovefixfix(&s, a);
+ a1 = &b->a[0];
+ na = nb;
+ } else {
+ mpmovefixfix(&s, b);
+ a1 = &a->a[0];
+ }
+ s.neg = 0;
+
+ mpmovecfix(&q, 0);
+ for(i=0; i<na; i++) {
+ x = *a1++;
+ for(j=0; j<Mpscale; j++) {
+ if(x & 1)
+ mpaddfixfix(&q, &s);
+ mplsh(&s);
+ x >>= 1;
+ }
+ }
+
+ q.neg = a->neg ^ b->neg;
+ mpmovefixfix(a, &q);
+ if(a->ovf)
+ yyerror("set ovf in mpmulfixfix");
+}
+
+void
+mpmulfract(Mpint *a, Mpint *b)
+{
+
+ int i, j;
+ long *a1, x;
+ Mpint s, q;
+
+ if(a->ovf || b->ovf) {
+ yyerror("ovf in mpmulflt");
+ a->ovf = 1;
+ return;
+ }
+
+ mpmovefixfix(&s, b);
+ a1 = &a->a[Mpprec];
+ s.neg = 0;
+ mpmovecfix(&q, 0);
+
+ x = *--a1;
+ if(x != 0)
+ yyerror("mpmulfract not normal");
+
+ for(i=0; i<Mpprec-1; i++) {
+ x = *--a1;
+ if(x == 0) {
+ mprshw(&s);
+ continue;
+ }
+ for(j=0; j<Mpscale; j++) {
+ x <<= 1;
+ if(x & Mpbase)
+ mpaddfixfix(&q, &s);
+ mprsh(&s);
+ }
+ }
+
+ q.neg = a->neg ^ b->neg;
+ mpmovefixfix(a, &q);
+ if(a->ovf)
+ yyerror("set ovf in mpmulflt");
+}
+
+void
+mporfixfix(Mpint *a, Mpint *b)
+{
+ int i;
+ long x, *a1, *b1;
+
+ if(a->ovf || b->ovf) {
+ yyerror("ovf in mporfixfix");
+ mpmovecfix(a, 0);
+ a->ovf = 1;
+ return;
+ }
+ if(a->neg) {
+ a->neg = 0;
+ mpneg(a);
+ }
+ if(b->neg)
+ mpneg(b);
+
+ a1 = &a->a[0];
+ b1 = &b->a[0];
+ for(i=0; i<Mpprec; i++) {
+ x = *a1 | *b1++;
+ *a1++ = x;
+ }
+
+ if(b->neg)
+ mpneg(b);
+ if(x & Mpsign) {
+ a->neg = 1;
+ mpneg(a);
+ }
+}
+
+void
+mpandfixfix(Mpint *a, Mpint *b)
+{
+ int i;
+ long x, *a1, *b1;
+
+ if(a->ovf || b->ovf) {
+ yyerror("ovf in mpandfixfix");
+ mpmovecfix(a, 0);
+ a->ovf = 1;
+ return;
+ }
+ if(a->neg) {
+ a->neg = 0;
+ mpneg(a);
+ }
+ if(b->neg)
+ mpneg(b);
+
+ a1 = &a->a[0];
+ b1 = &b->a[0];
+ for(i=0; i<Mpprec; i++) {
+ x = *a1 & *b1++;
+ *a1++ = x;
+ }
+
+ if(b->neg)
+ mpneg(b);
+ if(x & Mpsign) {
+ a->neg = 1;
+ mpneg(a);
+ }
+}
+
+void
+mpandnotfixfix(Mpint *a, Mpint *b)
+{
+ int i;
+ long x, *a1, *b1;
+
+ if(a->ovf || b->ovf) {
+ yyerror("ovf in mpandnotfixfix");
+ mpmovecfix(a, 0);
+ a->ovf = 1;
+ return;
+ }
+ if(a->neg) {
+ a->neg = 0;
+ mpneg(a);
+ }
+ if(b->neg)
+ mpneg(b);
+
+ a1 = &a->a[0];
+ b1 = &b->a[0];
+ for(i=0; i<Mpprec; i++) {
+ x = *a1 & ~*b1++;
+ *a1++ = x;
+ }
+
+ if(b->neg)
+ mpneg(b);
+ if(x & Mpsign) {
+ a->neg = 1;
+ mpneg(a);
+ }
+}
+
+void
+mpxorfixfix(Mpint *a, Mpint *b)
+{
+ int i;
+ long x, *a1, *b1;
+
+ if(a->ovf || b->ovf) {
+ yyerror("ovf in mporfixfix");
+ mpmovecfix(a, 0);
+ a->ovf = 1;
+ return;
+ }
+ if(a->neg) {
+ a->neg = 0;
+ mpneg(a);
+ }
+ if(b->neg)
+ mpneg(b);
+
+ a1 = &a->a[0];
+ b1 = &b->a[0];
+ for(i=0; i<Mpprec; i++) {
+ x = *a1 ^ *b1++;
+ *a1++ = x;
+ }
+
+ if(b->neg)
+ mpneg(b);
+ if(x & Mpsign) {
+ a->neg = 1;
+ mpneg(a);
+ }
+}
+
+void
+mplshfixfix(Mpint *a, Mpint *b)
+{
+ vlong s;
+
+ if(a->ovf || b->ovf) {
+ yyerror("ovf in mporfixfix");
+ mpmovecfix(a, 0);
+ a->ovf = 1;
+ return;
+ }
+ s = mpgetfix(b);
+ if(s < 0 || s >= Mpprec*Mpscale) {
+ yyerror("stupid shift: %lld", s);
+ mpmovecfix(a, 0);
+ return;
+ }
+
+ mpshiftfix(a, s);
+}
+
+void
+mprshfixfix(Mpint *a, Mpint *b)
+{
+ vlong s;
+
+ if(a->ovf || b->ovf) {
+ yyerror("ovf in mprshfixfix");
+ mpmovecfix(a, 0);
+ a->ovf = 1;
+ return;
+ }
+ s = mpgetfix(b);
+ if(s < 0 || s >= Mpprec*Mpscale) {
+ yyerror("stupid shift: %lld", s);
+ if(a->neg)
+ mpmovecfix(a, -1);
+ else
+ mpmovecfix(a, 0);
+ return;
+ }
+
+ mpshiftfix(a, -s);
+}
+
+void
+mpnegfix(Mpint *a)
+{
+ a->neg ^= 1;
+}
+
+vlong
+mpgetfix(Mpint *a)
+{
+ vlong v;
+
+ if(a->ovf) {
+ yyerror("constant overflow");
+ return 0;
+ }
+
+ v = (vlong)a->a[0];
+ v |= (vlong)a->a[1] << Mpscale;
+ v |= (vlong)a->a[2] << (Mpscale+Mpscale);
+ if(a->neg)
+ v = -v;
+ return v;
+}
+
+void
+mpmovecfix(Mpint *a, vlong c)
+{
+ int i;
+ long *a1;
+ vlong x;
+
+ a->neg = 0;
+ a->ovf = 0;
+
+ x = c;
+ if(x < 0) {
+ a->neg = 1;
+ x = -x;
+ }
+
+ a1 = &a->a[0];
+ for(i=0; i<Mpprec; i++) {
+ *a1++ = x&Mpmask;
+ x >>= Mpscale;
+ }
+}
+
+void
+mpdivmodfixfix(Mpint *q, Mpint *r, Mpint *n, Mpint *d)
+{
+ int i, ns, ds;
+
+ ns = n->neg;
+ ds = d->neg;
+ n->neg = 0;
+ d->neg = 0;
+
+ mpmovefixfix(r, n);
+ mpmovecfix(q, 0);
+
+ // shift denominator until it
+ // is larger than numerator
+ for(i=0; i<Mpprec*Mpscale; i++) {
+ if(mpcmp(d, r) > 0)
+ break;
+ mplsh(d);
+ }
+
+ // if it never happens
+ // denominator is probably zero
+ if(i >= Mpprec*Mpscale) {
+ q->ovf = 1;
+ r->ovf = 1;
+ n->neg = ns;
+ d->neg = ds;
+ yyerror("set ovf in mpdivmodfixfix");
+ return;
+ }
+
+ // shift denominator back creating
+ // quotient a bit at a time
+ // when done the remaining numerator
+ // will be the remainder
+ for(; i>0; i--) {
+ mplsh(q);
+ mprsh(d);
+ if(mpcmp(d, r) <= 0) {
+ mpaddcfix(q, 1);
+ mpsubfixfix(r, d);
+ }
+ }
+
+ n->neg = ns;
+ d->neg = ds;
+ r->neg = ns;
+ q->neg = ns^ds;
+}
+
+static int
+iszero(Mpint *a)
+{
+ long *a1;
+ int i;
+ a1 = &a->a[0] + Mpprec;
+ for(i=0; i<Mpprec; i++) {
+ if(*--a1 != 0)
+ return 0;
+ }
+ return 1;
+}
+
+void
+mpdivfract(Mpint *a, Mpint *b)
+{
+ Mpint n, d;
+ int i, j, neg;
+ long *a1, x;
+
+ mpmovefixfix(&n, a); // numerator
+ mpmovefixfix(&d, b); // denominator
+ a1 = &a->a[Mpprec]; // quotient
+
+ neg = n.neg ^ d.neg;
+ n.neg = 0;
+ d.neg = 0;
+ for(i=0; i<Mpprec; i++) {
+ x = 0;
+ for(j=0; j<Mpscale; j++) {
+ x <<= 1;
+ if(mpcmp(&d, &n) <= 0) {
+ if(!iszero(&d))
+ x |= 1;
+ mpsubfixfix(&n, &d);
+ }
+ mprsh(&d);
+ }
+ *--a1 = x;
+ }
+ a->neg = neg;
+}
+
+int
+mptestfix(Mpint *a)
+{
+ Mpint b;
+ int r;
+
+ mpmovecfix(&b, 0);
+ r = mpcmp(a, &b);
+ if(a->neg) {
+ if(r > 0)
+ return -1;
+ if(r < 0)
+ return +1;
+ }
+ return r;
+}
diff --git a/src/cmd/gc/mparith3.c b/src/cmd/gc/mparith3.c
new file mode 100644
index 000000000..b11a4f5f1
--- /dev/null
+++ b/src/cmd/gc/mparith3.c
@@ -0,0 +1,313 @@
+// Copyright 2009 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.
+
+#include "go.h"
+
+/*
+ * returns the leading non-zero
+ * word of the number
+ */
+int
+sigfig(Mpflt *a)
+{
+ int i;
+
+ for(i=Mpprec-1; i>=0; i--)
+ if(a->val.a[i] != 0)
+ break;
+//print("sigfig %d %d\n", i-z+1, z);
+ return i+1;
+}
+
+/*
+ * shifts the leading non-zero
+ * word of the number to Mpnorm
+ */
+void
+mpnorm(Mpflt *a)
+{
+ int s, os;
+ long x;
+
+ os = sigfig(a);
+ if(os == 0) {
+ // zero
+ a->exp = 0;
+ a->val.neg = 0;
+ return;
+ }
+
+ // this will normalize to the nearest word
+ x = a->val.a[os-1];
+ s = (Mpnorm-os) * Mpscale;
+
+ // further normalize to the nearest bit
+ for(;;) {
+ x <<= 1;
+ if(x & Mpbase)
+ break;
+ s++;
+ if(x == 0) {
+ // this error comes from trying to
+ // convert an Inf or something
+ // where the initial x=0x80000000
+ s = (Mpnorm-os) * Mpscale;
+ break;
+ }
+ }
+
+ mpshiftfix(&a->val, s);
+ a->exp -= s;
+}
+
+/// implements float arihmetic
+
+void
+mpaddfltflt(Mpflt *a, Mpflt *b)
+{
+ int sa, sb, s;
+ Mpflt c;
+
+ if(Mpdebug)
+ print("\n%F + %F", a, b);
+
+ sa = sigfig(a);
+ if(sa == 0) {
+ mpmovefltflt(a, b);
+ goto out;
+ }
+
+ sb = sigfig(b);
+ if(sb == 0)
+ goto out;
+
+ s = a->exp - b->exp;
+ if(s > 0) {
+ // a is larger, shift b right
+ mpmovefltflt(&c, b);
+ mpshiftfix(&c.val, -s);
+ mpaddfixfix(&a->val, &c.val);
+ goto out;
+ }
+ if(s < 0) {
+ // b is larger, shift a right
+ mpshiftfix(&a->val, s);
+ a->exp -= s;
+ mpaddfixfix(&a->val, &b->val);
+ goto out;
+ }
+ mpaddfixfix(&a->val, &b->val);
+
+out:
+ mpnorm(a);
+ if(Mpdebug)
+ print(" = %F\n\n", a);
+}
+
+void
+mpmulfltflt(Mpflt *a, Mpflt *b)
+{
+ int sa, sb;
+
+ if(Mpdebug)
+ print("%F\n * %F\n", a, b);
+
+ sa = sigfig(a);
+ if(sa == 0) {
+ // zero
+ a->exp = 0;
+ a->val.neg = 0;
+ return;
+ }
+
+ sb = sigfig(b);
+ if(sb == 0) {
+ // zero
+ mpmovefltflt(a, b);
+ return;
+ }
+
+ mpmulfract(&a->val, &b->val);
+ a->exp = (a->exp + b->exp) + Mpscale*Mpprec - Mpscale - 1;
+
+ mpnorm(a);
+ if(Mpdebug)
+ print(" = %F\n\n", a);
+}
+
+void
+mpdivfltflt(Mpflt *a, Mpflt *b)
+{
+ int sa, sb;
+ Mpflt c;
+
+ if(Mpdebug)
+ print("%F\n / %F\n", a, b);
+
+ sb = sigfig(b);
+ if(sb == 0) {
+ // zero and ovfl
+ a->exp = 0;
+ a->val.neg = 0;
+ a->val.ovf = 1;
+ yyerror("mpdivfltflt divide by zero");
+ return;
+ }
+
+ sa = sigfig(a);
+ if(sa == 0) {
+ // zero
+ a->exp = 0;
+ a->val.neg = 0;
+ return;
+ }
+
+ // adjust b to top
+ mpmovefltflt(&c, b);
+ mpshiftfix(&c.val, Mpscale);
+
+ // divide
+ mpdivfract(&a->val, &c.val);
+ a->exp = (a->exp-c.exp) - Mpscale*(Mpprec-1) + 1;
+
+ mpnorm(a);
+ if(Mpdebug)
+ print(" = %F\n\n", a);
+}
+
+double
+mpgetflt(Mpflt *a)
+{
+ int s, i, e;
+ uvlong v, vm;
+ double f;
+
+ if(a->val.ovf)
+ yyerror("mpgetflt ovf");
+
+ s = sigfig(a);
+ if(s == 0)
+ return 0;
+
+ if(s != Mpnorm) {
+ yyerror("mpgetflt norm");
+ mpnorm(a);
+ }
+
+ while((a->val.a[Mpnorm-1] & Mpsign) == 0) {
+ mpshiftfix(&a->val, 1);
+ a->exp -= 1;
+ }
+
+ // the magic numbers (64, 63, 53, 10, -1074) are
+ // IEEE specific. this should be done machine
+ // independently or in the 6g half of the compiler
+
+ // pick up the mantissa and a rounding bit in a uvlong
+ s = 53+1;
+ v = 0;
+ for(i=Mpnorm-1; s>=Mpscale; i--) {
+ v = (v<<Mpscale) | a->val.a[i];
+ s -= Mpscale;
+ }
+ vm = v;
+ if(s > 0)
+ vm = (vm<<s) | (a->val.a[i]>>(Mpscale-s));
+
+ // continue with 64 more bits
+ s += 64;
+ for(; s>=Mpscale; i--) {
+ v = (v<<Mpscale) | a->val.a[i];
+ s -= Mpscale;
+ }
+ if(s > 0)
+ v = (v<<s) | (a->val.a[i]>>(Mpscale-s));
+
+ // gradual underflow
+ e = Mpnorm*Mpscale + a->exp - 53;
+ if(e < -1074) {
+ s = -e - 1074;
+ if(s > 54)
+ s = 54;
+ v |= vm & ((1ULL<<s) - 1);
+ vm >>= s;
+ e = -1074;
+ }
+
+//print("vm=%.16llux v=%.16llux\n", vm, v);
+ // round toward even
+ if(v != 0 || (vm&2ULL) != 0)
+ vm = (vm>>1) + (vm&1ULL);
+ else
+ vm >>= 1;
+
+ f = (double)(vm);
+ f = ldexp(f, e);
+
+ if(a->val.neg)
+ f = -f;
+ return f;
+}
+
+void
+mpmovecflt(Mpflt *a, double c)
+{
+ int i;
+ double f;
+ long l;
+
+ if(Mpdebug)
+ print("\nconst %g", c);
+ mpmovecfix(&a->val, 0);
+ a->exp = 0;
+ if(c == 0)
+ goto out;
+ if(c < 0) {
+ a->val.neg = 1;
+ c = -c;
+ }
+
+ f = frexp(c, &i);
+ a->exp = i;
+
+ for(i=0; i<10; i++) {
+ f = f*Mpbase;
+ l = floor(f);
+ f = f - l;
+ a->exp -= Mpscale;
+ a->val.a[0] = l;
+ if(f == 0)
+ break;
+ mpshiftfix(&a->val, Mpscale);
+ }
+
+out:
+ mpnorm(a);
+ if(Mpdebug)
+ print(" = %F\n", a);
+}
+
+void
+mpnegflt(Mpflt *a)
+{
+ a->val.neg ^= 1;
+}
+
+int
+mptestflt(Mpflt *a)
+{
+ int s;
+
+ if(Mpdebug)
+ print("\n%F?", a);
+ s = sigfig(a);
+ if(s != 0) {
+ s = +1;
+ if(a->val.neg)
+ s = -1;
+ }
+ if(Mpdebug)
+ print(" = %d\n", s);
+ return s;
+}
diff --git a/src/cmd/gc/obj.c b/src/cmd/gc/obj.c
new file mode 100644
index 000000000..456aabb88
--- /dev/null
+++ b/src/cmd/gc/obj.c
@@ -0,0 +1,301 @@
+// Copyright 2009 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.
+
+#include "go.h"
+
+/*
+ * architecture-independent object file output
+ */
+
+static void outhist(Biobuf *b);
+static void dumpglobls(void);
+
+void
+dumpobj(void)
+{
+ bout = Bopen(outfile, OWRITE);
+ if(bout == nil) {
+ flusherrors();
+ print("can't create %s: %r\n", outfile);
+ errorexit();
+ }
+
+ Bprint(bout, "go object %s %s %s\n", getgoos(), thestring, getgoversion());
+ Bprint(bout, " exports automatically generated from\n");
+ Bprint(bout, " %s in package \"%s\"\n", curio.infile, localpkg->name);
+ dumpexport();
+ Bprint(bout, "\n!\n");
+
+ outhist(bout);
+
+ // add nil plist w AEND to catch
+ // auto-generated trampolines, data
+ newplist();
+
+ dumpglobls();
+ dumptypestructs();
+ dumpdata();
+ dumpfuncs();
+
+ Bterm(bout);
+}
+
+static void
+dumpglobls(void)
+{
+ Node *n;
+ NodeList *l;
+
+ // add globals
+ for(l=externdcl; l; l=l->next) {
+ n = l->n;
+ if(n->op != ONAME)
+ continue;
+
+ if(n->type == T)
+ fatal("external %#N nil type\n", n);
+ if(n->class == PFUNC)
+ continue;
+ if(n->sym->pkg != localpkg)
+ continue;
+ dowidth(n->type);
+
+ ggloblnod(n, n->type->width);
+ }
+}
+
+void
+Bputname(Biobuf *b, Sym *s)
+{
+ Bprint(b, "%s", s->pkg->prefix);
+ Bputc(b, '.');
+ Bwrite(b, s->name, strlen(s->name)+1);
+}
+
+static void
+outzfile(Biobuf *b, char *p)
+{
+ char *q, *q2;
+
+ while(p) {
+ q = utfrune(p, '/');
+ if(windows) {
+ q2 = utfrune(p, '\\');
+ if(q2 && (!q || q2 < q))
+ q = q2;
+ }
+ if(!q) {
+ zfile(b, p, strlen(p));
+ return;
+ }
+ if(q > p)
+ zfile(b, p, q-p);
+ p = q + 1;
+ }
+}
+
+#define isdelim(c) (c == '/' || c == '\\')
+
+static void
+outwinname(Biobuf *b, Hist *h, char *ds, char *p)
+{
+ if(isdelim(p[0])) {
+ // full rooted name
+ zfile(b, ds, 3); // leading "c:/"
+ outzfile(b, p+1);
+ } else {
+ // relative name
+ if(h->offset == 0 && pathname && pathname[1] == ':') {
+ if(tolowerrune(ds[0]) == tolowerrune(pathname[0])) {
+ // using current drive
+ zfile(b, pathname, 3); // leading "c:/"
+ outzfile(b, pathname+3);
+ } else {
+ // using drive other then current,
+ // we don't have any simple way to
+ // determine current working directory
+ // there, therefore will output name as is
+ zfile(b, ds, 2); // leading "c:"
+ }
+ }
+ outzfile(b, p);
+ }
+}
+
+static void
+outhist(Biobuf *b)
+{
+ Hist *h;
+ int i, depth = 0;
+ char *p, ds[] = {'c', ':', '/', 0};
+
+ for(h = hist; h != H; h = h->link) {
+ p = h->name;
+ if(p) {
+ if(windows) {
+ // if windows variable is set, then, we know already,
+ // pathname is started with windows drive specifier
+ // and all '\' were replaced with '/' (see lex.c)
+ if(isdelim(p[0]) && isdelim(p[1])) {
+ // file name has network name in it,
+ // like \\server\share\dir\file.go
+ zfile(b, "//", 2); // leading "//"
+ outzfile(b, p+2);
+ } else if(p[1] == ':') {
+ // file name has drive letter in it
+ ds[0] = p[0];
+ outwinname(b, h, ds, p+2);
+ } else {
+ // no drive letter in file name
+ outwinname(b, h, pathname, p);
+ }
+ } else {
+ if(p[0] == '/') {
+ // full rooted name, like /home/rsc/dir/file.go
+ zfile(b, "/", 1); // leading "/"
+ outzfile(b, p+1);
+ } else {
+ // relative name, like dir/file.go
+ if(h->offset >= 0 && pathname && pathname[0] == '/') {
+ zfile(b, "/", 1); // leading "/"
+ outzfile(b, pathname+1);
+ }
+ outzfile(b, p);
+ }
+ }
+ if(h->offset > 0) {
+ //line directive
+ depth++;
+ }
+ } else if(depth > 0) {
+ for(i = 0; i < depth; i++)
+ zhist(b, h->line, h->offset);
+ depth = 0;
+ }
+ zhist(b, h->line, h->offset);
+ }
+}
+
+void
+ieeedtod(uint64 *ieee, double native)
+{
+ double fr, ho, f;
+ int exp;
+ uint32 h, l;
+ uint64 bits;
+
+ if(native < 0) {
+ ieeedtod(ieee, -native);
+ *ieee |= 1ULL<<63;
+ return;
+ }
+ if(native == 0) {
+ *ieee = 0;
+ return;
+ }
+ fr = frexp(native, &exp);
+ f = 2097152L; /* shouldnt use fp constants here */
+ fr = modf(fr*f, &ho);
+ h = ho;
+ h &= 0xfffffL;
+ f = 65536L;
+ fr = modf(fr*f, &ho);
+ l = ho;
+ l <<= 16;
+ l |= (int32)(fr*f);
+ bits = ((uint64)h<<32) | l;
+ if(exp < -1021) {
+ // gradual underflow
+ bits |= 1LL<<52;
+ bits >>= -1021 - exp;
+ exp = -1022;
+ }
+ bits |= (uint64)(exp+1022L) << 52;
+ *ieee = bits;
+}
+
+int
+duint8(Sym *s, int off, uint8 v)
+{
+ return duintxx(s, off, v, 1);
+}
+
+int
+duint16(Sym *s, int off, uint16 v)
+{
+ return duintxx(s, off, v, 2);
+}
+
+int
+duint32(Sym *s, int off, uint32 v)
+{
+ return duintxx(s, off, v, 4);
+}
+
+int
+duint64(Sym *s, int off, uint64 v)
+{
+ return duintxx(s, off, v, 8);
+}
+
+int
+duintptr(Sym *s, int off, uint64 v)
+{
+ return duintxx(s, off, v, widthptr);
+}
+
+Sym*
+stringsym(char *s, int len)
+{
+ static int gen;
+ Sym *sym;
+ int off, n, m;
+ struct {
+ Strlit lit;
+ char buf[110];
+ } tmp;
+ Pkg *pkg;
+
+ if(len > 100) {
+ // huge strings are made static to avoid long names
+ snprint(namebuf, sizeof(namebuf), ".gostring.%d", ++gen);
+ pkg = localpkg;
+ } else {
+ // small strings get named by their contents,
+ // so that multiple modules using the same string
+ // can share it.
+ tmp.lit.len = len;
+ memmove(tmp.lit.s, s, len);
+ tmp.lit.s[len] = '\0';
+ snprint(namebuf, sizeof(namebuf), "\"%Z\"", &tmp);
+ pkg = gostringpkg;
+ }
+ sym = pkglookup(namebuf, pkg);
+
+ // SymUniq flag indicates that data is generated already
+ if(sym->flags & SymUniq)
+ return sym;
+ sym->flags |= SymUniq;
+
+ data();
+ off = 0;
+
+ // string header
+ off = dsymptr(sym, off, sym, widthptr+4);
+ off = duint32(sym, off, len);
+
+ // string data
+ for(n=0; n<len; n+=m) {
+ m = 8;
+ if(m > len-n)
+ m = len-n;
+ off = dsname(sym, off, s+n, m);
+ }
+ off = duint8(sym, off, 0); // terminating NUL for runtime
+ off = (off+widthptr-1)&~(widthptr-1); // round to pointer alignment
+ ggloblsym(sym, off, 1);
+ text();
+
+ return sym;
+}
diff --git a/src/cmd/gc/pgen.c b/src/cmd/gc/pgen.c
new file mode 100644
index 000000000..abe8ea892
--- /dev/null
+++ b/src/cmd/gc/pgen.c
@@ -0,0 +1,210 @@
+// 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.
+
+#include "gg.h"
+#include "opt.h"
+
+static void compactframe(Prog* p);
+
+void
+compile(Node *fn)
+{
+ Plist *pl;
+ Node nod1, *n;
+ Prog *ptxt;
+ int32 lno;
+ Type *t;
+ Iter save;
+ vlong oldstksize;
+
+ if(newproc == N) {
+ newproc = sysfunc("newproc");
+ deferproc = sysfunc("deferproc");
+ deferreturn = sysfunc("deferreturn");
+ panicindex = sysfunc("panicindex");
+ panicslice = sysfunc("panicslice");
+ throwreturn = sysfunc("throwreturn");
+ }
+
+ if(fn->nbody == nil)
+ return;
+
+ saveerrors();
+
+ // set up domain for labels
+ clearlabels();
+
+ lno = setlineno(fn);
+
+ curfn = fn;
+ dowidth(curfn->type);
+
+ if(curfn->type->outnamed) {
+ // add clearing of the output parameters
+ t = structfirst(&save, getoutarg(curfn->type));
+ while(t != T) {
+ if(t->nname != N) {
+ n = nod(OAS, t->nname, N);
+ typecheck(&n, Etop);
+ curfn->nbody = concat(list1(n), curfn->nbody);
+ }
+ t = structnext(&save);
+ }
+ }
+
+ hasdefer = 0;
+ walk(curfn);
+ if(nerrors != 0)
+ goto ret;
+
+ allocparams();
+
+ continpc = P;
+ breakpc = P;
+
+ pl = newplist();
+ pl->name = curfn->nname;
+
+ setlineno(curfn);
+
+ nodconst(&nod1, types[TINT32], 0);
+ ptxt = gins(ATEXT, isblank(curfn->nname) ? N : curfn->nname, &nod1);
+ afunclit(&ptxt->from);
+
+ ginit();
+ genlist(curfn->enter);
+
+ retpc = nil;
+ if(hasdefer || curfn->exit) {
+ Prog *p1;
+
+ p1 = gjmp(nil);
+ retpc = gjmp(nil);
+ patch(p1, pc);
+ }
+
+ genlist(curfn->nbody);
+ gclean();
+ checklabels();
+ if(nerrors != 0)
+ goto ret;
+ if(curfn->endlineno)
+ lineno = curfn->endlineno;
+
+ if(curfn->type->outtuple != 0)
+ ginscall(throwreturn, 0);
+
+ if(retpc)
+ patch(retpc, pc);
+ ginit();
+ if(hasdefer)
+ ginscall(deferreturn, 0);
+ if(curfn->exit)
+ genlist(curfn->exit);
+ gclean();
+ if(nerrors != 0)
+ goto ret;
+ pc->as = ARET; // overwrite AEND
+ pc->lineno = lineno;
+
+ if(!debug['N'] || debug['R'] || debug['P']) {
+ regopt(ptxt);
+ }
+
+ oldstksize = stksize;
+ compactframe(ptxt);
+ if(0)
+ print("compactframe: %ld to %ld\n", oldstksize, stksize);
+
+ defframe(ptxt);
+
+ if(0)
+ frame(0);
+
+ret:
+ lineno = lno;
+}
+
+
+// Sort the list of stack variables. autos after anything else,
+// within autos, unused after used, and within used on reverse alignment.
+// non-autos sort on offset.
+static int
+cmpstackvar(Node *a, Node *b)
+{
+ if (a->class != b->class)
+ return (a->class == PAUTO) ? 1 : -1;
+ if (a->class != PAUTO)
+ return a->xoffset - b->xoffset;
+ if ((a->used == 0) != (b->used == 0))
+ return b->used - a->used;
+ return b->type->align - a->type->align;
+
+}
+
+// TODO(lvd) find out where the PAUTO/OLITERAL nodes come from.
+static void
+compactframe(Prog* ptxt)
+{
+ NodeList *ll;
+ Node* n;
+ vlong w;
+
+ if (stksize == 0)
+ return;
+
+ // Mark the PAUTO's unused.
+ for(ll=curfn->dcl; ll != nil; ll=ll->next)
+ if (ll->n->class == PAUTO)
+ ll->n->used = 0;
+
+ markautoused(ptxt);
+
+ listsort(&curfn->dcl, cmpstackvar);
+
+ // Unused autos are at the end, chop 'em off.
+ ll = curfn->dcl;
+ n = ll->n;
+ if (n->class == PAUTO && n->op == ONAME && !n->used) {
+ curfn->dcl = nil;
+ stksize = 0;
+ return;
+ }
+
+ for(ll = curfn->dcl; ll->next != nil; ll=ll->next) {
+ n = ll->next->n;
+ if (n->class == PAUTO && n->op == ONAME && !n->used) {
+ ll->next = nil;
+ curfn->dcl->end = ll;
+ break;
+ }
+ }
+
+ // Reassign stack offsets of the locals that are still there.
+ stksize = 0;
+ for(ll = curfn->dcl; ll != nil; ll=ll->next) {
+ n = ll->n;
+ if (n->class != PAUTO || n->op != ONAME)
+ continue;
+
+ w = n->type->width;
+ if(w >= MAXWIDTH || w < 0)
+ fatal("bad width");
+ stksize += w;
+ stksize = rnd(stksize, n->type->align);
+ if(thechar == '5')
+ stksize = rnd(stksize, widthptr);
+ n->stkdelta = -stksize - n->xoffset;
+ }
+
+ fixautoused(ptxt);
+
+ // The debug information needs accurate offsets on the symbols.
+ for(ll = curfn->dcl ;ll != nil; ll=ll->next) {
+ if (ll->n->class != PAUTO || ll->n->op != ONAME)
+ continue;
+ ll->n->xoffset += ll->n->stkdelta;
+ ll->n->stkdelta = 0;
+ }
+}
diff --git a/src/cmd/gc/print.c b/src/cmd/gc/print.c
new file mode 100644
index 000000000..5913e848a
--- /dev/null
+++ b/src/cmd/gc/print.c
@@ -0,0 +1,480 @@
+// Copyright 2009 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.
+
+#include "go.h"
+
+enum
+{
+ PFIXME = 0,
+};
+
+void
+exprlistfmt(Fmt *f, NodeList *l)
+{
+ for(; l; l=l->next) {
+ exprfmt(f, l->n, 0);
+ if(l->next)
+ fmtprint(f, ", ");
+ }
+}
+
+void
+exprfmt(Fmt *f, Node *n, int prec)
+{
+ int nprec;
+ char *p;
+
+ nprec = 0;
+ if(n == nil) {
+ fmtprint(f, "<nil>");
+ return;
+ }
+
+ if(n->implicit) {
+ exprfmt(f, n->left, prec);
+ return;
+ }
+
+ switch(n->op) {
+ case OAPPEND:
+ case ONAME:
+ case ONONAME:
+ case OPACK:
+ case OLITERAL:
+ case ODOT:
+ case ODOTPTR:
+ case ODOTINTER:
+ case ODOTMETH:
+ case ODOTTYPE:
+ case ODOTTYPE2:
+ case OXDOT:
+ case OARRAYBYTESTR:
+ case OCAP:
+ case OCLOSE:
+ case OCOPY:
+ case OLEN:
+ case OMAKE:
+ case ONEW:
+ case OPANIC:
+ case OPRINT:
+ case OPRINTN:
+ case OCALL:
+ case OCALLMETH:
+ case OCALLINTER:
+ case OCALLFUNC:
+ case OCONV:
+ case OCONVNOP:
+ case OMAKESLICE:
+ case ORUNESTR:
+ case OADDR:
+ case OCOM:
+ case OIND:
+ case OMINUS:
+ case ONOT:
+ case OPLUS:
+ case ORECV:
+ case OCONVIFACE:
+ case OTPAREN:
+ case OINDEX:
+ case OINDEXMAP:
+ case OPAREN:
+ nprec = 7;
+ break;
+
+ case OMUL:
+ case ODIV:
+ case OMOD:
+ case OLSH:
+ case ORSH:
+ case OAND:
+ case OANDNOT:
+ nprec = 6;
+ break;
+
+ case OADD:
+ case OSUB:
+ case OOR:
+ case OXOR:
+ nprec = 5;
+ break;
+
+ case OEQ:
+ case OLT:
+ case OLE:
+ case OGE:
+ case OGT:
+ case ONE:
+ nprec = 4;
+ break;
+
+ case OSEND:
+ nprec = 3;
+ break;
+
+ case OANDAND:
+ nprec = 2;
+ break;
+
+ case OOROR:
+ nprec = 1;
+ break;
+
+ case OTYPE:
+ if(n->sym != S)
+ nprec = 7;
+ break;
+ }
+
+ if(prec > nprec)
+ fmtprint(f, "(");
+
+ switch(n->op) {
+ default:
+ bad:
+ fmtprint(f, "(node %O)", n->op);
+ break;
+
+ case OPAREN:
+ fmtprint(f, "(%#N)", n->left);
+ break;
+
+ case OREGISTER:
+ fmtprint(f, "%R", n->val.u.reg);
+ break;
+
+ case OLITERAL:
+ if(n->sym != S) {
+ fmtprint(f, "%S", n->sym);
+ break;
+ }
+ switch(n->val.ctype) {
+ default:
+ goto bad;
+ case CTINT:
+ fmtprint(f, "%B", n->val.u.xval);
+ break;
+ case CTBOOL:
+ if(n->val.u.bval)
+ fmtprint(f, "true");
+ else
+ fmtprint(f, "false");
+ break;
+ case CTCPLX:
+ fmtprint(f, "%.17g+%.17gi",
+ mpgetflt(&n->val.u.cval->real),
+ mpgetflt(&n->val.u.cval->imag));
+ break;
+ case CTFLT:
+ fmtprint(f, "%.17g", mpgetflt(n->val.u.fval));
+ break;
+ case CTSTR:
+ fmtprint(f, "\"%Z\"", n->val.u.sval);
+ break;
+ case CTNIL:
+ fmtprint(f, "nil");
+ break;
+ }
+ break;
+
+ case ONAME:
+ case OPACK:
+ case ONONAME:
+ fmtprint(f, "%S", n->sym);
+ break;
+
+ case OTYPE:
+ if(n->type == T && n->sym != S) {
+ fmtprint(f, "%S", n->sym);
+ break;
+ }
+ fmtprint(f, "%T", n->type);
+ break;
+
+ case OTARRAY:
+ fmtprint(f, "[]");
+ exprfmt(f, n->left, PFIXME);
+ break;
+
+ case OTPAREN:
+ fmtprint(f, "(");
+ exprfmt(f, n->left, 0);
+ fmtprint(f, ")");
+ break;
+
+ case OTMAP:
+ fmtprint(f, "map[");
+ exprfmt(f, n->left, 0);
+ fmtprint(f, "] ");
+ exprfmt(f, n->right, 0);
+ break;
+
+ case OTCHAN:
+ if(n->etype == Crecv)
+ fmtprint(f, "<-");
+ fmtprint(f, "chan");
+ if(n->etype == Csend) {
+ fmtprint(f, "<- ");
+ exprfmt(f, n->left, 0);
+ } else {
+ fmtprint(f, " ");
+ if(n->left->op == OTCHAN && n->left->sym == S && n->left->etype == Crecv) {
+ fmtprint(f, "(");
+ exprfmt(f, n->left, 0);
+ fmtprint(f, ")");
+ } else
+ exprfmt(f, n->left, 0);
+ }
+ break;
+
+ case OTSTRUCT:
+ fmtprint(f, "<struct>");
+ break;
+
+ case OTINTER:
+ fmtprint(f, "<inter>");
+ break;
+
+ case OTFUNC:
+ fmtprint(f, "<func>");
+ break;
+
+ case OAS:
+ exprfmt(f, n->left, 0);
+ fmtprint(f, " = ");
+ exprfmt(f, n->right, 0);
+ break;
+
+ case OASOP:
+ exprfmt(f, n->left, 0);
+ fmtprint(f, " %#O= ", n->etype);
+ exprfmt(f, n->right, 0);
+ break;
+
+ case OAS2:
+ case OAS2DOTTYPE:
+ case OAS2FUNC:
+ case OAS2MAPR:
+ case OAS2MAPW:
+ case OAS2RECV:
+ exprlistfmt(f, n->list);
+ fmtprint(f, " = ");
+ exprlistfmt(f, n->rlist);
+ break;
+
+ case OADD:
+ case OANDAND:
+ case OANDNOT:
+ case ODIV:
+ case OEQ:
+ case OGE:
+ case OGT:
+ case OLE:
+ case OLT:
+ case OLSH:
+ case OMOD:
+ case OMUL:
+ case ONE:
+ case OOR:
+ case OOROR:
+ case ORSH:
+ case OSEND:
+ case OSUB:
+ case OXOR:
+ exprfmt(f, n->left, nprec);
+ fmtprint(f, " %#O ", n->op);
+ exprfmt(f, n->right, nprec+1);
+ break;
+
+ case OADDR:
+ case OCOM:
+ case OIND:
+ case OMINUS:
+ case ONOT:
+ case OPLUS:
+ case ORECV:
+ fmtprint(f, "%#O", n->op);
+ if((n->op == OMINUS || n->op == OPLUS) && n->left->op == n->op)
+ fmtprint(f, " ");
+ exprfmt(f, n->left, 0);
+ break;
+
+ case OCLOSURE:
+ fmtprint(f, "func literal");
+ break;
+
+ case OCOMPLIT:
+ fmtprint(f, "composite literal");
+ break;
+
+ case OARRAYLIT:
+ if(isslice(n->type))
+ fmtprint(f, "slice literal");
+ else
+ fmtprint(f, "array literal");
+ break;
+
+ case OMAPLIT:
+ fmtprint(f, "map literal");
+ break;
+
+ case OSTRUCTLIT:
+ fmtprint(f, "struct literal");
+ break;
+
+ case OXDOT:
+ case ODOT:
+ case ODOTPTR:
+ case ODOTINTER:
+ case ODOTMETH:
+ exprfmt(f, n->left, 7);
+ if(n->right == N || n->right->sym == S)
+ fmtprint(f, ".<nil>");
+ else {
+ // skip leading type· in method name
+ p = utfrrune(n->right->sym->name, 0xb7);
+ if(p)
+ p+=2;
+ else
+ p = n->right->sym->name;
+ fmtprint(f, ".%s", p);
+ }
+ break;
+
+ case ODOTTYPE:
+ case ODOTTYPE2:
+ exprfmt(f, n->left, 7);
+ fmtprint(f, ".(");
+ if(n->right != N)
+ exprfmt(f, n->right, 0);
+ else
+ fmtprint(f, "%T", n->type);
+ fmtprint(f, ")");
+ break;
+
+ case OINDEX:
+ case OINDEXMAP:
+ exprfmt(f, n->left, 7);
+ fmtprint(f, "[");
+ exprfmt(f, n->right, 0);
+ fmtprint(f, "]");
+ break;
+
+ case OSLICE:
+ case OSLICESTR:
+ case OSLICEARR:
+ exprfmt(f, n->left, 7);
+ fmtprint(f, "[");
+ if(n->right->left != N)
+ exprfmt(f, n->right->left, 0);
+ fmtprint(f, ":");
+ if(n->right->right != N)
+ exprfmt(f, n->right->right, 0);
+ fmtprint(f, "]");
+ break;
+
+ case OCALL:
+ case OCALLFUNC:
+ case OCALLINTER:
+ case OCALLMETH:
+ exprfmt(f, n->left, 7);
+ fmtprint(f, "(");
+ exprlistfmt(f, n->list);
+ if(n->isddd)
+ fmtprint(f, "...");
+ fmtprint(f, ")");
+ break;
+
+ case OCOMPLEX:
+ fmtprint(f, "complex(");
+ exprfmt(f, n->left, 0);
+ fmtprint(f, ", ");
+ exprfmt(f, n->right, 0);
+ fmtprint(f, ")");
+ break;
+
+ case OREAL:
+ fmtprint(f, "real(");
+ exprfmt(f, n->left, 0);
+ fmtprint(f, ")");
+ break;
+
+ case OIMAG:
+ fmtprint(f, "imag(");
+ exprfmt(f, n->left, 0);
+ fmtprint(f, ")");
+ break;
+
+ case OCONV:
+ case OCONVIFACE:
+ case OCONVNOP:
+ case OARRAYBYTESTR:
+ case OSTRARRAYBYTE:
+ case ORUNESTR:
+ if(n->type == T || n->type->sym == S)
+ fmtprint(f, "(%T)(", n->type);
+ else
+ fmtprint(f, "%T(", n->type);
+ if(n->left == N)
+ exprlistfmt(f, n->list);
+ else
+ exprfmt(f, n->left, 0);
+ fmtprint(f, ")");
+ break;
+
+ case OAPPEND:
+ case OCAP:
+ case OCLOSE:
+ case OLEN:
+ case OCOPY:
+ case OMAKE:
+ case ONEW:
+ case OPANIC:
+ case OPRINT:
+ case OPRINTN:
+ fmtprint(f, "%#O(", n->op);
+ if(n->left)
+ exprfmt(f, n->left, 0);
+ else
+ exprlistfmt(f, n->list);
+ fmtprint(f, ")");
+ break;
+
+ case OMAKESLICE:
+ fmtprint(f, "make(%#T, ", n->type);
+ exprfmt(f, n->left, 0);
+ if(count(n->list) > 2) {
+ fmtprint(f, ", ");
+ exprfmt(f, n->right, 0);
+ }
+ fmtprint(f, ")");
+ break;
+
+ case OMAKEMAP:
+ case OMAKECHAN:
+ fmtprint(f, "make(%#T)", n->type);
+ break;
+
+ // Some statements
+
+ case ODCL:
+ fmtprint(f, "var %S %#T", n->left->sym, n->left->type);
+ break;
+
+ case ORETURN:
+ fmtprint(f, "return ");
+ exprlistfmt(f, n->list);
+ break;
+
+ case OPROC:
+ fmtprint(f, "go %#N", n->left);
+ break;
+
+ case ODEFER:
+ fmtprint(f, "defer %#N", n->left);
+ break;
+ }
+
+ if(prec > nprec)
+ fmtprint(f, ")");
+}
diff --git a/src/cmd/gc/range.c b/src/cmd/gc/range.c
new file mode 100644
index 000000000..5ce693ae3
--- /dev/null
+++ b/src/cmd/gc/range.c
@@ -0,0 +1,256 @@
+// Copyright 2009 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.
+
+/*
+ * range
+ */
+
+#include "go.h"
+
+void
+typecheckrange(Node *n)
+{
+ char *why;
+ Type *t, *t1, *t2;
+ Node *v1, *v2;
+ NodeList *ll;
+
+ // delicate little dance. see typecheckas2
+ for(ll=n->list; ll; ll=ll->next)
+ if(ll->n->defn != n)
+ typecheck(&ll->n, Erv | Easgn);
+
+ typecheck(&n->right, Erv);
+ if((t = n->right->type) == T)
+ goto out;
+ if(isptr[t->etype] && isfixedarray(t->type))
+ t = t->type;
+ n->type = t;
+
+ switch(t->etype) {
+ default:
+ yyerror("cannot range over %+N", n->right);
+ goto out;
+
+ case TARRAY:
+ t1 = types[TINT];
+ t2 = t->type;
+ break;
+
+ case TMAP:
+ t1 = t->down;
+ t2 = t->type;
+ break;
+
+ case TCHAN:
+ t1 = t->type;
+ t2 = nil;
+ if(count(n->list) == 2)
+ goto toomany;
+ break;
+
+ case TSTRING:
+ t1 = types[TINT];
+ t2 = types[TINT];
+ break;
+ }
+
+ if(count(n->list) > 2) {
+ toomany:
+ yyerror("too many variables in range");
+ }
+
+ v1 = n->list->n;
+ v2 = N;
+ if(n->list->next)
+ v2 = n->list->next->n;
+
+ if(v1->defn == n)
+ v1->type = t1;
+ else if(v1->type != T && assignop(t1, v1->type, &why) == 0)
+ yyerror("cannot assign type %T to %+N in range%s", t1, v1, why);
+ if(v2) {
+ if(v2->defn == n)
+ v2->type = t2;
+ else if(v2->type != T && assignop(t2, v2->type, &why) == 0)
+ yyerror("cannot assign type %T to %+N in range%s", t2, v2, why);
+ }
+
+out:
+ typechecklist(n->nbody, Etop);
+
+ // second half of dance
+ n->typecheck = 1;
+ for(ll=n->list; ll; ll=ll->next)
+ if(ll->n->typecheck == 0)
+ typecheck(&ll->n, Erv | Easgn);
+}
+
+void
+walkrange(Node *n)
+{
+ Node *ohv1, *hv1, *hv2; // hidden (old) val 1, 2
+ Node *ha, *hit; // hidden aggregate, iterator
+ Node *hn, *hp; // hidden len, pointer
+ Node *hb; // hidden bool
+ Node *a, *v1, *v2; // not hidden aggregate, val 1, 2
+ Node *fn, *tmp;
+ NodeList *body, *init;
+ Type *th, *t;
+ int lno;
+
+ t = n->type;
+ init = nil;
+
+ a = n->right;
+ lno = setlineno(a);
+ if(t->etype == TSTRING && !eqtype(t, types[TSTRING])) {
+ a = nod(OCONV, n->right, N);
+ a->type = types[TSTRING];
+ }
+
+ v1 = n->list->n;
+ hv1 = N;
+
+ v2 = N;
+ if(n->list->next)
+ v2 = n->list->next->n;
+ hv2 = N;
+
+ if(v2 == N && t->etype == TARRAY) {
+ // will have just one reference to argument.
+ // no need to make a potentially expensive copy.
+ ha = a;
+ } else {
+ ha = nod(OXXX, N, N);
+ tempname(ha, a->type);
+ init = list(init, nod(OAS, ha, a));
+ }
+
+ switch(t->etype) {
+ default:
+ fatal("walkrange");
+
+ case TARRAY:
+ hv1 = nod(OXXX, N, n);
+ tempname(hv1, types[TINT]);
+ hn = nod(OXXX, N, N);
+ tempname(hn, types[TINT]);
+ hp = nil;
+
+ init = list(init, nod(OAS, hv1, N));
+ init = list(init, nod(OAS, hn, nod(OLEN, ha, N)));
+ if(v2) {
+ hp = nod(OXXX, N, N);
+ tempname(hp, ptrto(n->type->type));
+ tmp = nod(OINDEX, ha, nodintconst(0));
+ tmp->etype = 1; // no bounds check
+ init = list(init, nod(OAS, hp, nod(OADDR, tmp, N)));
+ }
+
+ n->ntest = nod(OLT, hv1, hn);
+ n->nincr = nod(OASOP, hv1, nodintconst(1));
+ n->nincr->etype = OADD;
+ body = list1(nod(OAS, v1, hv1));
+ if(v2) {
+ body = list(body, nod(OAS, v2, nod(OIND, hp, N)));
+ tmp = nod(OADD, hp, nodintconst(t->type->width));
+ tmp->type = hp->type;
+ tmp->typecheck = 1;
+ tmp->right->type = types[tptr];
+ tmp->right->typecheck = 1;
+ body = list(body, nod(OAS, hp, tmp));
+ }
+ break;
+
+ case TMAP:
+ th = typ(TARRAY);
+ th->type = ptrto(types[TUINT8]);
+ th->bound = (sizeof(struct Hiter) + widthptr - 1) / widthptr;
+ hit = nod(OXXX, N, N);
+ tempname(hit, th);
+
+ fn = syslook("mapiterinit", 1);
+ argtype(fn, t->down);
+ argtype(fn, t->type);
+ argtype(fn, th);
+ init = list(init, mkcall1(fn, T, nil, typename(t), ha, nod(OADDR, hit, N)));
+ n->ntest = nod(ONE, nod(OINDEX, hit, nodintconst(0)), nodnil());
+
+ fn = syslook("mapiternext", 1);
+ argtype(fn, th);
+ n->nincr = mkcall1(fn, T, nil, nod(OADDR, hit, N));
+
+ if(v2 == N) {
+ fn = syslook("mapiter1", 1);
+ argtype(fn, th);
+ argtype(fn, t->down);
+ a = nod(OAS, v1, mkcall1(fn, t->down, nil, nod(OADDR, hit, N)));
+ } else {
+ fn = syslook("mapiter2", 1);
+ argtype(fn, th);
+ argtype(fn, t->down);
+ argtype(fn, t->type);
+ a = nod(OAS2, N, N);
+ a->list = list(list1(v1), v2);
+ a->rlist = list1(mkcall1(fn, getoutargx(fn->type), nil, nod(OADDR, hit, N)));
+ }
+ body = list1(a);
+ break;
+
+ case TCHAN:
+ hv1 = nod(OXXX, N, n);
+ tempname(hv1, t->type);
+ hb = nod(OXXX, N, N);
+ tempname(hb, types[TBOOL]);
+
+ n->ntest = nod(ONE, hb, nodbool(0));
+ a = nod(OAS2RECV, N, N);
+ a->typecheck = 1;
+ a->list = list(list1(hv1), hb);
+ a->rlist = list1(nod(ORECV, ha, N));
+ n->ntest->ninit = list1(a);
+ body = list1(nod(OAS, v1, hv1));
+ break;
+
+ case TSTRING:
+ ohv1 = nod(OXXX, N, N);
+ tempname(ohv1, types[TINT]);
+
+ hv1 = nod(OXXX, N, N);
+ tempname(hv1, types[TINT]);
+ init = list(init, nod(OAS, hv1, N));
+
+ if(v2 == N)
+ a = nod(OAS, hv1, mkcall("stringiter", types[TINT], nil, ha, hv1));
+ else {
+ hv2 = nod(OXXX, N, N);
+ tempname(hv2, types[TINT]);
+ a = nod(OAS2, N, N);
+ a->list = list(list1(hv1), hv2);
+ fn = syslook("stringiter2", 0);
+ a->rlist = list1(mkcall1(fn, getoutargx(fn->type), nil, ha, hv1));
+ }
+ n->ntest = nod(ONE, hv1, nodintconst(0));
+ n->ntest->ninit = list(list1(nod(OAS, ohv1, hv1)), a);
+
+ body = list1(nod(OAS, v1, ohv1));
+ if(v2 != N)
+ body = list(body, nod(OAS, v2, hv2));
+ break;
+ }
+
+ n->op = OFOR;
+ typechecklist(init, Etop);
+ n->ninit = concat(n->ninit, init);
+ typechecklist(n->ntest->ninit, Etop);
+ typecheck(&n->ntest, Erv);
+ typecheck(&n->nincr, Etop);
+ typechecklist(body, Etop);
+ n->nbody = concat(body, n->nbody);
+ walkstmt(&n);
+
+ lineno = lno;
+}
+
diff --git a/src/cmd/gc/reflect.c b/src/cmd/gc/reflect.c
new file mode 100644
index 000000000..810787d30
--- /dev/null
+++ b/src/cmd/gc/reflect.c
@@ -0,0 +1,939 @@
+// Copyright 2009 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.
+
+#include "go.h"
+
+/*
+ * runtime interface and reflection data structures
+ */
+
+static NodeList* signatlist;
+static Sym* dtypesym(Type*);
+static Sym* weaktypesym(Type*);
+
+static int
+sigcmp(Sig *a, Sig *b)
+{
+ int i;
+
+ i = strcmp(a->name, b->name);
+ if(i != 0)
+ return i;
+ if(a->pkg == b->pkg)
+ return 0;
+ if(a->pkg == nil)
+ return -1;
+ if(b->pkg == nil)
+ return +1;
+ return strcmp(a->pkg->path->s, b->pkg->path->s);
+}
+
+static Sig*
+lsort(Sig *l, int(*f)(Sig*, Sig*))
+{
+ Sig *l1, *l2, *le;
+
+ if(l == 0 || l->link == 0)
+ return l;
+
+ l1 = l;
+ l2 = l;
+ for(;;) {
+ l2 = l2->link;
+ if(l2 == 0)
+ break;
+ l2 = l2->link;
+ if(l2 == 0)
+ break;
+ l1 = l1->link;
+ }
+
+ l2 = l1->link;
+ l1->link = 0;
+ l1 = lsort(l, f);
+ l2 = lsort(l2, f);
+
+ /* set up lead element */
+ if((*f)(l1, l2) < 0) {
+ l = l1;
+ l1 = l1->link;
+ } else {
+ l = l2;
+ l2 = l2->link;
+ }
+ le = l;
+
+ for(;;) {
+ if(l1 == 0) {
+ while(l2) {
+ le->link = l2;
+ le = l2;
+ l2 = l2->link;
+ }
+ le->link = 0;
+ break;
+ }
+ if(l2 == 0) {
+ while(l1) {
+ le->link = l1;
+ le = l1;
+ l1 = l1->link;
+ }
+ break;
+ }
+ if((*f)(l1, l2) < 0) {
+ le->link = l1;
+ le = l1;
+ l1 = l1->link;
+ } else {
+ le->link = l2;
+ le = l2;
+ l2 = l2->link;
+ }
+ }
+ le->link = 0;
+ return l;
+}
+
+/*
+ * f is method type, with receiver.
+ * return function type, receiver as first argument (or not).
+ */
+Type*
+methodfunc(Type *f, Type *receiver)
+{
+ NodeList *in, *out;
+ Node *d;
+ Type *t;
+
+ in = nil;
+ if(receiver) {
+ d = nod(ODCLFIELD, N, N);
+ d->type = receiver;
+ in = list(in, d);
+ }
+ for(t=getinargx(f)->type; t; t=t->down) {
+ d = nod(ODCLFIELD, N, N);
+ d->type = t->type;
+ d->isddd = t->isddd;
+ in = list(in, d);
+ }
+
+ out = nil;
+ for(t=getoutargx(f)->type; t; t=t->down) {
+ d = nod(ODCLFIELD, N, N);
+ d->type = t->type;
+ out = list(out, d);
+ }
+
+ return functype(N, in, out);
+}
+
+/*
+ * return methods of non-interface type t, sorted by name.
+ * generates stub functions as needed.
+ */
+static Sig*
+methods(Type *t)
+{
+ Type *f, *mt, *it, *this;
+ Sig *a, *b;
+ Sym *method;
+ Prog *oldlist;
+
+ // named method type
+ mt = methtype(t);
+ if(mt == T)
+ return nil;
+ expandmeth(mt->sym, mt);
+
+ // type stored in interface word
+ it = t;
+ if(it->width > widthptr)
+ it = ptrto(t);
+
+ // make list of methods for t,
+ // generating code if necessary.
+ a = nil;
+ oldlist = nil;
+ for(f=mt->xmethod; f; f=f->down) {
+ if(f->type->etype != TFUNC)
+ continue;
+ if(f->etype != TFIELD)
+ fatal("methods: not field");
+ method = f->sym;
+ if(method == nil)
+ continue;
+
+ // get receiver type for this particular method.
+ // if pointer receiver but non-pointer t and
+ // this is not an embedded pointer inside a struct,
+ // method does not apply.
+ this = getthisx(f->type)->type->type;
+ if(isptr[this->etype] && this->type == t)
+ continue;
+ if(isptr[this->etype] && !isptr[t->etype]
+ && f->embedded != 2 && !isifacemethod(f->type))
+ continue;
+
+ b = mal(sizeof(*b));
+ b->link = a;
+ a = b;
+
+ a->name = method->name;
+ if(!exportname(method->name)) {
+ if(method->pkg == nil)
+ fatal("methods: missing package");
+ a->pkg = method->pkg;
+ }
+ a->isym = methodsym(method, it, 1);
+ a->tsym = methodsym(method, t, 0);
+ a->type = methodfunc(f->type, t);
+ a->mtype = methodfunc(f->type, nil);
+
+ if(!(a->isym->flags & SymSiggen)) {
+ a->isym->flags |= SymSiggen;
+ if(!eqtype(this, it) || this->width < types[tptr]->width) {
+ if(oldlist == nil)
+ oldlist = pc;
+ // Is okay to call genwrapper here always,
+ // but we can generate more efficient code
+ // using genembedtramp if all that is necessary
+ // is a pointer adjustment and a JMP.
+ if(isptr[it->etype] && isptr[this->etype]
+ && f->embedded && !isifacemethod(f->type))
+ genembedtramp(it, f, a->isym, 1);
+ else
+ genwrapper(it, f, a->isym, 1);
+ }
+ }
+
+ if(!(a->tsym->flags & SymSiggen)) {
+ a->tsym->flags |= SymSiggen;
+ if(!eqtype(this, t)) {
+ if(oldlist == nil)
+ oldlist = pc;
+ if(isptr[t->etype] && isptr[this->etype]
+ && f->embedded && !isifacemethod(f->type))
+ genembedtramp(t, f, a->tsym, 0);
+ else
+ genwrapper(t, f, a->tsym, 0);
+ }
+ }
+ }
+
+ // restore data output
+ if(oldlist) {
+ // old list ended with AEND; change to ANOP
+ // so that the trampolines that follow can be found.
+ nopout(oldlist);
+
+ // start new data list
+ newplist();
+ }
+
+ return lsort(a, sigcmp);
+}
+
+/*
+ * return methods of interface type t, sorted by name.
+ */
+static Sig*
+imethods(Type *t)
+{
+ Sig *a, *all, *last;
+ Type *f;
+ Sym *method, *isym;
+ Prog *oldlist;
+
+ all = nil;
+ last = nil;
+ oldlist = nil;
+ for(f=t->type; f; f=f->down) {
+ if(f->etype != TFIELD)
+ fatal("imethods: not field");
+ if(f->type->etype != TFUNC || f->sym == nil)
+ continue;
+ method = f->sym;
+ a = mal(sizeof(*a));
+ a->name = method->name;
+ if(!exportname(method->name)) {
+ if(method->pkg == nil)
+ fatal("imethods: missing package");
+ a->pkg = method->pkg;
+ }
+ a->mtype = f->type;
+ a->offset = 0;
+ a->type = methodfunc(f->type, nil);
+
+ if(last && sigcmp(last, a) >= 0)
+ fatal("sigcmp vs sortinter %s %s", last->name, a->name);
+ if(last == nil)
+ all = a;
+ else
+ last->link = a;
+ last = a;
+
+ // Compiler can only refer to wrappers for
+ // named interface types.
+ if(t->sym == S)
+ continue;
+
+ // NOTE(rsc): Perhaps an oversight that
+ // IfaceType.Method is not in the reflect data.
+ // Generate the method body, so that compiled
+ // code can refer to it.
+ isym = methodsym(method, t, 0);
+ if(!(isym->flags & SymSiggen)) {
+ isym->flags |= SymSiggen;
+ if(oldlist == nil)
+ oldlist = pc;
+ genwrapper(t, f, isym, 0);
+ }
+ }
+
+ if(oldlist) {
+ // old list ended with AEND; change to ANOP
+ // so that the trampolines that follow can be found.
+ nopout(oldlist);
+
+ // start new data list
+ newplist();
+ }
+
+ return all;
+}
+
+static void
+dimportpath(Pkg *p)
+{
+ static Pkg *gopkg;
+ char *nam;
+ Node *n;
+
+ if(p->pathsym != S)
+ return;
+
+ if(gopkg == nil) {
+ gopkg = mkpkg(strlit("go"));
+ gopkg->name = "go";
+ }
+ nam = smprint("importpath.%s.", p->prefix);
+
+ n = nod(ONAME, N, N);
+ n->sym = pkglookup(nam, gopkg);
+ free(nam);
+ n->class = PEXTERN;
+ n->xoffset = 0;
+ p->pathsym = n->sym;
+
+ gdatastring(n, p->path);
+ ggloblsym(n->sym, types[TSTRING]->width, 1);
+}
+
+static int
+dgopkgpath(Sym *s, int ot, Pkg *pkg)
+{
+ if(pkg == nil)
+ return dgostringptr(s, ot, nil);
+
+ // Emit reference to go.importpath.""., which 6l will
+ // rewrite using the correct import path. Every package
+ // that imports this one directly defines the symbol.
+ if(pkg == localpkg) {
+ static Sym *ns;
+
+ if(ns == nil)
+ ns = pkglookup("importpath.\"\".", mkpkg(strlit("go")));
+ return dsymptr(s, ot, ns, 0);
+ }
+
+ dimportpath(pkg);
+ return dsymptr(s, ot, pkg->pathsym, 0);
+}
+
+/*
+ * uncommonType
+ * ../../pkg/runtime/type.go:/uncommonType
+ */
+static int
+dextratype(Sym *sym, int off, Type *t, int ptroff)
+{
+ int ot, n;
+ Sym *s;
+ Sig *a, *m;
+
+ m = methods(t);
+ if(t->sym == nil && m == nil)
+ return off;
+
+ // fill in *extraType pointer in header
+ dsymptr(sym, ptroff, sym, off);
+
+ n = 0;
+ for(a=m; a; a=a->link) {
+ dtypesym(a->type);
+ n++;
+ }
+
+ ot = off;
+ s = sym;
+ if(t->sym) {
+ ot = dgostringptr(s, ot, t->sym->name);
+ if(t != types[t->etype])
+ ot = dgopkgpath(s, ot, t->sym->pkg);
+ else
+ ot = dgostringptr(s, ot, nil);
+ } else {
+ ot = dgostringptr(s, ot, nil);
+ ot = dgostringptr(s, ot, nil);
+ }
+
+ // slice header
+ ot = dsymptr(s, ot, s, ot + widthptr + 2*4);
+ ot = duint32(s, ot, n);
+ ot = duint32(s, ot, n);
+
+ // methods
+ for(a=m; a; a=a->link) {
+ // method
+ // ../../pkg/runtime/type.go:/method
+ ot = dgostringptr(s, ot, a->name);
+ ot = dgopkgpath(s, ot, a->pkg);
+ ot = dsymptr(s, ot, dtypesym(a->mtype), 0);
+ ot = dsymptr(s, ot, dtypesym(a->type), 0);
+ if(a->isym)
+ ot = dsymptr(s, ot, a->isym, 0);
+ else
+ ot = duintptr(s, ot, 0);
+ if(a->tsym)
+ ot = dsymptr(s, ot, a->tsym, 0);
+ else
+ ot = duintptr(s, ot, 0);
+ }
+
+ return ot;
+}
+
+enum {
+ KindBool = 1,
+ KindInt,
+ KindInt8,
+ KindInt16,
+ KindInt32,
+ KindInt64,
+ KindUint,
+ KindUint8,
+ KindUint16,
+ KindUint32,
+ KindUint64,
+ KindUintptr,
+ KindFloat32,
+ KindFloat64,
+ KindComplex64,
+ KindComplex128,
+ KindArray,
+ KindChan,
+ KindFunc,
+ KindInterface,
+ KindMap,
+ KindPtr,
+ KindSlice,
+ KindString,
+ KindStruct,
+ KindUnsafePointer,
+
+ KindNoPointers = 1<<7,
+};
+
+static int
+kinds[] =
+{
+ [TINT] = KindInt,
+ [TUINT] = KindUint,
+ [TINT8] = KindInt8,
+ [TUINT8] = KindUint8,
+ [TINT16] = KindInt16,
+ [TUINT16] = KindUint16,
+ [TINT32] = KindInt32,
+ [TUINT32] = KindUint32,
+ [TINT64] = KindInt64,
+ [TUINT64] = KindUint64,
+ [TUINTPTR] = KindUintptr,
+ [TFLOAT32] = KindFloat32,
+ [TFLOAT64] = KindFloat64,
+ [TBOOL] = KindBool,
+ [TSTRING] = KindString,
+ [TPTR32] = KindPtr,
+ [TPTR64] = KindPtr,
+ [TSTRUCT] = KindStruct,
+ [TINTER] = KindInterface,
+ [TCHAN] = KindChan,
+ [TMAP] = KindMap,
+ [TARRAY] = KindArray,
+ [TFUNC] = KindFunc,
+ [TCOMPLEX64] = KindComplex64,
+ [TCOMPLEX128] = KindComplex128,
+ [TUNSAFEPTR] = KindUnsafePointer,
+};
+
+static char*
+structnames[] =
+{
+ [TINT] = "*runtime.IntType",
+ [TUINT] = "*runtime.UintType",
+ [TINT8] = "*runtime.IntType",
+ [TUINT8] = "*runtime.UintType",
+ [TINT16] = "*runtime.IntType",
+ [TUINT16] = "*runtime.UintType",
+ [TINT32] = "*runtime.IntType",
+ [TUINT32] = "*runtime.UintType",
+ [TINT64] = "*runtime.IntType",
+ [TUINT64] = "*runtime.UintType",
+ [TUINTPTR] = "*runtime.UintType",
+ [TCOMPLEX64] = "*runtime.ComplexType",
+ [TCOMPLEX128] = "*runtime.ComplexType",
+ [TFLOAT32] = "*runtime.FloatType",
+ [TFLOAT64] = "*runtime.FloatType",
+ [TBOOL] = "*runtime.BoolType",
+ [TSTRING] = "*runtime.StringType",
+ [TUNSAFEPTR] = "*runtime.UnsafePointerType",
+
+ [TPTR32] = "*runtime.PtrType",
+ [TPTR64] = "*runtime.PtrType",
+ [TSTRUCT] = "*runtime.StructType",
+ [TINTER] = "*runtime.InterfaceType",
+ [TCHAN] = "*runtime.ChanType",
+ [TMAP] = "*runtime.MapType",
+ [TARRAY] = "*runtime.ArrayType",
+ [TFUNC] = "*runtime.FuncType",
+};
+
+static Sym*
+typestruct(Type *t)
+{
+ char *name;
+ int et;
+
+ et = t->etype;
+ if(et < 0 || et >= nelem(structnames) || (name = structnames[et]) == nil) {
+ fatal("typestruct %lT", t);
+ return nil; // silence gcc
+ }
+
+ if(isslice(t))
+ name = "*runtime.SliceType";
+
+ return pkglookup(name, typepkg);
+}
+
+static int
+haspointers(Type *t)
+{
+ Type *t1;
+
+ switch(t->etype) {
+ case TINT:
+ case TUINT:
+ case TINT8:
+ case TUINT8:
+ case TINT16:
+ case TUINT16:
+ case TINT32:
+ case TUINT32:
+ case TINT64:
+ case TUINT64:
+ case TUINTPTR:
+ case TFLOAT32:
+ case TFLOAT64:
+ case TBOOL:
+ return 0;
+ case TARRAY:
+ if(t->bound < 0) // slice
+ return 1;
+ return haspointers(t->type);
+ case TSTRUCT:
+ for(t1=t->type; t1!=T; t1=t1->down)
+ if(haspointers(t1->type))
+ return 1;
+ return 0;
+ case TSTRING:
+ case TPTR32:
+ case TPTR64:
+ case TUNSAFEPTR:
+ case TINTER:
+ case TCHAN:
+ case TMAP:
+ case TFUNC:
+ default:
+ return 1;
+ }
+}
+
+/*
+ * commonType
+ * ../../pkg/runtime/type.go:/commonType
+ */
+static int
+dcommontype(Sym *s, int ot, Type *t)
+{
+ int i;
+ Sym *sptr;
+ char *p;
+
+ dowidth(t);
+
+ sptr = nil;
+ if(t->sym != nil && !isptr[t->etype])
+ sptr = dtypesym(ptrto(t));
+ else
+ sptr = weaktypesym(ptrto(t));
+
+ // empty interface pointing at this type.
+ // all the references that we emit are *interface{};
+ // they point here.
+ ot = rnd(ot, widthptr);
+ ot = dsymptr(s, ot, typestruct(t), 0);
+ ot = dsymptr(s, ot, s, 2*widthptr);
+
+ // ../../pkg/runtime/type.go:/commonType
+ // actual type structure
+ // type commonType struct {
+ // size uintptr;
+ // hash uint32;
+ // alg uint8;
+ // align uint8;
+ // fieldAlign uint8;
+ // kind uint8;
+ // string *string;
+ // *extraType;
+ // ptrToThis *Type
+ // }
+ ot = duintptr(s, ot, t->width);
+ ot = duint32(s, ot, typehash(t));
+ ot = duint8(s, ot, algtype(t));
+ ot = duint8(s, ot, t->align); // align
+ ot = duint8(s, ot, t->align); // fieldAlign
+ i = kinds[t->etype];
+ if(t->etype == TARRAY && t->bound < 0)
+ i = KindSlice;
+ if(!haspointers(t))
+ i |= KindNoPointers;
+ ot = duint8(s, ot, i); // kind
+ longsymnames = 1;
+ p = smprint("%-T", t);
+ longsymnames = 0;
+ ot = dgostringptr(s, ot, p); // string
+ free(p);
+
+ // skip pointer to extraType,
+ // which follows the rest of this type structure.
+ // caller will fill in if needed.
+ // otherwise linker will assume 0.
+ ot += widthptr;
+
+ ot = dsymptr(s, ot, sptr, 0); // ptrto type
+ return ot;
+}
+
+Sym*
+typesym(Type *t)
+{
+ char *p;
+ Sym *s;
+
+ p = smprint("%#-T", t);
+ s = pkglookup(p, typepkg);
+ free(p);
+ return s;
+}
+
+Node*
+typename(Type *t)
+{
+ Sym *s;
+ Node *n;
+
+ if(t == T || (isptr[t->etype] && t->type == T) || isideal(t))
+ fatal("typename %T", t);
+ s = typesym(t);
+ if(s->def == N) {
+ n = nod(ONAME, N, N);
+ n->sym = s;
+ n->type = types[TUINT8];
+ n->addable = 1;
+ n->ullman = 1;
+ n->class = PEXTERN;
+ n->xoffset = 0;
+ s->def = n;
+
+ signatlist = list(signatlist, typenod(t));
+ }
+
+ n = nod(OADDR, s->def, N);
+ n->type = ptrto(s->def->type);
+ n->addable = 1;
+ n->ullman = 2;
+ return n;
+}
+
+static Sym*
+weaktypesym(Type *t)
+{
+ char *p;
+ Sym *s;
+ static Pkg *weak;
+
+ if(weak == nil) {
+ weak = mkpkg(strlit("weak.type"));
+ weak->name = "weak.type";
+ weak->prefix = "weak.type"; // not weak%2etype
+ }
+
+ p = smprint("%#-T", t);
+ s = pkglookup(p, weak);
+ free(p);
+ return s;
+}
+
+static Sym*
+dtypesym(Type *t)
+{
+ int ot, xt, n, isddd, dupok;
+ Sym *s, *s1, *s2;
+ Sig *a, *m;
+ Type *t1, *tbase, *t2;
+
+ if(isideal(t))
+ fatal("dtypesym %T", t);
+
+ s = typesym(t);
+ if(s->flags & SymSiggen)
+ return s;
+ s->flags |= SymSiggen;
+
+ // special case (look for runtime below):
+ // when compiling package runtime,
+ // emit the type structures for int, float, etc.
+ tbase = t;
+ if(isptr[t->etype] && t->sym == S && t->type->sym != S)
+ tbase = t->type;
+ dupok = tbase->sym == S;
+
+ if(compiling_runtime && tbase == types[tbase->etype]) // int, float, etc
+ goto ok;
+
+ // named types from other files are defined only by those files
+ if(tbase->sym && !tbase->local)
+ return s;
+ if(isforw[tbase->etype])
+ return s;
+
+ok:
+ ot = 0;
+ xt = 0;
+ switch(t->etype) {
+ default:
+ ot = dcommontype(s, ot, t);
+ xt = ot - 2*widthptr;
+ break;
+
+ case TARRAY:
+ if(t->bound >= 0) {
+ // ../../pkg/runtime/type.go:/ArrayType
+ s1 = dtypesym(t->type);
+ t2 = typ(TARRAY);
+ t2->type = t->type;
+ t2->bound = -1; // slice
+ s2 = dtypesym(t2);
+ ot = dcommontype(s, ot, t);
+ xt = ot - 2*widthptr;
+ ot = dsymptr(s, ot, s1, 0);
+ ot = dsymptr(s, ot, s2, 0);
+ ot = duintptr(s, ot, t->bound);
+ } else {
+ // ../../pkg/runtime/type.go:/SliceType
+ s1 = dtypesym(t->type);
+ ot = dcommontype(s, ot, t);
+ xt = ot - 2*widthptr;
+ ot = dsymptr(s, ot, s1, 0);
+ }
+ break;
+
+ case TCHAN:
+ // ../../pkg/runtime/type.go:/ChanType
+ s1 = dtypesym(t->type);
+ ot = dcommontype(s, ot, t);
+ xt = ot - 2*widthptr;
+ ot = dsymptr(s, ot, s1, 0);
+ ot = duintptr(s, ot, t->chan);
+ break;
+
+ case TFUNC:
+ for(t1=getthisx(t)->type; t1; t1=t1->down)
+ dtypesym(t1->type);
+ isddd = 0;
+ for(t1=getinargx(t)->type; t1; t1=t1->down) {
+ isddd = t1->isddd;
+ dtypesym(t1->type);
+ }
+ for(t1=getoutargx(t)->type; t1; t1=t1->down)
+ dtypesym(t1->type);
+
+ ot = dcommontype(s, ot, t);
+ xt = ot - 2*widthptr;
+ ot = duint8(s, ot, isddd);
+
+ // two slice headers: in and out.
+ ot = rnd(ot, widthptr);
+ ot = dsymptr(s, ot, s, ot+2*(widthptr+2*4));
+ n = t->thistuple + t->intuple;
+ ot = duint32(s, ot, n);
+ ot = duint32(s, ot, n);
+ ot = dsymptr(s, ot, s, ot+1*(widthptr+2*4)+n*widthptr);
+ ot = duint32(s, ot, t->outtuple);
+ ot = duint32(s, ot, t->outtuple);
+
+ // slice data
+ for(t1=getthisx(t)->type; t1; t1=t1->down, n++)
+ ot = dsymptr(s, ot, dtypesym(t1->type), 0);
+ for(t1=getinargx(t)->type; t1; t1=t1->down, n++)
+ ot = dsymptr(s, ot, dtypesym(t1->type), 0);
+ for(t1=getoutargx(t)->type; t1; t1=t1->down, n++)
+ ot = dsymptr(s, ot, dtypesym(t1->type), 0);
+ break;
+
+ case TINTER:
+ m = imethods(t);
+ n = 0;
+ for(a=m; a; a=a->link) {
+ dtypesym(a->type);
+ n++;
+ }
+
+ // ../../pkg/runtime/type.go:/InterfaceType
+ ot = dcommontype(s, ot, t);
+ xt = ot - 2*widthptr;
+ ot = dsymptr(s, ot, s, ot+widthptr+2*4);
+ ot = duint32(s, ot, n);
+ ot = duint32(s, ot, n);
+ for(a=m; a; a=a->link) {
+ // ../../pkg/runtime/type.go:/imethod
+ ot = dgostringptr(s, ot, a->name);
+ ot = dgopkgpath(s, ot, a->pkg);
+ ot = dsymptr(s, ot, dtypesym(a->type), 0);
+ }
+ break;
+
+ case TMAP:
+ // ../../pkg/runtime/type.go:/MapType
+ s1 = dtypesym(t->down);
+ s2 = dtypesym(t->type);
+ ot = dcommontype(s, ot, t);
+ xt = ot - 2*widthptr;
+ ot = dsymptr(s, ot, s1, 0);
+ ot = dsymptr(s, ot, s2, 0);
+ break;
+
+ case TPTR32:
+ case TPTR64:
+ if(t->type->etype == TANY) {
+ // ../../pkg/runtime/type.go:/UnsafePointerType
+ ot = dcommontype(s, ot, t);
+ break;
+ }
+ // ../../pkg/runtime/type.go:/PtrType
+ s1 = dtypesym(t->type);
+ ot = dcommontype(s, ot, t);
+ xt = ot - 2*widthptr;
+ ot = dsymptr(s, ot, s1, 0);
+ break;
+
+ case TSTRUCT:
+ // ../../pkg/runtime/type.go:/StructType
+ // for security, only the exported fields.
+ n = 0;
+ for(t1=t->type; t1!=T; t1=t1->down) {
+ dtypesym(t1->type);
+ n++;
+ }
+ ot = dcommontype(s, ot, t);
+ xt = ot - 2*widthptr;
+ ot = dsymptr(s, ot, s, ot+widthptr+2*4);
+ ot = duint32(s, ot, n);
+ ot = duint32(s, ot, n);
+ for(t1=t->type; t1!=T; t1=t1->down) {
+ // ../../pkg/runtime/type.go:/structField
+ if(t1->sym && !t1->embedded) {
+ ot = dgostringptr(s, ot, t1->sym->name);
+ if(exportname(t1->sym->name))
+ ot = dgostringptr(s, ot, nil);
+ else
+ ot = dgopkgpath(s, ot, t1->sym->pkg);
+ } else {
+ ot = dgostringptr(s, ot, nil);
+ ot = dgostringptr(s, ot, nil);
+ }
+ ot = dsymptr(s, ot, dtypesym(t1->type), 0);
+ ot = dgostrlitptr(s, ot, t1->note);
+ ot = duintptr(s, ot, t1->width); // field offset
+ }
+ break;
+ }
+ ot = dextratype(s, ot, t, xt);
+ ggloblsym(s, ot, dupok);
+ return s;
+}
+
+void
+dumptypestructs(void)
+{
+ int i;
+ NodeList *l;
+ Node *n;
+ Type *t;
+ Pkg *p;
+
+ // copy types from externdcl list to signatlist
+ for(l=externdcl; l; l=l->next) {
+ n = l->n;
+ if(n->op != OTYPE)
+ continue;
+ signatlist = list(signatlist, n);
+ }
+
+ // process signatlist
+ for(l=signatlist; l; l=l->next) {
+ n = l->n;
+ if(n->op != OTYPE)
+ continue;
+ t = n->type;
+ dtypesym(t);
+ if(t->sym)
+ dtypesym(ptrto(t));
+ }
+
+ // generate import strings for imported packages
+ for(i=0; i<nelem(phash); i++)
+ for(p=phash[i]; p; p=p->link)
+ if(p->direct)
+ dimportpath(p);
+
+ // do basic types if compiling package runtime.
+ // they have to be in at least one package,
+ // and runtime is always loaded implicitly,
+ // so this is as good as any.
+ // another possible choice would be package main,
+ // but using runtime means fewer copies in .6 files.
+ if(compiling_runtime) {
+ for(i=1; i<=TBOOL; i++)
+ dtypesym(ptrto(types[i]));
+ dtypesym(ptrto(types[TSTRING]));
+ dtypesym(ptrto(types[TUNSAFEPTR]));
+
+ // add paths for runtime and main, which 6l imports implicitly.
+ dimportpath(runtimepkg);
+ dimportpath(mkpkg(strlit("main")));
+ }
+}
diff --git a/src/cmd/gc/runtime.go b/src/cmd/gc/runtime.go
new file mode 100644
index 000000000..549f7abe3
--- /dev/null
+++ b/src/cmd/gc/runtime.go
@@ -0,0 +1,130 @@
+// Copyright 2009 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.
+
+// NOTE: If you change this file you must run "./mkbuiltin"
+// to update builtin.c.boot. This is not done automatically
+// to avoid depending on having a working compiler binary.
+
+package PACKAGE
+
+// emitted by compiler, not referred to by go programs
+
+func new(int32) *any
+func panicindex()
+func panicslice()
+func throwreturn()
+func throwinit()
+func panicwrap(string, string, string)
+
+func panic(interface{})
+func recover(*int32) interface{}
+
+func printbool(bool)
+func printfloat(float64)
+func printint(int64)
+func printuint(uint64)
+func printcomplex(complex128)
+func printstring(string)
+func printpointer(any)
+func printiface(any)
+func printeface(any)
+func printslice(any)
+func printnl()
+func printsp()
+func goprintf()
+
+// filled in by compiler: int n, string, string, ...
+func concatstring()
+
+// filled in by compiler: Type*, int n, Slice, ...
+func append()
+func appendslice(typ *byte, x any, y []any) any
+
+func cmpstring(string, string) int
+func slicestring(string, int, int) string
+func slicestring1(string, int) string
+func intstring(int64) string
+func slicebytetostring([]byte) string
+func sliceinttostring([]int) string
+func stringtoslicebyte(string) []byte
+func stringtosliceint(string) []int
+func stringiter(string, int) int
+func stringiter2(string, int) (retk int, retv int)
+func slicecopy(to any, fr any, wid uint32) int
+func slicestringcopy(to any, fr any) int
+
+// interface conversions
+func convI2E(elem any) (ret any)
+func convI2I(typ *byte, elem any) (ret any)
+func convT2E(typ *byte, elem any) (ret any)
+func convT2I(typ *byte, typ2 *byte, elem any) (ret any)
+
+// interface type assertions x.(T)
+func assertE2E(typ *byte, iface any) (ret any)
+func assertE2E2(typ *byte, iface any) (ret any, ok bool)
+func assertE2I(typ *byte, iface any) (ret any)
+func assertE2I2(typ *byte, iface any) (ret any, ok bool)
+func assertE2T(typ *byte, iface any) (ret any)
+func assertE2T2(typ *byte, iface any) (ret any, ok bool)
+func assertI2E(typ *byte, iface any) (ret any)
+func assertI2E2(typ *byte, iface any) (ret any, ok bool)
+func assertI2I(typ *byte, iface any) (ret any)
+func assertI2I2(typ *byte, iface any) (ret any, ok bool)
+func assertI2T(typ *byte, iface any) (ret any)
+func assertI2T2(typ *byte, iface any) (ret any, ok bool)
+
+func ifaceeq(i1 any, i2 any) (ret bool)
+func efaceeq(i1 any, i2 any) (ret bool)
+func ifacethash(i1 any) (ret uint32)
+func efacethash(i1 any) (ret uint32)
+
+// *byte is really *runtime.Type
+func makemap(mapType *byte, hint int64) (hmap map[any]any)
+func mapaccess1(mapType *byte, hmap map[any]any, key any) (val any)
+func mapaccess2(mapType *byte, hmap map[any]any, key any) (val any, pres bool)
+func mapassign1(mapType *byte, hmap map[any]any, key any, val any)
+func mapassign2(mapType *byte, hmap map[any]any, key any, val any, pres bool)
+func mapiterinit(mapType *byte, hmap map[any]any, hiter *any)
+func mapiternext(hiter *any)
+func mapiter1(hiter *any) (key any)
+func mapiter2(hiter *any) (key any, val any)
+
+// *byte is really *runtime.Type
+func makechan(chanType *byte, hint int64) (hchan chan any)
+func chanrecv1(chanType *byte, hchan <-chan any) (elem any)
+func chanrecv2(chanType *byte, hchan <-chan any) (elem any, received bool)
+func chansend1(chanType *byte, hchan chan<- any, elem any)
+func closechan(hchan any)
+
+func selectnbsend(chanType *byte, hchan chan<- any, elem any) bool
+func selectnbrecv(chanType *byte, elem *any, hchan <-chan any) bool
+func selectnbrecv2(chanType *byte, elem *any, received *bool, hchan <-chan any) bool
+
+func newselect(size int) (sel *byte)
+func selectsend(sel *byte, hchan chan<- any, elem *any) (selected bool)
+func selectrecv(sel *byte, hchan <-chan any, elem *any) (selected bool)
+func selectrecv2(sel *byte, hchan <-chan any, elem *any, received *bool) (selected bool)
+func selectdefault(sel *byte) (selected bool)
+func selectgo(sel *byte)
+func block()
+
+func makeslice(typ *byte, nel int64, cap int64) (ary []any)
+func growslice(typ *byte, old []any, n int64) (ary []any)
+func sliceslice1(old []any, lb uint64, width uint64) (ary []any)
+func sliceslice(old []any, lb uint64, hb uint64, width uint64) (ary []any)
+func slicearray(old *any, nel uint64, lb uint64, hb uint64, width uint64) (ary []any)
+
+func closure() // has args, but compiler fills in
+
+// only used on 32-bit
+func int64div(int64, int64) int64
+func uint64div(uint64, uint64) uint64
+func int64mod(int64, int64) int64
+func uint64mod(uint64, uint64) uint64
+func float64toint64(float64) int64
+func float64touint64(float64) uint64
+func int64tofloat64(int64) float64
+func uint64tofloat64(uint64) float64
+
+func complex128div(num complex128, den complex128) (quo complex128)
diff --git a/src/cmd/gc/select.c b/src/cmd/gc/select.c
new file mode 100644
index 000000000..909ad3aa4
--- /dev/null
+++ b/src/cmd/gc/select.c
@@ -0,0 +1,351 @@
+// Copyright 2009 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.
+
+/*
+ * select
+ */
+
+#include "go.h"
+
+void
+typecheckselect(Node *sel)
+{
+ Node *ncase, *n, *def;
+ NodeList *l;
+ int lno, count;
+
+ def = nil;
+ lno = setlineno(sel);
+ count = 0;
+ typechecklist(sel->ninit, Etop);
+ for(l=sel->list; l; l=l->next) {
+ count++;
+ ncase = l->n;
+ setlineno(ncase);
+ if(ncase->op != OXCASE)
+ fatal("typecheckselect %O", ncase->op);
+
+ if(ncase->list == nil) {
+ // default
+ if(def != N)
+ yyerror("multiple defaults in select (first at %L)", def->lineno);
+ else
+ def = ncase;
+ } else if(ncase->list->next) {
+ yyerror("select cases cannot be lists");
+ } else {
+ n = typecheck(&ncase->list->n, Etop);
+ ncase->left = n;
+ ncase->list = nil;
+ setlineno(n);
+ switch(n->op) {
+ default:
+ yyerror("select case must be receive, send or assign recv");
+ break;
+
+ case OAS:
+ // convert x = <-c into OSELRECV(x, <-c).
+ // remove implicit conversions; the eventual assignment
+ // will reintroduce them.
+ if((n->right->op == OCONVNOP || n->right->op == OCONVIFACE) && n->right->implicit)
+ n->right = n->right->left;
+
+ if(n->right->op != ORECV) {
+ yyerror("select assignment must have receive on right hand side");
+ break;
+ }
+ n->op = OSELRECV;
+ break;
+
+ case OAS2RECV:
+ // convert x, ok = <-c into OSELRECV(x, <-c) with ntest=ok
+ if(n->right->op != ORECV) {
+ yyerror("select assignment must have receive on right hand side");
+ break;
+ }
+ n->op = OSELRECV2;
+ n->left = n->list->n;
+ n->ntest = n->list->next->n;
+ n->right = n->rlist->n;
+ break;
+
+ case ORECV:
+ // convert <-c into OSELRECV(N, <-c)
+ n = nod(OSELRECV, N, n);
+ ncase->left = n;
+ break;
+
+ case OSEND:
+ break;
+ }
+ }
+ typechecklist(ncase->nbody, Etop);
+ }
+ sel->xoffset = count;
+ lineno = lno;
+}
+
+void
+walkselect(Node *sel)
+{
+ int lno, i;
+ Node *n, *r, *a, *tmp, *var, *cas, *dflt, *ch;
+ NodeList *l, *init;
+
+ if(sel->list == nil && sel->xoffset != 0)
+ fatal("double walkselect"); // already rewrote
+
+ lno = setlineno(sel);
+ i = count(sel->list);
+
+ // optimization: zero-case select
+ if(i == 0) {
+ sel->nbody = list1(mkcall("block", nil, nil));
+ goto out;
+ }
+
+ // optimization: one-case select: single op.
+ if(i == 1) {
+ cas = sel->list->n;
+ setlineno(cas);
+ l = cas->ninit;
+ if(cas->left != N) { // not default:
+ n = cas->left;
+ l = concat(l, n->ninit);
+ n->ninit = nil;
+ switch(n->op) {
+ default:
+ fatal("select %O", n->op);
+
+ case OSEND:
+ ch = cheapexpr(n->left, &l);
+ n->left = ch;
+ break;
+
+ case OSELRECV:
+ r = n->right;
+ ch = cheapexpr(r->left, &l);
+ r->left = ch;
+
+ if(n->left == N)
+ n = r;
+ else {
+ n = nod(OAS, n->left, r);
+ typecheck(&n, Etop);
+ }
+ break;
+
+ case OSELRECV2:
+ r = n->right;
+ ch = cheapexpr(r->left, &l);
+ r->left = ch;
+
+ a = nod(OAS2, N, N);
+ a->list = n->list;
+ a->rlist = n->rlist;
+ n = a;
+ typecheck(&n, Etop);
+ break;
+ }
+
+ // if ch == nil { block() }; n;
+ a = nod(OIF, N, N);
+ a->ntest = nod(OEQ, ch, nodnil());
+ a->nbody = list1(mkcall("block", nil, &l));
+ typecheck(&a, Etop);
+ l = list(l, a);
+ l = list(l, n);
+ }
+ l = concat(l, cas->nbody);
+ sel->nbody = l;
+ goto out;
+ }
+
+ // introduce temporary variables for OSELRECV where needed.
+ // this rewrite is used by both the general code and the next optimization.
+ for(l=sel->list; l; l=l->next) {
+ cas = l->n;
+ setlineno(cas);
+ n = cas->left;
+ if(n == N)
+ continue;
+ switch(n->op) {
+ case OSELRECV:
+ case OSELRECV2:
+ ch = n->right->left;
+
+ // If we can use the address of the target without
+ // violating addressability or order of operations, do so.
+ // Otherwise introduce a temporary.
+ // Also introduce a temporary for := variables that escape,
+ // so that we can delay the heap allocation until the case
+ // is selected.
+ if(n->op == OSELRECV2) {
+ if(n->ntest == N || isblank(n->ntest))
+ n->ntest = nodnil();
+ else if(n->ntest->op == ONAME &&
+ (!n->colas || (n->ntest->class&PHEAP) == 0) &&
+ convertop(types[TBOOL], n->ntest->type, nil) == OCONVNOP) {
+ n->ntest = nod(OADDR, n->ntest, N);
+ n->ntest->etype = 1; // pointer does not escape
+ typecheck(&n->ntest, Erv);
+ } else {
+ tmp = nod(OXXX, N, N);
+ tempname(tmp, types[TBOOL]);
+ a = nod(OADDR, tmp, N);
+ a->etype = 1; // pointer does not escape
+ typecheck(&a, Erv);
+ r = nod(OAS, n->ntest, tmp);
+ typecheck(&r, Etop);
+ cas->nbody = concat(list1(r), cas->nbody);
+ n->ntest = a;
+ }
+ }
+
+ if(n->left == N || isblank(n->left))
+ n->left = nodnil();
+ else if(n->left->op == ONAME &&
+ (!n->colas || (n->left->class&PHEAP) == 0) &&
+ convertop(ch->type->type, n->left->type, nil) == OCONVNOP) {
+ n->left = nod(OADDR, n->left, N);
+ n->left->etype = 1; // pointer does not escape
+ typecheck(&n->left, Erv);
+ } else {
+ tmp = nod(OXXX, N, N);
+ tempname(tmp, ch->type->type);
+ a = nod(OADDR, tmp, N);
+ a->etype = 1; // pointer does not escape
+ typecheck(&a, Erv);
+ r = nod(OAS, n->left, tmp);
+ typecheck(&r, Etop);
+ cas->nbody = concat(list1(r), cas->nbody);
+ n->left = a;
+ }
+
+ cas->nbody = concat(n->ninit, cas->nbody);
+ n->ninit = nil;
+ break;
+ }
+ }
+
+ // optimization: two-case select but one is default: single non-blocking op.
+ if(i == 2 && (sel->list->n->left == nil || sel->list->next->n->left == nil)) {
+ if(sel->list->n->left == nil) {
+ cas = sel->list->next->n;
+ dflt = sel->list->n;
+ } else {
+ dflt = sel->list->next->n;
+ cas = sel->list->n;
+ }
+
+ n = cas->left;
+ setlineno(n);
+ r = nod(OIF, N, N);
+ r->ninit = cas->ninit;
+ switch(n->op) {
+ default:
+ fatal("select %O", n->op);
+
+ case OSEND:
+ // if c != nil && selectnbsend(c, v) { body } else { default body }
+ ch = cheapexpr(n->left, &r->ninit);
+ r->ntest = mkcall1(chanfn("selectnbsend", 2, ch->type),
+ types[TBOOL], &r->ninit, typename(ch->type), ch, n->right);
+ break;
+
+ case OSELRECV:
+ // if c != nil && selectnbrecv(&v, c) { body } else { default body }
+ r = nod(OIF, N, N);
+ r->ninit = cas->ninit;
+ ch = cheapexpr(n->right->left, &r->ninit);
+ r->ntest = mkcall1(chanfn("selectnbrecv", 2, ch->type),
+ types[TBOOL], &r->ninit, typename(ch->type), n->left, ch);
+ break;
+
+ case OSELRECV2:
+ // if c != nil && selectnbrecv2(&v, c) { body } else { default body }
+ r = nod(OIF, N, N);
+ r->ninit = cas->ninit;
+ ch = cheapexpr(n->right->left, &r->ninit);
+ r->ntest = mkcall1(chanfn("selectnbrecv2", 2, ch->type),
+ types[TBOOL], &r->ninit, typename(ch->type), n->left, n->ntest, ch);
+ break;
+ }
+ typecheck(&r->ntest, Erv);
+ r->nbody = cas->nbody;
+ r->nelse = concat(dflt->ninit, dflt->nbody);
+ sel->nbody = list1(r);
+ goto out;
+ }
+
+ init = sel->ninit;
+ sel->ninit = nil;
+
+ // generate sel-struct
+ setlineno(sel);
+ var = nod(OXXX, N, N);
+ tempname(var, ptrto(types[TUINT8]));
+ r = nod(OAS, var, mkcall("newselect", var->type, nil, nodintconst(sel->xoffset)));
+ typecheck(&r, Etop);
+ init = list(init, r);
+
+ // register cases
+ for(l=sel->list; l; l=l->next) {
+ cas = l->n;
+ setlineno(cas);
+ n = cas->left;
+ r = nod(OIF, N, N);
+ r->nbody = cas->ninit;
+ cas->ninit = nil;
+ if(n != nil) {
+ r->nbody = concat(r->nbody, n->ninit);
+ n->ninit = nil;
+ }
+ if(n == nil) {
+ // selectdefault(sel *byte);
+ r->ntest = mkcall("selectdefault", types[TBOOL], &init, var);
+ } else {
+ switch(n->op) {
+ default:
+ fatal("select %O", n->op);
+
+ case OSEND:
+ // selectsend(sel *byte, hchan *chan any, elem *any) (selected bool);
+ n->left = safeexpr(n->left, &r->ninit);
+ n->right = localexpr(n->right, n->left->type->type, &r->ninit);
+ n->right = nod(OADDR, n->right, N);
+ n->right->etype = 1; // pointer does not escape
+ typecheck(&n->right, Erv);
+ r->ntest = mkcall1(chanfn("selectsend", 2, n->left->type), types[TBOOL],
+ &init, var, n->left, n->right);
+ break;
+
+ case OSELRECV:
+ // selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool);
+ r->ntest = mkcall1(chanfn("selectrecv", 2, n->right->left->type), types[TBOOL],
+ &init, var, n->right->left, n->left);
+ break;
+
+ case OSELRECV2:
+ // selectrecv2(sel *byte, hchan *chan any, elem *any, received *bool) (selected bool);
+ r->ntest = mkcall1(chanfn("selectrecv2", 2, n->right->left->type), types[TBOOL],
+ &init, var, n->right->left, n->left, n->ntest);
+ break;
+ }
+ }
+ r->nbody = concat(r->nbody, cas->nbody);
+ r->nbody = list(r->nbody, nod(OBREAK, N, N));
+ init = list(init, r);
+ }
+
+ // run the select
+ setlineno(sel);
+ init = list(init, mkcall("selectgo", T, nil, var));
+ sel->nbody = init;
+
+out:
+ sel->list = nil;
+ walkstmtlist(sel->nbody);
+ lineno = lno;
+}
diff --git a/src/cmd/gc/sinit.c b/src/cmd/gc/sinit.c
new file mode 100644
index 000000000..917e2ae6d
--- /dev/null
+++ b/src/cmd/gc/sinit.c
@@ -0,0 +1,971 @@
+// Copyright 2009 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.
+
+/*
+ * static initialization
+ */
+
+#include "go.h"
+
+static NodeList *initlist;
+static void init2(Node*, NodeList**);
+static void init2list(NodeList*, NodeList**);
+
+static void
+init1(Node *n, NodeList **out)
+{
+ NodeList *l;
+
+ if(n == N)
+ return;
+ init1(n->left, out);
+ init1(n->right, out);
+ for(l=n->list; l; l=l->next)
+ init1(l->n, out);
+
+ if(n->op != ONAME)
+ return;
+ switch(n->class) {
+ case PEXTERN:
+ case PFUNC:
+ break;
+ default:
+ if(isblank(n) && n->defn != N && !n->defn->initorder) {
+ n->defn->initorder = 1;
+ *out = list(*out, n->defn);
+ }
+ return;
+ }
+
+ if(n->initorder == 1)
+ return;
+ if(n->initorder == 2) {
+ if(n->class == PFUNC)
+ return;
+
+ // if there have already been errors printed,
+ // those errors probably confused us and
+ // there might not be a loop. let the user
+ // fix those first.
+ flusherrors();
+ if(nerrors > 0)
+ errorexit();
+
+ print("initialization loop:\n");
+ for(l=initlist;; l=l->next) {
+ if(l->next == nil)
+ break;
+ l->next->end = l;
+ }
+ for(; l; l=l->end)
+ print("\t%L %S refers to\n", l->n->lineno, l->n->sym);
+ print("\t%L %S\n", n->lineno, n->sym);
+ errorexit();
+ }
+ n->initorder = 2;
+ l = malloc(sizeof *l);
+ l->next = initlist;
+ l->n = n;
+ l->end = nil;
+ initlist = l;
+
+ // make sure that everything n depends on is initialized.
+ // n->defn is an assignment to n
+ if(n->defn != N) {
+ switch(n->defn->op) {
+ default:
+ goto bad;
+
+ case ODCLFUNC:
+ init2list(n->defn->nbody, out);
+ break;
+
+ case OAS:
+ if(n->defn->left != n)
+ goto bad;
+ n->defn->dodata = 1;
+ init1(n->defn->right, out);
+ if(debug['j'])
+ print("%S\n", n->sym);
+ *out = list(*out, n->defn);
+ break;
+
+ case OAS2FUNC:
+ case OAS2MAPR:
+ case OAS2DOTTYPE:
+ case OAS2RECV:
+ if(n->defn->initorder)
+ break;
+ n->defn->initorder = 1;
+ for(l=n->defn->rlist; l; l=l->next)
+ init1(l->n, out);
+ *out = list(*out, n->defn);
+ break;
+ }
+ }
+ l = initlist;
+ initlist = l->next;
+ if(l->n != n)
+ fatal("bad initlist");
+ free(l);
+ n->initorder = 1;
+ return;
+
+bad:
+ dump("defn", n->defn);
+ fatal("init1: bad defn");
+}
+
+// recurse over n, doing init1 everywhere.
+static void
+init2(Node *n, NodeList **out)
+{
+ if(n == N || n->initorder == 1)
+ return;
+ init1(n, out);
+ init2(n->left, out);
+ init2(n->right, out);
+ init2(n->ntest, out);
+ init2list(n->ninit, out);
+ init2list(n->list, out);
+ init2list(n->rlist, out);
+ init2list(n->nbody, out);
+ init2list(n->nelse, out);
+}
+
+static void
+init2list(NodeList *l, NodeList **out)
+{
+ for(; l; l=l->next)
+ init2(l->n, out);
+}
+
+
+static void
+initreorder(NodeList *l, NodeList **out)
+{
+ Node *n;
+
+ for(; l; l=l->next) {
+ n = l->n;
+ switch(n->op) {
+ case ODCLFUNC:
+ case ODCLCONST:
+ case ODCLTYPE:
+ continue;
+ }
+ initreorder(n->ninit, out);
+ n->ninit = nil;
+ init1(n, out);
+ }
+}
+
+NodeList*
+initfix(NodeList *l)
+{
+ NodeList *lout;
+
+ lout = nil;
+ initreorder(l, &lout);
+ return lout;
+}
+
+/*
+ * from here down is the walk analysis
+ * of composite literals.
+ * most of the work is to generate
+ * data statements for the constant
+ * part of the composite literal.
+ */
+
+static void structlit(int ctxt, int pass, Node *n, Node *var, NodeList **init);
+static void arraylit(int ctxt, int pass, Node *n, Node *var, NodeList **init);
+static void slicelit(int ctxt, Node *n, Node *var, NodeList **init);
+static void maplit(int ctxt, Node *n, Node *var, NodeList **init);
+
+static Node*
+staticname(Type *t, int ctxt)
+{
+ Node *n;
+
+ snprint(namebuf, sizeof(namebuf), "statictmp_%.4d", statuniqgen);
+ statuniqgen++;
+ n = newname(lookup(namebuf));
+ if(!ctxt)
+ n->readonly = 1;
+ addvar(n, t, PEXTERN);
+ return n;
+}
+
+static int
+isliteral(Node *n)
+{
+ if(n->op == OLITERAL)
+ if(n->val.ctype != CTNIL)
+ return 1;
+ return 0;
+}
+
+static int
+simplename(Node *n)
+{
+ if(n->op != ONAME)
+ goto no;
+ if(!n->addable)
+ goto no;
+ if(n->class & PHEAP)
+ goto no;
+ if(n->class == PPARAMREF)
+ goto no;
+ return 1;
+
+no:
+ return 0;
+}
+
+static void
+litas(Node *l, Node *r, NodeList **init)
+{
+ Node *a;
+
+ a = nod(OAS, l, r);
+ typecheck(&a, Etop);
+ walkexpr(&a, init);
+ *init = list(*init, a);
+}
+
+enum
+{
+ MODEDYNAM = 1,
+ MODECONST = 2,
+};
+
+static int
+getdyn(Node *n, int top)
+{
+ NodeList *nl;
+ Node *value;
+ int mode;
+
+ mode = 0;
+ switch(n->op) {
+ default:
+ if(isliteral(n))
+ return MODECONST;
+ return MODEDYNAM;
+ case OARRAYLIT:
+ if(!top && n->type->bound < 0)
+ return MODEDYNAM;
+ case OSTRUCTLIT:
+ break;
+ }
+
+ for(nl=n->list; nl; nl=nl->next) {
+ value = nl->n->right;
+ mode |= getdyn(value, 0);
+ if(mode == (MODEDYNAM|MODECONST))
+ break;
+ }
+ return mode;
+}
+
+static void
+structlit(int ctxt, int pass, Node *n, Node *var, NodeList **init)
+{
+ Node *r, *a;
+ NodeList *nl;
+ Node *index, *value;
+
+ for(nl=n->list; nl; nl=nl->next) {
+ r = nl->n;
+ if(r->op != OKEY)
+ fatal("structlit: rhs not OKEY: %N", r);
+ index = r->left;
+ value = r->right;
+
+ switch(value->op) {
+ case OARRAYLIT:
+ if(value->type->bound < 0) {
+ if(pass == 1 && ctxt != 0) {
+ a = nod(ODOT, var, newname(index->sym));
+ slicelit(ctxt, value, a, init);
+ } else
+ if(pass == 2 && ctxt == 0) {
+ a = nod(ODOT, var, newname(index->sym));
+ slicelit(ctxt, value, a, init);
+ } else
+ if(pass == 3)
+ break;
+ continue;
+ }
+ a = nod(ODOT, var, newname(index->sym));
+ arraylit(ctxt, pass, value, a, init);
+ continue;
+
+ case OSTRUCTLIT:
+ a = nod(ODOT, var, newname(index->sym));
+ structlit(ctxt, pass, value, a, init);
+ continue;
+ }
+
+ if(isliteral(value)) {
+ if(pass == 2)
+ continue;
+ } else
+ if(pass == 1)
+ continue;
+
+ // build list of var.field = expr
+ a = nod(ODOT, var, newname(index->sym));
+ a = nod(OAS, a, value);
+ typecheck(&a, Etop);
+ walkexpr(&a, init);
+ if(pass == 1) {
+ if(a->op != OAS)
+ fatal("structlit: not as");
+ a->dodata = 2;
+ }
+ *init = list(*init, a);
+ }
+}
+
+static void
+arraylit(int ctxt, int pass, Node *n, Node *var, NodeList **init)
+{
+ Node *r, *a;
+ NodeList *l;
+ Node *index, *value;
+
+ for(l=n->list; l; l=l->next) {
+ r = l->n;
+ if(r->op != OKEY)
+ fatal("arraylit: rhs not OKEY: %N", r);
+ index = r->left;
+ value = r->right;
+
+ switch(value->op) {
+ case OARRAYLIT:
+ if(value->type->bound < 0) {
+ if(pass == 1 && ctxt != 0) {
+ a = nod(OINDEX, var, index);
+ slicelit(ctxt, value, a, init);
+ } else
+ if(pass == 2 && ctxt == 0) {
+ a = nod(OINDEX, var, index);
+ slicelit(ctxt, value, a, init);
+ } else
+ if(pass == 3)
+ break;
+ continue;
+ }
+ a = nod(OINDEX, var, index);
+ arraylit(ctxt, pass, value, a, init);
+ continue;
+
+ case OSTRUCTLIT:
+ a = nod(OINDEX, var, index);
+ structlit(ctxt, pass, value, a, init);
+ continue;
+ }
+
+ if(isliteral(index) && isliteral(value)) {
+ if(pass == 2)
+ continue;
+ } else
+ if(pass == 1)
+ continue;
+
+ // build list of var[index] = value
+ a = nod(OINDEX, var, index);
+ a = nod(OAS, a, value);
+ typecheck(&a, Etop);
+ walkexpr(&a, init); // add any assignments in r to top
+ if(pass == 1) {
+ if(a->op != OAS)
+ fatal("structlit: not as");
+ a->dodata = 2;
+ }
+ *init = list(*init, a);
+ }
+}
+
+static void
+slicelit(int ctxt, Node *n, Node *var, NodeList **init)
+{
+ Node *r, *a;
+ NodeList *l;
+ Type *t;
+ Node *vstat, *vauto;
+ Node *index, *value;
+ int mode;
+
+ // make an array type
+ t = shallow(n->type);
+ t->bound = mpgetfix(n->right->val.u.xval);
+ t->width = 0;
+ t->sym = nil;
+ dowidth(t);
+
+ if(ctxt != 0) {
+
+ // put everything into static array
+ vstat = staticname(t, ctxt);
+ arraylit(ctxt, 1, n, vstat, init);
+ arraylit(ctxt, 2, n, vstat, init);
+
+ // copy static to slice
+ a = nod(OSLICE, vstat, nod(OKEY, N, N));
+ a = nod(OAS, var, a);
+ typecheck(&a, Etop);
+ a->dodata = 2;
+ *init = list(*init, a);
+ return;
+ }
+
+ // recipe for var = []t{...}
+ // 1. make a static array
+ // var vstat [...]t
+ // 2. assign (data statements) the constant part
+ // vstat = constpart{}
+ // 3. make an auto pointer to array and allocate heap to it
+ // var vauto *[...]t = new([...]t)
+ // 4. copy the static array to the auto array
+ // *vauto = vstat
+ // 5. assign slice of allocated heap to var
+ // var = [0:]*auto
+ // 6. for each dynamic part assign to the slice
+ // var[i] = dynamic part
+ //
+ // an optimization is done if there is no constant part
+ // 3. var vauto *[...]t = new([...]t)
+ // 5. var = [0:]*auto
+ // 6. var[i] = dynamic part
+
+ // if the literal contains constants,
+ // make static initialized array (1),(2)
+ vstat = N;
+ mode = getdyn(n, 1);
+ if(mode & MODECONST) {
+ vstat = staticname(t, ctxt);
+ arraylit(ctxt, 1, n, vstat, init);
+ }
+
+ // make new auto *array (3 declare)
+ vauto = nod(OXXX, N, N);
+ tempname(vauto, ptrto(t));
+
+ // set auto to point at new heap (3 assign)
+ a = nod(ONEW, N, N);
+ a->list = list1(typenod(t));
+ a = nod(OAS, vauto, a);
+ typecheck(&a, Etop);
+ walkexpr(&a, init);
+ *init = list(*init, a);
+
+ if(vstat != N) {
+ // copy static to heap (4)
+ a = nod(OIND, vauto, N);
+ a = nod(OAS, a, vstat);
+ typecheck(&a, Etop);
+ walkexpr(&a, init);
+ *init = list(*init, a);
+ }
+
+ // make slice out of heap (5)
+ a = nod(OAS, var, nod(OSLICE, vauto, nod(OKEY, N, N)));
+ typecheck(&a, Etop);
+ walkexpr(&a, init);
+ *init = list(*init, a);
+
+ // put dynamics into slice (6)
+ for(l=n->list; l; l=l->next) {
+ r = l->n;
+ if(r->op != OKEY)
+ fatal("slicelit: rhs not OKEY: %N", r);
+ index = r->left;
+ value = r->right;
+ a = nod(OINDEX, var, index);
+ a->etype = 1; // no bounds checking
+ // TODO need to check bounds?
+
+ switch(value->op) {
+ case OARRAYLIT:
+ if(value->type->bound < 0)
+ break;
+ arraylit(ctxt, 2, value, a, init);
+ continue;
+
+ case OSTRUCTLIT:
+ structlit(ctxt, 2, value, a, init);
+ continue;
+ }
+
+ if(isliteral(index) && isliteral(value))
+ continue;
+
+ // build list of var[c] = expr
+ a = nod(OAS, a, value);
+ typecheck(&a, Etop);
+ walkexpr(&a, init);
+ *init = list(*init, a);
+ }
+}
+
+static void
+maplit(int ctxt, Node *n, Node *var, NodeList **init)
+{
+ Node *r, *a;
+ NodeList *l;
+ int nerr, b;
+ Type *t, *tk, *tv, *t1;
+ Node *vstat, *index, *value;
+ Sym *syma, *symb;
+
+ctxt = 0;
+
+ // make the map var
+ nerr = nerrors;
+
+ a = nod(OMAKE, N, N);
+ a->list = list1(typenod(n->type));
+ litas(var, a, init);
+
+ // count the initializers
+ b = 0;
+ for(l=n->list; l; l=l->next) {
+ r = l->n;
+
+ if(r->op != OKEY)
+ fatal("slicelit: rhs not OKEY: %N", r);
+ index = r->left;
+ value = r->right;
+
+ if(isliteral(index) && isliteral(value))
+ b++;
+ }
+
+ t = T;
+ if(b != 0) {
+ // build type [count]struct { a Tindex, b Tvalue }
+ t = n->type;
+ tk = t->down;
+ tv = t->type;
+
+ symb = lookup("b");
+ t = typ(TFIELD);
+ t->type = tv;
+ t->sym = symb;
+
+ syma = lookup("a");
+ t1 = t;
+ t = typ(TFIELD);
+ t->type = tk;
+ t->sym = syma;
+ t->down = t1;
+
+ t1 = t;
+ t = typ(TSTRUCT);
+ t->type = t1;
+
+ t1 = t;
+ t = typ(TARRAY);
+ t->bound = b;
+ t->type = t1;
+
+ dowidth(t);
+
+ // make and initialize static array
+ vstat = staticname(t, ctxt);
+ b = 0;
+ for(l=n->list; l; l=l->next) {
+ r = l->n;
+
+ if(r->op != OKEY)
+ fatal("slicelit: rhs not OKEY: %N", r);
+ index = r->left;
+ value = r->right;
+
+ if(isliteral(index) && isliteral(value)) {
+ // build vstat[b].a = key;
+ a = nodintconst(b);
+ a = nod(OINDEX, vstat, a);
+ a = nod(ODOT, a, newname(syma));
+ a = nod(OAS, a, index);
+ typecheck(&a, Etop);
+ walkexpr(&a, init);
+ a->dodata = 2;
+ *init = list(*init, a);
+
+ // build vstat[b].b = value;
+ a = nodintconst(b);
+ a = nod(OINDEX, vstat, a);
+ a = nod(ODOT, a, newname(symb));
+ a = nod(OAS, a, value);
+ typecheck(&a, Etop);
+ walkexpr(&a, init);
+ a->dodata = 2;
+ *init = list(*init, a);
+
+ b++;
+ }
+ }
+
+ // loop adding structure elements to map
+ // for i = 0; i < len(vstat); i++ {
+ // map[vstat[i].a] = vstat[i].b
+ // }
+ index = nod(OXXX, N, N);
+ tempname(index, types[TINT]);
+
+ a = nod(OINDEX, vstat, index);
+ a->etype = 1; // no bounds checking
+ a = nod(ODOT, a, newname(symb));
+
+ r = nod(OINDEX, vstat, index);
+ r->etype = 1; // no bounds checking
+ r = nod(ODOT, r, newname(syma));
+ r = nod(OINDEX, var, r);
+
+ r = nod(OAS, r, a);
+
+ a = nod(OFOR, N, N);
+ a->nbody = list1(r);
+
+ a->ninit = list1(nod(OAS, index, nodintconst(0)));
+ a->ntest = nod(OLT, index, nodintconst(t->bound));
+ a->nincr = nod(OASOP, index, nodintconst(1));
+ a->nincr->etype = OADD;
+
+ typecheck(&a, Etop);
+ walkstmt(&a);
+ *init = list(*init, a);
+ }
+
+ // put in dynamic entries one-at-a-time
+ for(l=n->list; l; l=l->next) {
+ r = l->n;
+
+ if(r->op != OKEY)
+ fatal("slicelit: rhs not OKEY: %N", r);
+ index = r->left;
+ value = r->right;
+
+ if(isliteral(index) && isliteral(value))
+ continue;
+
+ // build list of var[c] = expr
+ a = nod(OINDEX, var, r->left);
+ a = nod(OAS, a, r->right);
+ typecheck(&a, Etop);
+ walkexpr(&a, init);
+ if(nerr != nerrors)
+ break;
+
+ *init = list(*init, a);
+ }
+}
+
+void
+anylit(int ctxt, Node *n, Node *var, NodeList **init)
+{
+ Type *t;
+ Node *a, *vstat;
+
+ t = n->type;
+ switch(n->op) {
+ default:
+ fatal("anylit: not lit");
+
+ case OSTRUCTLIT:
+ if(t->etype != TSTRUCT)
+ fatal("anylit: not struct");
+
+ if(simplename(var)) {
+
+ if(ctxt == 0) {
+ // lay out static data
+ vstat = staticname(t, ctxt);
+ structlit(ctxt, 1, n, vstat, init);
+
+ // copy static to var
+ a = nod(OAS, var, vstat);
+ typecheck(&a, Etop);
+ walkexpr(&a, init);
+ *init = list(*init, a);
+
+ // add expressions to automatic
+ structlit(ctxt, 2, n, var, init);
+ break;
+ }
+ structlit(ctxt, 1, n, var, init);
+ structlit(ctxt, 2, n, var, init);
+ break;
+ }
+
+ // initialize of not completely specified
+ if(count(n->list) < structcount(t)) {
+ a = nod(OAS, var, N);
+ typecheck(&a, Etop);
+ walkexpr(&a, init);
+ *init = list(*init, a);
+ }
+ structlit(ctxt, 3, n, var, init);
+ break;
+
+ case OARRAYLIT:
+ if(t->etype != TARRAY)
+ fatal("anylit: not array");
+ if(t->bound < 0) {
+ slicelit(ctxt, n, var, init);
+ break;
+ }
+
+ if(simplename(var)) {
+
+ if(ctxt == 0) {
+ // lay out static data
+ vstat = staticname(t, ctxt);
+ arraylit(1, 1, n, vstat, init);
+
+ // copy static to automatic
+ a = nod(OAS, var, vstat);
+ typecheck(&a, Etop);
+ walkexpr(&a, init);
+ *init = list(*init, a);
+
+ // add expressions to automatic
+ arraylit(ctxt, 2, n, var, init);
+ break;
+ }
+ arraylit(ctxt, 1, n, var, init);
+ arraylit(ctxt, 2, n, var, init);
+ break;
+ }
+
+ // initialize of not completely specified
+ if(count(n->list) < t->bound) {
+ a = nod(OAS, var, N);
+ typecheck(&a, Etop);
+ walkexpr(&a, init);
+ *init = list(*init, a);
+ }
+ arraylit(ctxt, 3, n, var, init);
+ break;
+
+ case OMAPLIT:
+ if(t->etype != TMAP)
+ fatal("anylit: not map");
+ maplit(ctxt, n, var, init);
+ break;
+ }
+}
+
+int
+oaslit(Node *n, NodeList **init)
+{
+ int ctxt;
+
+ if(n->left == N || n->right == N)
+ goto no;
+ if(n->left->type == T || n->right->type == T)
+ goto no;
+ if(!simplename(n->left))
+ goto no;
+ if(!eqtype(n->left->type, n->right->type))
+ goto no;
+
+ // context is init() function.
+ // implies generated data executed
+ // exactly once and not subject to races.
+ ctxt = 0;
+// if(n->dodata == 1)
+// ctxt = 1;
+
+ switch(n->right->op) {
+ default:
+ goto no;
+
+ case OSTRUCTLIT:
+ case OARRAYLIT:
+ case OMAPLIT:
+ if(vmatch1(n->left, n->right))
+ goto no;
+ anylit(ctxt, n->right, n->left, init);
+ break;
+ }
+ n->op = OEMPTY;
+ return 1;
+
+no:
+ // not a special composit literal assignment
+ return 0;
+}
+
+static int
+getlit(Node *lit)
+{
+ if(smallintconst(lit))
+ return mpgetfix(lit->val.u.xval);
+ return -1;
+}
+
+int
+stataddr(Node *nam, Node *n)
+{
+ int l;
+
+ if(n == N)
+ goto no;
+
+ switch(n->op) {
+
+ case ONAME:
+ *nam = *n;
+ return n->addable;
+
+ case ODOT:
+ if(!stataddr(nam, n->left))
+ break;
+ nam->xoffset += n->xoffset;
+ nam->type = n->type;
+ return 1;
+
+ case OINDEX:
+ if(n->left->type->bound < 0)
+ break;
+ if(!stataddr(nam, n->left))
+ break;
+ l = getlit(n->right);
+ if(l < 0)
+ break;
+ nam->xoffset += l*n->type->width;
+ nam->type = n->type;
+ return 1;
+ }
+
+no:
+ return 0;
+}
+
+int
+gen_as_init(Node *n)
+{
+ Node *nr, *nl;
+ Node nam, nod1;
+
+ if(n->dodata == 0)
+ goto no;
+
+ nr = n->right;
+ nl = n->left;
+ if(nr == N) {
+ if(!stataddr(&nam, nl))
+ goto no;
+ if(nam.class != PEXTERN)
+ goto no;
+ goto yes;
+ }
+
+ if(nr->type == T || !eqtype(nl->type, nr->type))
+ goto no;
+
+ if(!stataddr(&nam, nl))
+ goto no;
+
+ if(nam.class != PEXTERN)
+ goto no;
+
+ switch(nr->op) {
+ default:
+ goto no;
+
+ case OCONVNOP:
+ nr = nr->left;
+ if(nr == N || nr->op != OSLICEARR)
+ goto no;
+ // fall through
+
+ case OSLICEARR:
+ if(nr->right->op == OKEY && nr->right->left == N && nr->right->right == N) {
+ nr = nr->left;
+ goto slice;
+ }
+ goto no;
+
+ case OLITERAL:
+ break;
+ }
+
+ switch(nr->type->etype) {
+ default:
+ goto no;
+
+ case TBOOL:
+ case TINT8:
+ case TUINT8:
+ case TINT16:
+ case TUINT16:
+ case TINT32:
+ case TUINT32:
+ case TINT64:
+ case TUINT64:
+ case TINT:
+ case TUINT:
+ case TUINTPTR:
+ case TPTR32:
+ case TPTR64:
+ case TFLOAT32:
+ case TFLOAT64:
+ gused(N); // in case the data is the dest of a goto
+ gdata(&nam, nr, nr->type->width);
+ break;
+
+ case TCOMPLEX64:
+ case TCOMPLEX128:
+ gused(N); // in case the data is the dest of a goto
+ gdatacomplex(&nam, nr->val.u.cval);
+ break;
+
+ case TSTRING:
+ gused(N); // in case the data is the dest of a goto
+ gdatastring(&nam, nr->val.u.sval);
+ break;
+ }
+
+yes:
+ return 1;
+
+slice:
+ gused(N); // in case the data is the dest of a goto
+ nl = nr;
+ if(nr == N || nr->op != OADDR)
+ goto no;
+ nr = nr->left;
+ if(nr == N || nr->op != ONAME)
+ goto no;
+
+ // nr is the array being converted to a slice
+ if(nr->type == T || nr->type->etype != TARRAY || nr->type->bound < 0)
+ goto no;
+
+ nam.xoffset += Array_array;
+ gdata(&nam, nl, types[tptr]->width);
+
+ nam.xoffset += Array_nel-Array_array;
+ nodconst(&nod1, types[TINT32], nr->type->bound);
+ gdata(&nam, &nod1, types[TINT32]->width);
+
+ nam.xoffset += Array_cap-Array_nel;
+ gdata(&nam, &nod1, types[TINT32]->width);
+
+ goto yes;
+
+no:
+ if(n->dodata == 2) {
+ dump("\ngen_as_init", n);
+ fatal("gen_as_init couldnt make data statement");
+ }
+ return 0;
+}
+
diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c
new file mode 100644
index 000000000..1a05d43d0
--- /dev/null
+++ b/src/cmd/gc/subr.c
@@ -0,0 +1,3885 @@
+// Copyright 2009 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.
+
+#include "go.h"
+#include "md5.h"
+#include "y.tab.h"
+#include "opnames.h"
+#include "yerr.h"
+
+static void dodump(Node*, int);
+
+typedef struct Error Error;
+struct Error
+{
+ int lineno;
+ int seq;
+ char *msg;
+};
+static Error *err;
+static int nerr;
+static int merr;
+
+void
+errorexit(void)
+{
+ flusherrors();
+ if(outfile)
+ remove(outfile);
+ exit(1);
+}
+
+extern int yychar;
+int
+parserline(void)
+{
+ if(yychar != 0 && yychar != -2) // parser has one symbol lookahead
+ return prevlineno;
+ return lineno;
+}
+
+static void
+adderr(int line, char *fmt, va_list arg)
+{
+ Fmt f;
+ Error *p;
+
+ erroring++;
+ fmtstrinit(&f);
+ fmtprint(&f, "%L: ", line);
+ fmtvprint(&f, fmt, arg);
+ fmtprint(&f, "\n");
+ erroring--;
+
+ if(nerr >= merr) {
+ if(merr == 0)
+ merr = 16;
+ else
+ merr *= 2;
+ p = realloc(err, merr*sizeof err[0]);
+ if(p == nil) {
+ merr = nerr;
+ flusherrors();
+ print("out of memory\n");
+ errorexit();
+ }
+ err = p;
+ }
+ err[nerr].seq = nerr;
+ err[nerr].lineno = line;
+ err[nerr].msg = fmtstrflush(&f);
+ nerr++;
+}
+
+static int
+errcmp(const void *va, const void *vb)
+{
+ Error *a, *b;
+
+ a = (Error*)va;
+ b = (Error*)vb;
+ if(a->lineno != b->lineno)
+ return a->lineno - b->lineno;
+ if(a->seq != b->seq)
+ return a->seq - b->seq;
+ return strcmp(a->msg, b->msg);
+}
+
+void
+flusherrors(void)
+{
+ int i;
+
+ if(nerr == 0)
+ return;
+ qsort(err, nerr, sizeof err[0], errcmp);
+ for(i=0; i<nerr; i++)
+ if(i==0 || strcmp(err[i].msg, err[i-1].msg) != 0)
+ print("%s", err[i].msg);
+ nerr = 0;
+}
+
+static void
+hcrash(void)
+{
+ if(debug['h']) {
+ flusherrors();
+ if(outfile)
+ unlink(outfile);
+ *(volatile int*)0 = 0;
+ }
+}
+
+void
+yyerrorl(int line, char *fmt, ...)
+{
+ va_list arg;
+
+ va_start(arg, fmt);
+ adderr(line, fmt, arg);
+ va_end(arg);
+
+ hcrash();
+ nerrors++;
+ if(nerrors >= 10 && !debug['e']) {
+ flusherrors();
+ print("%L: too many errors\n", line);
+ errorexit();
+ }
+}
+
+extern int yystate, yychar;
+
+void
+yyerror(char *fmt, ...)
+{
+ int i;
+ static int lastsyntax;
+ va_list arg;
+ char buf[512], *p;
+
+ if(strncmp(fmt, "syntax error", 12) == 0) {
+ nsyntaxerrors++;
+
+ if(debug['x'])
+ print("yyerror: yystate=%d yychar=%d\n", yystate, yychar);
+
+ // only one syntax error per line
+ if(lastsyntax == lexlineno)
+ return;
+ lastsyntax = lexlineno;
+
+ if(strstr(fmt, "{ or {")) {
+ // The grammar has { and LBRACE but both show up as {.
+ // Rewrite syntax error referring to "{ or {" to say just "{".
+ strecpy(buf, buf+sizeof buf, fmt);
+ p = strstr(buf, "{ or {");
+ if(p)
+ memmove(p+1, p+6, strlen(p+6)+1);
+ fmt = buf;
+ }
+
+ // look for parse state-specific errors in list (see go.errors).
+ for(i=0; i<nelem(yymsg); i++) {
+ if(yymsg[i].yystate == yystate && yymsg[i].yychar == yychar) {
+ yyerrorl(lexlineno, "syntax error: %s", yymsg[i].msg);
+ return;
+ }
+ }
+
+ // plain "syntax error" gets "near foo" added
+ if(strcmp(fmt, "syntax error") == 0) {
+ yyerrorl(lexlineno, "syntax error near %s", lexbuf);
+ return;
+ }
+
+ // if bison says "syntax error, more info"; print "syntax error: more info".
+ if(fmt[12] == ',') {
+ yyerrorl(lexlineno, "syntax error:%s", fmt+13);
+ return;
+ }
+
+ yyerrorl(lexlineno, "%s", fmt);
+ return;
+ }
+
+ va_start(arg, fmt);
+ adderr(parserline(), fmt, arg);
+ va_end(arg);
+
+ hcrash();
+ nerrors++;
+ if(nerrors >= 10 && !debug['e']) {
+ flusherrors();
+ print("%L: too many errors\n", parserline());
+ errorexit();
+ }
+}
+
+void
+warn(char *fmt, ...)
+{
+ va_list arg;
+
+ va_start(arg, fmt);
+ adderr(parserline(), fmt, arg);
+ va_end(arg);
+
+ hcrash();
+}
+
+void
+fatal(char *fmt, ...)
+{
+ va_list arg;
+
+ flusherrors();
+
+ print("%L: internal compiler error: ", lineno);
+ va_start(arg, fmt);
+ vfprint(1, fmt, arg);
+ va_end(arg);
+ print("\n");
+
+ // If this is a released compiler version, ask for a bug report.
+ if(strncmp(getgoversion(), "release", 7) == 0) {
+ print("\n");
+ print("Please file a bug report including a short program that triggers the error.\n");
+ print("http://code.google.com/p/go/issues/entry?template=compilerbug\n");
+ }
+ hcrash();
+ errorexit();
+}
+
+void
+linehist(char *file, int32 off, int relative)
+{
+ Hist *h;
+ char *cp;
+
+ if(debug['i']) {
+ if(file != nil) {
+ if(off < 0)
+ print("pragma %s", file);
+ else
+ if(off > 0)
+ print("line %s", file);
+ else
+ print("import %s", file);
+ } else
+ print("end of import");
+ print(" at line %L\n", lexlineno);
+ }
+
+ if(off < 0 && file[0] != '/' && !relative) {
+ cp = mal(strlen(file) + strlen(pathname) + 2);
+ sprint(cp, "%s/%s", pathname, file);
+ file = cp;
+ }
+
+ h = mal(sizeof(Hist));
+ h->name = file;
+ h->line = lexlineno;
+ h->offset = off;
+ h->link = H;
+ if(ehist == H) {
+ hist = h;
+ ehist = h;
+ return;
+ }
+ ehist->link = h;
+ ehist = h;
+}
+
+int32
+setlineno(Node *n)
+{
+ int32 lno;
+
+ lno = lineno;
+ if(n != N)
+ switch(n->op) {
+ case ONAME:
+ case OTYPE:
+ case OPACK:
+ case OLITERAL:
+ break;
+ default:
+ lineno = n->lineno;
+ if(lineno == 0) {
+ if(debug['K'])
+ warn("setlineno: line 0");
+ lineno = lno;
+ }
+ }
+ return lno;
+}
+
+uint32
+stringhash(char *p)
+{
+ int32 h;
+ int c;
+
+ h = 0;
+ for(;;) {
+ c = *p++;
+ if(c == 0)
+ break;
+ h = h*PRIME1 + c;
+ }
+
+ if(h < 0) {
+ h = -h;
+ if(h < 0)
+ h = 0;
+ }
+ return h;
+}
+
+Sym*
+lookup(char *name)
+{
+ return pkglookup(name, localpkg);
+}
+
+Sym*
+pkglookup(char *name, Pkg *pkg)
+{
+ Sym *s;
+ uint32 h;
+ int c;
+
+ h = stringhash(name) % NHASH;
+ c = name[0];
+ for(s = hash[h]; s != S; s = s->link) {
+ if(s->name[0] != c || s->pkg != pkg)
+ continue;
+ if(strcmp(s->name, name) == 0)
+ return s;
+ }
+
+ s = mal(sizeof(*s));
+ s->name = mal(strlen(name)+1);
+ strcpy(s->name, name);
+
+ s->pkg = pkg;
+
+ s->link = hash[h];
+ hash[h] = s;
+ s->lexical = LNAME;
+
+ return s;
+}
+
+Sym*
+restrictlookup(char *name, Pkg *pkg)
+{
+ if(!exportname(name) && pkg != localpkg)
+ yyerror("cannot refer to unexported name %s.%s", pkg->name, name);
+ return pkglookup(name, pkg);
+}
+
+
+// find all the exported symbols in package opkg
+// and make them available in the current package
+void
+importdot(Pkg *opkg, Node *pack)
+{
+ Sym *s, *s1;
+ uint32 h;
+ int n;
+
+ n = 0;
+ for(h=0; h<NHASH; h++) {
+ for(s = hash[h]; s != S; s = s->link) {
+ if(s->pkg != opkg)
+ continue;
+ if(s->def == N)
+ continue;
+ if(!exportname(s->name) || utfrune(s->name, 0xb7)) // 0xb7 = center dot
+ continue;
+ s1 = lookup(s->name);
+ if(s1->def != N) {
+ redeclare(s1, "during import");
+ continue;
+ }
+ s1->def = s->def;
+ s1->block = s->block;
+ s1->def->pack = pack;
+ n++;
+ }
+ }
+ if(n == 0) {
+ // can't possibly be used - there were no symbols
+ yyerrorl(pack->lineno, "imported and not used: %Z", opkg->path);
+ }
+}
+
+static void
+gethunk(void)
+{
+ char *h;
+ int32 nh;
+
+ nh = NHUNK;
+ if(thunk >= 10L*NHUNK)
+ nh = 10L*NHUNK;
+ h = (char*)malloc(nh);
+ if(h == nil) {
+ flusherrors();
+ yyerror("out of memory");
+ errorexit();
+ }
+ hunk = h;
+ nhunk = nh;
+ thunk += nh;
+}
+
+void*
+mal(int32 n)
+{
+ void *p;
+
+ if(n >= NHUNK) {
+ p = malloc(n);
+ if(p == nil) {
+ flusherrors();
+ yyerror("out of memory");
+ errorexit();
+ }
+ memset(p, 0, n);
+ return p;
+ }
+
+ while((uintptr)hunk & MAXALIGN) {
+ hunk++;
+ nhunk--;
+ }
+ if(nhunk < n)
+ gethunk();
+
+ p = hunk;
+ nhunk -= n;
+ hunk += n;
+ memset(p, 0, n);
+ return p;
+}
+
+void*
+remal(void *p, int32 on, int32 n)
+{
+ void *q;
+
+ q = (uchar*)p + on;
+ if(q != hunk || nhunk < n) {
+ if(on+n >= NHUNK) {
+ q = mal(on+n);
+ memmove(q, p, on);
+ return q;
+ }
+ if(nhunk < on+n)
+ gethunk();
+ memmove(hunk, p, on);
+ p = hunk;
+ hunk += on;
+ nhunk -= on;
+ }
+ hunk += n;
+ nhunk -= n;
+ return p;
+}
+
+Node*
+nod(int op, Node *nleft, Node *nright)
+{
+ Node *n;
+
+ n = mal(sizeof(*n));
+ n->op = op;
+ n->left = nleft;
+ n->right = nright;
+ n->lineno = parserline();
+ n->xoffset = BADWIDTH;
+ n->orig = n;
+ return n;
+}
+
+int
+algtype(Type *t)
+{
+ int a;
+
+ if(issimple[t->etype] || isptr[t->etype] ||
+ t->etype == TCHAN || t->etype == TFUNC || t->etype == TMAP) {
+ if(t->width == 1)
+ a = AMEM8;
+ else if(t->width == 2)
+ a = AMEM16;
+ else if(t->width == 4)
+ a = AMEM32;
+ else if(t->width == 8)
+ a = AMEM64;
+ else if(t->width == 16)
+ a = AMEM128;
+ else
+ a = AMEM; // just bytes (int, ptr, etc)
+ } else if(t->etype == TSTRING)
+ a = ASTRING; // string
+ else if(isnilinter(t))
+ a = ANILINTER; // nil interface
+ else if(t->etype == TINTER)
+ a = AINTER; // interface
+ else if(isslice(t))
+ a = ASLICE; // slice
+ else {
+ if(t->width == 1)
+ a = ANOEQ8;
+ else if(t->width == 2)
+ a = ANOEQ16;
+ else if(t->width == 4)
+ a = ANOEQ32;
+ else if(t->width == 8)
+ a = ANOEQ64;
+ else if(t->width == 16)
+ a = ANOEQ128;
+ else
+ a = ANOEQ; // just bytes, but no hash/eq
+ }
+ return a;
+}
+
+Type*
+maptype(Type *key, Type *val)
+{
+ Type *t;
+
+
+ if(key != nil && key->etype != TANY && algtype(key) == ANOEQ) {
+ if(key->etype == TFORW) {
+ // map[key] used during definition of key.
+ // postpone check until key is fully defined.
+ // if there are multiple uses of map[key]
+ // before key is fully defined, the error
+ // will only be printed for the first one.
+ // good enough.
+ if(key->maplineno == 0)
+ key->maplineno = lineno;
+ } else
+ yyerror("invalid map key type %T", key);
+ }
+ t = typ(TMAP);
+ t->down = key;
+ t->type = val;
+ return t;
+}
+
+Type*
+typ(int et)
+{
+ Type *t;
+
+ t = mal(sizeof(*t));
+ t->etype = et;
+ t->width = BADWIDTH;
+ t->lineno = lineno;
+ t->orig = t;
+ return t;
+}
+
+static int
+methcmp(const void *va, const void *vb)
+{
+ Type *a, *b;
+ int i;
+
+ a = *(Type**)va;
+ b = *(Type**)vb;
+ i = strcmp(a->sym->name, b->sym->name);
+ if(i != 0)
+ return i;
+ if(!exportname(a->sym->name)) {
+ i = strcmp(a->sym->pkg->path->s, b->sym->pkg->path->s);
+ if(i != 0)
+ return i;
+ }
+ return 0;
+}
+
+Type*
+sortinter(Type *t)
+{
+ Type *f;
+ int i;
+ Type **a;
+
+ if(t->type == nil || t->type->down == nil)
+ return t;
+
+ i=0;
+ for(f=t->type; f; f=f->down)
+ i++;
+ a = mal(i*sizeof f);
+ i = 0;
+ for(f=t->type; f; f=f->down)
+ a[i++] = f;
+ qsort(a, i, sizeof a[0], methcmp);
+ while(i-- > 0) {
+ a[i]->down = f;
+ f = a[i];
+ }
+ t->type = f;
+ return t;
+}
+
+Node*
+nodintconst(int64 v)
+{
+ Node *c;
+
+ c = nod(OLITERAL, N, N);
+ c->addable = 1;
+ c->val.u.xval = mal(sizeof(*c->val.u.xval));
+ mpmovecfix(c->val.u.xval, v);
+ c->val.ctype = CTINT;
+ c->type = types[TIDEAL];
+ ullmancalc(c);
+ return c;
+}
+
+Node*
+nodfltconst(Mpflt* v)
+{
+ Node *c;
+
+ c = nod(OLITERAL, N, N);
+ c->addable = 1;
+ c->val.u.fval = mal(sizeof(*c->val.u.fval));
+ mpmovefltflt(c->val.u.fval, v);
+ c->val.ctype = CTFLT;
+ c->type = types[TIDEAL];
+ ullmancalc(c);
+ return c;
+}
+
+void
+nodconst(Node *n, Type *t, int64 v)
+{
+ memset(n, 0, sizeof(*n));
+ n->op = OLITERAL;
+ n->addable = 1;
+ ullmancalc(n);
+ n->val.u.xval = mal(sizeof(*n->val.u.xval));
+ mpmovecfix(n->val.u.xval, v);
+ n->val.ctype = CTINT;
+ n->type = t;
+
+ if(isfloat[t->etype])
+ fatal("nodconst: bad type %T", t);
+}
+
+Node*
+nodnil(void)
+{
+ Node *c;
+
+ c = nodintconst(0);
+ c->val.ctype = CTNIL;
+ c->type = types[TNIL];
+ return c;
+}
+
+Node*
+nodbool(int b)
+{
+ Node *c;
+
+ c = nodintconst(0);
+ c->val.ctype = CTBOOL;
+ c->val.u.bval = b;
+ c->type = idealbool;
+ return c;
+}
+
+Type*
+aindex(Node *b, Type *t)
+{
+ Type *r;
+ int bound;
+
+ bound = -1; // open bound
+ typecheck(&b, Erv);
+ if(b != nil) {
+ switch(consttype(b)) {
+ default:
+ yyerror("array bound must be an integer expression");
+ break;
+ case CTINT:
+ bound = mpgetfix(b->val.u.xval);
+ if(bound < 0)
+ yyerror("array bound must be non negative");
+ break;
+ }
+ }
+
+ // fixed array
+ r = typ(TARRAY);
+ r->type = t;
+ r->bound = bound;
+ return r;
+}
+
+static void
+indent(int dep)
+{
+ int i;
+
+ for(i=0; i<dep; i++)
+ print(". ");
+}
+
+static void
+dodumplist(NodeList *l, int dep)
+{
+ for(; l; l=l->next)
+ dodump(l->n, dep);
+}
+
+static void
+dodump(Node *n, int dep)
+{
+ if(n == N)
+ return;
+
+ indent(dep);
+ if(dep > 10) {
+ print("...\n");
+ return;
+ }
+
+ if(n->ninit != nil) {
+ print("%O-init\n", n->op);
+ dodumplist(n->ninit, dep+1);
+ indent(dep);
+ }
+
+ switch(n->op) {
+ default:
+ print("%N\n", n);
+ dodump(n->left, dep+1);
+ dodump(n->right, dep+1);
+ break;
+
+ case OTYPE:
+ print("%O %S type=%T\n", n->op, n->sym, n->type);
+ if(n->type == T && n->ntype) {
+ indent(dep);
+ print("%O-ntype\n", n->op);
+ dodump(n->ntype, dep+1);
+ }
+ break;
+
+ case OIF:
+ print("%O%J\n", n->op, n);
+ dodump(n->ntest, dep+1);
+ if(n->nbody != nil) {
+ indent(dep);
+ print("%O-then\n", n->op);
+ dodumplist(n->nbody, dep+1);
+ }
+ if(n->nelse != nil) {
+ indent(dep);
+ print("%O-else\n", n->op);
+ dodumplist(n->nelse, dep+1);
+ }
+ break;
+
+ case OSELECT:
+ print("%O%J\n", n->op, n);
+ dodumplist(n->nbody, dep+1);
+ break;
+
+ case OSWITCH:
+ case OFOR:
+ print("%O%J\n", n->op, n);
+ dodump(n->ntest, dep+1);
+
+ if(n->nbody != nil) {
+ indent(dep);
+ print("%O-body\n", n->op);
+ dodumplist(n->nbody, dep+1);
+ }
+
+ if(n->nincr != N) {
+ indent(dep);
+ print("%O-incr\n", n->op);
+ dodump(n->nincr, dep+1);
+ }
+ break;
+
+ case OCASE:
+ // the right side points to label of the body
+ if(n->right != N && n->right->op == OGOTO && n->right->left->op == ONAME)
+ print("%O%J GOTO %N\n", n->op, n, n->right->left);
+ else
+ print("%O%J\n", n->op, n);
+ dodump(n->left, dep+1);
+ break;
+
+ case OXCASE:
+ print("%N\n", n);
+ dodump(n->left, dep+1);
+ dodump(n->right, dep+1);
+ indent(dep);
+ print("%O-nbody\n", n->op);
+ dodumplist(n->nbody, dep+1);
+ break;
+ }
+
+ if(0 && n->ntype != nil) {
+ indent(dep);
+ print("%O-ntype\n", n->op);
+ dodump(n->ntype, dep+1);
+ }
+ if(n->list != nil) {
+ indent(dep);
+ print("%O-list\n", n->op);
+ dodumplist(n->list, dep+1);
+ }
+ if(n->rlist != nil) {
+ indent(dep);
+ print("%O-rlist\n", n->op);
+ dodumplist(n->rlist, dep+1);
+ }
+ if(n->op != OIF && n->nbody != nil) {
+ indent(dep);
+ print("%O-nbody\n", n->op);
+ dodumplist(n->nbody, dep+1);
+ }
+}
+
+void
+dumplist(char *s, NodeList *l)
+{
+ print("%s\n", s);
+ dodumplist(l, 1);
+}
+
+void
+dump(char *s, Node *n)
+{
+ print("%s [%p]\n", s, n);
+ dodump(n, 1);
+}
+
+static char*
+goopnames[] =
+{
+ [OADDR] = "&",
+ [OADD] = "+",
+ [OANDAND] = "&&",
+ [OANDNOT] = "&^",
+ [OAND] = "&",
+ [OAPPEND] = "append",
+ [OAS] = "=",
+ [OAS2] = "=",
+ [OBREAK] = "break",
+ [OCALL] = "function call",
+ [OCAP] = "cap",
+ [OCASE] = "case",
+ [OCLOSE] = "close",
+ [OCOMPLEX] = "complex",
+ [OCOM] = "^",
+ [OCONTINUE] = "continue",
+ [OCOPY] = "copy",
+ [ODEC] = "--",
+ [ODEFER] = "defer",
+ [ODIV] = "/",
+ [OEQ] = "==",
+ [OFALL] = "fallthrough",
+ [OFOR] = "for",
+ [OGE] = ">=",
+ [OGOTO] = "goto",
+ [OGT] = ">",
+ [OIF] = "if",
+ [OIMAG] = "imag",
+ [OINC] = "++",
+ [OIND] = "*",
+ [OLEN] = "len",
+ [OLE] = "<=",
+ [OLSH] = "<<",
+ [OLT] = "<",
+ [OMAKE] = "make",
+ [OMINUS] = "-",
+ [OMOD] = "%",
+ [OMUL] = "*",
+ [ONEW] = "new",
+ [ONE] = "!=",
+ [ONOT] = "!",
+ [OOROR] = "||",
+ [OOR] = "|",
+ [OPANIC] = "panic",
+ [OPLUS] = "+",
+ [OPRINTN] = "println",
+ [OPRINT] = "print",
+ [ORANGE] = "range",
+ [OREAL] = "real",
+ [ORECV] = "<-",
+ [ORETURN] = "return",
+ [ORSH] = ">>",
+ [OSELECT] = "select",
+ [OSEND] = "<-",
+ [OSUB] = "-",
+ [OSWITCH] = "switch",
+ [OXOR] = "^",
+};
+
+int
+Oconv(Fmt *fp)
+{
+ int o;
+
+ o = va_arg(fp->args, int);
+ if((fp->flags & FmtSharp) && o >= 0 && o < nelem(goopnames) && goopnames[o] != nil)
+ return fmtstrcpy(fp, goopnames[o]);
+ if(o < 0 || o >= nelem(opnames) || opnames[o] == nil)
+ return fmtprint(fp, "O-%d", o);
+ return fmtstrcpy(fp, opnames[o]);
+}
+
+int
+Lconv(Fmt *fp)
+{
+ struct
+ {
+ Hist* incl; /* start of this include file */
+ int32 idel; /* delta line number to apply to include */
+ Hist* line; /* start of this #line directive */
+ int32 ldel; /* delta line number to apply to #line */
+ } a[HISTSZ];
+ int32 lno, d;
+ int i, n;
+ Hist *h;
+
+ lno = va_arg(fp->args, int32);
+
+ n = 0;
+ for(h=hist; h!=H; h=h->link) {
+ if(h->offset < 0)
+ continue;
+ if(lno < h->line)
+ break;
+ if(h->name) {
+ if(h->offset > 0) {
+ // #line directive
+ if(n > 0 && n < HISTSZ) {
+ a[n-1].line = h;
+ a[n-1].ldel = h->line - h->offset + 1;
+ }
+ } else {
+ // beginning of file
+ if(n < HISTSZ) {
+ a[n].incl = h;
+ a[n].idel = h->line;
+ a[n].line = 0;
+ }
+ n++;
+ }
+ continue;
+ }
+ n--;
+ if(n > 0 && n < HISTSZ) {
+ d = h->line - a[n].incl->line;
+ a[n-1].ldel += d;
+ a[n-1].idel += d;
+ }
+ }
+
+ if(n > HISTSZ)
+ n = HISTSZ;
+
+ for(i=n-1; i>=0; i--) {
+ if(i != n-1) {
+ if(fp->flags & ~(FmtWidth|FmtPrec))
+ break;
+ fmtprint(fp, " ");
+ }
+ if(debug['L'])
+ fmtprint(fp, "%s/", pathname);
+ if(a[i].line)
+ fmtprint(fp, "%s:%d[%s:%d]",
+ a[i].line->name, lno-a[i].ldel+1,
+ a[i].incl->name, lno-a[i].idel+1);
+ else
+ fmtprint(fp, "%s:%d",
+ a[i].incl->name, lno-a[i].idel+1);
+ lno = a[i].incl->line - 1; // now print out start of this file
+ }
+ if(n == 0)
+ fmtprint(fp, "<epoch>");
+
+ return 0;
+}
+
+/*
+s%,%,\n%g
+s%\n+%\n%g
+s%^[ ]*T%%g
+s%,.*%%g
+s%.+% [T&] = "&",%g
+s%^ ........*\]%&~%g
+s%~ %%g
+*/
+
+static char*
+etnames[] =
+{
+ [TINT] = "INT",
+ [TUINT] = "UINT",
+ [TINT8] = "INT8",
+ [TUINT8] = "UINT8",
+ [TINT16] = "INT16",
+ [TUINT16] = "UINT16",
+ [TINT32] = "INT32",
+ [TUINT32] = "UINT32",
+ [TINT64] = "INT64",
+ [TUINT64] = "UINT64",
+ [TUINTPTR] = "UINTPTR",
+ [TFLOAT32] = "FLOAT32",
+ [TFLOAT64] = "FLOAT64",
+ [TCOMPLEX64] = "COMPLEX64",
+ [TCOMPLEX128] = "COMPLEX128",
+ [TBOOL] = "BOOL",
+ [TPTR32] = "PTR32",
+ [TPTR64] = "PTR64",
+ [TFUNC] = "FUNC",
+ [TARRAY] = "ARRAY",
+ [TSTRUCT] = "STRUCT",
+ [TCHAN] = "CHAN",
+ [TMAP] = "MAP",
+ [TINTER] = "INTER",
+ [TFORW] = "FORW",
+ [TFIELD] = "FIELD",
+ [TSTRING] = "STRING",
+ [TANY] = "ANY",
+};
+
+int
+Econv(Fmt *fp)
+{
+ int et;
+
+ et = va_arg(fp->args, int);
+ if(et < 0 || et >= nelem(etnames) || etnames[et] == nil)
+ return fmtprint(fp, "E-%d", et);
+ return fmtstrcpy(fp, etnames[et]);
+}
+
+static const char* classnames[] = {
+ "Pxxx",
+ "PEXTERN",
+ "PAUTO",
+ "PPARAM",
+ "PPARAMOUT",
+ "PPARAMREF",
+ "PFUNC",
+};
+
+int
+Jconv(Fmt *fp)
+{
+ Node *n;
+ char *s;
+ int c;
+
+ n = va_arg(fp->args, Node*);
+
+ c = fp->flags&FmtShort;
+
+ if(!c && n->ullman != 0)
+ fmtprint(fp, " u(%d)", n->ullman);
+
+ if(!c && n->addable != 0)
+ fmtprint(fp, " a(%d)", n->addable);
+
+ if(!c && n->vargen != 0)
+ fmtprint(fp, " g(%d)", n->vargen);
+
+ if(n->lineno != 0)
+ fmtprint(fp, " l(%d)", n->lineno);
+
+ if(!c && n->xoffset != BADWIDTH)
+ fmtprint(fp, " x(%lld%+d)", n->xoffset, n->stkdelta);
+
+ if(n->class != 0) {
+ s = "";
+ if (n->class & PHEAP) s = ",heap";
+ if ((n->class & ~PHEAP) < nelem(classnames))
+ fmtprint(fp, " class(%s%s)", classnames[n->class&~PHEAP], s);
+ else
+ fmtprint(fp, " class(%d?%s)", n->class&~PHEAP, s);
+ }
+
+ if(n->colas != 0)
+ fmtprint(fp, " colas(%d)", n->colas);
+
+ if(n->funcdepth != 0)
+ fmtprint(fp, " f(%d)", n->funcdepth);
+
+ if(n->noescape != 0)
+ fmtprint(fp, " ne(%d)", n->noescape);
+
+ if(!c && n->typecheck != 0)
+ fmtprint(fp, " tc(%d)", n->typecheck);
+
+ if(!c && n->dodata != 0)
+ fmtprint(fp, " dd(%d)", n->dodata);
+
+ if(n->isddd != 0)
+ fmtprint(fp, " isddd(%d)", n->isddd);
+
+ if(n->implicit != 0)
+ fmtprint(fp, " implicit(%d)", n->implicit);
+
+ if(!c && n->pun != 0)
+ fmtprint(fp, " pun(%d)", n->pun);
+
+ if(!c && n->used != 0)
+ fmtprint(fp, " used(%d)", n->used);
+ return 0;
+}
+
+int
+Sconv(Fmt *fp)
+{
+ Sym *s;
+
+ s = va_arg(fp->args, Sym*);
+ if(s == S) {
+ fmtstrcpy(fp, "<S>");
+ return 0;
+ }
+
+ if(fp->flags & FmtShort)
+ goto shrt;
+
+ if(exporting || (fp->flags & FmtSharp)) {
+ if(packagequotes)
+ fmtprint(fp, "\"%Z\"", s->pkg->path);
+ else
+ fmtprint(fp, "%s", s->pkg->prefix);
+ fmtprint(fp, ".%s", s->name);
+ return 0;
+ }
+
+ if(s->pkg && s->pkg != localpkg || longsymnames || (fp->flags & FmtLong)) {
+ // This one is for the user. If the package name
+ // was used by multiple packages, give the full
+ // import path to disambiguate.
+ if(erroring && pkglookup(s->pkg->name, nil)->npkg > 1) {
+ fmtprint(fp, "\"%Z\".%s", s->pkg->path, s->name);
+ return 0;
+ }
+ fmtprint(fp, "%s.%s", s->pkg->name, s->name);
+ return 0;
+ }
+
+shrt:
+ fmtstrcpy(fp, s->name);
+ return 0;
+}
+
+static char*
+basicnames[] =
+{
+ [TINT] = "int",
+ [TUINT] = "uint",
+ [TINT8] = "int8",
+ [TUINT8] = "uint8",
+ [TINT16] = "int16",
+ [TUINT16] = "uint16",
+ [TINT32] = "int32",
+ [TUINT32] = "uint32",
+ [TINT64] = "int64",
+ [TUINT64] = "uint64",
+ [TUINTPTR] = "uintptr",
+ [TFLOAT32] = "float32",
+ [TFLOAT64] = "float64",
+ [TCOMPLEX64] = "complex64",
+ [TCOMPLEX128] = "complex128",
+ [TBOOL] = "bool",
+ [TANY] = "any",
+ [TSTRING] = "string",
+ [TNIL] = "nil",
+ [TIDEAL] = "ideal",
+ [TBLANK] = "blank",
+};
+
+int
+Tpretty(Fmt *fp, Type *t)
+{
+ Type *t1;
+ Sym *s;
+
+ if(0 && debug['r']) {
+ debug['r'] = 0;
+ fmtprint(fp, "%T (orig=%T)", t, t->orig);
+ debug['r'] = 1;
+ return 0;
+ }
+
+ if(t->etype != TFIELD
+ && t->sym != S
+ && !(fp->flags&FmtLong)) {
+ s = t->sym;
+ if(t == types[t->etype] && t->etype != TUNSAFEPTR)
+ return fmtprint(fp, "%s", s->name);
+ if(exporting) {
+ if(fp->flags & FmtShort)
+ fmtprint(fp, "%hS", s);
+ else
+ fmtprint(fp, "%S", s);
+ if(s->pkg != localpkg)
+ return 0;
+ if(t->vargen)
+ fmtprint(fp, "·%d", t->vargen);
+ return 0;
+ }
+ return fmtprint(fp, "%S", s);
+ }
+
+ if(t->etype < nelem(basicnames) && basicnames[t->etype] != nil) {
+ if(isideal(t) && t->etype != TIDEAL && t->etype != TNIL)
+ fmtprint(fp, "ideal ");
+ return fmtprint(fp, "%s", basicnames[t->etype]);
+ }
+
+ switch(t->etype) {
+ case TPTR32:
+ case TPTR64:
+ if(fp->flags&FmtShort) // pass flag thru for methodsym
+ return fmtprint(fp, "*%hT", t->type);
+ return fmtprint(fp, "*%T", t->type);
+
+ case TCHAN:
+ switch(t->chan) {
+ case Crecv:
+ return fmtprint(fp, "<-chan %T", t->type);
+ case Csend:
+ return fmtprint(fp, "chan<- %T", t->type);
+ }
+ if(t->type != T && t->type->etype == TCHAN && t->type->sym == S && t->type->chan == Crecv)
+ return fmtprint(fp, "chan (%T)", t->type);
+ return fmtprint(fp, "chan %T", t->type);
+
+ case TMAP:
+ return fmtprint(fp, "map[%T] %T", t->down, t->type);
+
+ case TFUNC:
+ // t->type is method struct
+ // t->type->down is result struct
+ // t->type->down->down is arg struct
+ if(t->thistuple && !(fp->flags&FmtSharp) && !(fp->flags&FmtShort)) {
+ fmtprint(fp, "method(");
+ for(t1=getthisx(t)->type; t1; t1=t1->down) {
+ fmtprint(fp, "%T", t1);
+ if(t1->down)
+ fmtprint(fp, ", ");
+ }
+ fmtprint(fp, ")");
+ }
+
+ if(!(fp->flags&FmtByte))
+ fmtprint(fp, "func");
+ fmtprint(fp, "(");
+ for(t1=getinargx(t)->type; t1; t1=t1->down) {
+ if(noargnames && t1->etype == TFIELD) {
+ if(t1->isddd)
+ fmtprint(fp, "...%T", t1->type->type);
+ else
+ fmtprint(fp, "%T", t1->type);
+ } else
+ fmtprint(fp, "%T", t1);
+ if(t1->down)
+ fmtprint(fp, ", ");
+ }
+ fmtprint(fp, ")");
+ switch(t->outtuple) {
+ case 0:
+ break;
+ case 1:
+ t1 = getoutargx(t)->type;
+ if(t1 == T) {
+ // failure to typecheck earlier; don't know the type
+ fmtprint(fp, " ?unknown-type?");
+ break;
+ }
+ if(t1->etype == TFIELD)
+ t1 = t1->type;
+ fmtprint(fp, " %T", t1);
+ break;
+ default:
+ t1 = getoutargx(t)->type;
+ fmtprint(fp, " (");
+ for(; t1; t1=t1->down) {
+ if(noargnames && t1->etype == TFIELD)
+ fmtprint(fp, "%T", t1->type);
+ else
+ fmtprint(fp, "%T", t1);
+ if(t1->down)
+ fmtprint(fp, ", ");
+ }
+ fmtprint(fp, ")");
+ break;
+ }
+ return 0;
+
+ case TARRAY:
+ if(t->bound >= 0)
+ return fmtprint(fp, "[%d]%T", (int)t->bound, t->type);
+ if(t->bound == -100)
+ return fmtprint(fp, "[...]%T", t->type);
+ return fmtprint(fp, "[]%T", t->type);
+
+ case TINTER:
+ fmtprint(fp, "interface {");
+ for(t1=t->type; t1!=T; t1=t1->down) {
+ fmtprint(fp, " ");
+ if(exportname(t1->sym->name))
+ fmtprint(fp, "%hS", t1->sym);
+ else
+ fmtprint(fp, "%S", t1->sym);
+ fmtprint(fp, "%hhT", t1->type);
+ if(t1->down)
+ fmtprint(fp, ";");
+ }
+ return fmtprint(fp, " }");
+
+ case TSTRUCT:
+ if(t->funarg) {
+ fmtprint(fp, "(");
+ for(t1=t->type; t1!=T; t1=t1->down) {
+ fmtprint(fp, "%T", t1);
+ if(t1->down)
+ fmtprint(fp, ", ");
+ }
+ return fmtprint(fp, ")");
+ }
+ fmtprint(fp, "struct {");
+ for(t1=t->type; t1!=T; t1=t1->down) {
+ fmtprint(fp, " %T", t1);
+ if(t1->down)
+ fmtprint(fp, ";");
+ }
+ return fmtprint(fp, " }");
+
+ case TFIELD:
+ if(t->sym == S || t->embedded) {
+ if(exporting)
+ fmtprint(fp, "? ");
+ } else
+ fmtprint(fp, "%hS ", t->sym);
+ if(t->isddd)
+ fmtprint(fp, "...%T", t->type->type);
+ else
+ fmtprint(fp, "%T", t->type);
+ if(t->note) {
+ fmtprint(fp, " ");
+ if(exporting)
+ fmtprint(fp, ":");
+ fmtprint(fp, "\"%Z\"", t->note);
+ }
+ return 0;
+
+ case TFORW:
+ if(exporting)
+ yyerror("undefined type %S", t->sym);
+ if(t->sym)
+ return fmtprint(fp, "undefined %S", t->sym);
+ return fmtprint(fp, "undefined");
+
+ case TUNSAFEPTR:
+ if(exporting)
+ return fmtprint(fp, "\"unsafe\".Pointer");
+ return fmtprint(fp, "unsafe.Pointer");
+ }
+
+ // Don't know how to handle - fall back to detailed prints.
+ return -1;
+}
+
+int
+Tconv(Fmt *fp)
+{
+ Type *t, *t1;
+ int r, et, sharp, minus;
+
+ sharp = (fp->flags & FmtSharp);
+ minus = (fp->flags & FmtLeft);
+ fp->flags &= ~(FmtSharp|FmtLeft);
+
+ t = va_arg(fp->args, Type*);
+ if(t == T)
+ return fmtstrcpy(fp, "<T>");
+
+ t->trecur++;
+ if(t->trecur > 5) {
+ fmtprint(fp, "...");
+ goto out;
+ }
+
+ if(!debug['t']) {
+ if(sharp)
+ exporting++;
+ if(minus)
+ noargnames++;
+ r = Tpretty(fp, t);
+ if(sharp)
+ exporting--;
+ if(minus)
+ noargnames--;
+ if(r >= 0) {
+ t->trecur--;
+ return 0;
+ }
+ }
+
+ if(sharp || exporting)
+ fatal("missing %E case during export", t->etype);
+
+ et = t->etype;
+ fmtprint(fp, "%E ", et);
+ if(t->sym != S)
+ fmtprint(fp, "<%S>", t->sym);
+
+ switch(et) {
+ default:
+ if(t->type != T)
+ fmtprint(fp, " %T", t->type);
+ break;
+
+ case TFIELD:
+ fmtprint(fp, "%T", t->type);
+ break;
+
+ case TFUNC:
+ if(fp->flags & FmtLong)
+ fmtprint(fp, "%d%d%d(%lT,%lT)%lT",
+ t->thistuple, t->intuple, t->outtuple,
+ t->type, t->type->down->down, t->type->down);
+ else
+ fmtprint(fp, "%d%d%d(%T,%T)%T",
+ t->thistuple, t->intuple, t->outtuple,
+ t->type, t->type->down->down, t->type->down);
+ break;
+
+ case TINTER:
+ fmtprint(fp, "{");
+ if(fp->flags & FmtLong)
+ for(t1=t->type; t1!=T; t1=t1->down)
+ fmtprint(fp, "%lT;", t1);
+ fmtprint(fp, "}");
+ break;
+
+ case TSTRUCT:
+ fmtprint(fp, "{");
+ if(fp->flags & FmtLong)
+ for(t1=t->type; t1!=T; t1=t1->down)
+ fmtprint(fp, "%lT;", t1);
+ fmtprint(fp, "}");
+ break;
+
+ case TMAP:
+ fmtprint(fp, "[%T]%T", t->down, t->type);
+ break;
+
+ case TARRAY:
+ if(t->bound >= 0)
+ fmtprint(fp, "[%d]%T", t->bound, t->type);
+ else
+ fmtprint(fp, "[]%T", t->type);
+ break;
+
+ case TPTR32:
+ case TPTR64:
+ fmtprint(fp, "%T", t->type);
+ break;
+ }
+
+out:
+ t->trecur--;
+ return 0;
+}
+
+int
+Nconv(Fmt *fp)
+{
+ char buf1[500];
+ Node *n;
+
+ n = va_arg(fp->args, Node*);
+ if(n == N) {
+ fmtprint(fp, "<N>");
+ goto out;
+ }
+
+ if(fp->flags & FmtSign) {
+ if(n->type == T)
+ fmtprint(fp, "%#N", n);
+ else if(n->type->etype == TNIL)
+ fmtprint(fp, "nil");
+ else
+ fmtprint(fp, "%#N (type %T)", n, n->type);
+ goto out;
+ }
+
+ if(fp->flags & FmtSharp) {
+ if(n->orig != N)
+ n = n->orig;
+ exprfmt(fp, n, 0);
+ goto out;
+ }
+
+ switch(n->op) {
+ default:
+ if (fp->flags & FmtShort)
+ fmtprint(fp, "%O%hJ", n->op, n);
+ else
+ fmtprint(fp, "%O%J", n->op, n);
+ break;
+
+ case ONAME:
+ case ONONAME:
+ if(n->sym == S) {
+ if (fp->flags & FmtShort)
+ fmtprint(fp, "%O%hJ", n->op, n);
+ else
+ fmtprint(fp, "%O%J", n->op, n);
+ break;
+ }
+ if (fp->flags & FmtShort)
+ fmtprint(fp, "%O-%S%hJ", n->op, n->sym, n);
+ else
+ fmtprint(fp, "%O-%S%J", n->op, n->sym, n);
+ goto ptyp;
+
+ case OREGISTER:
+ fmtprint(fp, "%O-%R%J", n->op, n->val.u.reg, n);
+ break;
+
+ case OLITERAL:
+ switch(n->val.ctype) {
+ default:
+ snprint(buf1, sizeof(buf1), "LITERAL-ctype=%d", n->val.ctype);
+ break;
+ case CTINT:
+ snprint(buf1, sizeof(buf1), "I%B", n->val.u.xval);
+ break;
+ case CTFLT:
+ snprint(buf1, sizeof(buf1), "F%g", mpgetflt(n->val.u.fval));
+ break;
+ case CTCPLX:
+ snprint(buf1, sizeof(buf1), "(F%g+F%gi)",
+ mpgetflt(&n->val.u.cval->real),
+ mpgetflt(&n->val.u.cval->imag));
+ break;
+ case CTSTR:
+ snprint(buf1, sizeof(buf1), "S\"%Z\"", n->val.u.sval);
+ break;
+ case CTBOOL:
+ snprint(buf1, sizeof(buf1), "B%d", n->val.u.bval);
+ break;
+ case CTNIL:
+ snprint(buf1, sizeof(buf1), "N");
+ break;
+ }
+ fmtprint(fp, "%O-%s%J", n->op, buf1, n);
+ break;
+
+ case OASOP:
+ fmtprint(fp, "%O-%O%J", n->op, n->etype, n);
+ break;
+
+ case OTYPE:
+ fmtprint(fp, "%O %T", n->op, n->type);
+ break;
+ }
+ if(n->sym != S)
+ fmtprint(fp, " %S G%d", n->sym, n->vargen);
+
+ptyp:
+ if(n->type != T)
+ fmtprint(fp, " %T", n->type);
+
+out:
+ return 0;
+}
+
+Node*
+treecopy(Node *n)
+{
+ Node *m;
+
+ if(n == N)
+ return N;
+
+ switch(n->op) {
+ default:
+ m = nod(OXXX, N, N);
+ *m = *n;
+ m->left = treecopy(n->left);
+ m->right = treecopy(n->right);
+ m->list = listtreecopy(n->list);
+ if(m->defn)
+ abort();
+ break;
+
+ case ONONAME:
+ if(n->sym == lookup("iota")) {
+ // Not sure yet whether this is the real iota,
+ // but make a copy of the Node* just in case,
+ // so that all the copies of this const definition
+ // don't have the same iota value.
+ m = nod(OXXX, N, N);
+ *m = *n;
+ m->iota = iota;
+ break;
+ }
+ // fall through
+ case ONAME:
+ case OLITERAL:
+ case OTYPE:
+ m = n;
+ break;
+ }
+ return m;
+}
+
+int
+Zconv(Fmt *fp)
+{
+ Rune r;
+ Strlit *sp;
+ char *s, *se;
+ int n;
+
+ sp = va_arg(fp->args, Strlit*);
+ if(sp == nil)
+ return fmtstrcpy(fp, "<nil>");
+
+ s = sp->s;
+ se = s + sp->len;
+ while(s < se) {
+ n = chartorune(&r, s);
+ s += n;
+ switch(r) {
+ case Runeerror:
+ if(n == 1) {
+ fmtprint(fp, "\\x%02x", (uchar)*(s-1));
+ break;
+ }
+ // fall through
+ default:
+ if(r < ' ') {
+ fmtprint(fp, "\\x%02x", r);
+ break;
+ }
+ fmtrune(fp, r);
+ break;
+ case '\t':
+ fmtstrcpy(fp, "\\t");
+ break;
+ case '\n':
+ fmtstrcpy(fp, "\\n");
+ break;
+ case '\"':
+ case '\\':
+ fmtrune(fp, '\\');
+ fmtrune(fp, r);
+ break;
+ }
+ }
+ return 0;
+}
+
+int
+isnil(Node *n)
+{
+ if(n == N)
+ return 0;
+ if(n->op != OLITERAL)
+ return 0;
+ if(n->val.ctype != CTNIL)
+ return 0;
+ return 1;
+}
+
+int
+isptrto(Type *t, int et)
+{
+ if(t == T)
+ return 0;
+ if(!isptr[t->etype])
+ return 0;
+ t = t->type;
+ if(t == T)
+ return 0;
+ if(t->etype != et)
+ return 0;
+ return 1;
+}
+
+int
+istype(Type *t, int et)
+{
+ return t != T && t->etype == et;
+}
+
+int
+isfixedarray(Type *t)
+{
+ return t != T && t->etype == TARRAY && t->bound >= 0;
+}
+
+int
+isslice(Type *t)
+{
+ return t != T && t->etype == TARRAY && t->bound < 0;
+}
+
+int
+isblank(Node *n)
+{
+ char *p;
+
+ if(n == N || n->sym == S)
+ return 0;
+ p = n->sym->name;
+ if(p == nil)
+ return 0;
+ return p[0] == '_' && p[1] == '\0';
+}
+
+int
+isinter(Type *t)
+{
+ return t != T && t->etype == TINTER;
+}
+
+int
+isnilinter(Type *t)
+{
+ if(!isinter(t))
+ return 0;
+ if(t->type != T)
+ return 0;
+ return 1;
+}
+
+int
+isideal(Type *t)
+{
+ if(t == T)
+ return 0;
+ if(t == idealstring || t == idealbool)
+ return 1;
+ switch(t->etype) {
+ case TNIL:
+ case TIDEAL:
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * given receiver of type t (t == r or t == *r)
+ * return type to hang methods off (r).
+ */
+Type*
+methtype(Type *t)
+{
+ if(t == T)
+ return T;
+
+ // strip away pointer if it's there
+ if(isptr[t->etype]) {
+ if(t->sym != S)
+ return T;
+ t = t->type;
+ if(t == T)
+ return T;
+ }
+
+ // need a type name
+ if(t->sym == S)
+ return T;
+
+ // check types
+ if(!issimple[t->etype])
+ switch(t->etype) {
+ default:
+ return T;
+ case TSTRUCT:
+ case TARRAY:
+ case TMAP:
+ case TCHAN:
+ case TSTRING:
+ case TFUNC:
+ break;
+ }
+
+ return t;
+}
+
+int
+cplxsubtype(int et)
+{
+ switch(et) {
+ case TCOMPLEX64:
+ return TFLOAT32;
+ case TCOMPLEX128:
+ return TFLOAT64;
+ }
+ fatal("cplxsubtype: %E\n", et);
+ return 0;
+}
+
+static int
+eqnote(Strlit *a, Strlit *b)
+{
+ if(a == b)
+ return 1;
+ if(a == nil || b == nil)
+ return 0;
+ if(a->len != b->len)
+ return 0;
+ return memcmp(a->s, b->s, a->len) == 0;
+}
+
+// Return 1 if t1 and t2 are identical, following the spec rules.
+//
+// Any cyclic type must go through a named type, and if one is
+// named, it is only identical to the other if they are the same
+// pointer (t1 == t2), so there's no chance of chasing cycles
+// ad infinitum, so no need for a depth counter.
+int
+eqtype(Type *t1, Type *t2)
+{
+ if(t1 == t2)
+ return 1;
+ if(t1 == T || t2 == T || t1->etype != t2->etype || t1->sym || t2->sym)
+ return 0;
+
+ switch(t1->etype) {
+ case TINTER:
+ case TSTRUCT:
+ for(t1=t1->type, t2=t2->type; t1 && t2; t1=t1->down, t2=t2->down) {
+ if(t1->etype != TFIELD || t2->etype != TFIELD)
+ fatal("struct/interface missing field: %T %T", t1, t2);
+ if(t1->sym != t2->sym || t1->embedded != t2->embedded || !eqtype(t1->type, t2->type) || !eqnote(t1->note, t2->note))
+ return 0;
+ }
+ return t1 == T && t2 == T;
+
+ case TFUNC:
+ // Loop over structs: receiver, in, out.
+ for(t1=t1->type, t2=t2->type; t1 && t2; t1=t1->down, t2=t2->down) {
+ Type *ta, *tb;
+
+ if(t1->etype != TSTRUCT || t2->etype != TSTRUCT)
+ fatal("func missing struct: %T %T", t1, t2);
+
+ // Loop over fields in structs, ignoring argument names.
+ for(ta=t1->type, tb=t2->type; ta && tb; ta=ta->down, tb=tb->down) {
+ if(ta->etype != TFIELD || tb->etype != TFIELD)
+ fatal("func struct missing field: %T %T", ta, tb);
+ if(ta->isddd != tb->isddd || !eqtype(ta->type, tb->type))
+ return 0;
+ }
+ if(ta != T || tb != T)
+ return 0;
+ }
+ return t1 == T && t2 == T;
+
+ case TARRAY:
+ if(t1->bound != t2->bound)
+ return 0;
+ break;
+
+ case TCHAN:
+ if(t1->chan != t2->chan)
+ return 0;
+ break;
+ }
+
+ return eqtype(t1->down, t2->down) && eqtype(t1->type, t2->type);
+}
+
+// Are t1 and t2 equal struct types when field names are ignored?
+// For deciding whether the result struct from g can be copied
+// directly when compiling f(g()).
+int
+eqtypenoname(Type *t1, Type *t2)
+{
+ if(t1 == T || t2 == T || t1->etype != TSTRUCT || t2->etype != TSTRUCT)
+ return 0;
+
+ t1 = t1->type;
+ t2 = t2->type;
+ for(;;) {
+ if(!eqtype(t1, t2))
+ return 0;
+ if(t1 == T)
+ return 1;
+ t1 = t1->down;
+ t2 = t2->down;
+ }
+}
+
+// Is type src assignment compatible to type dst?
+// If so, return op code to use in conversion.
+// If not, return 0.
+//
+// It is the caller's responsibility to call exportassignok
+// to check for assignments to other packages' unexported fields,
+int
+assignop(Type *src, Type *dst, char **why)
+{
+ Type *missing, *have;
+ int ptr;
+
+ if(why != nil)
+ *why = "";
+
+ if(safemode && src != T && src->etype == TUNSAFEPTR) {
+ yyerror("cannot use unsafe.Pointer");
+ errorexit();
+ }
+
+ if(src == dst)
+ return OCONVNOP;
+ if(src == T || dst == T || src->etype == TFORW || dst->etype == TFORW || src->orig == T || dst->orig == T)
+ return 0;
+
+ // 1. src type is identical to dst.
+ if(eqtype(src, dst))
+ return OCONVNOP;
+
+ // 2. src and dst have identical underlying types
+ // and either src or dst is not a named type or
+ // both are interface types.
+ if(eqtype(src->orig, dst->orig) && (src->sym == S || dst->sym == S || src->etype == TINTER))
+ return OCONVNOP;
+
+ // 3. dst is an interface type and src implements dst.
+ if(dst->etype == TINTER && src->etype != TNIL) {
+ if(implements(src, dst, &missing, &have, &ptr))
+ return OCONVIFACE;
+ if(why != nil) {
+ if(isptrto(src, TINTER))
+ *why = smprint(":\n\t%T is pointer to interface, not interface", src);
+ else if(have && have->sym == missing->sym)
+ *why = smprint(":\n\t%T does not implement %T (wrong type for %S method)\n"
+ "\t\thave %S%hhT\n\t\twant %S%hhT", src, dst, missing->sym,
+ have->sym, have->type, missing->sym, missing->type);
+ else if(ptr)
+ *why = smprint(":\n\t%T does not implement %T (%S method requires pointer receiver)",
+ src, dst, missing->sym);
+ else if(have)
+ *why = smprint(":\n\t%T does not implement %T (missing %S method)\n"
+ "\t\thave %S%hhT\n\t\twant %S%hhT", src, dst, missing->sym,
+ have->sym, have->type, missing->sym, missing->type);
+ else
+ *why = smprint(":\n\t%T does not implement %T (missing %S method)",
+ src, dst, missing->sym);
+ }
+ return 0;
+ }
+ if(isptrto(dst, TINTER)) {
+ if(why != nil)
+ *why = smprint(":\n\t%T is pointer to interface, not interface", dst);
+ return 0;
+ }
+ if(src->etype == TINTER && dst->etype != TBLANK) {
+ if(why != nil)
+ *why = ": need type assertion";
+ return 0;
+ }
+
+ // 4. src is a bidirectional channel value, dst is a channel type,
+ // src and dst have identical element types, and
+ // either src or dst is not a named type.
+ if(src->etype == TCHAN && src->chan == Cboth && dst->etype == TCHAN)
+ if(eqtype(src->type, dst->type) && (src->sym == S || dst->sym == S))
+ return OCONVNOP;
+
+ // 5. src is the predeclared identifier nil and dst is a nillable type.
+ if(src->etype == TNIL) {
+ switch(dst->etype) {
+ case TARRAY:
+ if(dst->bound != -100) // not slice
+ break;
+ case TPTR32:
+ case TPTR64:
+ case TFUNC:
+ case TMAP:
+ case TCHAN:
+ case TINTER:
+ return OCONVNOP;
+ }
+ }
+
+ // 6. rule about untyped constants - already converted by defaultlit.
+
+ // 7. Any typed value can be assigned to the blank identifier.
+ if(dst->etype == TBLANK)
+ return OCONVNOP;
+
+ return 0;
+}
+
+// Can we convert a value of type src to a value of type dst?
+// If so, return op code to use in conversion (maybe OCONVNOP).
+// If not, return 0.
+int
+convertop(Type *src, Type *dst, char **why)
+{
+ int op;
+
+ if(why != nil)
+ *why = "";
+
+ if(src == dst)
+ return OCONVNOP;
+ if(src == T || dst == T)
+ return 0;
+
+ // 1. src can be assigned to dst.
+ if((op = assignop(src, dst, why)) != 0)
+ return op;
+
+ // The rules for interfaces are no different in conversions
+ // than assignments. If interfaces are involved, stop now
+ // with the good message from assignop.
+ // Otherwise clear the error.
+ if(src->etype == TINTER || dst->etype == TINTER)
+ return 0;
+ if(why != nil)
+ *why = "";
+
+ // 2. src and dst have identical underlying types.
+ if(eqtype(src->orig, dst->orig))
+ return OCONVNOP;
+
+ // 3. src and dst are unnamed pointer types
+ // and their base types have identical underlying types.
+ if(isptr[src->etype] && isptr[dst->etype] && src->sym == S && dst->sym == S)
+ if(eqtype(src->type->orig, dst->type->orig))
+ return OCONVNOP;
+
+ // 4. src and dst are both integer or floating point types.
+ if((isint[src->etype] || isfloat[src->etype]) && (isint[dst->etype] || isfloat[dst->etype])) {
+ if(simtype[src->etype] == simtype[dst->etype])
+ return OCONVNOP;
+ return OCONV;
+ }
+
+ // 5. src and dst are both complex types.
+ if(iscomplex[src->etype] && iscomplex[dst->etype]) {
+ if(simtype[src->etype] == simtype[dst->etype])
+ return OCONVNOP;
+ return OCONV;
+ }
+
+ // 6. src is an integer or has type []byte or []int
+ // and dst is a string type.
+ if(isint[src->etype] && dst->etype == TSTRING)
+ return ORUNESTR;
+
+ if(isslice(src) && src->sym == nil && src->type == types[src->type->etype] && dst->etype == TSTRING) {
+ switch(src->type->etype) {
+ case TUINT8:
+ return OARRAYBYTESTR;
+ case TINT:
+ return OARRAYRUNESTR;
+ }
+ }
+
+ // 7. src is a string and dst is []byte or []int.
+ // String to slice.
+ if(src->etype == TSTRING && isslice(dst) && dst->sym == nil && dst->type == types[dst->type->etype]) {
+ switch(dst->type->etype) {
+ case TUINT8:
+ return OSTRARRAYBYTE;
+ case TINT:
+ return OSTRARRAYRUNE;
+ }
+ }
+
+ // 8. src is a pointer or uintptr and dst is unsafe.Pointer.
+ if((isptr[src->etype] || src->etype == TUINTPTR) && dst->etype == TUNSAFEPTR)
+ return OCONVNOP;
+
+ // 9. src is unsafe.Pointer and dst is a pointer or uintptr.
+ if(src->etype == TUNSAFEPTR && (isptr[dst->etype] || dst->etype == TUINTPTR))
+ return OCONVNOP;
+
+ return 0;
+}
+
+// Convert node n for assignment to type t.
+Node*
+assignconv(Node *n, Type *t, char *context)
+{
+ int op;
+ Node *r, *old;
+ char *why;
+
+ if(n == N || n->type == T)
+ return n;
+
+ old = n;
+ old->diag++; // silence errors about n; we'll issue one below
+ defaultlit(&n, t);
+ old->diag--;
+ if(t->etype == TBLANK)
+ return n;
+
+ exportassignok(n->type, context);
+ if(eqtype(n->type, t))
+ return n;
+
+ op = assignop(n->type, t, &why);
+ if(op == 0) {
+ yyerror("cannot use %+N as type %T in %s%s", n, t, context, why);
+ op = OCONV;
+ }
+
+ r = nod(op, n, N);
+ r->type = t;
+ r->typecheck = 1;
+ r->implicit = 1;
+ return r;
+}
+
+static int
+subtype(Type **stp, Type *t, int d)
+{
+ Type *st;
+
+loop:
+ st = *stp;
+ if(st == T)
+ return 0;
+
+ d++;
+ if(d >= 10)
+ return 0;
+
+ switch(st->etype) {
+ default:
+ return 0;
+
+ case TPTR32:
+ case TPTR64:
+ case TCHAN:
+ case TARRAY:
+ stp = &st->type;
+ goto loop;
+
+ case TANY:
+ if(!st->copyany)
+ return 0;
+ *stp = t;
+ break;
+
+ case TMAP:
+ if(subtype(&st->down, t, d))
+ break;
+ stp = &st->type;
+ goto loop;
+
+ case TFUNC:
+ for(;;) {
+ if(subtype(&st->type, t, d))
+ break;
+ if(subtype(&st->type->down->down, t, d))
+ break;
+ if(subtype(&st->type->down, t, d))
+ break;
+ return 0;
+ }
+ break;
+
+ case TSTRUCT:
+ for(st=st->type; st!=T; st=st->down)
+ if(subtype(&st->type, t, d))
+ return 1;
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Is this a 64-bit type?
+ */
+int
+is64(Type *t)
+{
+ if(t == T)
+ return 0;
+ switch(simtype[t->etype]) {
+ case TINT64:
+ case TUINT64:
+ case TPTR64:
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Is a conversion between t1 and t2 a no-op?
+ */
+int
+noconv(Type *t1, Type *t2)
+{
+ int e1, e2;
+
+ e1 = simtype[t1->etype];
+ e2 = simtype[t2->etype];
+
+ switch(e1) {
+ case TINT8:
+ case TUINT8:
+ return e2 == TINT8 || e2 == TUINT8;
+
+ case TINT16:
+ case TUINT16:
+ return e2 == TINT16 || e2 == TUINT16;
+
+ case TINT32:
+ case TUINT32:
+ case TPTR32:
+ return e2 == TINT32 || e2 == TUINT32 || e2 == TPTR32;
+
+ case TINT64:
+ case TUINT64:
+ case TPTR64:
+ return e2 == TINT64 || e2 == TUINT64 || e2 == TPTR64;
+
+ case TFLOAT32:
+ return e2 == TFLOAT32;
+
+ case TFLOAT64:
+ return e2 == TFLOAT64;
+ }
+ return 0;
+}
+
+void
+argtype(Node *on, Type *t)
+{
+ dowidth(t);
+ if(!subtype(&on->type, t, 0))
+ fatal("argtype: failed %N %T\n", on, t);
+}
+
+Type*
+shallow(Type *t)
+{
+ Type *nt;
+
+ if(t == T)
+ return T;
+ nt = typ(0);
+ *nt = *t;
+ if(t->orig == t)
+ nt->orig = nt;
+ return nt;
+}
+
+static Type*
+deep(Type *t)
+{
+ Type *nt, *xt;
+
+ if(t == T)
+ return T;
+
+ switch(t->etype) {
+ default:
+ nt = t; // share from here down
+ break;
+
+ case TANY:
+ nt = shallow(t);
+ nt->copyany = 1;
+ break;
+
+ case TPTR32:
+ case TPTR64:
+ case TCHAN:
+ case TARRAY:
+ nt = shallow(t);
+ nt->type = deep(t->type);
+ break;
+
+ case TMAP:
+ nt = shallow(t);
+ nt->down = deep(t->down);
+ nt->type = deep(t->type);
+ break;
+
+ case TFUNC:
+ nt = shallow(t);
+ nt->type = deep(t->type);
+ nt->type->down = deep(t->type->down);
+ nt->type->down->down = deep(t->type->down->down);
+ break;
+
+ case TSTRUCT:
+ nt = shallow(t);
+ nt->type = shallow(t->type);
+ xt = nt->type;
+
+ for(t=t->type; t!=T; t=t->down) {
+ xt->type = deep(t->type);
+ xt->down = shallow(t->down);
+ xt = xt->down;
+ }
+ break;
+ }
+ return nt;
+}
+
+Node*
+syslook(char *name, int copy)
+{
+ Sym *s;
+ Node *n;
+
+ s = pkglookup(name, runtimepkg);
+ if(s == S || s->def == N)
+ fatal("syslook: can't find runtime.%s", name);
+
+ if(!copy)
+ return s->def;
+
+ n = nod(0, N, N);
+ *n = *s->def;
+ n->type = deep(s->def->type);
+
+ return n;
+}
+
+/*
+ * compute a hash value for type t.
+ * if t is a method type, ignore the receiver
+ * so that the hash can be used in interface checks.
+ * %-T (which calls Tpretty, above) already contains
+ * all the necessary logic to generate a representation
+ * of the type that completely describes it.
+ * using smprint here avoids duplicating that code.
+ * using md5 here is overkill, but i got tired of
+ * accidental collisions making the runtime think
+ * two types are equal when they really aren't.
+ */
+uint32
+typehash(Type *t)
+{
+ char *p;
+ MD5 d;
+
+ longsymnames = 1;
+ if(t->thistuple) {
+ // hide method receiver from Tpretty
+ t->thistuple = 0;
+ p = smprint("%-T", t);
+ t->thistuple = 1;
+ }else
+ p = smprint("%-T", t);
+ longsymnames = 0;
+ md5reset(&d);
+ md5write(&d, (uchar*)p, strlen(p));
+ free(p);
+ return md5sum(&d);
+}
+
+Type*
+ptrto(Type *t)
+{
+ Type *t1;
+
+ if(tptr == 0)
+ fatal("ptrto: nil");
+ t1 = typ(tptr);
+ t1->type = t;
+ t1->width = widthptr;
+ t1->align = widthptr;
+ return t1;
+}
+
+void
+frame(int context)
+{
+ char *p;
+ NodeList *l;
+ Node *n;
+ int flag;
+
+ p = "stack";
+ l = nil;
+ if(curfn)
+ l = curfn->dcl;
+ if(context) {
+ p = "external";
+ l = externdcl;
+ }
+
+ flag = 1;
+ for(; l; l=l->next) {
+ n = l->n;
+ switch(n->op) {
+ case ONAME:
+ if(flag)
+ print("--- %s frame ---\n", p);
+ print("%O %S G%d %T\n", n->op, n->sym, n->vargen, n->type);
+ flag = 0;
+ break;
+
+ case OTYPE:
+ if(flag)
+ print("--- %s frame ---\n", p);
+ print("%O %T\n", n->op, n->type);
+ flag = 0;
+ break;
+ }
+ }
+}
+
+/*
+ * calculate sethi/ullman number
+ * roughly how many registers needed to
+ * compile a node. used to compile the
+ * hardest side first to minimize registers.
+ */
+void
+ullmancalc(Node *n)
+{
+ int ul, ur;
+
+ if(n == N)
+ return;
+
+ switch(n->op) {
+ case OREGISTER:
+ case OLITERAL:
+ case ONAME:
+ ul = 1;
+ if(n->class == PPARAMREF || (n->class & PHEAP))
+ ul++;
+ goto out;
+ case OCALL:
+ case OCALLFUNC:
+ case OCALLMETH:
+ case OCALLINTER:
+ ul = UINF;
+ goto out;
+ }
+ ul = 1;
+ if(n->left != N)
+ ul = n->left->ullman;
+ ur = 1;
+ if(n->right != N)
+ ur = n->right->ullman;
+ if(ul == ur)
+ ul += 1;
+ if(ur > ul)
+ ul = ur;
+
+out:
+ n->ullman = ul;
+}
+
+void
+badtype(int o, Type *tl, Type *tr)
+{
+ Fmt fmt;
+ char *s;
+
+ fmtstrinit(&fmt);
+ if(tl != T)
+ fmtprint(&fmt, "\n %T", tl);
+ if(tr != T)
+ fmtprint(&fmt, "\n %T", tr);
+
+ // common mistake: *struct and *interface.
+ if(tl && tr && isptr[tl->etype] && isptr[tr->etype]) {
+ if(tl->type->etype == TSTRUCT && tr->type->etype == TINTER)
+ fmtprint(&fmt, "\n (*struct vs *interface)");
+ else if(tl->type->etype == TINTER && tr->type->etype == TSTRUCT)
+ fmtprint(&fmt, "\n (*interface vs *struct)");
+ }
+ s = fmtstrflush(&fmt);
+ yyerror("illegal types for operand: %O%s", o, s);
+}
+
+/*
+ * iterator to walk a structure declaration
+ */
+Type*
+structfirst(Iter *s, Type **nn)
+{
+ Type *n, *t;
+
+ n = *nn;
+ if(n == T)
+ goto bad;
+
+ switch(n->etype) {
+ default:
+ goto bad;
+
+ case TSTRUCT:
+ case TINTER:
+ case TFUNC:
+ break;
+ }
+
+ t = n->type;
+ if(t == T)
+ goto rnil;
+
+ if(t->etype != TFIELD)
+ fatal("structfirst: not field %T", t);
+
+ s->t = t;
+ return t;
+
+bad:
+ fatal("structfirst: not struct %T", n);
+
+rnil:
+ return T;
+}
+
+Type*
+structnext(Iter *s)
+{
+ Type *n, *t;
+
+ n = s->t;
+ t = n->down;
+ if(t == T)
+ goto rnil;
+
+ if(t->etype != TFIELD)
+ goto bad;
+
+ s->t = t;
+ return t;
+
+bad:
+ fatal("structnext: not struct %T", n);
+
+rnil:
+ return T;
+}
+
+/*
+ * iterator to this and inargs in a function
+ */
+Type*
+funcfirst(Iter *s, Type *t)
+{
+ Type *fp;
+
+ if(t == T)
+ goto bad;
+
+ if(t->etype != TFUNC)
+ goto bad;
+
+ s->tfunc = t;
+ s->done = 0;
+ fp = structfirst(s, getthis(t));
+ if(fp == T) {
+ s->done = 1;
+ fp = structfirst(s, getinarg(t));
+ }
+ return fp;
+
+bad:
+ fatal("funcfirst: not func %T", t);
+ return T;
+}
+
+Type*
+funcnext(Iter *s)
+{
+ Type *fp;
+
+ fp = structnext(s);
+ if(fp == T && !s->done) {
+ s->done = 1;
+ fp = structfirst(s, getinarg(s->tfunc));
+ }
+ return fp;
+}
+
+Type**
+getthis(Type *t)
+{
+ if(t->etype != TFUNC)
+ fatal("getthis: not a func %T", t);
+ return &t->type;
+}
+
+Type**
+getoutarg(Type *t)
+{
+ if(t->etype != TFUNC)
+ fatal("getoutarg: not a func %T", t);
+ return &t->type->down;
+}
+
+Type**
+getinarg(Type *t)
+{
+ if(t->etype != TFUNC)
+ fatal("getinarg: not a func %T", t);
+ return &t->type->down->down;
+}
+
+Type*
+getthisx(Type *t)
+{
+ return *getthis(t);
+}
+
+Type*
+getoutargx(Type *t)
+{
+ return *getoutarg(t);
+}
+
+Type*
+getinargx(Type *t)
+{
+ return *getinarg(t);
+}
+
+/*
+ * return !(op)
+ * eg == <=> !=
+ */
+int
+brcom(int a)
+{
+ switch(a) {
+ case OEQ: return ONE;
+ case ONE: return OEQ;
+ case OLT: return OGE;
+ case OGT: return OLE;
+ case OLE: return OGT;
+ case OGE: return OLT;
+ }
+ fatal("brcom: no com for %A\n", a);
+ return a;
+}
+
+/*
+ * return reverse(op)
+ * eg a op b <=> b r(op) a
+ */
+int
+brrev(int a)
+{
+ switch(a) {
+ case OEQ: return OEQ;
+ case ONE: return ONE;
+ case OLT: return OGT;
+ case OGT: return OLT;
+ case OLE: return OGE;
+ case OGE: return OLE;
+ }
+ fatal("brcom: no rev for %A\n", a);
+ return a;
+}
+
+/*
+ * return side effect-free n, appending side effects to init.
+ * result is assignable if n is.
+ */
+Node*
+safeexpr(Node *n, NodeList **init)
+{
+ Node *l;
+ Node *r;
+ Node *a;
+
+ if(n == N)
+ return N;
+
+ switch(n->op) {
+ case ONAME:
+ case OLITERAL:
+ return n;
+
+ case ODOT:
+ l = safeexpr(n->left, init);
+ if(l == n->left)
+ return n;
+ r = nod(OXXX, N, N);
+ *r = *n;
+ r->left = l;
+ typecheck(&r, Erv);
+ walkexpr(&r, init);
+ return r;
+
+ case ODOTPTR:
+ case OIND:
+ l = safeexpr(n->left, init);
+ if(l == n->left)
+ return n;
+ a = nod(OXXX, N, N);
+ *a = *n;
+ a->left = l;
+ walkexpr(&a, init);
+ return a;
+
+ case OINDEX:
+ case OINDEXMAP:
+ l = safeexpr(n->left, init);
+ r = safeexpr(n->right, init);
+ if(l == n->left && r == n->right)
+ return n;
+ a = nod(OXXX, N, N);
+ *a = *n;
+ a->left = l;
+ a->right = r;
+ walkexpr(&a, init);
+ return a;
+ }
+
+ // make a copy; must not be used as an lvalue
+ if(islvalue(n))
+ fatal("missing lvalue case in safeexpr: %N", n);
+ return cheapexpr(n, init);
+}
+
+static Node*
+copyexpr(Node *n, Type *t, NodeList **init)
+{
+ Node *a, *l;
+
+ l = nod(OXXX, N, N);
+ tempname(l, t);
+ a = nod(OAS, l, n);
+ typecheck(&a, Etop);
+ walkexpr(&a, init);
+ *init = list(*init, a);
+ return l;
+}
+
+/*
+ * return side-effect free and cheap n, appending side effects to init.
+ * result may not be assignable.
+ */
+Node*
+cheapexpr(Node *n, NodeList **init)
+{
+ switch(n->op) {
+ case ONAME:
+ case OLITERAL:
+ return n;
+ }
+
+ return copyexpr(n, n->type, init);
+}
+
+/*
+ * return n in a local variable of type t if it is not already.
+ */
+Node*
+localexpr(Node *n, Type *t, NodeList **init)
+{
+ if(n->op == ONAME &&
+ (n->class == PAUTO || n->class == PPARAM || n->class == PPARAMOUT) &&
+ convertop(n->type, t, nil) == OCONVNOP)
+ return n;
+
+ return copyexpr(n, t, init);
+}
+
+void
+setmaxarg(Type *t)
+{
+ int32 w;
+
+ dowidth(t);
+ w = t->argwid;
+ if(t->argwid >= MAXWIDTH)
+ fatal("bad argwid %T", t);
+ if(w > maxarg)
+ maxarg = w;
+}
+
+/* unicode-aware case-insensitive strcmp */
+
+static int
+cistrcmp(char *p, char *q)
+{
+ Rune rp, rq;
+
+ while(*p || *q) {
+ if(*p == 0)
+ return +1;
+ if(*q == 0)
+ return -1;
+ p += chartorune(&rp, p);
+ q += chartorune(&rq, q);
+ rp = tolowerrune(rp);
+ rq = tolowerrune(rq);
+ if(rp < rq)
+ return -1;
+ if(rp > rq)
+ return +1;
+ }
+ return 0;
+}
+
+/*
+ * code to resolve elided DOTs
+ * in embedded types
+ */
+
+// search depth 0 --
+// return count of fields+methods
+// found with a given name
+static int
+lookdot0(Sym *s, Type *t, Type **save, int ignorecase)
+{
+ Type *f, *u;
+ int c;
+
+ u = t;
+ if(isptr[u->etype])
+ u = u->type;
+
+ c = 0;
+ if(u->etype == TSTRUCT || u->etype == TINTER) {
+ for(f=u->type; f!=T; f=f->down)
+ if(f->sym == s || (ignorecase && cistrcmp(f->sym->name, s->name) == 0)) {
+ if(save)
+ *save = f;
+ c++;
+ }
+ }
+ u = methtype(t);
+ if(u != T) {
+ for(f=u->method; f!=T; f=f->down)
+ if(f->embedded == 0 && (f->sym == s || (ignorecase && cistrcmp(f->sym->name, s->name) == 0))) {
+ if(save)
+ *save = f;
+ c++;
+ }
+ }
+ return c;
+}
+
+// search depth d --
+// return count of fields+methods
+// found at search depth.
+// answer is in dotlist array and
+// count of number of ways is returned.
+int
+adddot1(Sym *s, Type *t, int d, Type **save, int ignorecase)
+{
+ Type *f, *u;
+ int c, a;
+
+ if(t->trecur)
+ return 0;
+ t->trecur = 1;
+
+ if(d == 0) {
+ c = lookdot0(s, t, save, ignorecase);
+ goto out;
+ }
+
+ c = 0;
+ u = t;
+ if(isptr[u->etype])
+ u = u->type;
+ if(u->etype != TSTRUCT && u->etype != TINTER)
+ goto out;
+
+ d--;
+ for(f=u->type; f!=T; f=f->down) {
+ if(!f->embedded)
+ continue;
+ if(f->sym == S)
+ continue;
+ a = adddot1(s, f->type, d, save, ignorecase);
+ if(a != 0 && c == 0)
+ dotlist[d].field = f;
+ c += a;
+ }
+
+out:
+ t->trecur = 0;
+ return c;
+}
+
+// in T.field
+// find missing fields that
+// will give shortest unique addressing.
+// modify the tree with missing type names.
+Node*
+adddot(Node *n)
+{
+ Type *t;
+ Sym *s;
+ int c, d;
+
+ typecheck(&n->left, Etype|Erv);
+ t = n->left->type;
+ if(t == T)
+ goto ret;
+
+ if(n->left->op == OTYPE)
+ goto ret;
+
+ if(n->right->op != ONAME)
+ goto ret;
+ s = n->right->sym;
+ if(s == S)
+ goto ret;
+
+ for(d=0; d<nelem(dotlist); d++) {
+ c = adddot1(s, t, d, nil, 0);
+ if(c > 0)
+ goto out;
+ }
+ goto ret;
+
+out:
+ if(c > 1)
+ yyerror("ambiguous DOT reference %T.%S", t, s);
+
+ // rebuild elided dots
+ for(c=d-1; c>=0; c--)
+ n->left = nod(ODOT, n->left, newname(dotlist[c].field->sym));
+ret:
+ return n;
+}
+
+
+/*
+ * code to help generate trampoline
+ * functions for methods on embedded
+ * subtypes.
+ * these are approx the same as
+ * the corresponding adddot routines
+ * except that they expect to be called
+ * with unique tasks and they return
+ * the actual methods.
+ */
+
+typedef struct Symlink Symlink;
+struct Symlink
+{
+ Type* field;
+ uchar good;
+ uchar followptr;
+ Symlink* link;
+};
+static Symlink* slist;
+
+static void
+expand0(Type *t, int followptr)
+{
+ Type *f, *u;
+ Symlink *sl;
+
+ u = t;
+ if(isptr[u->etype]) {
+ followptr = 1;
+ u = u->type;
+ }
+
+ if(u->etype == TINTER) {
+ for(f=u->type; f!=T; f=f->down) {
+ if(!exportname(f->sym->name) && f->sym->pkg != localpkg)
+ continue;
+ if(f->sym->flags & SymUniq)
+ continue;
+ f->sym->flags |= SymUniq;
+ sl = mal(sizeof(*sl));
+ sl->field = f;
+ sl->link = slist;
+ sl->followptr = followptr;
+ slist = sl;
+ }
+ return;
+ }
+
+ u = methtype(t);
+ if(u != T) {
+ for(f=u->method; f!=T; f=f->down) {
+ if(!exportname(f->sym->name) && f->sym->pkg != localpkg)
+ continue;
+ if(f->sym->flags & SymUniq)
+ continue;
+ f->sym->flags |= SymUniq;
+ sl = mal(sizeof(*sl));
+ sl->field = f;
+ sl->link = slist;
+ sl->followptr = followptr;
+ slist = sl;
+ }
+ }
+}
+
+static void
+expand1(Type *t, int d, int followptr)
+{
+ Type *f, *u;
+
+ if(t->trecur)
+ return;
+ if(d == 0)
+ return;
+ t->trecur = 1;
+
+ if(d != nelem(dotlist)-1)
+ expand0(t, followptr);
+
+ u = t;
+ if(isptr[u->etype]) {
+ followptr = 1;
+ u = u->type;
+ }
+ if(u->etype != TSTRUCT && u->etype != TINTER)
+ goto out;
+
+ for(f=u->type; f!=T; f=f->down) {
+ if(!f->embedded)
+ continue;
+ if(f->sym == S)
+ continue;
+ expand1(f->type, d-1, followptr);
+ }
+
+out:
+ t->trecur = 0;
+}
+
+void
+expandmeth(Sym *s, Type *t)
+{
+ Symlink *sl;
+ Type *f;
+ int c, d;
+
+ if(s == S)
+ return;
+ if(t == T || t->xmethod != nil)
+ return;
+
+ // mark top-level method symbols
+ // so that expand1 doesn't consider them.
+ for(f=t->method; f != nil; f=f->down)
+ f->sym->flags |= SymUniq;
+
+ // generate all reachable methods
+ slist = nil;
+ expand1(t, nelem(dotlist)-1, 0);
+
+ // check each method to be uniquely reachable
+ for(sl=slist; sl!=nil; sl=sl->link) {
+ sl->field->sym->flags &= ~SymUniq;
+ for(d=0; d<nelem(dotlist); d++) {
+ c = adddot1(sl->field->sym, t, d, &f, 0);
+ if(c == 0)
+ continue;
+ if(c == 1) {
+ sl->good = 1;
+ sl->field = f;
+ }
+ break;
+ }
+ }
+
+ for(f=t->method; f != nil; f=f->down)
+ f->sym->flags &= ~SymUniq;
+
+ t->xmethod = t->method;
+ for(sl=slist; sl!=nil; sl=sl->link) {
+ if(sl->good) {
+ // add it to the base type method list
+ f = typ(TFIELD);
+ *f = *sl->field;
+ f->embedded = 1; // needs a trampoline
+ if(sl->followptr)
+ f->embedded = 2;
+ f->down = t->xmethod;
+ t->xmethod = f;
+ }
+ }
+}
+
+/*
+ * Given funarg struct list, return list of ODCLFIELD Node fn args.
+ */
+static NodeList*
+structargs(Type **tl, int mustname)
+{
+ Iter savet;
+ Node *a, *n;
+ NodeList *args;
+ Type *t;
+ char buf[100];
+ int gen;
+
+ args = nil;
+ gen = 0;
+ for(t = structfirst(&savet, tl); t != T; t = structnext(&savet)) {
+ n = N;
+ if(t->sym)
+ n = newname(t->sym);
+ else if(mustname) {
+ // have to give it a name so we can refer to it in trampoline
+ snprint(buf, sizeof buf, ".anon%d", gen++);
+ n = newname(lookup(buf));
+ }
+ a = nod(ODCLFIELD, n, typenod(t->type));
+ a->isddd = t->isddd;
+ if(n != N)
+ n->isddd = t->isddd;
+ args = list(args, a);
+ }
+ return args;
+}
+
+/*
+ * Generate a wrapper function to convert from
+ * a receiver of type T to a receiver of type U.
+ * That is,
+ *
+ * func (t T) M() {
+ * ...
+ * }
+ *
+ * already exists; this function generates
+ *
+ * func (u U) M() {
+ * u.M()
+ * }
+ *
+ * where the types T and U are such that u.M() is valid
+ * and calls the T.M method.
+ * The resulting function is for use in method tables.
+ *
+ * rcvr - U
+ * method - M func (t T)(), a TFIELD type struct
+ * newnam - the eventual mangled name of this function
+ */
+void
+genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface)
+{
+ Node *this, *fn, *call, *n, *t, *pad;
+ NodeList *l, *args, *in, *out;
+ Type *tpad;
+ int isddd;
+ Val v;
+
+ if(debug['r'])
+ print("genwrapper rcvrtype=%T method=%T newnam=%S\n",
+ rcvr, method, newnam);
+
+ lineno = 1; // less confusing than end of input
+
+ dclcontext = PEXTERN;
+ markdcl();
+
+ this = nod(ODCLFIELD, newname(lookup(".this")), typenod(rcvr));
+ this->left->ntype = this->right;
+ in = structargs(getinarg(method->type), 1);
+ out = structargs(getoutarg(method->type), 0);
+
+ fn = nod(ODCLFUNC, N, N);
+ fn->nname = newname(newnam);
+ t = nod(OTFUNC, N, N);
+ l = list1(this);
+ if(iface && rcvr->width < types[tptr]->width) {
+ // Building method for interface table and receiver
+ // is smaller than the single pointer-sized word
+ // that the interface call will pass in.
+ // Add a dummy padding argument after the
+ // receiver to make up the difference.
+ tpad = typ(TARRAY);
+ tpad->type = types[TUINT8];
+ tpad->bound = types[tptr]->width - rcvr->width;
+ pad = nod(ODCLFIELD, newname(lookup(".pad")), typenod(tpad));
+ l = list(l, pad);
+ }
+ t->list = concat(l, in);
+ t->rlist = out;
+ fn->nname->ntype = t;
+ funchdr(fn);
+
+ // arg list
+ args = nil;
+ isddd = 0;
+ for(l=in; l; l=l->next) {
+ args = list(args, l->n->left);
+ isddd = l->n->left->isddd;
+ }
+
+ // generate nil pointer check for better error
+ if(isptr[rcvr->etype] && rcvr->type == getthisx(method->type)->type->type) {
+ // generating wrapper from *T to T.
+ n = nod(OIF, N, N);
+ n->ntest = nod(OEQ, this->left, nodnil());
+ // these strings are already in the reflect tables,
+ // so no space cost to use them here.
+ l = nil;
+ v.ctype = CTSTR;
+ v.u.sval = strlit(rcvr->type->sym->pkg->name); // package name
+ l = list(l, nodlit(v));
+ v.u.sval = strlit(rcvr->type->sym->name); // type name
+ l = list(l, nodlit(v));
+ v.u.sval = strlit(method->sym->name);
+ l = list(l, nodlit(v)); // method name
+ call = nod(OCALL, syslook("panicwrap", 0), N);
+ call->list = l;
+ n->nbody = list1(call);
+ fn->nbody = list(fn->nbody, n);
+ }
+
+ // generate call
+ call = nod(OCALL, adddot(nod(OXDOT, this->left, newname(method->sym))), N);
+ call->list = args;
+ call->isddd = isddd;
+ if(method->type->outtuple > 0) {
+ n = nod(ORETURN, N, N);
+ n->list = list1(call);
+ call = n;
+ }
+ fn->nbody = list(fn->nbody, call);
+
+ if(0 && debug['r'])
+ dumplist("genwrapper body", fn->nbody);
+
+ funcbody(fn);
+ curfn = fn;
+ typecheck(&fn, Etop);
+ typechecklist(fn->nbody, Etop);
+ curfn = nil;
+ funccompile(fn, 0);
+}
+
+static Type*
+ifacelookdot(Sym *s, Type *t, int *followptr, int ignorecase)
+{
+ int i, c, d;
+ Type *m;
+
+ *followptr = 0;
+
+ if(t == T)
+ return T;
+
+ for(d=0; d<nelem(dotlist); d++) {
+ c = adddot1(s, t, d, &m, ignorecase);
+ if(c > 1) {
+ yyerror("%T.%S is ambiguous", t, s);
+ return T;
+ }
+ if(c == 1) {
+ for(i=0; i<d; i++) {
+ if(isptr[dotlist[i].field->type->etype]) {
+ *followptr = 1;
+ break;
+ }
+ }
+ if(m->type->etype != TFUNC || m->type->thistuple == 0) {
+ yyerror("%T.%S is a field, not a method", t, s);
+ return T;
+ }
+ return m;
+ }
+ }
+ return T;
+}
+
+int
+implements(Type *t, Type *iface, Type **m, Type **samename, int *ptr)
+{
+ Type *t0, *im, *tm, *rcvr, *imtype;
+ int followptr;
+
+ t0 = t;
+ if(t == T)
+ return 0;
+
+ // if this is too slow,
+ // could sort these first
+ // and then do one loop.
+
+ if(t->etype == TINTER) {
+ for(im=iface->type; im; im=im->down) {
+ for(tm=t->type; tm; tm=tm->down) {
+ if(tm->sym == im->sym) {
+ if(eqtype(tm->type, im->type))
+ goto found;
+ *m = im;
+ *samename = tm;
+ *ptr = 0;
+ return 0;
+ }
+ }
+ *m = im;
+ *samename = nil;
+ *ptr = 0;
+ return 0;
+ found:;
+ }
+ return 1;
+ }
+
+ t = methtype(t);
+ if(t != T)
+ expandmeth(t->sym, t);
+ for(im=iface->type; im; im=im->down) {
+ imtype = methodfunc(im->type, 0);
+ tm = ifacelookdot(im->sym, t, &followptr, 0);
+ if(tm == T || !eqtype(methodfunc(tm->type, 0), imtype)) {
+ if(tm == T)
+ tm = ifacelookdot(im->sym, t, &followptr, 1);
+ *m = im;
+ *samename = tm;
+ *ptr = 0;
+ return 0;
+ }
+ // if pointer receiver in method,
+ // the method does not exist for value types.
+ rcvr = getthisx(tm->type)->type->type;
+ if(isptr[rcvr->etype] && !isptr[t0->etype] && !followptr && !isifacemethod(tm->type)) {
+ if(0 && debug['r'])
+ yyerror("interface pointer mismatch");
+
+ *m = im;
+ *samename = nil;
+ *ptr = 1;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/*
+ * even simpler simtype; get rid of ptr, bool.
+ * assuming that the front end has rejected
+ * all the invalid conversions (like ptr -> bool)
+ */
+int
+simsimtype(Type *t)
+{
+ int et;
+
+ if(t == 0)
+ return 0;
+
+ et = simtype[t->etype];
+ switch(et) {
+ case TPTR32:
+ et = TUINT32;
+ break;
+ case TPTR64:
+ et = TUINT64;
+ break;
+ case TBOOL:
+ et = TUINT8;
+ break;
+ }
+ return et;
+}
+
+NodeList*
+concat(NodeList *a, NodeList *b)
+{
+ if(a == nil)
+ return b;
+ if(b == nil)
+ return a;
+
+ a->end->next = b;
+ a->end = b->end;
+ b->end = nil;
+ return a;
+}
+
+NodeList*
+list1(Node *n)
+{
+ NodeList *l;
+
+ if(n == nil)
+ return nil;
+ if(n->op == OBLOCK && n->ninit == nil)
+ return n->list;
+ l = mal(sizeof *l);
+ l->n = n;
+ l->end = l;
+ return l;
+}
+
+NodeList*
+list(NodeList *l, Node *n)
+{
+ return concat(l, list1(n));
+}
+
+void
+listsort(NodeList** l, int(*f)(Node*, Node*))
+{
+ NodeList *l1, *l2, *le;
+
+ if(*l == nil || (*l)->next == nil)
+ return;
+
+ l1 = *l;
+ l2 = *l;
+ for(;;) {
+ l2 = l2->next;
+ if(l2 == nil)
+ break;
+ l2 = l2->next;
+ if(l2 == nil)
+ break;
+ l1 = l1->next;
+ }
+
+ l2 = l1->next;
+ l1->next = nil;
+ l2->end = (*l)->end;
+ (*l)->end = l1;
+
+ l1 = *l;
+ listsort(&l1, f);
+ listsort(&l2, f);
+
+ if ((*f)(l1->n, l2->n) < 0) {
+ *l = l1;
+ } else {
+ *l = l2;
+ l2 = l1;
+ l1 = *l;
+ }
+
+ // now l1 == *l; and l1 < l2
+
+ while ((l1 != nil) && (l2 != nil)) {
+ while ((l1->next != nil) && (*f)(l1->next->n, l2->n) < 0)
+ l1 = l1->next;
+
+ // l1 is last one from l1 that is < l2
+ le = l1->next; // le is the rest of l1, first one that is >= l2
+ if (le != nil)
+ le->end = (*l)->end;
+
+ (*l)->end = l1; // cut *l at l1
+ *l = concat(*l, l2); // glue l2 to *l's tail
+
+ l1 = l2; // l1 is the first element of *l that is < the new l2
+ l2 = le; // ... because l2 now is the old tail of l1
+ }
+
+ *l = concat(*l, l2); // any remainder
+}
+
+NodeList*
+listtreecopy(NodeList *l)
+{
+ NodeList *out;
+
+ out = nil;
+ for(; l; l=l->next)
+ out = list(out, treecopy(l->n));
+ return out;
+}
+
+Node*
+liststmt(NodeList *l)
+{
+ Node *n;
+
+ n = nod(OBLOCK, N, N);
+ n->list = l;
+ if(l)
+ n->lineno = l->n->lineno;
+ return n;
+}
+
+/*
+ * return nelem of list
+ */
+int
+count(NodeList *l)
+{
+ int n;
+
+ n = 0;
+ for(; l; l=l->next)
+ n++;
+ return n;
+}
+
+/*
+ * return nelem of list
+ */
+int
+structcount(Type *t)
+{
+ int v;
+ Iter s;
+
+ v = 0;
+ for(t = structfirst(&s, &t); t != T; t = structnext(&s))
+ v++;
+ return v;
+}
+
+/*
+ * return power of 2 of the constant
+ * operand. -1 if it is not a power of 2.
+ * 1000+ if it is a -(power of 2)
+ */
+int
+powtwo(Node *n)
+{
+ uvlong v, b;
+ int i;
+
+ if(n == N || n->op != OLITERAL || n->type == T)
+ goto no;
+ if(!isint[n->type->etype])
+ goto no;
+
+ v = mpgetfix(n->val.u.xval);
+ b = 1ULL;
+ for(i=0; i<64; i++) {
+ if(b == v)
+ return i;
+ b = b<<1;
+ }
+
+ if(!issigned[n->type->etype])
+ goto no;
+
+ v = -v;
+ b = 1ULL;
+ for(i=0; i<64; i++) {
+ if(b == v)
+ return i+1000;
+ b = b<<1;
+ }
+
+no:
+ return -1;
+}
+
+/*
+ * return the unsigned type for
+ * a signed integer type.
+ * returns T if input is not a
+ * signed integer type.
+ */
+Type*
+tounsigned(Type *t)
+{
+
+ // this is types[et+1], but not sure
+ // that this relation is immutable
+ switch(t->etype) {
+ default:
+ print("tounsigned: unknown type %T\n", t);
+ t = T;
+ break;
+ case TINT:
+ t = types[TUINT];
+ break;
+ case TINT8:
+ t = types[TUINT8];
+ break;
+ case TINT16:
+ t = types[TUINT16];
+ break;
+ case TINT32:
+ t = types[TUINT32];
+ break;
+ case TINT64:
+ t = types[TUINT64];
+ break;
+ }
+ return t;
+}
+
+/*
+ * magic number for signed division
+ * see hacker's delight chapter 10
+ */
+void
+smagic(Magic *m)
+{
+ int p;
+ uint64 ad, anc, delta, q1, r1, q2, r2, t;
+ uint64 mask, two31;
+
+ m->bad = 0;
+ switch(m->w) {
+ default:
+ m->bad = 1;
+ return;
+ case 8:
+ mask = 0xffLL;
+ break;
+ case 16:
+ mask = 0xffffLL;
+ break;
+ case 32:
+ mask = 0xffffffffLL;
+ break;
+ case 64:
+ mask = 0xffffffffffffffffLL;
+ break;
+ }
+ two31 = mask ^ (mask>>1);
+
+ p = m->w-1;
+ ad = m->sd;
+ if(m->sd < 0)
+ ad = -m->sd;
+
+ // bad denominators
+ if(ad == 0 || ad == 1 || ad == two31) {
+ m->bad = 1;
+ return;
+ }
+
+ t = two31;
+ ad &= mask;
+
+ anc = t - 1 - t%ad;
+ anc &= mask;
+
+ q1 = two31/anc;
+ r1 = two31 - q1*anc;
+ q1 &= mask;
+ r1 &= mask;
+
+ q2 = two31/ad;
+ r2 = two31 - q2*ad;
+ q2 &= mask;
+ r2 &= mask;
+
+ for(;;) {
+ p++;
+ q1 <<= 1;
+ r1 <<= 1;
+ q1 &= mask;
+ r1 &= mask;
+ if(r1 >= anc) {
+ q1++;
+ r1 -= anc;
+ q1 &= mask;
+ r1 &= mask;
+ }
+
+ q2 <<= 1;
+ r2 <<= 1;
+ q2 &= mask;
+ r2 &= mask;
+ if(r2 >= ad) {
+ q2++;
+ r2 -= ad;
+ q2 &= mask;
+ r2 &= mask;
+ }
+
+ delta = ad - r2;
+ delta &= mask;
+ if(q1 < delta || (q1 == delta && r1 == 0)) {
+ continue;
+ }
+ break;
+ }
+
+ m->sm = q2+1;
+ if(m->sm & two31)
+ m->sm |= ~mask;
+ m->s = p-m->w;
+}
+
+/*
+ * magic number for unsigned division
+ * see hacker's delight chapter 10
+ */
+void
+umagic(Magic *m)
+{
+ int p;
+ uint64 nc, delta, q1, r1, q2, r2;
+ uint64 mask, two31;
+
+ m->bad = 0;
+ m->ua = 0;
+
+ switch(m->w) {
+ default:
+ m->bad = 1;
+ return;
+ case 8:
+ mask = 0xffLL;
+ break;
+ case 16:
+ mask = 0xffffLL;
+ break;
+ case 32:
+ mask = 0xffffffffLL;
+ break;
+ case 64:
+ mask = 0xffffffffffffffffLL;
+ break;
+ }
+ two31 = mask ^ (mask>>1);
+
+ m->ud &= mask;
+ if(m->ud == 0 || m->ud == two31) {
+ m->bad = 1;
+ return;
+ }
+ nc = mask - (-m->ud&mask)%m->ud;
+ p = m->w-1;
+
+ q1 = two31/nc;
+ r1 = two31 - q1*nc;
+ q1 &= mask;
+ r1 &= mask;
+
+ q2 = (two31-1) / m->ud;
+ r2 = (two31-1) - q2*m->ud;
+ q2 &= mask;
+ r2 &= mask;
+
+ for(;;) {
+ p++;
+ if(r1 >= nc-r1) {
+ q1 <<= 1;
+ q1++;
+ r1 <<= 1;
+ r1 -= nc;
+ } else {
+ q1 <<= 1;
+ r1 <<= 1;
+ }
+ q1 &= mask;
+ r1 &= mask;
+ if(r2+1 >= m->ud-r2) {
+ if(q2 >= two31-1) {
+ m->ua = 1;
+ }
+ q2 <<= 1;
+ q2++;
+ r2 <<= 1;
+ r2++;
+ r2 -= m->ud;
+ } else {
+ if(q2 >= two31) {
+ m->ua = 1;
+ }
+ q2 <<= 1;
+ r2 <<= 1;
+ r2++;
+ }
+ q2 &= mask;
+ r2 &= mask;
+
+ delta = m->ud - 1 - r2;
+ delta &= mask;
+
+ if(p < m->w+m->w)
+ if(q1 < delta || (q1 == delta && r1 == 0)) {
+ continue;
+ }
+ break;
+ }
+ m->um = q2+1;
+ m->s = p-m->w;
+}
+
+Sym*
+ngotype(Node *n)
+{
+ if(n->sym != S && n->realtype != T)
+ if(strncmp(n->sym->name, "autotmp_", 8) != 0)
+ if(strncmp(n->sym->name, "statictmp_", 8) != 0)
+ return typename(n->realtype)->left->sym;
+
+ return S;
+}
+
+/*
+ * Convert raw string to the prefix that will be used in the symbol table.
+ * Invalid bytes turn into %xx. Right now the only bytes that need
+ * escaping are %, ., and ", but we escape all control characters too.
+ */
+static char*
+pathtoprefix(char *s)
+{
+ static char hex[] = "0123456789abcdef";
+ char *p, *r, *w;
+ int n;
+
+ // check for chars that need escaping
+ n = 0;
+ for(r=s; *r; r++)
+ if(*r <= ' ' || *r == '.' || *r == '%' || *r == '"')
+ n++;
+
+ // quick exit
+ if(n == 0)
+ return s;
+
+ // escape
+ p = mal((r-s)+1+2*n);
+ for(r=s, w=p; *r; r++) {
+ if(*r <= ' ' || *r == '.' || *r == '%' || *r == '"') {
+ *w++ = '%';
+ *w++ = hex[(*r>>4)&0xF];
+ *w++ = hex[*r&0xF];
+ } else
+ *w++ = *r;
+ }
+ *w = '\0';
+ return p;
+}
+
+Pkg*
+mkpkg(Strlit *path)
+{
+ Pkg *p;
+ int h;
+
+ if(strlen(path->s) != path->len) {
+ yyerror("import path contains NUL byte");
+ errorexit();
+ }
+
+ h = stringhash(path->s) & (nelem(phash)-1);
+ for(p=phash[h]; p; p=p->link)
+ if(p->path->len == path->len && memcmp(path->s, p->path->s, path->len) == 0)
+ return p;
+
+ p = mal(sizeof *p);
+ p->path = path;
+ p->prefix = pathtoprefix(path->s);
+ p->link = phash[h];
+ phash[h] = p;
+ return p;
+}
+
+Strlit*
+strlit(char *s)
+{
+ Strlit *t;
+
+ t = mal(sizeof *t + strlen(s));
+ strcpy(t->s, s);
+ t->len = strlen(s);
+ return t;
+}
diff --git a/src/cmd/gc/swt.c b/src/cmd/gc/swt.c
new file mode 100644
index 000000000..c2968c44b
--- /dev/null
+++ b/src/cmd/gc/swt.c
@@ -0,0 +1,896 @@
+// Copyright 2009 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.
+
+#include "go.h"
+
+enum
+{
+ Snorm = 0,
+ Strue,
+ Sfalse,
+ Stype,
+
+ Tdefault, // default case
+ Texprconst, // normal constant case
+ Texprvar, // normal variable case
+ Ttypenil, // case nil
+ Ttypeconst, // type hashes
+ Ttypevar, // interface type
+
+ Ncase = 4, // count needed to split
+};
+
+typedef struct Case Case;
+struct Case
+{
+ Node* node; // points at case statement
+ uint32 hash; // hash of a type switch
+ uint8 type; // type of case
+ uint8 diag; // suppress multiple diagnostics
+ uint16 ordinal; // position in switch
+ Case* link; // linked list to link
+};
+#define C ((Case*)nil)
+
+void
+dumpcase(Case *c0)
+{
+ Case *c;
+
+ for(c=c0; c!=C; c=c->link) {
+ switch(c->type) {
+ case Tdefault:
+ print("case-default\n");
+ print(" ord=%d\n", c->ordinal);
+ break;
+ case Texprconst:
+ print("case-exprconst\n");
+ print(" ord=%d\n", c->ordinal);
+ break;
+ case Texprvar:
+ print("case-exprvar\n");
+ print(" ord=%d\n", c->ordinal);
+ print(" op=%O\n", c->node->left->op);
+ break;
+ case Ttypenil:
+ print("case-typenil\n");
+ print(" ord=%d\n", c->ordinal);
+ break;
+ case Ttypeconst:
+ print("case-typeconst\n");
+ print(" ord=%d\n", c->ordinal);
+ print(" hash=%ux\n", c->hash);
+ break;
+ case Ttypevar:
+ print("case-typevar\n");
+ print(" ord=%d\n", c->ordinal);
+ break;
+ default:
+ print("case-???\n");
+ print(" ord=%d\n", c->ordinal);
+ print(" op=%O\n", c->node->left->op);
+ print(" hash=%ux\n", c->hash);
+ break;
+ }
+ }
+ print("\n");
+}
+
+static int
+ordlcmp(Case *c1, Case *c2)
+{
+ // sort default first
+ if(c1->type == Tdefault)
+ return -1;
+ if(c2->type == Tdefault)
+ return +1;
+
+ // sort nil second
+ if(c1->type == Ttypenil)
+ return -1;
+ if(c2->type == Ttypenil)
+ return +1;
+
+ // sort by ordinal
+ if(c1->ordinal > c2->ordinal)
+ return +1;
+ if(c1->ordinal < c2->ordinal)
+ return -1;
+ return 0;
+}
+
+static int
+exprcmp(Case *c1, Case *c2)
+{
+ int ct, n;
+ Node *n1, *n2;
+
+ // sort non-constants last
+ if(c1->type != Texprconst)
+ return +1;
+ if(c2->type != Texprconst)
+ return -1;
+
+ n1 = c1->node->left;
+ n2 = c2->node->left;
+
+ ct = n1->val.ctype;
+ if(ct != n2->val.ctype) {
+ // invalid program, but return a sort
+ // order so that we can give a better
+ // error later.
+ return ct - n2->val.ctype;
+ }
+
+ // sort by constant value
+ n = 0;
+ switch(ct) {
+ case CTFLT:
+ n = mpcmpfltflt(n1->val.u.fval, n2->val.u.fval);
+ break;
+ case CTINT:
+ n = mpcmpfixfix(n1->val.u.xval, n2->val.u.xval);
+ break;
+ case CTSTR:
+ n = cmpslit(n1, n2);
+ break;
+ }
+
+ return n;
+}
+
+static int
+typecmp(Case *c1, Case *c2)
+{
+
+ // sort non-constants last
+ if(c1->type != Ttypeconst)
+ return +1;
+ if(c2->type != Ttypeconst)
+ return -1;
+
+ // sort by hash code
+ if(c1->hash > c2->hash)
+ return +1;
+ if(c1->hash < c2->hash)
+ return -1;
+
+ // sort by ordinal so duplicate error
+ // happens on later case.
+ if(c1->ordinal > c2->ordinal)
+ return +1;
+ if(c1->ordinal < c2->ordinal)
+ return -1;
+ return 0;
+}
+
+static Case*
+csort(Case *l, int(*f)(Case*, Case*))
+{
+ Case *l1, *l2, *le;
+
+ if(l == C || l->link == C)
+ return l;
+
+ l1 = l;
+ l2 = l;
+ for(;;) {
+ l2 = l2->link;
+ if(l2 == C)
+ break;
+ l2 = l2->link;
+ if(l2 == C)
+ break;
+ l1 = l1->link;
+ }
+
+ l2 = l1->link;
+ l1->link = C;
+ l1 = csort(l, f);
+ l2 = csort(l2, f);
+
+ /* set up lead element */
+ if((*f)(l1, l2) < 0) {
+ l = l1;
+ l1 = l1->link;
+ } else {
+ l = l2;
+ l2 = l2->link;
+ }
+ le = l;
+
+ for(;;) {
+ if(l1 == C) {
+ while(l2) {
+ le->link = l2;
+ le = l2;
+ l2 = l2->link;
+ }
+ le->link = C;
+ break;
+ }
+ if(l2 == C) {
+ while(l1) {
+ le->link = l1;
+ le = l1;
+ l1 = l1->link;
+ }
+ break;
+ }
+ if((*f)(l1, l2) < 0) {
+ le->link = l1;
+ le = l1;
+ l1 = l1->link;
+ } else {
+ le->link = l2;
+ le = l2;
+ l2 = l2->link;
+ }
+ }
+ le->link = C;
+ return l;
+}
+
+static Node*
+newlabel(void)
+{
+ static int label;
+
+ label++;
+ snprint(namebuf, sizeof(namebuf), "%.6d", label);
+ return newname(lookup(namebuf));
+}
+
+/*
+ * build separate list of statements and cases
+ * make labels between cases and statements
+ * deal with fallthrough, break, unreachable statements
+ */
+static void
+casebody(Node *sw, Node *typeswvar)
+{
+ Node *n, *c, *last;
+ Node *def;
+ NodeList *cas, *stat, *l, *lc;
+ Node *go, *br;
+ int32 lno, needvar;
+
+ lno = setlineno(sw);
+ if(sw->list == nil)
+ return;
+
+ cas = nil; // cases
+ stat = nil; // statements
+ def = N; // defaults
+ br = nod(OBREAK, N, N);
+
+ for(l=sw->list; l; l=l->next) {
+ n = l->n;
+ lno = setlineno(n);
+ if(n->op != OXCASE)
+ fatal("casebody %O", n->op);
+ n->op = OCASE;
+ needvar = count(n->list) != 1 || n->list->n->op == OLITERAL;
+
+ go = nod(OGOTO, newlabel(), N);
+ if(n->list == nil) {
+ if(def != N)
+ yyerror("more than one default case");
+ // reuse original default case
+ n->right = go;
+ def = n;
+ }
+
+ if(n->list != nil && n->list->next == nil) {
+ // one case - reuse OCASE node.
+ c = n->list->n;
+ n->left = c;
+ n->right = go;
+ n->list = nil;
+ cas = list(cas, n);
+ } else {
+ // expand multi-valued cases
+ for(lc=n->list; lc; lc=lc->next) {
+ c = lc->n;
+ cas = list(cas, nod(OCASE, c, go));
+ }
+ }
+
+ stat = list(stat, nod(OLABEL, go->left, N));
+ if(typeswvar && needvar && n->nname != N) {
+ NodeList *l;
+
+ l = list1(nod(ODCL, n->nname, N));
+ l = list(l, nod(OAS, n->nname, typeswvar));
+ typechecklist(l, Etop);
+ stat = concat(stat, l);
+ }
+ stat = concat(stat, n->nbody);
+
+ // botch - shouldnt fall thru declaration
+ last = stat->end->n;
+ if(last->op == OXFALL) {
+ if(typeswvar) {
+ setlineno(last);
+ yyerror("cannot fallthrough in type switch");
+ }
+ last->op = OFALL;
+ } else
+ stat = list(stat, br);
+ }
+
+ stat = list(stat, br);
+ if(def)
+ cas = list(cas, def);
+
+ sw->list = cas;
+ sw->nbody = stat;
+ lineno = lno;
+}
+
+static Case*
+mkcaselist(Node *sw, int arg)
+{
+ Node *n;
+ Case *c, *c1, *c2;
+ NodeList *l;
+ int ord;
+
+ c = C;
+ ord = 0;
+
+ for(l=sw->list; l; l=l->next) {
+ n = l->n;
+ c1 = mal(sizeof(*c1));
+ c1->link = c;
+ c = c1;
+
+ ord++;
+ c->ordinal = ord;
+ c->node = n;
+
+ if(n->left == N) {
+ c->type = Tdefault;
+ continue;
+ }
+
+ switch(arg) {
+ case Stype:
+ c->hash = 0;
+ if(n->left->op == OLITERAL) {
+ c->type = Ttypenil;
+ continue;
+ }
+ if(istype(n->left->type, TINTER)) {
+ c->type = Ttypevar;
+ continue;
+ }
+
+ c->hash = typehash(n->left->type);
+ c->type = Ttypeconst;
+ continue;
+
+ case Snorm:
+ case Strue:
+ case Sfalse:
+ c->type = Texprvar;
+ switch(consttype(n->left)) {
+ case CTFLT:
+ case CTINT:
+ case CTSTR:
+ c->type = Texprconst;
+ }
+ continue;
+ }
+ }
+
+ if(c == C)
+ return C;
+
+ // sort by value and diagnose duplicate cases
+ switch(arg) {
+ case Stype:
+ c = csort(c, typecmp);
+ for(c1=c; c1!=C; c1=c1->link) {
+ for(c2=c1->link; c2!=C && c2->hash==c1->hash; c2=c2->link) {
+ if(c1->type == Ttypenil || c1->type == Tdefault)
+ break;
+ if(c2->type == Ttypenil || c2->type == Tdefault)
+ break;
+ if(!eqtype(c1->node->left->type, c2->node->left->type))
+ continue;
+ yyerrorl(c2->node->lineno, "duplicate case in switch\n\tprevious case at %L", c1->node->lineno);
+ }
+ }
+ break;
+ case Snorm:
+ case Strue:
+ case Sfalse:
+ c = csort(c, exprcmp);
+ for(c1=c; c1->link!=C; c1=c1->link) {
+ if(exprcmp(c1, c1->link) != 0)
+ continue;
+ setlineno(c1->link->node);
+ yyerror("duplicate case in switch\n\tprevious case at %L", c1->node->lineno);
+ }
+ break;
+ }
+
+ // put list back in processing order
+ c = csort(c, ordlcmp);
+ return c;
+}
+
+static Node* exprname;
+
+static Node*
+exprbsw(Case *c0, int ncase, int arg)
+{
+ NodeList *cas;
+ Node *a, *n;
+ Case *c;
+ int i, half, lno;
+
+ cas = nil;
+ if(ncase < Ncase) {
+ for(i=0; i<ncase; i++) {
+ n = c0->node;
+ lno = setlineno(n);
+
+ switch(arg) {
+ case Strue:
+ a = nod(OIF, N, N);
+ a->ntest = n->left; // if val
+ a->nbody = list1(n->right); // then goto l
+ break;
+
+ case Sfalse:
+ a = nod(OIF, N, N);
+ a->ntest = nod(ONOT, n->left, N); // if !val
+ typecheck(&a->ntest, Erv);
+ a->nbody = list1(n->right); // then goto l
+ break;
+
+ default:
+ a = nod(OIF, N, N);
+ a->ntest = nod(OEQ, exprname, n->left); // if name == val
+ typecheck(&a->ntest, Erv);
+ a->nbody = list1(n->right); // then goto l
+ break;
+ }
+
+ cas = list(cas, a);
+ c0 = c0->link;
+ lineno = lno;
+ }
+ return liststmt(cas);
+ }
+
+ // find the middle and recur
+ c = c0;
+ half = ncase>>1;
+ for(i=1; i<half; i++)
+ c = c->link;
+ a = nod(OIF, N, N);
+ a->ntest = nod(OLE, exprname, c->node->left);
+ typecheck(&a->ntest, Erv);
+ a->nbody = list1(exprbsw(c0, half, arg));
+ a->nelse = list1(exprbsw(c->link, ncase-half, arg));
+ return a;
+}
+
+/*
+ * normal (expression) switch.
+ * rebulid case statements into if .. goto
+ */
+static void
+exprswitch(Node *sw)
+{
+ Node *def;
+ NodeList *cas;
+ Node *a;
+ Case *c0, *c, *c1;
+ Type *t;
+ int arg, ncase;
+
+ casebody(sw, N);
+
+ arg = Snorm;
+ if(isconst(sw->ntest, CTBOOL)) {
+ arg = Strue;
+ if(sw->ntest->val.u.bval == 0)
+ arg = Sfalse;
+ }
+ walkexpr(&sw->ntest, &sw->ninit);
+ t = sw->type;
+ if(t == T)
+ return;
+
+ /*
+ * convert the switch into OIF statements
+ */
+ exprname = N;
+ cas = nil;
+ if(arg != Strue && arg != Sfalse) {
+ exprname = nod(OXXX, N, N);
+ tempname(exprname, sw->ntest->type);
+ cas = list1(nod(OAS, exprname, sw->ntest));
+ typechecklist(cas, Etop);
+ }
+
+ c0 = mkcaselist(sw, arg);
+ if(c0 != C && c0->type == Tdefault) {
+ def = c0->node->right;
+ c0 = c0->link;
+ } else {
+ def = nod(OBREAK, N, N);
+ }
+
+loop:
+ if(c0 == C) {
+ cas = list(cas, def);
+ sw->nbody = concat(cas, sw->nbody);
+ sw->list = nil;
+ walkstmtlist(sw->nbody);
+ return;
+ }
+
+ // deal with the variables one-at-a-time
+ if(c0->type != Texprconst) {
+ a = exprbsw(c0, 1, arg);
+ cas = list(cas, a);
+ c0 = c0->link;
+ goto loop;
+ }
+
+ // do binary search on run of constants
+ ncase = 1;
+ for(c=c0; c->link!=C; c=c->link) {
+ if(c->link->type != Texprconst)
+ break;
+ ncase++;
+ }
+
+ // break the chain at the count
+ c1 = c->link;
+ c->link = C;
+
+ // sort and compile constants
+ c0 = csort(c0, exprcmp);
+ a = exprbsw(c0, ncase, arg);
+ cas = list(cas, a);
+
+ c0 = c1;
+ goto loop;
+
+}
+
+static Node* hashname;
+static Node* facename;
+static Node* boolname;
+
+static Node*
+typeone(Node *t)
+{
+ NodeList *init;
+ Node *a, *b, *var;
+
+ var = t->nname;
+ init = nil;
+ if(var == N) {
+ typecheck(&nblank, Erv | Easgn);
+ var = nblank;
+ } else
+ init = list1(nod(ODCL, var, N));
+
+ a = nod(OAS2, N, N);
+ a->list = list(list1(var), boolname); // var,bool =
+ b = nod(ODOTTYPE, facename, N);
+ b->type = t->left->type; // interface.(type)
+ a->rlist = list1(b);
+ typecheck(&a, Etop);
+ init = list(init, a);
+
+ b = nod(OIF, N, N);
+ b->ntest = boolname;
+ b->nbody = list1(t->right); // if bool { goto l }
+ a = liststmt(list(init, b));
+ return a;
+}
+
+static Node*
+typebsw(Case *c0, int ncase)
+{
+ NodeList *cas;
+ Node *a, *n;
+ Case *c;
+ int i, half;
+
+ cas = nil;
+
+ if(ncase < Ncase) {
+ for(i=0; i<ncase; i++) {
+ n = c0->node;
+ if(c0->type != Ttypeconst)
+ fatal("typebsw");
+ a = nod(OIF, N, N);
+ a->ntest = nod(OEQ, hashname, nodintconst(c0->hash));
+ typecheck(&a->ntest, Erv);
+ a->nbody = list1(n->right);
+ cas = list(cas, a);
+ c0 = c0->link;
+ }
+ return liststmt(cas);
+ }
+
+ // find the middle and recur
+ c = c0;
+ half = ncase>>1;
+ for(i=1; i<half; i++)
+ c = c->link;
+ a = nod(OIF, N, N);
+ a->ntest = nod(OLE, hashname, nodintconst(c->hash));
+ typecheck(&a->ntest, Erv);
+ a->nbody = list1(typebsw(c0, half));
+ a->nelse = list1(typebsw(c->link, ncase-half));
+ return a;
+}
+
+/*
+ * convert switch of the form
+ * switch v := i.(type) { case t1: ..; case t2: ..; }
+ * into if statements
+ */
+static void
+typeswitch(Node *sw)
+{
+ Node *def;
+ NodeList *cas, *hash;
+ Node *a, *n;
+ Case *c, *c0, *c1;
+ int ncase;
+ Type *t;
+ Val v;
+
+ if(sw->ntest == nil)
+ return;
+ if(sw->ntest->right == nil) {
+ setlineno(sw);
+ yyerror("type switch must have an assignment");
+ return;
+ }
+ walkexpr(&sw->ntest->right, &sw->ninit);
+ if(!istype(sw->ntest->right->type, TINTER)) {
+ yyerror("type switch must be on an interface");
+ return;
+ }
+ cas = nil;
+
+ /*
+ * predeclare temporary variables
+ * and the boolean var
+ */
+ facename = nod(OXXX, N, N);
+ tempname(facename, sw->ntest->right->type);
+ a = nod(OAS, facename, sw->ntest->right);
+ typecheck(&a, Etop);
+ cas = list(cas, a);
+
+ casebody(sw, facename);
+
+ boolname = nod(OXXX, N, N);
+ tempname(boolname, types[TBOOL]);
+ typecheck(&boolname, Erv);
+
+ hashname = nod(OXXX, N, N);
+ tempname(hashname, types[TUINT32]);
+ typecheck(&hashname, Erv);
+
+ t = sw->ntest->right->type;
+ if(isnilinter(t))
+ a = syslook("efacethash", 1);
+ else
+ a = syslook("ifacethash", 1);
+ argtype(a, t);
+ a = nod(OCALL, a, N);
+ a->list = list1(facename);
+ a = nod(OAS, hashname, a);
+ typecheck(&a, Etop);
+ cas = list(cas, a);
+
+ c0 = mkcaselist(sw, Stype);
+ if(c0 != C && c0->type == Tdefault) {
+ def = c0->node->right;
+ c0 = c0->link;
+ } else {
+ def = nod(OBREAK, N, N);
+ }
+
+ /*
+ * insert if statement into each case block
+ */
+ for(c=c0; c!=C; c=c->link) {
+ n = c->node;
+ switch(c->type) {
+
+ case Ttypenil:
+ v.ctype = CTNIL;
+ a = nod(OIF, N, N);
+ a->ntest = nod(OEQ, facename, nodlit(v));
+ typecheck(&a->ntest, Erv);
+ a->nbody = list1(n->right); // if i==nil { goto l }
+ n->right = a;
+ break;
+
+ case Ttypevar:
+ case Ttypeconst:
+ n->right = typeone(n);
+ break;
+ }
+ }
+
+ /*
+ * generate list of if statements, binary search for constant sequences
+ */
+ while(c0 != C) {
+ if(c0->type != Ttypeconst) {
+ n = c0->node;
+ cas = list(cas, n->right);
+ c0=c0->link;
+ continue;
+ }
+
+ // identify run of constants
+ c1 = c = c0;
+ while(c->link!=C && c->link->type==Ttypeconst)
+ c = c->link;
+ c0 = c->link;
+ c->link = nil;
+
+ // sort by hash
+ c1 = csort(c1, typecmp);
+
+ // for debugging: linear search
+ if(0) {
+ for(c=c1; c!=C; c=c->link) {
+ n = c->node;
+ cas = list(cas, n->right);
+ }
+ continue;
+ }
+
+ // combine adjacent cases with the same hash
+ ncase = 0;
+ for(c=c1; c!=C; c=c->link) {
+ ncase++;
+ hash = list1(c->node->right);
+ while(c->link != C && c->link->hash == c->hash) {
+ hash = list(hash, c->link->node->right);
+ c->link = c->link->link;
+ }
+ c->node->right = liststmt(hash);
+ }
+
+ // binary search among cases to narrow by hash
+ cas = list(cas, typebsw(c1, ncase));
+ }
+ if(nerrors == 0) {
+ cas = list(cas, def);
+ sw->nbody = concat(cas, sw->nbody);
+ sw->list = nil;
+ walkstmtlist(sw->nbody);
+ }
+}
+
+void
+walkswitch(Node *sw)
+{
+
+ /*
+ * reorder the body into (OLIST, cases, statements)
+ * cases have OGOTO into statements.
+ * both have inserted OBREAK statements
+ */
+ walkstmtlist(sw->ninit);
+ if(sw->ntest == N) {
+ sw->ntest = nodbool(1);
+ typecheck(&sw->ntest, Erv);
+ }
+
+ if(sw->ntest->op == OTYPESW) {
+ typeswitch(sw);
+//dump("sw", sw);
+ return;
+ }
+ exprswitch(sw);
+}
+
+/*
+ * type check switch statement
+ */
+void
+typecheckswitch(Node *n)
+{
+ int top, lno;
+ Type *t;
+ NodeList *l, *ll;
+ Node *ncase, *nvar;
+ Node *def;
+
+ lno = lineno;
+ typechecklist(n->ninit, Etop);
+
+ if(n->ntest != N && n->ntest->op == OTYPESW) {
+ // type switch
+ top = Etype;
+ typecheck(&n->ntest->right, Erv);
+ t = n->ntest->right->type;
+ if(t != T && t->etype != TINTER)
+ yyerror("cannot type switch on non-interface value %+N", n->ntest->right);
+ } else {
+ // value switch
+ top = Erv;
+ if(n->ntest) {
+ typecheck(&n->ntest, Erv);
+ defaultlit(&n->ntest, T);
+ t = n->ntest->type;
+ } else
+ t = types[TBOOL];
+ }
+ n->type = t;
+
+ def = N;
+ for(l=n->list; l; l=l->next) {
+ ncase = l->n;
+ setlineno(n);
+ if(ncase->list == nil) {
+ // default
+ if(def != N)
+ yyerror("multiple defaults in switch (first at %L)", def->lineno);
+ else
+ def = ncase;
+ } else {
+ for(ll=ncase->list; ll; ll=ll->next) {
+ setlineno(ll->n);
+ typecheck(&ll->n, Erv | Etype);
+ if(ll->n->type == T || t == T)
+ continue;
+ switch(top) {
+ case Erv: // expression switch
+ defaultlit(&ll->n, t);
+ if(ll->n->op == OTYPE)
+ yyerror("type %T is not an expression", ll->n->type);
+ else if(ll->n->type != T && !eqtype(ll->n->type, t))
+ yyerror("case %+N in %T switch", ll->n, t);
+ break;
+ case Etype: // type switch
+ if(ll->n->op == OLITERAL && istype(ll->n->type, TNIL))
+ ;
+ else if(ll->n->op != OTYPE && ll->n->type != T) {
+ yyerror("%#N is not a type", ll->n);
+ // reset to original type
+ ll->n = n->ntest->right;
+ }
+ break;
+ }
+ }
+ }
+ if(top == Etype && n->type != T) {
+ ll = ncase->list;
+ nvar = ncase->nname;
+ if(nvar != N) {
+ if(ll && ll->next == nil && ll->n->type != T && !istype(ll->n->type, TNIL)) {
+ // single entry type switch
+ nvar->ntype = typenod(ll->n->type);
+ } else {
+ // multiple entry type switch or default
+ nvar->ntype = typenod(n->type);
+ }
+ }
+ }
+ typechecklist(ncase->nbody, Etop);
+ }
+
+ lineno = lno;
+}
diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c
new file mode 100644
index 000000000..78cdb5bf2
--- /dev/null
+++ b/src/cmd/gc/typecheck.c
@@ -0,0 +1,2827 @@
+// Copyright 2009 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.
+
+/*
+ * type check the whole tree of an expression.
+ * calculates expression types.
+ * evaluates compile time constants.
+ * marks variables that escape the local frame.
+ * rewrites n->op to be more specific in some cases.
+ */
+
+#include "go.h"
+
+static void implicitstar(Node**);
+static int onearg(Node*, char*, ...);
+static int twoarg(Node*);
+static int lookdot(Node*, Type*, int);
+static int looktypedot(Node*, Type*, int);
+static void typecheckaste(int, Node*, int, Type*, NodeList*, char*);
+static Type* lookdot1(Sym *s, Type *t, Type *f, int);
+static int nokeys(NodeList*);
+static void typecheckcomplit(Node**);
+static void addrescapes(Node*);
+static void typecheckas2(Node*);
+static void typecheckas(Node*);
+static void typecheckfunc(Node*);
+static void checklvalue(Node*, char*);
+static void checkassign(Node*);
+static void checkassignlist(NodeList*);
+static void stringtoarraylit(Node**);
+static Node* resolve(Node*);
+static Type* getforwtype(Node*);
+
+static NodeList* typecheckdefstack;
+
+/*
+ * resolve ONONAME to definition, if any.
+ */
+Node*
+resolve(Node *n)
+{
+ Node *r;
+
+ if(n != N && n->op == ONONAME && (r = n->sym->def) != N) {
+ if(r->op != OIOTA)
+ n = r;
+ else if(n->iota >= 0)
+ n = nodintconst(n->iota);
+ }
+ return n;
+}
+
+void
+typechecklist(NodeList *l, int top)
+{
+ for(; l; l=l->next)
+ typecheck(&l->n, top);
+}
+
+static char* _typekind[] = {
+ [TINT] = "int",
+ [TUINT] = "uint",
+ [TINT8] = "int8",
+ [TUINT8] = "uint8",
+ [TINT16] = "int16",
+ [TUINT16] = "uint16",
+ [TINT32] = "int32",
+ [TUINT32] = "uint32",
+ [TINT64] = "int64",
+ [TUINT64] = "uint64",
+ [TUINTPTR] = "uintptr",
+ [TCOMPLEX64] = "complex64",
+ [TCOMPLEX128] = "complex128",
+ [TFLOAT32] = "float32",
+ [TFLOAT64] = "float64",
+ [TBOOL] = "bool",
+ [TSTRING] = "string",
+ [TPTR32] = "pointer",
+ [TPTR64] = "pointer",
+ [TSTRUCT] = "struct",
+ [TINTER] = "interface",
+ [TCHAN] = "chan",
+ [TMAP] = "map",
+ [TARRAY] = "array",
+ [TFUNC] = "func",
+ [TNIL] = "nil",
+ [TIDEAL] = "ideal number",
+};
+
+static char*
+typekind(int et)
+{
+ static char buf[50];
+ char *s;
+
+ if(0 <= et && et < nelem(_typekind) && (s=_typekind[et]) != nil)
+ return s;
+ snprint(buf, sizeof buf, "etype=%d", et);
+ return buf;
+}
+
+/*
+ * type check node *np.
+ * replaces *np with a new pointer in some cases.
+ * returns the final value of *np as a convenience.
+ */
+Node*
+typecheck(Node **np, int top)
+{
+ int et, aop, op, ptr;
+ Node *n, *l, *r;
+ NodeList *args;
+ int lno, ok, ntop;
+ Type *t, *tp, *ft, *missing, *have;
+ Sym *sym;
+ Val v;
+ char *why;
+
+ // cannot type check until all the source has been parsed
+ if(!typecheckok)
+ fatal("early typecheck");
+
+ n = *np;
+ if(n == N)
+ return N;
+
+ lno = setlineno(n);
+
+ // Skip over parens.
+ while(n->op == OPAREN)
+ n = n->left;
+
+ // Resolve definition of name and value of iota lazily.
+ n = resolve(n);
+
+ *np = n;
+
+ // Skip typecheck if already done.
+ // But re-typecheck ONAME/OTYPE/OLITERAL/OPACK node in case context has changed.
+ if(n->typecheck == 1) {
+ switch(n->op) {
+ case ONAME:
+ case OTYPE:
+ case OLITERAL:
+ case OPACK:
+ break;
+ default:
+ lineno = lno;
+ return n;
+ }
+ }
+
+ if(n->typecheck == 2) {
+ yyerror("typechecking loop");
+ lineno = lno;
+ return n;
+ }
+ n->typecheck = 2;
+
+ if(n->sym) {
+ if(n->op == ONAME && n->etype != 0 && !(top & Ecall)) {
+ yyerror("use of builtin %S not in function call", n->sym);
+ goto error;
+ }
+
+ // a dance to handle forward-declared recursive pointer types.
+ if(n->op == OTYPE && (ft = getforwtype(n->ntype)) != T)
+ defertypecopy(n, ft);
+
+ typecheckdef(n);
+ n->realtype = n->type;
+ if(n->op == ONONAME)
+ goto error;
+ }
+ *np = n;
+
+reswitch:
+ ok = 0;
+ switch(n->op) {
+ default:
+ // until typecheck is complete, do nothing.
+ dump("typecheck", n);
+ fatal("typecheck %O", n->op);
+
+ /*
+ * names
+ */
+ case OLITERAL:
+ ok |= Erv;
+ if(n->type == T && n->val.ctype == CTSTR)
+ n->type = idealstring;
+ goto ret;
+
+ case ONONAME:
+ ok |= Erv;
+ goto ret;
+
+ case ONAME:
+ if(n->etype != 0) {
+ ok |= Ecall;
+ goto ret;
+ }
+ if(!(top & Easgn)) {
+ // not a write to the variable
+ if(isblank(n)) {
+ yyerror("cannot use _ as value");
+ goto error;
+ }
+ n->used = 1;
+ }
+ ok |= Erv;
+ goto ret;
+
+ case OPACK:
+ yyerror("use of package %S not in selector", n->sym);
+ goto error;
+
+ case ODDD:
+ break;
+
+ /*
+ * types (OIND is with exprs)
+ */
+ case OTYPE:
+ ok |= Etype;
+ if(n->type == T)
+ goto error;
+ break;
+
+ case OTPAREN:
+ ok |= Etype;
+ l = typecheck(&n->left, Etype);
+ if(l->type == T)
+ goto error;
+ n->op = OTYPE;
+ n->type = l->type;
+ n->left = N;
+ break;
+
+ case OTARRAY:
+ ok |= Etype;
+ t = typ(TARRAY);
+ l = n->left;
+ r = n->right;
+ if(l == nil) {
+ t->bound = -1; // slice
+ } else if(l->op == ODDD) {
+ t->bound = -100; // to be filled in
+ if(!(top&Ecomplit))
+ yyerror("use of [...] array outside of array literal");
+ } else {
+ l = typecheck(&n->left, Erv);
+ switch(consttype(l)) {
+ case CTINT:
+ v = l->val;
+ break;
+ case CTFLT:
+ v = toint(l->val);
+ break;
+ default:
+ yyerror("invalid array bound %#N", l);
+ goto error;
+ }
+ t->bound = mpgetfix(v.u.xval);
+ if(t->bound < 0) {
+ yyerror("array bound must be non-negative");
+ goto error;
+ } else
+ overflow(v, types[TINT]);
+ }
+ typecheck(&r, Etype);
+ if(r->type == T)
+ goto error;
+ t->type = r->type;
+ n->op = OTYPE;
+ n->type = t;
+ n->left = N;
+ n->right = N;
+ if(t->bound != -100)
+ checkwidth(t);
+ break;
+
+ case OTMAP:
+ ok |= Etype;
+ l = typecheck(&n->left, Etype);
+ r = typecheck(&n->right, Etype);
+ if(l->type == T || r->type == T)
+ goto error;
+ n->op = OTYPE;
+ n->type = maptype(l->type, r->type);
+ n->left = N;
+ n->right = N;
+ break;
+
+ case OTCHAN:
+ ok |= Etype;
+ l = typecheck(&n->left, Etype);
+ if(l->type == T)
+ goto error;
+ t = typ(TCHAN);
+ t->type = l->type;
+ t->chan = n->etype;
+ n->op = OTYPE;
+ n->type = t;
+ n->left = N;
+ n->etype = 0;
+ break;
+
+ case OTSTRUCT:
+ ok |= Etype;
+ n->op = OTYPE;
+ n->type = dostruct(n->list, TSTRUCT);
+ if(n->type == T)
+ goto error;
+ n->list = nil;
+ break;
+
+ case OTINTER:
+ ok |= Etype;
+ n->op = OTYPE;
+ n->type = dostruct(n->list, TINTER);
+ if(n->type == T)
+ goto error;
+ break;
+
+ case OTFUNC:
+ ok |= Etype;
+ n->op = OTYPE;
+ n->type = functype(n->left, n->list, n->rlist);
+ if(n->type == T)
+ goto error;
+ break;
+
+ /*
+ * type or expr
+ */
+ case OIND:
+ ntop = Erv | Etype;
+ if(!(top & Eaddr))
+ ntop |= Eindir;
+ l = typecheck(&n->left, ntop);
+ if((t = l->type) == T)
+ goto error;
+ if(l->op == OTYPE) {
+ ok |= Etype;
+ n->op = OTYPE;
+ n->type = ptrto(l->type);
+ n->left = N;
+ goto ret;
+ }
+ if(!isptr[t->etype]) {
+ yyerror("invalid indirect of %+N", n->left);
+ goto error;
+ }
+ ok |= Erv;
+ n->type = t->type;
+ goto ret;
+
+ /*
+ * arithmetic exprs
+ */
+ case OASOP:
+ ok |= Etop;
+ l = typecheck(&n->left, Erv);
+ checkassign(n->left);
+ r = typecheck(&n->right, Erv);
+ if(l->type == T || r->type == T)
+ goto error;
+ op = n->etype;
+ goto arith;
+
+ case OADD:
+ case OAND:
+ case OANDAND:
+ case OANDNOT:
+ case ODIV:
+ case OEQ:
+ case OGE:
+ case OGT:
+ case OLE:
+ case OLT:
+ case OLSH:
+ case ORSH:
+ case OMOD:
+ case OMUL:
+ case ONE:
+ case OOR:
+ case OOROR:
+ case OSUB:
+ case OXOR:
+ ok |= Erv;
+ l = typecheck(&n->left, Erv | (top & Eiota));
+ r = typecheck(&n->right, Erv | (top & Eiota));
+ if(l->type == T || r->type == T)
+ goto error;
+ op = n->op;
+ arith:
+ if(op == OLSH || op == ORSH)
+ goto shift;
+ // ideal mixed with non-ideal
+ defaultlit2(&l, &r, 0);
+ n->left = l;
+ n->right = r;
+ if(l->type == T || r->type == T)
+ goto error;
+ t = l->type;
+ if(t->etype == TIDEAL)
+ t = r->type;
+ et = t->etype;
+ if(et == TIDEAL)
+ et = TINT;
+ if(iscmp[n->op] && t->etype != TIDEAL && !eqtype(l->type, r->type)) {
+ // comparison is okay as long as one side is
+ // assignable to the other. convert so they have
+ // the same type. (the only conversion that isn't
+ // a no-op is concrete == interface.)
+ if(r->type->etype != TBLANK && (aop = assignop(l->type, r->type, nil)) != 0) {
+ l = nod(aop, l, N);
+ l->type = r->type;
+ l->typecheck = 1;
+ n->left = l;
+ t = l->type;
+ } else if(l->type->etype != TBLANK && (aop = assignop(r->type, l->type, nil)) != 0) {
+ r = nod(aop, r, N);
+ r->type = l->type;
+ r->typecheck = 1;
+ n->right = r;
+ t = r->type;
+ }
+ et = t->etype;
+ }
+ if(t->etype != TIDEAL && !eqtype(l->type, r->type)) {
+ defaultlit2(&l, &r, 1);
+ yyerror("invalid operation: %#N (mismatched types %T and %T)", n, l->type, r->type);
+ goto error;
+ }
+ if(!okfor[op][et]) {
+ notokfor:
+ yyerror("invalid operation: %#N (operator %#O not defined on %s)", n, op, typekind(et));
+ goto error;
+ }
+ // okfor allows any array == array;
+ // restrict to slice == nil and nil == slice.
+ if(l->type->etype == TARRAY && !isslice(l->type))
+ goto notokfor;
+ if(r->type->etype == TARRAY && !isslice(r->type))
+ goto notokfor;
+ if(isslice(l->type) && !isnil(l) && !isnil(r)) {
+ yyerror("invalid operation: %#N (slice can only be compared to nil)", n);
+ goto error;
+ }
+ t = l->type;
+ if(iscmp[n->op]) {
+ evconst(n);
+ t = types[TBOOL];
+ if(n->op != OLITERAL) {
+ defaultlit2(&l, &r, 1);
+ n->left = l;
+ n->right = r;
+ }
+ }
+ if(et == TSTRING) {
+ if(iscmp[n->op]) {
+ n->etype = n->op;
+ n->op = OCMPSTR;
+ } else if(n->op == OADD)
+ n->op = OADDSTR;
+ }
+ if(et == TINTER) {
+ if(l->op == OLITERAL && l->val.ctype == CTNIL) {
+ // swap for back end
+ n->left = r;
+ n->right = l;
+ } else if(r->op == OLITERAL && r->val.ctype == CTNIL) {
+ // leave alone for back end
+ } else {
+ n->etype = n->op;
+ n->op = OCMPIFACE;
+ }
+ }
+ n->type = t;
+ goto ret;
+
+ shift:
+ defaultlit(&r, types[TUINT]);
+ n->right = r;
+ t = r->type;
+ if(!isint[t->etype] || issigned[t->etype]) {
+ yyerror("invalid operation: %#N (shift count type %T, must be unsigned integer)", n, r->type);
+ goto error;
+ }
+ t = l->type;
+ if(t != T && t->etype != TIDEAL && !isint[t->etype]) {
+ yyerror("invalid operation: %#N (shift of type %T)", n, t);
+ goto error;
+ }
+ // no defaultlit for left
+ // the outer context gives the type
+ n->type = l->type;
+ goto ret;
+
+ case OCOM:
+ case OMINUS:
+ case ONOT:
+ case OPLUS:
+ ok |= Erv;
+ l = typecheck(&n->left, Erv | (top & Eiota));
+ if((t = l->type) == T)
+ goto error;
+ if(!okfor[n->op][t->etype]) {
+ yyerror("invalid operation: %#O %T", n->op, t);
+ goto error;
+ }
+ n->type = t;
+ goto ret;
+
+ /*
+ * exprs
+ */
+ case OADDR:
+ ok |= Erv;
+ typecheck(&n->left, Erv | Eaddr);
+ if(n->left->type == T)
+ goto error;
+ switch(n->left->op) {
+ case OMAPLIT:
+ case OSTRUCTLIT:
+ case OARRAYLIT:
+ break;
+ default:
+ checklvalue(n->left, "take the address of");
+ }
+ defaultlit(&n->left, T);
+ l = n->left;
+ if((t = l->type) == T)
+ goto error;
+ if(!(top & Eindir) && !n->etype)
+ addrescapes(n->left);
+ n->type = ptrto(t);
+ goto ret;
+
+ case OCOMPLIT:
+ ok |= Erv;
+ typecheckcomplit(&n);
+ if(n->type == T)
+ goto error;
+ goto ret;
+
+ case OXDOT:
+ n = adddot(n);
+ n->op = ODOT;
+ // fall through
+ case ODOT:
+ typecheck(&n->left, Erv|Etype);
+ defaultlit(&n->left, T);
+ l = n->left;
+ if((t = l->type) == T)
+ goto error;
+ if(n->right->op != ONAME) {
+ yyerror("rhs of . must be a name"); // impossible
+ goto error;
+ }
+ sym = n->right->sym;
+ if(l->op == OTYPE) {
+ if(!looktypedot(n, t, 0)) {
+ if(looktypedot(n, t, 1))
+ yyerror("%#N undefined (cannot refer to unexported method %S)", n, n->right->sym);
+ else
+ yyerror("%#N undefined (type %T has no method %S)", n, t, n->right->sym);
+ goto error;
+ }
+ if(n->type->etype != TFUNC || n->type->thistuple != 1) {
+ yyerror("type %T has no method %hS", n->left->type, sym);
+ n->type = T;
+ goto error;
+ }
+ n->op = ONAME;
+ n->sym = methodsym(sym, l->type, 0);
+ n->type = methodfunc(n->type, l->type);
+ n->xoffset = 0;
+ n->class = PFUNC;
+ ok = Erv;
+ goto ret;
+ }
+ tp = t;
+ if(isptr[t->etype] && t->type->etype != TINTER) {
+ t = t->type;
+ if(t == T)
+ goto error;
+ n->op = ODOTPTR;
+ checkwidth(t);
+ }
+ if(!lookdot(n, t, 0)) {
+ if(lookdot(n, t, 1))
+ yyerror("%#N undefined (cannot refer to unexported field or method %S)", n, n->right->sym);
+ else
+ yyerror("%#N undefined (type %T has no field or method %S)", n, tp, n->right->sym);
+ goto error;
+ }
+ switch(n->op) {
+ case ODOTINTER:
+ case ODOTMETH:
+ ok |= Ecall;
+ break;
+ default:
+ ok |= Erv;
+ break;
+ }
+ goto ret;
+
+ case ODOTTYPE:
+ ok |= Erv;
+ typecheck(&n->left, Erv);
+ defaultlit(&n->left, T);
+ l = n->left;
+ if((t = l->type) == T)
+ goto error;
+ if(!isinter(t)) {
+ yyerror("invalid type assertion: %#N (non-interface type %T on left)", n, t);
+ goto error;
+ }
+ if(n->right != N) {
+ typecheck(&n->right, Etype);
+ n->type = n->right->type;
+ n->right = N;
+ if(n->type == T)
+ goto error;
+ }
+ if(n->type != T && n->type->etype != TINTER)
+ if(!implements(n->type, t, &missing, &have, &ptr)) {
+ if(have)
+ yyerror("impossible type assertion: %+N cannot have dynamic type %T"
+ " (wrong type for %S method)\n\thave %S%hhT\n\twant %S%hhT",
+ l, n->type, missing->sym, have->sym, have->type,
+ missing->sym, missing->type);
+ else
+ yyerror("impossible type assertion: %+N cannot have dynamic type %T"
+ " (missing %S method)", l, n->type, missing->sym);
+ goto error;
+ }
+ goto ret;
+
+ case OINDEX:
+ ok |= Erv;
+ typecheck(&n->left, Erv);
+ defaultlit(&n->left, T);
+ implicitstar(&n->left);
+ l = n->left;
+ typecheck(&n->right, Erv);
+ r = n->right;
+ if((t = l->type) == T || r->type == T)
+ goto error;
+ switch(t->etype) {
+ default:
+ yyerror("invalid operation: %#N (index of type %T)", n, t);
+ goto error;
+
+ case TARRAY:
+ defaultlit(&n->right, T);
+ if(n->right->type != T && !isint[n->right->type->etype])
+ yyerror("non-integer array index %#N", n->right);
+ n->type = t->type;
+ break;
+
+ case TMAP:
+ n->etype = 0;
+ defaultlit(&n->right, t->down);
+ if(n->right->type != T)
+ n->right = assignconv(n->right, t->down, "map index");
+ n->type = t->type;
+ n->op = OINDEXMAP;
+ break;
+
+ case TSTRING:
+ defaultlit(&n->right, types[TUINT]);
+ if(n->right->type != T && !isint[n->right->type->etype])
+ yyerror("non-integer string index %#N", n->right);
+ n->type = types[TUINT8];
+ break;
+ }
+ goto ret;
+
+ case ORECV:
+ ok |= Etop | Erv;
+ typecheck(&n->left, Erv);
+ defaultlit(&n->left, T);
+ l = n->left;
+ if((t = l->type) == T)
+ goto error;
+ if(t->etype != TCHAN) {
+ yyerror("invalid operation: %#N (receive from non-chan type %T)", n, t);
+ goto error;
+ }
+ if(!(t->chan & Crecv)) {
+ yyerror("invalid operation: %#N (receive from send-only type %T)", n, t);
+ goto error;
+ }
+ n->type = t->type;
+ goto ret;
+
+ case OSEND:
+ if(top & Erv) {
+ yyerror("send statement %#N used as value; use select for non-blocking send", n);
+ goto error;
+ }
+ ok |= Etop | Erv;
+ l = typecheck(&n->left, Erv);
+ typecheck(&n->right, Erv);
+ defaultlit(&n->left, T);
+ l = n->left;
+ if((t = l->type) == T)
+ goto error;
+ if(t->etype != TCHAN) {
+ yyerror("invalid operation: %#N (send to non-chan type %T)", n, t);
+ goto error;
+ }
+ if(!(t->chan & Csend)) {
+ yyerror("invalid operation: %#N (send to receive-only type %T)", n, t);
+ goto error;
+ }
+ defaultlit(&n->right, t->type);
+ r = n->right;
+ if((t = r->type) == T)
+ goto error;
+ r = assignconv(r, l->type->type, "send");
+ // TODO: more aggressive
+ n->etype = 0;
+ n->type = T;
+ goto ret;
+
+ case OSLICE:
+ ok |= Erv;
+ typecheck(&n->left, top);
+ typecheck(&n->right->left, Erv);
+ typecheck(&n->right->right, Erv);
+ defaultlit(&n->left, T);
+ defaultlit(&n->right->left, T);
+ defaultlit(&n->right->right, T);
+ if(isfixedarray(n->left->type)) {
+ n->left = nod(OADDR, n->left, N);
+ typecheck(&n->left, top);
+ }
+ if(n->right->left != N) {
+ if((t = n->right->left->type) == T)
+ goto error;
+ if(!isint[t->etype]) {
+ yyerror("invalid slice index %#N (type %T)", n->right->left, t);
+ goto error;
+ }
+ }
+ if(n->right->right != N) {
+ if((t = n->right->right->type) == T)
+ goto error;
+ if(!isint[t->etype]) {
+ yyerror("invalid slice index %#N (type %T)", n->right->right, t);
+ goto error;
+ }
+ }
+ l = n->left;
+ if((t = l->type) == T)
+ goto error;
+ if(istype(t, TSTRING)) {
+ n->type = t;
+ n->op = OSLICESTR;
+ goto ret;
+ }
+ if(isptr[t->etype] && isfixedarray(t->type)) {
+ n->type = typ(TARRAY);
+ n->type->type = t->type->type;
+ n->type->bound = -1;
+ dowidth(n->type);
+ n->op = OSLICEARR;
+ goto ret;
+ }
+ if(isslice(t)) {
+ n->type = t;
+ goto ret;
+ }
+ yyerror("cannot slice %#N (type %T)", l, t);
+ goto error;
+
+ /*
+ * call and call like
+ */
+ case OCALL:
+ l = n->left;
+ if(l->op == ONAME && (r = unsafenmagic(n)) != N) {
+ if(n->isddd)
+ yyerror("invalid use of ... with builtin %#N", l);
+ n = r;
+ goto reswitch;
+ }
+ typecheck(&n->left, Erv | Etype | Ecall |(top&Eproc));
+ l = n->left;
+ if(l->op == ONAME && l->etype != 0) {
+ if(n->isddd && l->etype != OAPPEND)
+ yyerror("invalid use of ... with builtin %#N", l);
+ // builtin: OLEN, OCAP, etc.
+ n->op = l->etype;
+ n->left = n->right;
+ n->right = N;
+ goto reswitch;
+ }
+ defaultlit(&n->left, T);
+ l = n->left;
+ if(l->op == OTYPE) {
+ if(n->isddd || l->type->bound == -100)
+ yyerror("invalid use of ... in type conversion", l);
+ // pick off before type-checking arguments
+ ok |= Erv;
+ // turn CALL(type, arg) into CONV(arg) w/ type
+ n->left = N;
+ n->op = OCONV;
+ n->type = l->type;
+ if(onearg(n, "conversion to %T", l->type) < 0)
+ goto error;
+ goto doconv;
+ }
+
+ if(count(n->list) == 1)
+ typecheck(&n->list->n, Erv | Efnstruct);
+ else
+ typechecklist(n->list, Erv);
+ if((t = l->type) == T)
+ goto error;
+ checkwidth(t);
+
+ switch(l->op) {
+ case ODOTINTER:
+ n->op = OCALLINTER;
+ break;
+
+ case ODOTMETH:
+ n->op = OCALLMETH;
+ // typecheckaste was used here but there wasn't enough
+ // information further down the call chain to know if we
+ // were testing a method receiver for unexported fields.
+ // It isn't necessary, so just do a sanity check.
+ tp = getthisx(t)->type->type;
+ if(l->left == N || !eqtype(l->left->type, tp))
+ fatal("method receiver");
+ break;
+
+ default:
+ n->op = OCALLFUNC;
+ if(t->etype != TFUNC) {
+ yyerror("cannot call non-function %#N (type %T)", l, t);
+ goto error;
+ }
+ break;
+ }
+ typecheckaste(OCALL, n->left, n->isddd, getinargx(t), n->list, "function argument");
+ ok |= Etop;
+ if(t->outtuple == 0)
+ goto ret;
+ ok |= Erv;
+ if(t->outtuple == 1) {
+ t = getoutargx(l->type)->type;
+ if(t == T)
+ goto error;
+ if(t->etype == TFIELD)
+ t = t->type;
+ n->type = t;
+ goto ret;
+ }
+ // multiple return
+ if(!(top & (Efnstruct | Etop))) {
+ yyerror("multiple-value %#N() in single-value context", l);
+ goto ret;
+ }
+ n->type = getoutargx(l->type);
+ goto ret;
+
+ case OCAP:
+ case OLEN:
+ case OREAL:
+ case OIMAG:
+ ok |= Erv;
+ if(onearg(n, "%#O", n->op) < 0)
+ goto error;
+ typecheck(&n->left, Erv);
+ defaultlit(&n->left, T);
+ implicitstar(&n->left);
+ l = n->left;
+ t = l->type;
+ if(t == T)
+ goto error;
+ switch(n->op) {
+ case OCAP:
+ if(!okforcap[t->etype])
+ goto badcall1;
+ break;
+ case OLEN:
+ if(!okforlen[t->etype])
+ goto badcall1;
+ break;
+ case OREAL:
+ case OIMAG:
+ if(!iscomplex[t->etype])
+ goto badcall1;
+ if(isconst(l, CTCPLX)){
+ if(n->op == OREAL)
+ n = nodfltconst(&l->val.u.cval->real);
+ else
+ n = nodfltconst(&l->val.u.cval->imag);
+ }
+ n->type = types[cplxsubtype(t->etype)];
+ goto ret;
+ }
+ // might be constant
+ switch(t->etype) {
+ case TSTRING:
+ if(isconst(l, CTSTR)) {
+ r = nod(OXXX, N, N);
+ nodconst(r, types[TINT], l->val.u.sval->len);
+ r->orig = n;
+ n = r;
+ }
+ break;
+ case TARRAY:
+ if(t->bound >= 0 && l->op == ONAME) {
+ r = nod(OXXX, N, N);
+ nodconst(r, types[TINT], t->bound);
+ r->orig = n;
+ n = r;
+ }
+ break;
+ }
+ n->type = types[TINT];
+ goto ret;
+
+ case OCOMPLEX:
+ ok |= Erv;
+ if(twoarg(n) < 0)
+ goto error;
+ l = typecheck(&n->left, Erv | (top & Eiota));
+ r = typecheck(&n->right, Erv | (top & Eiota));
+ if(l->type == T || r->type == T)
+ goto error;
+ defaultlit2(&l, &r, 0);
+ n->left = l;
+ n->right = r;
+ if(l->type->etype != r->type->etype) {
+ badcmplx:
+ yyerror("invalid operation: %#N (complex of types %T, %T)", n, l->type, r->type);
+ goto error;
+ }
+ switch(l->type->etype) {
+ default:
+ goto badcmplx;
+ case TIDEAL:
+ t = types[TIDEAL];
+ break;
+ case TFLOAT32:
+ t = types[TCOMPLEX64];
+ break;
+ case TFLOAT64:
+ t = types[TCOMPLEX128];
+ break;
+ }
+ if(l->op == OLITERAL && r->op == OLITERAL) {
+ // make it a complex literal
+ n = nodcplxlit(l->val, r->val);
+ }
+ n->type = t;
+ goto ret;
+
+ case OCLOSE:
+ if(onearg(n, "%#O", n->op) < 0)
+ goto error;
+ typecheck(&n->left, Erv);
+ defaultlit(&n->left, T);
+ l = n->left;
+ if((t = l->type) == T)
+ goto error;
+ if(t->etype != TCHAN) {
+ yyerror("invalid operation: %#N (non-chan type %T)", n, t);
+ goto error;
+ }
+ ok |= Etop;
+ goto ret;
+
+ case OAPPEND:
+ ok |= Erv;
+ args = n->list;
+ if(args == nil) {
+ yyerror("missing arguments to append");
+ goto error;
+ }
+ typechecklist(args, Erv);
+ if((t = args->n->type) == T)
+ goto error;
+ n->type = t;
+ if(!isslice(t)) {
+ yyerror("first argument to append must be slice; have %lT", t);
+ goto error;
+ }
+ if(n->isddd) {
+ if(args->next == nil) {
+ yyerror("cannot use ... on first argument to append");
+ goto error;
+ }
+ if(args->next->next != nil) {
+ yyerror("too many arguments to append");
+ goto error;
+ }
+ args->next->n = assignconv(args->next->n, t->orig, "append");
+ goto ret;
+ }
+ for(args=args->next; args != nil; args=args->next) {
+ if(args->n->type == T)
+ continue;
+ args->n = assignconv(args->n, t->type, "append");
+ }
+ goto ret;
+
+ case OCOPY:
+ ok |= Etop|Erv;
+ args = n->list;
+ if(args == nil || args->next == nil) {
+ yyerror("missing arguments to copy");
+ goto error;
+ }
+ if(args->next->next != nil) {
+ yyerror("too many arguments to copy");
+ goto error;
+ }
+ n->left = args->n;
+ n->right = args->next->n;
+ n->type = types[TINT];
+ typecheck(&n->left, Erv);
+ typecheck(&n->right, Erv);
+ if(n->left->type == T || n->right->type == T)
+ goto error;
+ defaultlit(&n->left, T);
+ defaultlit(&n->right, T);
+
+ // copy([]byte, string)
+ if(isslice(n->left->type) && n->right->type->etype == TSTRING) {
+ if (n->left->type->type == types[TUINT8])
+ goto ret;
+ yyerror("arguments to copy have different element types: %lT and string", n->left->type);
+ goto error;
+ }
+
+ if(!isslice(n->left->type) || !isslice(n->right->type)) {
+ if(!isslice(n->left->type) && !isslice(n->right->type))
+ yyerror("arguments to copy must be slices; have %lT, %lT", n->left->type, n->right->type);
+ else if(!isslice(n->left->type))
+ yyerror("first argument to copy should be slice; have %lT", n->left->type);
+ else
+ yyerror("second argument to copy should be slice or string; have %lT", n->right->type);
+ goto error;
+ }
+ if(!eqtype(n->left->type->type, n->right->type->type)) {
+ yyerror("arguments to copy have different element types: %lT and %lT", n->left->type, n->right->type);
+ goto error;
+ }
+ goto ret;
+
+ case OCONV:
+ doconv:
+ ok |= Erv;
+ typecheck(&n->left, Erv | (top & (Eindir | Eiota)));
+ convlit1(&n->left, n->type, 1);
+ if((t = n->left->type) == T || n->type == T)
+ goto error;
+ if((n->op = convertop(t, n->type, &why)) == 0) {
+ yyerror("cannot convert %+N to type %T%s", n->left, n->type, why);
+ op = OCONV;
+ }
+ switch(n->op) {
+ case OCONVNOP:
+ if(n->left->op == OLITERAL) {
+ n->op = OLITERAL;
+ n->val = n->left->val;
+ }
+ break;
+ case OSTRARRAYBYTE:
+ case OSTRARRAYRUNE:
+ if(n->left->op == OLITERAL)
+ stringtoarraylit(&n);
+ break;
+ }
+ goto ret;
+
+ case OMAKE:
+ ok |= Erv;
+ args = n->list;
+ if(args == nil) {
+ yyerror("missing argument to make");
+ goto error;
+ }
+ l = args->n;
+ args = args->next;
+ typecheck(&l, Etype);
+ if((t = l->type) == T)
+ goto error;
+
+ switch(t->etype) {
+ default:
+ badmake:
+ yyerror("cannot make type %T", t);
+ goto error;
+
+ case TARRAY:
+ if(!isslice(t))
+ goto badmake;
+ if(args == nil) {
+ yyerror("missing len argument to make(%T)", t);
+ goto error;
+ }
+ l = args->n;
+ args = args->next;
+ typecheck(&l, Erv);
+ defaultlit(&l, types[TINT]);
+ r = N;
+ if(args != nil) {
+ r = args->n;
+ args = args->next;
+ typecheck(&r, Erv);
+ defaultlit(&r, types[TINT]);
+ }
+ if(l->type == T || (r && r->type == T))
+ goto error;
+ if(!isint[l->type->etype]) {
+ yyerror("non-integer len argument to make(%T)", t);
+ goto error;
+ }
+ if(r && !isint[r->type->etype]) {
+ yyerror("non-integer cap argument to make(%T)", t);
+ goto error;
+ }
+ n->left = l;
+ n->right = r;
+ n->op = OMAKESLICE;
+ break;
+
+ case TMAP:
+ if(args != nil) {
+ l = args->n;
+ args = args->next;
+ typecheck(&l, Erv);
+ defaultlit(&l, types[TINT]);
+ if(l->type == T)
+ goto error;
+ if(!isint[l->type->etype]) {
+ yyerror("non-integer size argument to make(%T)", t);
+ goto error;
+ }
+ n->left = l;
+ } else
+ n->left = nodintconst(0);
+ n->op = OMAKEMAP;
+ break;
+
+ case TCHAN:
+ l = N;
+ if(args != nil) {
+ l = args->n;
+ args = args->next;
+ typecheck(&l, Erv);
+ defaultlit(&l, types[TINT]);
+ if(l->type == T)
+ goto error;
+ if(!isint[l->type->etype]) {
+ yyerror("non-integer buffer argument to make(%T)", t);
+ goto error;
+ }
+ n->left = l;
+ } else
+ n->left = nodintconst(0);
+ n->op = OMAKECHAN;
+ break;
+ }
+ if(args != nil) {
+ yyerror("too many arguments to make(%T)", t);
+ n->op = OMAKE;
+ goto error;
+ }
+ n->type = t;
+ goto ret;
+
+ case ONEW:
+ ok |= Erv;
+ args = n->list;
+ if(args == nil) {
+ yyerror("missing argument to new");
+ goto error;
+ }
+ l = args->n;
+ typecheck(&l, Etype);
+ if((t = l->type) == T)
+ goto error;
+ if(args->next != nil) {
+ yyerror("too many arguments to new(%T)", t);
+ goto error;
+ }
+ n->left = l;
+ n->type = ptrto(t);
+ goto ret;
+
+ case OPRINT:
+ case OPRINTN:
+ ok |= Etop;
+ typechecklist(n->list, Erv | Eindir); // Eindir: address does not escape
+ goto ret;
+
+ case OPANIC:
+ ok |= Etop;
+ if(onearg(n, "panic") < 0)
+ goto error;
+ typecheck(&n->left, Erv);
+ defaultlit(&n->left, types[TINTER]);
+ if(n->left->type == T)
+ goto error;
+ goto ret;
+
+ case ORECOVER:
+ ok |= Erv|Etop;
+ if(n->list != nil) {
+ yyerror("too many arguments to recover");
+ goto error;
+ }
+ n->type = types[TINTER];
+ goto ret;
+
+ case OCLOSURE:
+ ok |= Erv;
+ typecheckclosure(n, top);
+ if(n->type == T)
+ goto error;
+ goto ret;
+
+ /*
+ * statements
+ */
+ case OAS:
+ ok |= Etop;
+ typecheckas(n);
+ goto ret;
+
+ case OAS2:
+ ok |= Etop;
+ typecheckas2(n);
+ goto ret;
+
+ case OBREAK:
+ case OCONTINUE:
+ case ODCL:
+ case OEMPTY:
+ case OGOTO:
+ case OLABEL:
+ case OXFALL:
+ ok |= Etop;
+ goto ret;
+
+ case ODEFER:
+ ok |= Etop;
+ typecheck(&n->left, Etop);
+ goto ret;
+
+ case OPROC:
+ ok |= Etop;
+ typecheck(&n->left, Etop|Eproc);
+ goto ret;
+
+ case OFOR:
+ ok |= Etop;
+ typechecklist(n->ninit, Etop);
+ typecheck(&n->ntest, Erv);
+ if(n->ntest != N && (t = n->ntest->type) != T && t->etype != TBOOL)
+ yyerror("non-bool %+N used as for condition", n->ntest);
+ typecheck(&n->nincr, Etop);
+ typechecklist(n->nbody, Etop);
+ goto ret;
+
+ case OIF:
+ ok |= Etop;
+ typechecklist(n->ninit, Etop);
+ typecheck(&n->ntest, Erv);
+ if(n->ntest != N && (t = n->ntest->type) != T && t->etype != TBOOL)
+ yyerror("non-bool %+N used as if condition", n->ntest);
+ typechecklist(n->nbody, Etop);
+ typechecklist(n->nelse, Etop);
+ goto ret;
+
+ case ORETURN:
+ ok |= Etop;
+ typechecklist(n->list, Erv | Efnstruct);
+ if(curfn == N) {
+ yyerror("return outside function");
+ goto error;
+ }
+ if(curfn->type->outnamed && n->list == nil)
+ goto ret;
+ typecheckaste(ORETURN, nil, 0, getoutargx(curfn->type), n->list, "return argument");
+ goto ret;
+
+ case OSELECT:
+ ok |= Etop;
+ typecheckselect(n);
+ goto ret;
+
+ case OSWITCH:
+ ok |= Etop;
+ typecheckswitch(n);
+ goto ret;
+
+ case ORANGE:
+ ok |= Etop;
+ typecheckrange(n);
+ goto ret;
+
+ case OTYPESW:
+ yyerror("use of .(type) outside type switch");
+ goto error;
+
+ case OXCASE:
+ ok |= Etop;
+ typechecklist(n->list, Erv);
+ typechecklist(n->nbody, Etop);
+ goto ret;
+
+ case ODCLFUNC:
+ ok |= Etop;
+ typecheckfunc(n);
+ goto ret;
+
+ case ODCLCONST:
+ ok |= Etop;
+ typecheck(&n->left, Erv);
+ goto ret;
+
+ case ODCLTYPE:
+ ok |= Etop;
+ typecheck(&n->left, Etype);
+ if(!incannedimport)
+ checkwidth(n->left->type);
+ goto ret;
+ }
+
+ret:
+ t = n->type;
+ if(t && !t->funarg && n->op != OTYPE) {
+ switch(t->etype) {
+ case TFUNC: // might have TANY; wait until its called
+ case TANY:
+ case TFORW:
+ case TIDEAL:
+ case TNIL:
+ case TBLANK:
+ break;
+ default:
+ checkwidth(t);
+ }
+ }
+
+ // TODO(rsc): should not need to check importpkg,
+ // but reflect mentions unsafe.Pointer.
+ if(safemode && !incannedimport && !importpkg && t && t->etype == TUNSAFEPTR)
+ yyerror("cannot use unsafe.Pointer");
+
+ evconst(n);
+ if(n->op == OTYPE && !(top & Etype)) {
+ yyerror("type %T is not an expression", n->type);
+ goto error;
+ }
+ if((top & (Erv|Etype)) == Etype && n->op != OTYPE) {
+ yyerror("%#N is not a type", n);
+ goto error;
+ }
+ if((ok & Ecall) && !(top & Ecall)) {
+ yyerror("method %#N is not an expression, must be called", n);
+ goto error;
+ }
+ // TODO(rsc): simplify
+ if((top & (Ecall|Erv|Etype)) && !(top & Etop) && !(ok & (Erv|Etype|Ecall))) {
+ yyerror("%#N used as value", n);
+ goto error;
+ }
+ if((top & Etop) && !(top & (Ecall|Erv|Etype)) && !(ok & Etop)) {
+ if(n->diag == 0) {
+ yyerror("%#N not used", n);
+ n->diag = 1;
+ }
+ goto error;
+ }
+
+ /* TODO
+ if(n->type == T)
+ fatal("typecheck nil type");
+ */
+ goto out;
+
+badcall1:
+ yyerror("invalid argument %#N (type %T) for %#O", n->left, n->left->type, n->op);
+ goto error;
+
+error:
+ n->type = T;
+
+out:
+ lineno = lno;
+ n->typecheck = 1;
+ *np = n;
+ return n;
+}
+
+static void
+implicitstar(Node **nn)
+{
+ Type *t;
+ Node *n;
+
+ // insert implicit * if needed for fixed array
+ n = *nn;
+ t = n->type;
+ if(t == T || !isptr[t->etype])
+ return;
+ t = t->type;
+ if(t == T)
+ return;
+ if(!isfixedarray(t))
+ return;
+ n = nod(OIND, n, N);
+ typecheck(&n, Erv);
+ *nn = n;
+}
+
+static int
+onearg(Node *n, char *f, ...)
+{
+ va_list arg;
+ char *p;
+
+ if(n->left != N)
+ return 0;
+ if(n->list == nil) {
+ va_start(arg, f);
+ p = vsmprint(f, arg);
+ va_end(arg);
+ yyerror("missing argument to %s: %#N", p, n);
+ return -1;
+ }
+ if(n->list->next != nil) {
+ va_start(arg, f);
+ p = vsmprint(f, arg);
+ va_end(arg);
+ yyerror("too many arguments to %s: %#N", p, n);
+ n->left = n->list->n;
+ n->list = nil;
+ return -1;
+ }
+ n->left = n->list->n;
+ n->list = nil;
+ return 0;
+}
+
+static int
+twoarg(Node *n)
+{
+ if(n->left != N)
+ return 0;
+ if(n->list == nil) {
+ yyerror("missing argument to %#O - %#N", n->op, n);
+ return -1;
+ }
+ n->left = n->list->n;
+ if(n->list->next == nil) {
+ yyerror("missing argument to %#O - %#N", n->op, n);
+ n->list = nil;
+ return -1;
+ }
+ if(n->list->next->next != nil) {
+ yyerror("too many arguments to %#O - %#N", n->op, n);
+ n->list = nil;
+ return -1;
+ }
+ n->right = n->list->next->n;
+ n->list = nil;
+ return 0;
+}
+
+static Type*
+lookdot1(Sym *s, Type *t, Type *f, int dostrcmp)
+{
+ Type *r;
+
+ r = T;
+ for(; f!=T; f=f->down) {
+ if(dostrcmp && strcmp(f->sym->name, s->name) == 0)
+ return f;
+ if(f->sym != s)
+ continue;
+ if(r != T) {
+ yyerror("ambiguous DOT reference %T.%S", t, s);
+ break;
+ }
+ r = f;
+ }
+ return r;
+}
+
+static int
+looktypedot(Node *n, Type *t, int dostrcmp)
+{
+ Type *f1, *f2, *tt;
+ Sym *s;
+
+ s = n->right->sym;
+
+ if(t->etype == TINTER) {
+ f1 = lookdot1(s, t, t->type, dostrcmp);
+ if(f1 == T)
+ return 0;
+
+ if(f1->width == BADWIDTH)
+ fatal("lookdot badwidth %T %p", f1, f1);
+ n->right = methodname(n->right, t);
+ n->xoffset = f1->width;
+ n->type = f1->type;
+ n->op = ODOTINTER;
+ return 1;
+ }
+
+ tt = t;
+ if(t->sym == S && isptr[t->etype])
+ tt = t->type;
+
+ f2 = methtype(tt);
+ if(f2 == T)
+ return 0;
+
+ expandmeth(f2->sym, f2);
+ f2 = lookdot1(s, f2, f2->xmethod, dostrcmp);
+ if(f2 == T)
+ return 0;
+
+ // disallow T.m if m requires *T receiver
+ if(isptr[getthisx(f2->type)->type->type->etype]
+ && !isptr[t->etype]
+ && f2->embedded != 2
+ && !isifacemethod(f2->type)) {
+ yyerror("invalid method expression %#N (needs pointer receiver: (*%T).%s)", n, t, f2->sym->name);
+ return 0;
+ }
+
+ n->right = methodname(n->right, t);
+ n->xoffset = f2->width;
+ n->type = f2->type;
+ n->op = ODOTMETH;
+ return 1;
+}
+
+static int
+lookdot(Node *n, Type *t, int dostrcmp)
+{
+ Type *f1, *f2, *tt, *rcvr;
+ Sym *s;
+
+ s = n->right->sym;
+
+ dowidth(t);
+ f1 = T;
+ if(t->etype == TSTRUCT || t->etype == TINTER)
+ f1 = lookdot1(s, t, t->type, dostrcmp);
+
+ f2 = T;
+ if(n->left->type == t || n->left->type->sym == S) {
+ f2 = methtype(t);
+ if(f2 != T) {
+ // Use f2->method, not f2->xmethod: adddot has
+ // already inserted all the necessary embedded dots.
+ f2 = lookdot1(s, f2, f2->method, dostrcmp);
+ }
+ }
+
+ if(f1 != T) {
+ if(f2 != T)
+ yyerror("ambiguous DOT reference %S as both field and method",
+ n->right->sym);
+ if(f1->width == BADWIDTH)
+ fatal("lookdot badwidth %T %p", f1, f1);
+ n->xoffset = f1->width;
+ n->type = f1->type;
+ if(t->etype == TINTER) {
+ if(isptr[n->left->type->etype]) {
+ n->left = nod(OIND, n->left, N); // implicitstar
+ typecheck(&n->left, Erv);
+ }
+ n->op = ODOTINTER;
+ }
+ return 1;
+ }
+
+ if(f2 != T) {
+ tt = n->left->type;
+ dowidth(tt);
+ rcvr = getthisx(f2->type)->type->type;
+ if(!eqtype(rcvr, tt)) {
+ if(rcvr->etype == tptr && eqtype(rcvr->type, tt)) {
+ checklvalue(n->left, "call pointer method on");
+ addrescapes(n->left);
+ n->left = nod(OADDR, n->left, N);
+ n->left->implicit = 1;
+ typecheck(&n->left, Etype|Erv);
+ } else if(tt->etype == tptr && eqtype(tt->type, rcvr)) {
+ n->left = nod(OIND, n->left, N);
+ n->left->implicit = 1;
+ typecheck(&n->left, Etype|Erv);
+ } else {
+ // method is attached to wrong type?
+ fatal("method mismatch: %T for %T", rcvr, tt);
+ }
+ }
+ n->right = methodname(n->right, n->left->type);
+ n->xoffset = f2->width;
+ n->type = f2->type;
+ n->op = ODOTMETH;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+nokeys(NodeList *l)
+{
+ for(; l; l=l->next)
+ if(l->n->op == OKEY)
+ return 0;
+ return 1;
+}
+
+/*
+ * typecheck assignment: type list = expression list
+ */
+static void
+typecheckaste(int op, Node *call, int isddd, Type *tstruct, NodeList *nl, char *desc)
+{
+ Type *t, *tl, *tn;
+ Node *n;
+ int lno;
+ char *why;
+
+ lno = lineno;
+
+ if(tstruct->broke)
+ goto out;
+
+ if(nl != nil && nl->next == nil && (n = nl->n)->type != T)
+ if(n->type->etype == TSTRUCT && n->type->funarg) {
+ tn = n->type->type;
+ for(tl=tstruct->type; tl; tl=tl->down) {
+ if(tl->isddd) {
+ for(; tn; tn=tn->down) {
+ exportassignok(tn->type, desc);
+ if(assignop(tn->type, tl->type->type, &why) == 0) {
+ if(call != N)
+ yyerror("cannot use %T as type %T in argument to %#N%s", tn->type, tl->type->type, call, why);
+ else
+ yyerror("cannot use %T as type %T in %s%s", tn->type, tl->type->type, desc, why);
+ }
+ }
+ goto out;
+ }
+ if(tn == T)
+ goto notenough;
+ exportassignok(tn->type, desc);
+ if(assignop(tn->type, tl->type, &why) == 0) {
+ if(call != N)
+ yyerror("cannot use %T as type %T in argument to %#N%s", tn->type, tl->type, call, why);
+ else
+ yyerror("cannot use %T as type %T in %s%s", tn->type, tl->type, desc, why);
+ }
+ tn = tn->down;
+ }
+ if(tn != T)
+ goto toomany;
+ goto out;
+ }
+
+ for(tl=tstruct->type; tl; tl=tl->down) {
+ t = tl->type;
+ if(tl->isddd) {
+ if(isddd) {
+ if(nl == nil)
+ goto notenough;
+ if(nl->next != nil)
+ goto toomany;
+ n = nl->n;
+ setlineno(n);
+ if(n->type != T)
+ nl->n = assignconv(n, t, desc);
+ goto out;
+ }
+ for(; nl; nl=nl->next) {
+ n = nl->n;
+ setlineno(nl->n);
+ if(n->type != T)
+ nl->n = assignconv(n, t->type, desc);
+ }
+ goto out;
+ }
+ if(nl == nil)
+ goto notenough;
+ n = nl->n;
+ setlineno(n);
+ if(n->type != T)
+ nl->n = assignconv(n, t, desc);
+ nl = nl->next;
+ }
+ if(nl != nil)
+ goto toomany;
+ if(isddd) {
+ if(call != N)
+ yyerror("invalid use of ... in call to %#N", call);
+ else
+ yyerror("invalid use of ... in %#O", op);
+ }
+
+out:
+ lineno = lno;
+ return;
+
+notenough:
+ if(call != N)
+ yyerror("not enough arguments in call to %#N", call);
+ else
+ yyerror("not enough arguments to %#O", op);
+ goto out;
+
+toomany:
+ if(call != N)
+ yyerror("too many arguments in call to %#N", call);
+ else
+ yyerror("too many arguments to %#O", op);
+ goto out;
+}
+
+/*
+ * do the export rules allow writing to this type?
+ * cannot be implicitly assigning to any type with
+ * an unavailable field.
+ */
+int
+exportassignok(Type *t, char *desc)
+{
+ Type *f;
+ Sym *s;
+
+ if(t == T)
+ return 1;
+ if(t->trecur)
+ return 1;
+ t->trecur = 1;
+
+ switch(t->etype) {
+ default:
+ // most types can't contain others; they're all fine.
+ break;
+ case TSTRUCT:
+ for(f=t->type; f; f=f->down) {
+ if(f->etype != TFIELD)
+ fatal("structas: not field");
+ s = f->sym;
+ // s == nil doesn't happen for embedded fields (they get the type symbol).
+ // it only happens for fields in a ... struct.
+ if(s != nil && !exportname(s->name) && s->pkg != localpkg) {
+ char *prefix;
+
+ prefix = "";
+ if(desc != nil)
+ prefix = " in ";
+ else
+ desc = "";
+ yyerror("implicit assignment of unexported field '%s' of %T%s%s", s->name, t, prefix, desc);
+ goto no;
+ }
+ if(!exportassignok(f->type, desc))
+ goto no;
+ }
+ break;
+
+ case TARRAY:
+ if(t->bound < 0) // slices are pointers; that's fine
+ break;
+ if(!exportassignok(t->type, desc))
+ goto no;
+ break;
+ }
+ t->trecur = 0;
+ return 1;
+
+no:
+ t->trecur = 0;
+ return 0;
+}
+
+
+/*
+ * type check composite
+ */
+
+static void
+fielddup(Node *n, Node *hash[], ulong nhash)
+{
+ uint h;
+ char *s;
+ Node *a;
+
+ if(n->op != ONAME)
+ fatal("fielddup: not ONAME");
+ s = n->sym->name;
+ h = stringhash(s)%nhash;
+ for(a=hash[h]; a!=N; a=a->ntest) {
+ if(strcmp(a->sym->name, s) == 0) {
+ yyerror("duplicate field name in struct literal: %s", s);
+ return;
+ }
+ }
+ n->ntest = hash[h];
+ hash[h] = n;
+}
+
+static void
+keydup(Node *n, Node *hash[], ulong nhash)
+{
+ uint h;
+ ulong b;
+ double d;
+ int i;
+ Node *a;
+ Node cmp;
+ char *s;
+
+ evconst(n);
+ if(n->op != OLITERAL)
+ return; // we dont check variables
+
+ switch(n->val.ctype) {
+ default: // unknown, bool, nil
+ b = 23;
+ break;
+ case CTINT:
+ b = mpgetfix(n->val.u.xval);
+ break;
+ case CTFLT:
+ d = mpgetflt(n->val.u.fval);
+ s = (char*)&d;
+ b = 0;
+ for(i=sizeof(d); i>0; i--)
+ b = b*PRIME1 + *s++;
+ break;
+ case CTSTR:
+ b = 0;
+ s = n->val.u.sval->s;
+ for(i=n->val.u.sval->len; i>0; i--)
+ b = b*PRIME1 + *s++;
+ break;
+ }
+
+ h = b%nhash;
+ memset(&cmp, 0, sizeof(cmp));
+ for(a=hash[h]; a!=N; a=a->ntest) {
+ cmp.op = OEQ;
+ cmp.left = n;
+ cmp.right = a;
+ evconst(&cmp);
+ b = cmp.val.u.bval;
+ if(b) {
+ // too lazy to print the literal
+ yyerror("duplicate key in map literal");
+ return;
+ }
+ }
+ n->ntest = hash[h];
+ hash[h] = n;
+}
+
+static void
+indexdup(Node *n, Node *hash[], ulong nhash)
+{
+ uint h;
+ Node *a;
+ ulong b, c;
+
+ if(n->op != OLITERAL)
+ fatal("indexdup: not OLITERAL");
+
+ b = mpgetfix(n->val.u.xval);
+ h = b%nhash;
+ for(a=hash[h]; a!=N; a=a->ntest) {
+ c = mpgetfix(a->val.u.xval);
+ if(b == c) {
+ yyerror("duplicate index in array literal: %ld", b);
+ return;
+ }
+ }
+ n->ntest = hash[h];
+ hash[h] = n;
+}
+
+static int
+prime(ulong h, ulong sr)
+{
+ ulong n;
+
+ for(n=3; n<=sr; n+=2)
+ if(h%n == 0)
+ return 0;
+ return 1;
+}
+
+static ulong
+inithash(Node *n, Node ***hash, Node **autohash, ulong nautohash)
+{
+ ulong h, sr;
+ NodeList *ll;
+ int i;
+
+ // count the number of entries
+ h = 0;
+ for(ll=n->list; ll; ll=ll->next)
+ h++;
+
+ // if the auto hash table is
+ // large enough use it.
+ if(h <= nautohash) {
+ *hash = autohash;
+ memset(*hash, 0, nautohash * sizeof(**hash));
+ return nautohash;
+ }
+
+ // make hash size odd and 12% larger than entries
+ h += h/8;
+ h |= 1;
+
+ // calculate sqrt of h
+ sr = h/2;
+ for(i=0; i<5; i++)
+ sr = (sr + h/sr)/2;
+
+ // check for primeality
+ while(!prime(h, sr))
+ h += 2;
+
+ // build and return a throw-away hash table
+ *hash = mal(h * sizeof(**hash));
+ memset(*hash, 0, h * sizeof(**hash));
+ return h;
+}
+
+static void
+typecheckcomplit(Node **np)
+{
+ int bad, i, len, nerr;
+ Node *l, *n, **hash;
+ NodeList *ll;
+ Type *t, *f, *pushtype;
+ Sym *s;
+ int32 lno;
+ ulong nhash;
+ Node *autohash[101];
+
+ n = *np;
+ lno = lineno;
+
+ if(n->right == N) {
+ if(n->list != nil)
+ setlineno(n->list->n);
+ yyerror("missing type in composite literal");
+ goto error;
+ }
+
+ setlineno(n->right);
+ l = typecheck(&n->right /* sic */, Etype|Ecomplit);
+ if((t = l->type) == T)
+ goto error;
+ nerr = nerrors;
+
+ // can omit type on composite literal values if the outer
+ // composite literal is array, slice, or map, and the
+ // element type is itself a struct, array, slice, or map.
+ pushtype = T;
+ if(t->etype == TARRAY || t->etype == TMAP) {
+ pushtype = t->type;
+ if(pushtype != T) {
+ switch(pushtype->etype) {
+ case TSTRUCT:
+ case TARRAY:
+ case TMAP:
+ break;
+ default:
+ pushtype = T;
+ break;
+ }
+ }
+ }
+
+ switch(t->etype) {
+ default:
+ yyerror("invalid type for composite literal: %T", t);
+ n->type = T;
+ break;
+
+ case TARRAY:
+ nhash = inithash(n, &hash, autohash, nelem(autohash));
+
+ len = 0;
+ i = 0;
+ for(ll=n->list; ll; ll=ll->next) {
+ l = ll->n;
+ setlineno(l);
+ if(l->op != OKEY) {
+ l = nod(OKEY, nodintconst(i), l);
+ l->left->type = types[TINT];
+ l->left->typecheck = 1;
+ ll->n = l;
+ }
+
+ typecheck(&l->left, Erv);
+ evconst(l->left);
+ i = nonnegconst(l->left);
+ if(i < 0) {
+ yyerror("array index must be non-negative integer constant");
+ i = -(1<<30); // stay negative for a while
+ }
+ if(i >= 0)
+ indexdup(l->left, hash, nhash);
+ i++;
+ if(i > len) {
+ len = i;
+ if(t->bound >= 0 && len > t->bound) {
+ setlineno(l);
+ yyerror("array index %d out of bounds [0:%d]", len, t->bound);
+ t->bound = -1; // no more errors
+ }
+ }
+
+ if(l->right->op == OCOMPLIT && l->right->right == N && pushtype != T)
+ l->right->right = typenod(pushtype);
+ typecheck(&l->right, Erv);
+ defaultlit(&l->right, t->type);
+ l->right = assignconv(l->right, t->type, "array element");
+ }
+ if(t->bound == -100)
+ t->bound = len;
+ if(t->bound < 0)
+ n->right = nodintconst(len);
+ n->op = OARRAYLIT;
+ break;
+
+ case TMAP:
+ nhash = inithash(n, &hash, autohash, nelem(autohash));
+
+ for(ll=n->list; ll; ll=ll->next) {
+ l = ll->n;
+ setlineno(l);
+ if(l->op != OKEY) {
+ typecheck(&ll->n, Erv);
+ yyerror("missing key in map literal");
+ continue;
+ }
+
+ typecheck(&l->left, Erv);
+ defaultlit(&l->left, t->down);
+ l->left = assignconv(l->left, t->down, "map key");
+ keydup(l->left, hash, nhash);
+
+ if(l->right->op == OCOMPLIT && l->right->right == N && pushtype != T)
+ l->right->right = typenod(pushtype);
+ typecheck(&l->right, Erv);
+ defaultlit(&l->right, t->type);
+ l->right = assignconv(l->right, t->type, "map value");
+ }
+ n->op = OMAPLIT;
+ break;
+
+ case TSTRUCT:
+ bad = 0;
+ if(n->list != nil && nokeys(n->list)) {
+ // simple list of variables
+ f = t->type;
+ for(ll=n->list; ll; ll=ll->next) {
+ setlineno(ll->n);
+ typecheck(&ll->n, Erv);
+ if(f == nil) {
+ if(!bad++)
+ yyerror("too many values in struct initializer");
+ continue;
+ }
+ s = f->sym;
+ if(s != nil && !exportname(s->name) && s->pkg != localpkg)
+ yyerror("implicit assignment of unexported field '%s' in %T literal", s->name, t);
+ ll->n = assignconv(ll->n, f->type, "field value");
+ ll->n = nod(OKEY, newname(f->sym), ll->n);
+ ll->n->left->typecheck = 1;
+ f = f->down;
+ }
+ if(f != nil)
+ yyerror("too few values in struct initializer");
+ } else {
+ nhash = inithash(n, &hash, autohash, nelem(autohash));
+
+ // keyed list
+ for(ll=n->list; ll; ll=ll->next) {
+ l = ll->n;
+ setlineno(l);
+ if(l->op != OKEY) {
+ if(!bad++)
+ yyerror("mixture of field:value and value initializers");
+ typecheck(&ll->n, Erv);
+ continue;
+ }
+ s = l->left->sym;
+ if(s == S) {
+ yyerror("invalid field name %#N in struct initializer", l->left);
+ typecheck(&l->right, Erv);
+ continue;
+ }
+ // Sym might have resolved to name in other top-level
+ // package, because of import dot. Redirect to correct sym
+ // before we do the lookup.
+ if(s->pkg != localpkg)
+ s = lookup(s->name);
+ l->left = newname(s);
+ l->left->typecheck = 1;
+ f = lookdot1(s, t, t->type, 0);
+ typecheck(&l->right, Erv);
+ if(f == nil) {
+ yyerror("unknown %T field '%s' in struct literal", t, s->name);
+ continue;
+ }
+ s = f->sym;
+ fielddup(newname(s), hash, nhash);
+ l->right = assignconv(l->right, f->type, "field value");
+ }
+ }
+ n->op = OSTRUCTLIT;
+ break;
+ }
+ if(nerr != nerrors)
+ goto error;
+ n->type = t;
+
+ *np = n;
+ lineno = lno;
+ return;
+
+error:
+ n->type = T;
+ *np = n;
+ lineno = lno;
+}
+
+/*
+ * the address of n has been taken and might be used after
+ * the current function returns. mark any local vars
+ * as needing to move to the heap.
+ */
+static void
+addrescapes(Node *n)
+{
+ char buf[100];
+ switch(n->op) {
+ default:
+ // probably a type error already.
+ // dump("addrescapes", n);
+ break;
+
+ case ONAME:
+ if(n->noescape)
+ break;
+ switch(n->class) {
+ case PPARAMREF:
+ addrescapes(n->defn);
+ break;
+ case PPARAM:
+ case PPARAMOUT:
+ // if func param, need separate temporary
+ // to hold heap pointer.
+ // the function type has already been checked
+ // (we're in the function body)
+ // so the param already has a valid xoffset.
+
+ // expression to refer to stack copy
+ n->stackparam = nod(OPARAM, n, N);
+ n->stackparam->type = n->type;
+ n->stackparam->addable = 1;
+ if(n->xoffset == BADWIDTH)
+ fatal("addrescapes before param assignment");
+ n->stackparam->xoffset = n->xoffset;
+ n->xoffset = 0;
+ // fallthrough
+ case PAUTO:
+
+ n->class |= PHEAP;
+ n->addable = 0;
+ n->ullman = 2;
+ n->xoffset = 0;
+
+ // create stack variable to hold pointer to heap
+ n->heapaddr = nod(ONAME, N, N);
+ n->heapaddr->type = ptrto(n->type);
+ snprint(buf, sizeof buf, "&%S", n->sym);
+ n->heapaddr->sym = lookup(buf);
+ n->heapaddr->class = PHEAP-1; // defer tempname to allocparams
+ n->heapaddr->ullman = 1;
+ n->curfn->dcl = list(n->curfn->dcl, n->heapaddr);
+
+ break;
+ }
+ break;
+
+ case OIND:
+ case ODOTPTR:
+ break;
+
+ case ODOT:
+ case OINDEX:
+ // ODOTPTR has already been introduced,
+ // so these are the non-pointer ODOT and OINDEX.
+ // In &x[0], if x is a slice, then x does not
+ // escape--the pointer inside x does, but that
+ // is always a heap pointer anyway.
+ if(!isslice(n->left->type))
+ addrescapes(n->left);
+ break;
+ }
+}
+
+/*
+ * lvalue etc
+ */
+int
+islvalue(Node *n)
+{
+ switch(n->op) {
+ case OINDEX:
+ if(isfixedarray(n->left->type))
+ return islvalue(n->left);
+ if(n->left->type != T && n->left->type->etype == TSTRING)
+ return 0;
+ // fall through
+ case OIND:
+ case ODOTPTR:
+ return 1;
+ case ODOT:
+ return islvalue(n->left);
+ case ONAME:
+ if(n->class == PFUNC)
+ return 0;
+ return 1;
+ }
+ return 0;
+}
+
+static void
+checklvalue(Node *n, char *verb)
+{
+ if(!islvalue(n))
+ yyerror("cannot %s %#N", verb, n);
+}
+
+static void
+checkassign(Node *n)
+{
+ if(islvalue(n))
+ return;
+ if(n->op == OINDEXMAP) {
+ n->etype = 1;
+ return;
+ }
+ yyerror("cannot assign to %#N", n);
+}
+
+static void
+checkassignlist(NodeList *l)
+{
+ for(; l; l=l->next)
+ checkassign(l->n);
+}
+
+/*
+ * type check assignment.
+ * if this assignment is the definition of a var on the left side,
+ * fill in the var's type.
+ */
+
+static void
+typecheckas(Node *n)
+{
+ // delicate little dance.
+ // the definition of n may refer to this assignment
+ // as its definition, in which case it will call typecheckas.
+ // in that case, do not call typecheck back, or it will cycle.
+ // if the variable has a type (ntype) then typechecking
+ // will not look at defn, so it is okay (and desirable,
+ // so that the conversion below happens).
+ n->left = resolve(n->left);
+ if(n->left->defn != n || n->left->ntype)
+ typecheck(&n->left, Erv | Easgn);
+
+ checkassign(n->left);
+ typecheck(&n->right, Erv);
+ if(n->right && n->right->type != T) {
+ if(n->left->type != T)
+ n->right = assignconv(n->right, n->left->type, "assignment");
+ else if(!isblank(n->left))
+ exportassignok(n->right->type, "assignment");
+ }
+ if(n->left->defn == n && n->left->ntype == N) {
+ defaultlit(&n->right, T);
+ n->left->type = n->right->type;
+ }
+
+ // second half of dance.
+ // now that right is done, typecheck the left
+ // just to get it over with. see dance above.
+ n->typecheck = 1;
+ if(n->left->typecheck == 0)
+ typecheck(&n->left, Erv | Easgn);
+}
+
+static void
+checkassignto(Type *src, Node *dst)
+{
+ char *why;
+
+ if(assignop(src, dst->type, &why) == 0) {
+ yyerror("cannot assign %T to %+N in multiple assignment%s", src, dst, why);
+ return;
+ }
+ exportassignok(dst->type, "multiple assignment");
+}
+
+static void
+typecheckas2(Node *n)
+{
+ int cl, cr;
+ NodeList *ll, *lr;
+ Node *l, *r;
+ Iter s;
+ Type *t;
+
+ for(ll=n->list; ll; ll=ll->next) {
+ // delicate little dance.
+ ll->n = resolve(ll->n);
+ if(ll->n->defn != n || ll->n->ntype)
+ typecheck(&ll->n, Erv | Easgn);
+ }
+ cl = count(n->list);
+ cr = count(n->rlist);
+ checkassignlist(n->list);
+ if(cl > 1 && cr == 1)
+ typecheck(&n->rlist->n, Erv | Efnstruct);
+ else
+ typechecklist(n->rlist, Erv);
+
+ if(cl == cr) {
+ // easy
+ for(ll=n->list, lr=n->rlist; ll; ll=ll->next, lr=lr->next) {
+ if(ll->n->type != T && lr->n->type != T)
+ lr->n = assignconv(lr->n, ll->n->type, "assignment");
+ if(ll->n->defn == n && ll->n->ntype == N) {
+ defaultlit(&lr->n, T);
+ ll->n->type = lr->n->type;
+ }
+ }
+ goto out;
+ }
+
+
+ l = n->list->n;
+ r = n->rlist->n;
+
+ // m[i] = x, ok
+ if(cl == 1 && cr == 2 && l->op == OINDEXMAP) {
+ if(l->type == T)
+ goto out;
+ n->op = OAS2MAPW;
+ n->rlist->n = assignconv(r, l->type, "assignment");
+ r = n->rlist->next->n;
+ n->rlist->next->n = assignconv(r, types[TBOOL], "assignment");
+ goto out;
+ }
+
+ // x,y,z = f()
+ if(cr == 1) {
+ if(r->type == T)
+ goto out;
+ switch(r->op) {
+ case OCALLMETH:
+ case OCALLINTER:
+ case OCALLFUNC:
+ if(r->type->etype != TSTRUCT || r->type->funarg == 0)
+ break;
+ cr = structcount(r->type);
+ if(cr != cl)
+ goto mismatch;
+ n->op = OAS2FUNC;
+ t = structfirst(&s, &r->type);
+ for(ll=n->list; ll; ll=ll->next) {
+ if(ll->n->type != T)
+ checkassignto(t->type, ll->n);
+ if(ll->n->defn == n && ll->n->ntype == N)
+ ll->n->type = t->type;
+ t = structnext(&s);
+ }
+ goto out;
+ }
+ }
+
+ // x, ok = y
+ if(cl == 2 && cr == 1) {
+ if(r->type == T)
+ goto out;
+ switch(r->op) {
+ case OINDEXMAP:
+ n->op = OAS2MAPR;
+ goto common;
+ case ORECV:
+ n->op = OAS2RECV;
+ n->right = n->rlist->n;
+ goto common;
+ case ODOTTYPE:
+ n->op = OAS2DOTTYPE;
+ r->op = ODOTTYPE2;
+ common:
+ if(l->type != T)
+ checkassignto(r->type, l);
+ if(l->defn == n)
+ l->type = r->type;
+ l = n->list->next->n;
+ if(l->type != T)
+ checkassignto(types[TBOOL], l);
+ if(l->defn == n && l->ntype == N)
+ l->type = types[TBOOL];
+ goto out;
+ }
+ }
+
+mismatch:
+ yyerror("assignment count mismatch: %d = %d", cl, cr);
+
+out:
+ // second half of dance
+ n->typecheck = 1;
+ for(ll=n->list; ll; ll=ll->next)
+ if(ll->n->typecheck == 0)
+ typecheck(&ll->n, Erv | Easgn);
+}
+
+/*
+ * type check function definition
+ */
+static void
+typecheckfunc(Node *n)
+{
+ Type *t, *rcvr;
+
+//dump("nname", n->nname);
+ typecheck(&n->nname, Erv | Easgn);
+ if((t = n->nname->type) == T)
+ return;
+ n->type = t;
+
+ rcvr = getthisx(t)->type;
+ if(rcvr != nil && n->shortname != N && !isblank(n->shortname))
+ addmethod(n->shortname->sym, t, 1);
+}
+
+static void
+stringtoarraylit(Node **np)
+{
+ int32 i;
+ NodeList *l;
+ Strlit *s;
+ char *p, *ep;
+ Rune r;
+ Node *nn, *n;
+
+ n = *np;
+ if(n->left->op != OLITERAL || n->left->val.ctype != CTSTR)
+ fatal("stringtoarraylit %N", n);
+
+ s = n->left->val.u.sval;
+ l = nil;
+ p = s->s;
+ ep = s->s + s->len;
+ i = 0;
+ if(n->type->type->etype == TUINT8) {
+ // raw []byte
+ while(p < ep)
+ l = list(l, nod(OKEY, nodintconst(i++), nodintconst((uchar)*p++)));
+ } else {
+ // utf-8 []int
+ while(p < ep) {
+ p += chartorune(&r, p);
+ l = list(l, nod(OKEY, nodintconst(i++), nodintconst(r)));
+ }
+ }
+ nn = nod(OCOMPLIT, N, typenod(n->type));
+ nn->list = l;
+ typecheck(&nn, Erv);
+ *np = nn;
+}
+
+static Type*
+getforwtype(Node *n)
+{
+ Node *f1, *f2;
+
+ for(f1=f2=n; ; n=n->ntype) {
+ if((n = resolve(n)) == N || n->op != OTYPE)
+ return T;
+
+ if(n->type != T && n->type->etype == TFORW)
+ return n->type;
+
+ // Check for ntype cycle.
+ if((f2 = resolve(f2)) != N && (f1 = resolve(f2->ntype)) != N) {
+ f2 = resolve(f1->ntype);
+ if(f1 == n || f2 == n)
+ return T;
+ }
+ }
+}
+
+static int ntypecheckdeftype;
+static NodeList *methodqueue;
+
+static void
+domethod(Node *n)
+{
+ Node *nt;
+
+ nt = n->type->nname;
+ typecheck(&nt, Etype);
+ if(nt->type == T) {
+ // type check failed; leave empty func
+ n->type->etype = TFUNC;
+ n->type->nod = N;
+ return;
+ }
+ *n->type = *nt->type;
+ n->type->nod = N;
+ checkwidth(n->type);
+}
+
+typedef struct NodeTypeList NodeTypeList;
+struct NodeTypeList {
+ Node *n;
+ Type *t;
+ NodeTypeList *next;
+};
+
+static NodeTypeList *dntq;
+static NodeTypeList *dntend;
+
+void
+defertypecopy(Node *n, Type *t)
+{
+ NodeTypeList *ntl;
+
+ if(n == N || t == T)
+ return;
+
+ ntl = mal(sizeof *ntl);
+ ntl->n = n;
+ ntl->t = t;
+ ntl->next = nil;
+
+ if(dntq == nil)
+ dntq = ntl;
+ else
+ dntend->next = ntl;
+
+ dntend = ntl;
+}
+
+void
+resumetypecopy(void)
+{
+ NodeTypeList *l;
+
+ for(l=dntq; l; l=l->next)
+ copytype(l->n, l->t);
+}
+
+void
+copytype(Node *n, Type *t)
+{
+ *n->type = *t;
+
+ t = n->type;
+ t->sym = n->sym;
+ t->local = n->local;
+ t->vargen = n->vargen;
+ t->siggen = 0;
+ t->method = nil;
+ t->nod = N;
+ t->printed = 0;
+ t->deferwidth = 0;
+}
+
+static void
+typecheckdeftype(Node *n)
+{
+ int maplineno, embedlineno, lno;
+ Type *t;
+ NodeList *l;
+
+ ntypecheckdeftype++;
+ lno = lineno;
+ setlineno(n);
+ n->type->sym = n->sym;
+ n->typecheck = 1;
+ typecheck(&n->ntype, Etype);
+ if((t = n->ntype->type) == T) {
+ n->diag = 1;
+ goto ret;
+ }
+ if(n->type == T) {
+ n->diag = 1;
+ goto ret;
+ }
+
+ maplineno = n->type->maplineno;
+ embedlineno = n->type->embedlineno;
+
+ // copy new type and clear fields
+ // that don't come along.
+ // anything zeroed here must be zeroed in
+ // typedcl2 too.
+ copytype(n, t);
+
+ // double-check use of type as map key.
+ if(maplineno) {
+ lineno = maplineno;
+ maptype(n->type, types[TBOOL]);
+ }
+ if(embedlineno) {
+ lineno = embedlineno;
+ if(isptr[t->etype])
+ yyerror("embedded type cannot be a pointer");
+ }
+
+ret:
+ lineno = lno;
+
+ // if there are no type definitions going on, it's safe to
+ // try to resolve the method types for the interfaces
+ // we just read.
+ if(ntypecheckdeftype == 1) {
+ while((l = methodqueue) != nil) {
+ methodqueue = nil;
+ for(; l; l=l->next)
+ domethod(l->n);
+ }
+ }
+ ntypecheckdeftype--;
+}
+
+void
+queuemethod(Node *n)
+{
+ if(ntypecheckdeftype == 0) {
+ domethod(n);
+ return;
+ }
+ methodqueue = list(methodqueue, n);
+}
+
+Node*
+typecheckdef(Node *n)
+{
+ int lno;
+ Node *e;
+ Type *t;
+ NodeList *l;
+
+ lno = lineno;
+ setlineno(n);
+
+ if(n->op == ONONAME) {
+ if(!n->diag) {
+ n->diag = 1;
+ if(n->lineno != 0)
+ lineno = n->lineno;
+ yyerror("undefined: %S", n->sym);
+ }
+ return n;
+ }
+
+ if(n->walkdef == 1)
+ return n;
+
+ l = mal(sizeof *l);
+ l->n = n;
+ l->next = typecheckdefstack;
+ typecheckdefstack = l;
+
+ if(n->walkdef == 2) {
+ flusherrors();
+ print("typecheckdef loop:");
+ for(l=typecheckdefstack; l; l=l->next)
+ print(" %S", l->n->sym);
+ print("\n");
+ fatal("typecheckdef loop");
+ }
+ n->walkdef = 2;
+
+ if(n->type != T || n->sym == S) // builtin or no name
+ goto ret;
+
+ switch(n->op) {
+ default:
+ fatal("typecheckdef %O", n->op);
+
+ case OGOTO:
+ case OLABEL:
+ // not really syms
+ break;
+
+ case OLITERAL:
+ if(n->ntype != N) {
+ typecheck(&n->ntype, Etype);
+ n->type = n->ntype->type;
+ n->ntype = N;
+ if(n->type == T) {
+ n->diag = 1;
+ goto ret;
+ }
+ }
+ e = n->defn;
+ n->defn = N;
+ if(e == N) {
+ lineno = n->lineno;
+ dump("typecheckdef nil defn", n);
+ yyerror("xxx");
+ }
+ typecheck(&e, Erv | Eiota);
+ if(e->type != T && e->op != OLITERAL) {
+ yyerror("const initializer must be constant");
+ goto ret;
+ }
+ if(isconst(e, CTNIL)) {
+ yyerror("const initializer cannot be nil");
+ goto ret;
+ }
+ t = n->type;
+ if(t != T) {
+ if(!okforconst[t->etype]) {
+ yyerror("invalid constant type %T", t);
+ goto ret;
+ }
+ if(!isideal(e->type) && !eqtype(t, e->type)) {
+ yyerror("cannot use %+N as type %T in const initializer", e, t);
+ goto ret;
+ }
+ convlit(&e, t);
+ }
+ n->val = e->val;
+ n->type = e->type;
+ break;
+
+ case ONAME:
+ if(n->ntype != N) {
+ typecheck(&n->ntype, Etype);
+ n->type = n->ntype->type;
+ if(n->type == T) {
+ n->diag = 1;
+ goto ret;
+ }
+ }
+ if(n->type != T)
+ break;
+ if(n->defn == N) {
+ if(n->etype != 0) // like OPRINTN
+ break;
+ 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
+ // bother adding to the noise.
+ break;
+ }
+ fatal("var without type, init: %S", n->sym);
+ }
+ if(n->defn->op == ONAME) {
+ typecheck(&n->defn, Erv);
+ n->type = n->defn->type;
+ break;
+ }
+ typecheck(&n->defn, Etop); // fills in n->type
+ break;
+
+ case OTYPE:
+ if(curfn)
+ defercheckwidth();
+ n->walkdef = 1;
+ n->type = typ(TFORW);
+ n->type->sym = n->sym;
+ typecheckdeftype(n);
+ if(curfn)
+ resumecheckwidth();
+ break;
+
+ case OPACK:
+ // nothing to see here
+ break;
+ }
+
+ret:
+ if(typecheckdefstack->n != n)
+ fatal("typecheckdefstack mismatch");
+ l = typecheckdefstack;
+ typecheckdefstack = l->next;
+
+ lineno = lno;
+ n->walkdef = 1;
+ return n;
+}
diff --git a/src/cmd/gc/unsafe.c b/src/cmd/gc/unsafe.c
new file mode 100644
index 000000000..d304077c8
--- /dev/null
+++ b/src/cmd/gc/unsafe.c
@@ -0,0 +1,97 @@
+// Copyright 2009 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.
+
+#include "go.h"
+
+/*
+ * look for
+ * unsafe.Sizeof
+ * unsafe.Offsetof
+ * rewrite with a constant
+ */
+Node*
+unsafenmagic(Node *nn)
+{
+ Node *r, *n;
+ Sym *s;
+ Type *t, *tr;
+ long v;
+ Val val;
+ Node *fn;
+ NodeList *args;
+
+ fn = nn->left;
+ args = nn->list;
+
+ if(safemode || fn == N || fn->op != ONAME || (s = fn->sym) == S)
+ goto no;
+ if(s->pkg != unsafepkg)
+ goto no;
+
+ if(args == nil) {
+ yyerror("missing argument for %S", s);
+ goto no;
+ }
+ r = args->n;
+
+ if(strcmp(s->name, "Sizeof") == 0) {
+ typecheck(&r, Erv);
+ defaultlit(&r, T);
+ tr = r->type;
+ if(tr == T)
+ goto bad;
+ dowidth(tr);
+ v = tr->width;
+ goto yes;
+ }
+ if(strcmp(s->name, "Offsetof") == 0) {
+ typecheck(&r, Erv);
+ if(r->op != ODOT && r->op != ODOTPTR)
+ goto bad;
+ typecheck(&r, Erv);
+ v = r->xoffset;
+ goto yes;
+ }
+ if(strcmp(s->name, "Alignof") == 0) {
+ typecheck(&r, Erv);
+ defaultlit(&r, T);
+ tr = r->type;
+ if(tr == T)
+ goto bad;
+
+ // make struct { byte; T; }
+ t = typ(TSTRUCT);
+ t->type = typ(TFIELD);
+ t->type->type = types[TUINT8];
+ t->type->down = typ(TFIELD);
+ t->type->down->type = tr;
+ // compute struct widths
+ dowidth(t);
+
+ // the offset of T is its required alignment
+ v = t->type->down->width;
+ goto yes;
+ }
+
+no:
+ return N;
+
+bad:
+ yyerror("invalid expression %#N", nn);
+ v = 0;
+ goto ret;
+
+yes:
+ if(args->next != nil)
+ yyerror("extra arguments for %S", s);
+ret:
+ // any side effects disappear; ignore init
+ val.ctype = CTINT;
+ val.u.xval = mal(sizeof(*n->val.u.xval));
+ mpmovecfix(val.u.xval, v);
+ n = nod(OLITERAL, N, N);
+ n->val = val;
+ n->type = types[TUINTPTR];
+ return n;
+}
diff --git a/src/cmd/gc/unsafe.go b/src/cmd/gc/unsafe.go
new file mode 100644
index 000000000..db27d7425
--- /dev/null
+++ b/src/cmd/gc/unsafe.go
@@ -0,0 +1,22 @@
+// Copyright 2009 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.
+
+// NOTE: If you change this file you must run "./mkbuiltin"
+// to update builtin.c.boot. This is not done automatically
+// to avoid depending on having a working compiler binary.
+
+package PACKAGE
+
+type Pointer uintptr // not really; filled in by compiler
+
+// return types here are ignored; see unsafe.c
+func Offsetof(any) uintptr
+func Sizeof(any) uintptr
+func Alignof(any) uintptr
+
+func Typeof(i interface{}) (typ interface{})
+func Reflect(i interface{}) (typ interface{}, addr Pointer)
+func Unreflect(typ interface{}, addr Pointer) (ret interface{})
+func New(typ interface{}) Pointer
+func NewArray(typ interface{}, n int) Pointer
diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c
new file mode 100644
index 000000000..9cd4ee919
--- /dev/null
+++ b/src/cmd/gc/walk.c
@@ -0,0 +1,2166 @@
+// Copyright 2009 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.
+
+#include "go.h"
+
+static Node* walkprint(Node*, NodeList**, int);
+static Node* conv(Node*, Type*);
+static Node* mapfn(char*, Type*);
+static Node* makenewvar(Type*, NodeList**, Node**);
+static Node* ascompatee1(int, Node*, Node*, NodeList**);
+static NodeList* ascompatee(int, NodeList*, NodeList*, NodeList**);
+static NodeList* ascompatet(int, NodeList*, Type**, int, NodeList**);
+static NodeList* ascompatte(int, int, Type**, NodeList*, int, NodeList**);
+static Node* convas(Node*, NodeList**);
+static void heapmoves(void);
+static NodeList* paramstoheap(Type **argin, int out);
+static NodeList* reorder1(NodeList*);
+static NodeList* reorder3(NodeList*);
+static Node* addstr(Node*, NodeList**);
+static Node* appendslice(Node*, NodeList**);
+static Node* append(Node*, NodeList**);
+
+// 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
+walk(Node *fn)
+{
+ char s[50];
+ NodeList *l;
+ Node *n;
+ int lno;
+
+ curfn = fn;
+
+ if(debug['W']) {
+ snprint(s, sizeof(s), "\nbefore %S", curfn->nname->sym);
+ dumplist(s, curfn->nbody);
+ }
+ if(curfn->type->outtuple)
+ if(walkret(curfn->nbody))
+ yyerror("function ends without a return statement");
+
+ lno = lineno;
+ for(l=fn->dcl; l; l=l->next) {
+ n = l->n;
+ if(n->op != ONAME || n->class != PAUTO)
+ continue;
+ lineno = n->lineno;
+ typecheck(&n, Erv | Easgn); // only needed for unused variables
+ if(!n->used && n->sym->name[0] != '&' && !nsyntaxerrors)
+ yyerror("%S declared and not used", n->sym);
+ }
+ lineno = lno;
+ if(nerrors != 0)
+ return;
+ walkstmtlist(curfn->nbody);
+ if(debug['W']) {
+ snprint(s, sizeof(s), "after walk %S", curfn->nname->sym);
+ dumplist(s, curfn->nbody);
+ }
+ heapmoves();
+ if(debug['W'] && curfn->enter != nil) {
+ snprint(s, sizeof(s), "enter %S", curfn->nname->sym);
+ dumplist(s, curfn->enter);
+ }
+}
+
+
+void
+walkstmtlist(NodeList *l)
+{
+ for(; l; l=l->next)
+ walkstmt(&l->n);
+}
+
+static int
+samelist(NodeList *a, NodeList *b)
+{
+ for(; a && b; a=a->next, b=b->next)
+ if(a->n != b->n)
+ return 0;
+ return a == b;
+}
+
+static int
+paramoutheap(Node *fn)
+{
+ NodeList *l;
+
+ for(l=fn->dcl; l; l=l->next) {
+ switch(l->n->class) {
+ case PPARAMOUT|PHEAP:
+ return 1;
+ case PAUTO:
+ case PAUTO|PHEAP:
+ // stop early - parameters are over
+ return 0;
+ }
+ }
+ return 0;
+}
+
+void
+walkstmt(Node **np)
+{
+ NodeList *init;
+ NodeList *ll, *rl;
+ int cl;
+ Node *n, *f;
+
+ n = *np;
+ if(n == N)
+ return;
+
+ setlineno(n);
+
+ switch(n->op) {
+ default:
+ if(n->op == ONAME)
+ yyerror("%S is not a top level statement", n->sym);
+ else
+ yyerror("%O is not a top level statement", n->op);
+ dump("nottop", n);
+ break;
+
+ case OASOP:
+ case OAS:
+ case OAS2:
+ case OAS2DOTTYPE:
+ case OAS2RECV:
+ case OAS2FUNC:
+ case OAS2MAPW:
+ case OAS2MAPR:
+ case OCLOSE:
+ case OCOPY:
+ case OCALLMETH:
+ case OCALLINTER:
+ case OCALL:
+ case OCALLFUNC:
+ case OSEND:
+ case ORECV:
+ case OPRINT:
+ case OPRINTN:
+ case OPANIC:
+ case OEMPTY:
+ case ORECOVER:
+ if(n->typecheck == 0) {
+ dump("missing typecheck:", n);
+ fatal("missing typecheck");
+ }
+ init = n->ninit;
+ n->ninit = nil;
+ walkexpr(&n, &init);
+ n->ninit = concat(init, n->ninit);
+ break;
+
+ case OBREAK:
+ case ODCL:
+ case OCONTINUE:
+ case OFALL:
+ case OGOTO:
+ case OLABEL:
+ case ODCLCONST:
+ case ODCLTYPE:
+ break;
+
+ case OBLOCK:
+ walkstmtlist(n->list);
+ break;
+
+ case OXCASE:
+ yyerror("case statement out of place");
+ n->op = OCASE;
+ case OCASE:
+ walkstmt(&n->right);
+ break;
+
+ case ODEFER:
+ hasdefer = 1;
+ switch(n->left->op) {
+ case OPRINT:
+ case OPRINTN:
+ walkexprlist(n->left->list, &n->ninit);
+ n->left = walkprint(n->left, &n->ninit, 1);
+ break;
+ default:
+ walkexpr(&n->left, &n->ninit);
+ break;
+ }
+ break;
+
+ case OFOR:
+ walkstmtlist(n->ninit);
+ if(n->ntest != N) {
+ walkstmtlist(n->ntest->ninit);
+ init = n->ntest->ninit;
+ n->ntest->ninit = nil;
+ walkexpr(&n->ntest, &init);
+ n->ntest->ninit = concat(init, n->ntest->ninit);
+ }
+ walkstmt(&n->nincr);
+ walkstmtlist(n->nbody);
+ break;
+
+ case OIF:
+ walkstmtlist(n->ninit);
+ walkexpr(&n->ntest, &n->ninit);
+ walkstmtlist(n->nbody);
+ walkstmtlist(n->nelse);
+ break;
+
+ case OPROC:
+ switch(n->left->op) {
+ case OPRINT:
+ case OPRINTN:
+ walkexprlist(n->left->list, &n->ninit);
+ n->left = walkprint(n->left, &n->ninit, 1);
+ break;
+ default:
+ walkexpr(&n->left, &n->ninit);
+ break;
+ }
+ break;
+
+ case ORETURN:
+ walkexprlist(n->list, &n->ninit);
+ if(n->list == nil)
+ break;
+ if((curfn->type->outnamed && count(n->list) > 1) || paramoutheap(curfn)) {
+ // assign to the function out parameters,
+ // so that reorder3 can fix up conflicts
+ rl = nil;
+ for(ll=curfn->dcl; ll != nil; ll=ll->next) {
+ cl = ll->n->class & ~PHEAP;
+ if(cl == PAUTO)
+ break;
+ if(cl == PPARAMOUT)
+ rl = list(rl, ll->n);
+ }
+ if(samelist(rl, n->list)) {
+ // special return in disguise
+ n->list = nil;
+ break;
+ }
+ if(count(n->list) == 1 && count(rl) > 1) {
+ // OAS2FUNC in disguise
+ f = n->list->n;
+ if(f->op != OCALLFUNC && f->op != OCALLMETH && f->op != OCALLINTER)
+ fatal("expected return of call, have %#N", f);
+ n->list = concat(list1(f), ascompatet(n->op, rl, &f->type, 0, &n->ninit));
+ break;
+ }
+ ll = ascompatee(n->op, rl, n->list, &n->ninit);
+ n->list = reorder3(ll);
+ break;
+ }
+ ll = ascompatte(n->op, 0, getoutarg(curfn->type), n->list, 1, &n->ninit);
+ n->list = ll;
+ break;
+
+ case OSELECT:
+ walkselect(n);
+ break;
+
+ case OSWITCH:
+ walkswitch(n);
+ break;
+
+ case ORANGE:
+ walkrange(n);
+ break;
+
+ case OXFALL:
+ yyerror("fallthrough statement out of place");
+ n->op = OFALL;
+ break;
+ }
+
+ *np = n;
+}
+
+
+/*
+ * walk the whole tree of the body of an
+ * expression or simple statement.
+ * the types expressions are calculated.
+ * compile-time constants are evaluated.
+ * complex side effects like statements are appended to init
+ */
+
+void
+walkexprlist(NodeList *l, NodeList **init)
+{
+ for(; l; l=l->next)
+ walkexpr(&l->n, init);
+}
+
+void
+walkexprlistsafe(NodeList *l, NodeList **init)
+{
+ for(; l; l=l->next) {
+ l->n = safeexpr(l->n, init);
+ walkexpr(&l->n, init);
+ }
+}
+
+void
+walkexpr(Node **np, NodeList **init)
+{
+ Node *r, *l, *var, *a;
+ NodeList *ll, *lr, *lpost;
+ Type *t;
+ int et;
+ int64 v, v1, v2, len;
+ int32 lno;
+ Node *n, *fn;
+ char buf[100], *p;
+
+ n = *np;
+
+ if(n == N)
+ return;
+
+ if(init == &n->ninit) {
+ // not okay to use n->ninit when walking n,
+ // because we might replace n with some other node
+ // and would lose the init list.
+ fatal("walkexpr init == &n->ninit");
+ }
+
+ // annoying case - not typechecked
+ if(n->op == OKEY) {
+ walkexpr(&n->left, init);
+ walkexpr(&n->right, init);
+ return;
+ }
+
+ lno = setlineno(n);
+
+ if(debug['w'] > 1)
+ dump("walk-before", n);
+
+ if(n->typecheck != 1) {
+ dump("missed typecheck", n);
+ fatal("missed typecheck");
+ }
+
+ t = T;
+ et = Txxx;
+
+ switch(n->op) {
+ default:
+ dump("walk", n);
+ fatal("walkexpr: switch 1 unknown op %N", n);
+ goto ret;
+
+ case OTYPE:
+ case ONONAME:
+ case OINDREG:
+ case OEMPTY:
+ goto ret;
+
+ case ONOT:
+ case OMINUS:
+ case OPLUS:
+ case OCOM:
+ case OREAL:
+ case OIMAG:
+ case ODOT:
+ case ODOTPTR:
+ case ODOTMETH:
+ case ODOTINTER:
+ case OIND:
+ walkexpr(&n->left, init);
+ goto ret;
+
+ case OLEN:
+ case OCAP:
+ walkexpr(&n->left, init);
+
+ // replace len(*[10]int) with 10.
+ // delayed until now to preserve side effects.
+ t = n->left->type;
+ if(isptr[t->etype])
+ t = t->type;
+ if(isfixedarray(t)) {
+ safeexpr(n->left, init);
+ nodconst(n, n->type, t->bound);
+ n->typecheck = 1;
+ }
+ goto ret;
+
+ case OLSH:
+ case ORSH:
+ case OAND:
+ case OOR:
+ case OXOR:
+ case OSUB:
+ case OMUL:
+ case OEQ:
+ case ONE:
+ case OLT:
+ case OLE:
+ case OGE:
+ case OGT:
+ case OADD:
+ case OCOMPLEX:
+ walkexpr(&n->left, init);
+ walkexpr(&n->right, init);
+ goto ret;
+
+ case OANDAND:
+ case OOROR:
+ walkexpr(&n->left, init);
+ // cannot put side effects from n->right on init,
+ // because they cannot run before n->left is checked.
+ // save elsewhere and store on the eventual n->right.
+ ll = nil;
+ walkexpr(&n->right, &ll);
+ n->right->ninit = concat(n->right->ninit, ll);
+ goto ret;
+
+ case OPRINT:
+ case OPRINTN:
+ walkexprlist(n->list, init);
+ n = walkprint(n, init, 0);
+ goto ret;
+
+ case OPANIC:
+ n = mkcall("panic", T, init, n->left);
+ goto ret;
+
+ case ORECOVER:
+ n = mkcall("recover", n->type, init, nod(OADDR, nodfp, N));
+ goto ret;
+
+ case OLITERAL:
+ n->addable = 1;
+ goto ret;
+
+ case ONAME:
+ if(!(n->class & PHEAP) && n->class != PPARAMREF)
+ n->addable = 1;
+ goto ret;
+
+ case OCALLINTER:
+ t = n->left->type;
+ if(n->list && n->list->n->op == OAS)
+ goto ret;
+ walkexpr(&n->left, init);
+ walkexprlist(n->list, init);
+ ll = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init);
+ n->list = reorder1(ll);
+ goto ret;
+
+ case OCALLFUNC:
+ t = n->left->type;
+ if(n->list && n->list->n->op == OAS)
+ goto ret;
+
+ if(n->left->op == OCLOSURE) {
+ walkcallclosure(n, init);
+ t = n->left->type;
+ }
+
+ walkexpr(&n->left, init);
+ walkexprlist(n->list, init);
+
+ ll = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init);
+ n->list = reorder1(ll);
+ goto ret;
+
+ case OCALLMETH:
+ t = n->left->type;
+ if(n->list && n->list->n->op == OAS)
+ goto ret;
+ walkexpr(&n->left, init);
+ walkexprlist(n->list, init);
+ ll = ascompatte(n->op, 0, getthis(t), list1(n->left->left), 0, init);
+ lr = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init);
+ ll = concat(ll, lr);
+ n->left->left = N;
+ ullmancalc(n->left);
+ n->list = reorder1(ll);
+ goto ret;
+
+ case OAS:
+ *init = concat(*init, n->ninit);
+ n->ninit = nil;
+ walkexpr(&n->left, init);
+ n->left = safeexpr(n->left, init);
+
+ if(oaslit(n, init))
+ goto ret;
+
+ walkexpr(&n->right, init);
+ if(n->left != N && n->right != N) {
+ r = convas(nod(OAS, n->left, n->right), init);
+ r->dodata = n->dodata;
+ n = r;
+ }
+
+ goto ret;
+
+ case OAS2:
+ *init = concat(*init, n->ninit);
+ n->ninit = nil;
+ walkexprlistsafe(n->list, init);
+ walkexprlistsafe(n->rlist, init);
+ ll = ascompatee(OAS, n->list, n->rlist, init);
+ ll = reorder3(ll);
+ n = liststmt(ll);
+ goto ret;
+
+ case OAS2FUNC:
+ as2func:
+ // a,b,... = fn()
+ *init = concat(*init, n->ninit);
+ n->ninit = nil;
+ r = n->rlist->n;
+ walkexprlistsafe(n->list, init);
+ walkexpr(&r, init);
+ l = n->list->n;
+
+ // all the really hard stuff - explicit function calls and so on -
+ // is gone, but map assignments remain.
+ // if there are map assignments here, assign via
+ // temporaries, because ascompatet assumes
+ // the targets can be addressed without function calls
+ // and map index has an implicit one.
+ lpost = nil;
+ if(l->op == OINDEXMAP) {
+ var = nod(OXXX, N, N);
+ tempname(var, l->type);
+ n->list->n = var;
+ a = nod(OAS, l, var);
+ typecheck(&a, Etop);
+ lpost = list(lpost, a);
+ }
+ l = n->list->next->n;
+ if(l->op == OINDEXMAP) {
+ var = nod(OXXX, N, N);
+ tempname(var, l->type);
+ n->list->next->n = var;
+ a = nod(OAS, l, var);
+ typecheck(&a, Etop);
+ lpost = list(lpost, a);
+ }
+ ll = ascompatet(n->op, n->list, &r->type, 0, init);
+ walkexprlist(lpost, init);
+ n = liststmt(concat(concat(list1(r), ll), lpost));
+ goto ret;
+
+ case OAS2RECV:
+ *init = concat(*init, n->ninit);
+ n->ninit = nil;
+ r = n->rlist->n;
+ walkexprlistsafe(n->list, init);
+ walkexpr(&r->left, init);
+ fn = chanfn("chanrecv2", 2, r->left->type);
+ r = mkcall1(fn, getoutargx(fn->type), init, typename(r->left->type), r->left);
+ n->rlist->n = r;
+ n->op = OAS2FUNC;
+ goto as2func;
+
+ case OAS2MAPR:
+ // a,b = m[i];
+ *init = concat(*init, n->ninit);
+ n->ninit = nil;
+ r = n->rlist->n;
+ walkexprlistsafe(n->list, init);
+ walkexpr(&r->left, init);
+ fn = mapfn("mapaccess2", r->left->type);
+ r = mkcall1(fn, getoutargx(fn->type), init, typename(r->left->type), r->left, r->right);
+ n->rlist = list1(r);
+ n->op = OAS2FUNC;
+ goto as2func;
+
+ case OAS2MAPW:
+ // map[] = a,b - mapassign2
+ // a,b = m[i];
+ *init = concat(*init, n->ninit);
+ n->ninit = nil;
+ walkexprlistsafe(n->list, init);
+ l = n->list->n;
+ t = l->left->type;
+ n = mkcall1(mapfn("mapassign2", t), T, init, typename(t), l->left, l->right, n->rlist->n, n->rlist->next->n);
+ goto ret;
+
+ case OAS2DOTTYPE:
+ // a,b = i.(T)
+ *init = concat(*init, n->ninit);
+ n->ninit = nil;
+ r = n->rlist->n;
+ walkexprlistsafe(n->list, init);
+ r->op = ODOTTYPE2;
+ walkexpr(&r, init);
+ ll = ascompatet(n->op, n->list, &r->type, 0, init);
+ n = liststmt(concat(list1(r), ll));
+ goto ret;
+
+ case ODOTTYPE:
+ case ODOTTYPE2:
+ // Build name of function: assertI2E2 etc.
+ strcpy(buf, "assert");
+ p = buf+strlen(buf);
+ if(isnilinter(n->left->type))
+ *p++ = 'E';
+ else
+ *p++ = 'I';
+ *p++ = '2';
+ if(isnilinter(n->type))
+ *p++ = 'E';
+ else if(isinter(n->type))
+ *p++ = 'I';
+ else
+ *p++ = 'T';
+ if(n->op == ODOTTYPE2)
+ *p++ = '2';
+ *p = '\0';
+
+ fn = syslook(buf, 1);
+ ll = list1(typename(n->type));
+ ll = list(ll, n->left);
+ argtype(fn, n->left->type);
+ argtype(fn, n->type);
+ n = nod(OCALL, fn, N);
+ n->list = ll;
+ typecheck(&n, Erv | Efnstruct);
+ walkexpr(&n, init);
+ goto ret;
+
+ case OCONVIFACE:
+ // Build name of function: convI2E etc.
+ // Not all names are possible
+ // (e.g., we'll never generate convE2E or convE2I).
+ walkexpr(&n->left, init);
+ strcpy(buf, "conv");
+ p = buf+strlen(buf);
+ if(isnilinter(n->left->type))
+ *p++ = 'E';
+ else if(isinter(n->left->type))
+ *p++ = 'I';
+ else
+ *p++ = 'T';
+ *p++ = '2';
+ if(isnilinter(n->type))
+ *p++ = 'E';
+ else
+ *p++ = 'I';
+ *p = '\0';
+
+ fn = syslook(buf, 1);
+ ll = nil;
+ if(!isinter(n->left->type))
+ ll = list(ll, typename(n->left->type));
+ if(!isnilinter(n->type))
+ ll = list(ll, typename(n->type));
+ ll = list(ll, n->left);
+ argtype(fn, n->left->type);
+ argtype(fn, n->type);
+ dowidth(fn->type);
+ n = nod(OCALL, fn, N);
+ n->list = ll;
+ typecheck(&n, Erv);
+ walkexpr(&n, init);
+ goto ret;
+
+ case OCONV:
+ case OCONVNOP:
+ if(thechar == '5') {
+ if(isfloat[n->left->type->etype]) {
+ if(n->type->etype == TINT64) {
+ n = mkcall("float64toint64", n->type, init, conv(n->left, types[TFLOAT64]));
+ goto ret;
+ }
+ if(n->type->etype == TUINT64) {
+ n = mkcall("float64touint64", n->type, init, conv(n->left, types[TFLOAT64]));
+ goto ret;
+ }
+ }
+ if(isfloat[n->type->etype]) {
+ if(n->left->type->etype == TINT64) {
+ n = mkcall("int64tofloat64", n->type, init, conv(n->left, types[TINT64]));
+ goto ret;
+ }
+ if(n->left->type->etype == TUINT64) {
+ n = mkcall("uint64tofloat64", n->type, init, conv(n->left, types[TUINT64]));
+ goto ret;
+ }
+ }
+ }
+ walkexpr(&n->left, init);
+ goto ret;
+
+ case OASOP:
+ if(n->etype == OANDNOT) {
+ n->etype = OAND;
+ n->right = nod(OCOM, n->right, N);
+ typecheck(&n->right, Erv);
+ }
+ n->left = safeexpr(n->left, init);
+ walkexpr(&n->left, init);
+ l = n->left;
+ walkexpr(&n->right, init);
+
+ /*
+ * on 32-bit arch, rewrite 64-bit ops into l = l op r.
+ * on 386, rewrite float ops into l = l op r.
+ * everywhere, rewrite map ops into l = l op r.
+ * everywhere, rewrite string += into l = l op r.
+ * everywhere, rewrite complex /= into l = l op r.
+ * TODO(rsc): Maybe this rewrite should be done always?
+ */
+ et = n->left->type->etype;
+ if((widthptr == 4 && (et == TUINT64 || et == TINT64)) ||
+ (thechar == '8' && isfloat[et]) ||
+ l->op == OINDEXMAP ||
+ et == TSTRING ||
+ (iscomplex[et] && n->etype == ODIV)) {
+ l = safeexpr(n->left, init);
+ a = l;
+ if(a->op == OINDEXMAP) {
+ // map index has "lhs" bit set in a->etype.
+ // make a copy so we can clear it on the rhs.
+ a = nod(OXXX, N, N);
+ *a = *l;
+ a->etype = 0;
+ }
+ r = nod(OAS, l, nod(n->etype, a, n->right));
+ typecheck(&r, Etop);
+ walkexpr(&r, init);
+ n = r;
+ }
+ goto ret;
+
+ case OANDNOT:
+ walkexpr(&n->left, init);
+ walkexpr(&n->right, init);
+ n->op = OAND;
+ n->right = nod(OCOM, n->right, N);
+ typecheck(&n->right, Erv);
+ goto ret;
+
+ case ODIV:
+ case OMOD:
+ walkexpr(&n->left, init);
+ walkexpr(&n->right, init);
+ /*
+ * rewrite complex div into function call.
+ */
+ et = n->left->type->etype;
+ if(iscomplex[et] && n->op == ODIV) {
+ t = n->type;
+ n = mkcall("complex128div", types[TCOMPLEX128], init,
+ conv(n->left, types[TCOMPLEX128]),
+ conv(n->right, types[TCOMPLEX128]));
+ n = conv(n, t);
+ goto ret;
+ }
+ /*
+ * rewrite div and mod into function calls
+ * on 32-bit architectures.
+ */
+ if(widthptr > 4 || (et != TUINT64 && et != TINT64))
+ goto ret;
+ if(et == TINT64)
+ strcpy(namebuf, "int64");
+ else
+ strcpy(namebuf, "uint64");
+ if(n->op == ODIV)
+ strcat(namebuf, "div");
+ else
+ strcat(namebuf, "mod");
+ n = mkcall(namebuf, n->type, init,
+ conv(n->left, types[et]), conv(n->right, types[et]));
+ goto ret;
+
+ case OINDEX:
+ walkexpr(&n->left, init);
+ walkexpr(&n->right, init);
+
+ // if range of type cannot exceed static array bound,
+ // disable bounds check
+ if(isfixedarray(n->left->type))
+ if(n->right->type->width < 4)
+ if((1<<(8*n->right->type->width)) <= n->left->type->bound)
+ n->etype = 1;
+
+ if(isconst(n->left, CTSTR))
+ if(n->right->type->width < 4)
+ if((1<<(8*n->right->type->width)) <= n->left->val.u.sval->len)
+ n->etype = 1;
+
+ // check for static out of bounds
+ if(isconst(n->right, CTINT) && !n->etype) {
+ v = mpgetfix(n->right->val.u.xval);
+ len = 1LL<<60;
+ t = n->left->type;
+ if(isconst(n->left, CTSTR))
+ len = n->left->val.u.sval->len;
+ if(t != T && isptr[t->etype])
+ t = t->type;
+ if(isfixedarray(t))
+ len = t->bound;
+ if(v < 0 || v >= (1LL<<31) || v >= len)
+ yyerror("index out of bounds");
+ else if(isconst(n->left, CTSTR)) {
+ // replace "abc"[2] with 'b'.
+ // delayed until now because "abc"[2] is not
+ // an ideal constant.
+ nodconst(n, n->type, n->left->val.u.sval->s[v]);
+ }
+ }
+ goto ret;
+
+ case OINDEXMAP:
+ if(n->etype == 1)
+ goto ret;
+
+ t = n->left->type;
+ n = mkcall1(mapfn("mapaccess1", t), t->type, init, typename(t), n->left, n->right);
+ goto ret;
+
+ case ORECV:
+ walkexpr(&n->left, init);
+ walkexpr(&n->right, init);
+ n = mkcall1(chanfn("chanrecv1", 2, n->left->type), n->type, init, typename(n->left->type), n->left);
+ goto ret;
+
+ case OSLICE:
+ case OSLICEARR:
+ walkexpr(&n->left, init);
+ n->left = safeexpr(n->left, init);
+ walkexpr(&n->right->left, init);
+ n->right->left = safeexpr(n->right->left, init);
+ walkexpr(&n->right->right, init);
+ n->right->right = safeexpr(n->right->right, init);
+
+ len = 1LL<<60;
+ t = n->left->type;
+ if(t != T && isptr[t->etype])
+ t = t->type;
+ if(isfixedarray(t))
+ len = t->bound;
+
+ // check for static out of bounds
+ // NOTE: v > len not v >= len.
+ v1 = -1;
+ v2 = -1;
+ if(isconst(n->right->left, CTINT)) {
+ v1 = mpgetfix(n->right->left->val.u.xval);
+ if(v1 < 0 || v1 >= (1LL<<31) || v1 > len) {
+ yyerror("slice index out of bounds");
+ v1 = -1;
+ }
+ }
+ if(isconst(n->right->right, CTINT)) {
+ v2 = mpgetfix(n->right->right->val.u.xval);
+ if(v2 < 0 || v2 >= (1LL<<31) || v2 > len) {
+ yyerror("slice index out of bounds");
+ v2 = -1;
+ }
+ }
+ if(v1 >= 0 && v2 >= 0 && v1 > v2)
+ yyerror("inverted slice range");
+
+ if(n->op == OSLICEARR)
+ goto slicearray;
+
+ // dynamic slice
+ // sliceslice(old []any, lb uint64, hb uint64, width uint64) (ary []any)
+ // sliceslice1(old []any, lb uint64, width uint64) (ary []any)
+ t = n->type;
+ et = n->etype;
+ if(n->right->left == N)
+ l = nodintconst(0);
+ else
+ l = conv(n->right->left, types[TUINT64]);
+ if(n->right->right != N) {
+ fn = syslook("sliceslice", 1);
+ argtype(fn, t->type); // any-1
+ argtype(fn, t->type); // any-2
+ n = mkcall1(fn, t, init,
+ n->left,
+ l,
+ conv(n->right->right, types[TUINT64]),
+ nodintconst(t->type->width));
+ } else {
+ fn = syslook("sliceslice1", 1);
+ argtype(fn, t->type); // any-1
+ argtype(fn, t->type); // any-2
+ n = mkcall1(fn, t, init,
+ n->left,
+ l,
+ nodintconst(t->type->width));
+ }
+ n->etype = et; // preserve no-typecheck flag from OSLICE to the slice* call.
+ goto ret;
+
+ slicearray:
+ // static slice
+ // slicearray(old *any, uint64 nel, lb uint64, hb uint64, width uint64) (ary []any)
+ t = n->type;
+ fn = syslook("slicearray", 1);
+ argtype(fn, n->left->type->type); // any-1
+ argtype(fn, t->type); // any-2
+ if(n->right->left == N)
+ l = nodintconst(0);
+ else
+ l = conv(n->right->left, types[TUINT64]);
+ if(n->right->right == N)
+ r = nodintconst(n->left->type->type->bound);
+ else
+ r = conv(n->right->right, types[TUINT64]);
+ n = mkcall1(fn, t, init,
+ n->left, nodintconst(n->left->type->type->bound),
+ l,
+ r,
+ nodintconst(t->type->width));
+ goto ret;
+
+ case OADDR:;
+ Node *nvar, *nstar;
+
+ // turn &Point(1, 2) or &[]int(1, 2) or &[...]int(1, 2) into allocation.
+ // initialize with
+ // nvar := new(*Point);
+ // *nvar = Point(1, 2);
+ // and replace expression with nvar
+ switch(n->left->op) {
+ case OARRAYLIT:
+ case OMAPLIT:
+ case OSTRUCTLIT:
+ nvar = makenewvar(n->type, init, &nstar);
+ anylit(0, n->left, nstar, init);
+ n = nvar;
+ goto ret;
+ }
+
+ walkexpr(&n->left, init);
+ goto ret;
+
+ case ONEW:
+ n = callnew(n->type->type);
+ goto ret;
+
+ case OCMPSTR:
+ // If one argument to the comparison is an empty string,
+ // comparing the lengths instead will yield the same result
+ // without the function call.
+ if((isconst(n->left, CTSTR) && n->left->val.u.sval->len == 0) ||
+ (isconst(n->right, CTSTR) && n->right->val.u.sval->len == 0)) {
+ r = nod(n->etype, nod(OLEN, n->left, N), nod(OLEN, n->right, N));
+ typecheck(&r, Erv);
+ walkexpr(&r, init);
+ n = r;
+ goto ret;
+ }
+
+ // s + "badgerbadgerbadger" == "badgerbadgerbadger"
+ if((n->etype == OEQ || n->etype == ONE) &&
+ isconst(n->right, CTSTR) &&
+ n->left->op == OADDSTR && isconst(n->left->right, CTSTR) &&
+ cmpslit(n->right, n->left->right) == 0) {
+ r = nod(n->etype, nod(OLEN, n->left->left, N), nodintconst(0));
+ typecheck(&r, Erv);
+ walkexpr(&r, init);
+ n = r;
+ goto ret;
+ }
+
+ // prepare for rewrite below
+ if(n->etype == OEQ || n->etype == ONE) {
+ n->left = cheapexpr(n->left, init);
+ n->right = cheapexpr(n->right, init);
+ }
+
+ // sys_cmpstring(s1, s2) :: 0
+ r = mkcall("cmpstring", types[TINT], init,
+ conv(n->left, types[TSTRING]),
+ conv(n->right, types[TSTRING]));
+ r = nod(n->etype, r, nodintconst(0));
+
+ // quick check of len before full compare for == or !=
+ if(n->etype == OEQ || n->etype == ONE) {
+ if(n->etype == OEQ)
+ r = nod(OANDAND, nod(OEQ, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r);
+ else
+ r = nod(OOROR, nod(ONE, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r);
+ typecheck(&r, Erv);
+ walkexpr(&r, nil);
+ }
+ typecheck(&r, Erv);
+ n = r;
+ goto ret;
+
+ case OADDSTR:
+ n = addstr(n, init);
+ goto ret;
+
+ case OSLICESTR:
+ // sys_slicestring(s, lb, hb)
+ if(n->right->left == N)
+ l = nodintconst(0);
+ else
+ l = conv(n->right->left, types[TINT]);
+ if(n->right->right) {
+ n = mkcall("slicestring", n->type, init,
+ conv(n->left, types[TSTRING]),
+ l,
+ conv(n->right->right, types[TINT]));
+ } else {
+ n = mkcall("slicestring1", n->type, init,
+ conv(n->left, types[TSTRING]),
+ l);
+ }
+ goto ret;
+
+ case OAPPEND:
+ if(n->isddd)
+ n = appendslice(n, init);
+ else
+ n = append(n, init);
+ goto ret;
+
+ case OCOPY:
+ if(n->right->type->etype == TSTRING)
+ fn = syslook("slicestringcopy", 1);
+ else
+ fn = syslook("slicecopy", 1);
+ argtype(fn, n->left->type);
+ argtype(fn, n->right->type);
+ n = mkcall1(fn, n->type, init,
+ n->left, n->right,
+ nodintconst(n->left->type->type->width));
+ goto ret;
+
+ case OCLOSE:
+ // cannot use chanfn - closechan takes any, not chan any
+ fn = syslook("closechan", 1);
+ argtype(fn, n->left->type);
+ n = mkcall1(fn, T, init, n->left);
+ goto ret;
+
+ case OMAKECHAN:
+ n = mkcall1(chanfn("makechan", 1, n->type), n->type, init,
+ typename(n->type),
+ conv(n->left, types[TINT64]));
+ goto ret;
+
+ case OMAKEMAP:
+ t = n->type;
+
+ fn = syslook("makemap", 1);
+ argtype(fn, t->down); // any-1
+ argtype(fn, t->type); // any-2
+
+ n = mkcall1(fn, n->type, init,
+ typename(n->type),
+ conv(n->left, types[TINT64]));
+ goto ret;
+
+ case OMAKESLICE:
+ // makeslice(t *Type, nel int64, max int64) (ary []any)
+ l = n->left;
+ r = n->right;
+ if(r == nil)
+ l = r = safeexpr(l, init);
+ t = n->type;
+ fn = syslook("makeslice", 1);
+ argtype(fn, t->type); // any-1
+ n = mkcall1(fn, n->type, init,
+ typename(n->type),
+ conv(l, types[TINT64]),
+ conv(r, types[TINT64]));
+ goto ret;
+
+ case ORUNESTR:
+ // sys_intstring(v)
+ n = mkcall("intstring", n->type, init,
+ conv(n->left, types[TINT64]));
+ goto ret;
+
+ case OARRAYBYTESTR:
+ // slicebytetostring([]byte) string;
+ n = mkcall("slicebytetostring", n->type, init, n->left);
+ goto ret;
+
+ case OARRAYRUNESTR:
+ // sliceinttostring([]int) string;
+ n = mkcall("sliceinttostring", n->type, init, n->left);
+ goto ret;
+
+ case OSTRARRAYBYTE:
+ // stringtoslicebyte(string) []byte;
+ n = mkcall("stringtoslicebyte", n->type, init, conv(n->left, types[TSTRING]));
+ goto ret;
+
+ case OSTRARRAYRUNE:
+ // stringtosliceint(string) []int
+ n = mkcall("stringtosliceint", n->type, init, n->left);
+ goto ret;
+
+ case OCMPIFACE:
+ // ifaceeq(i1 any-1, i2 any-2) (ret bool);
+ if(!eqtype(n->left->type, n->right->type))
+ fatal("ifaceeq %O %T %T", n->op, n->left->type, n->right->type);
+ if(isnilinter(n->left->type))
+ fn = syslook("efaceeq", 1);
+ else
+ fn = syslook("ifaceeq", 1);
+ argtype(fn, n->right->type);
+ argtype(fn, n->left->type);
+ r = mkcall1(fn, n->type, init, n->left, n->right);
+ if(n->etype == ONE) {
+ r = nod(ONOT, r, N);
+ typecheck(&r, Erv);
+ }
+ n = r;
+ goto ret;
+
+ case OARRAYLIT:
+ case OMAPLIT:
+ case OSTRUCTLIT:
+ nvar = nod(OXXX, N, N);
+ tempname(nvar, n->type);
+ anylit(0, n, nvar, init);
+ n = nvar;
+ goto ret;
+
+ case OSEND:
+ n = mkcall1(chanfn("chansend1", 2, n->left->type), T, init, typename(n->left->type), n->left, n->right);
+ goto ret;
+
+ case OCLOSURE:
+ n = walkclosure(n, init);
+ goto ret;
+ }
+ fatal("missing switch %O", n->op);
+
+ret:
+ if(debug['w'] && n != N)
+ dump("walk", n);
+
+ ullmancalc(n);
+ lineno = lno;
+ *np = n;
+}
+
+static Node*
+makenewvar(Type *t, NodeList **init, Node **nstar)
+{
+ Node *nvar, *nas;
+
+ nvar = nod(OXXX, N, N);
+ tempname(nvar, t);
+ nas = nod(OAS, nvar, callnew(t->type));
+ typecheck(&nas, Etop);
+ walkexpr(&nas, init);
+ *init = list(*init, nas);
+
+ *nstar = nod(OIND, nvar, N);
+ typecheck(nstar, Erv);
+ return nvar;
+}
+
+static Node*
+ascompatee1(int op, Node *l, Node *r, NodeList **init)
+{
+ return convas(nod(OAS, l, r), init);
+}
+
+static NodeList*
+ascompatee(int op, NodeList *nl, NodeList *nr, NodeList **init)
+{
+ NodeList *ll, *lr, *nn;
+
+ /*
+ * check assign expression list to
+ * a expression list. called in
+ * expr-list = expr-list
+ */
+
+ // ensure order of evaluation for function calls
+ for(ll=nl; ll; ll=ll->next)
+ ll->n = safeexpr(ll->n, init);
+ for(lr=nr; lr; lr=lr->next)
+ lr->n = safeexpr(lr->n, init);
+
+ nn = nil;
+ for(ll=nl, lr=nr; ll && lr; ll=ll->next, lr=lr->next)
+ nn = list(nn, ascompatee1(op, ll->n, lr->n, init));
+
+ // cannot happen: caller checked that lists had same length
+ if(ll || lr)
+ yyerror("error in shape across %O", op);
+ return nn;
+}
+
+/*
+ * l is an lv and rt is the type of an rv
+ * return 1 if this implies a function call
+ * evaluating the lv or a function call
+ * in the conversion of the types
+ */
+static int
+fncall(Node *l, Type *rt)
+{
+ if(l->ullman >= UINF || l->op == OINDEXMAP)
+ return 1;
+ if(eqtype(l->type, rt))
+ return 0;
+ return 1;
+}
+
+static NodeList*
+ascompatet(int op, NodeList *nl, Type **nr, int fp, NodeList **init)
+{
+ Node *l, *tmp, *a;
+ NodeList *ll;
+ Type *r;
+ Iter saver;
+ int ucount;
+ NodeList *nn, *mm;
+
+ /*
+ * check assign type list to
+ * a expression list. called in
+ * expr-list = func()
+ */
+ r = structfirst(&saver, nr);
+ nn = nil;
+ mm = nil;
+ ucount = 0;
+ for(ll=nl; ll; ll=ll->next) {
+ if(r == T)
+ break;
+ l = ll->n;
+ if(isblank(l)) {
+ r = structnext(&saver);
+ continue;
+ }
+
+ // any lv that causes a fn call must be
+ // deferred until all the return arguments
+ // have been pulled from the output arguments
+ if(fncall(l, r->type)) {
+ tmp = nod(OXXX, N, N);
+ tempname(tmp, r->type);
+ typecheck(&tmp, Erv);
+ a = nod(OAS, l, tmp);
+ a = convas(a, init);
+ mm = list(mm, a);
+ l = tmp;
+ }
+
+ a = nod(OAS, l, nodarg(r, fp));
+ a = convas(a, init);
+ ullmancalc(a);
+ if(a->ullman >= UINF)
+ ucount++;
+ nn = list(nn, a);
+ r = structnext(&saver);
+ }
+
+ if(ll != nil || r != T)
+ yyerror("assignment count mismatch: %d = %d",
+ count(nl), structcount(*nr));
+ if(ucount)
+ fatal("reorder2: too many function calls evaluating parameters");
+ return concat(nn, mm);
+}
+
+ /*
+ * package all the arguments that match a ... T parameter into a []T.
+ */
+static NodeList*
+mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init)
+{
+ Node *a, *n;
+ Type *tslice;
+
+ tslice = typ(TARRAY);
+ tslice->type = l->type->type;
+ tslice->bound = -1;
+
+ n = nod(OCOMPLIT, N, typenod(tslice));
+ n->list = lr0;
+ typecheck(&n, Erv);
+ if(n->type == T)
+ fatal("mkdotargslice: typecheck failed");
+ walkexpr(&n, init);
+
+ a = nod(OAS, nodarg(l, fp), n);
+ nn = list(nn, convas(a, init));
+ return nn;
+}
+
+/*
+ * helpers for shape errors
+ */
+static char*
+dumptypes(Type **nl, char *what)
+{
+ int first;
+ Type *l;
+ Iter savel;
+ Fmt fmt;
+
+ fmtstrinit(&fmt);
+ fmtprint(&fmt, "\t");
+ l = structfirst(&savel, nl);
+ first = 1;
+ for(l = structfirst(&savel, nl); l != T; l = structnext(&savel)) {
+ if(first)
+ first = 0;
+ else
+ fmtprint(&fmt, ", ");
+ fmtprint(&fmt, "%T", l);
+ }
+ if(first)
+ fmtprint(&fmt, "[no arguments %s]", what);
+ return fmtstrflush(&fmt);
+}
+
+static char*
+dumpnodetypes(NodeList *l, char *what)
+{
+ int first;
+ Node *r;
+ Fmt fmt;
+
+ fmtstrinit(&fmt);
+ fmtprint(&fmt, "\t");
+ first = 1;
+ for(; l; l=l->next) {
+ r = l->n;
+ if(first)
+ first = 0;
+ else
+ fmtprint(&fmt, ", ");
+ fmtprint(&fmt, "%T", r->type);
+ }
+ if(first)
+ fmtprint(&fmt, "[no arguments %s]", what);
+ return fmtstrflush(&fmt);
+}
+
+/*
+ * check assign expression list to
+ * a type list. called in
+ * return expr-list
+ * func(expr-list)
+ */
+static NodeList*
+ascompatte(int op, int isddd, Type **nl, NodeList *lr, int fp, NodeList **init)
+{
+ Type *l, *ll;
+ Node *r, *a;
+ NodeList *nn, *lr0, *alist;
+ Iter savel;
+ char *l1, *l2;
+
+ lr0 = lr;
+ l = structfirst(&savel, nl);
+ r = N;
+ if(lr)
+ r = lr->n;
+ nn = nil;
+
+ // f(g()) where g has multiple return values
+ if(r != N && lr->next == nil && r->type->etype == TSTRUCT && r->type->funarg) {
+ // optimization - can do block copy
+ if(eqtypenoname(r->type, *nl)) {
+ a = nodarg(*nl, fp);
+ a->type = r->type;
+ nn = list1(convas(nod(OAS, a, r), init));
+ goto ret;
+ }
+
+ // conversions involved.
+ // copy into temporaries.
+ alist = nil;
+ for(l=structfirst(&savel, &r->type); l; l=structnext(&savel)) {
+ a = nod(OXXX, N, N);
+ tempname(a, l->type);
+ alist = list(alist, a);
+ }
+ a = nod(OAS2, N, N);
+ a->list = alist;
+ a->rlist = lr;
+ typecheck(&a, Etop);
+ walkstmt(&a);
+ *init = list(*init, a);
+ lr = alist;
+ r = lr->n;
+ l = structfirst(&savel, nl);
+ }
+
+loop:
+ if(l != T && l->isddd) {
+ // the ddd parameter must be last
+ ll = structnext(&savel);
+ if(ll != T)
+ yyerror("... must be last argument");
+
+ // special case --
+ // only if we are assigning a single ddd
+ // argument to a ddd parameter then it is
+ // passed thru unencapsulated
+ if(r != N && lr->next == nil && isddd && eqtype(l->type, r->type)) {
+ a = nod(OAS, nodarg(l, fp), r);
+ a = convas(a, init);
+ nn = list(nn, a);
+ goto ret;
+ }
+
+ // normal case -- make a slice of all
+ // remaining arguments and pass it to
+ // the ddd parameter.
+ nn = mkdotargslice(lr, nn, l, fp, init);
+ goto ret;
+ }
+
+ if(l == T || r == N) {
+ if(l != T || r != N) {
+ l1 = dumptypes(nl, "expected");
+ l2 = dumpnodetypes(lr0, "given");
+ if(l != T)
+ yyerror("not enough arguments to %O\n%s\n%s", op, l1, l2);
+ else
+ yyerror("too many arguments to %O\n%s\n%s", op, l1, l2);
+ }
+ goto ret;
+ }
+
+ a = nod(OAS, nodarg(l, fp), r);
+ a = convas(a, init);
+ nn = list(nn, a);
+
+ l = structnext(&savel);
+ r = N;
+ lr = lr->next;
+ if(lr != nil)
+ r = lr->n;
+ goto loop;
+
+ret:
+ for(lr=nn; lr; lr=lr->next)
+ lr->n->typecheck = 1;
+ return nn;
+}
+
+// generate code for print
+static Node*
+walkprint(Node *nn, NodeList **init, int defer)
+{
+ Node *r;
+ Node *n;
+ NodeList *l, *all;
+ Node *on;
+ Type *t;
+ int notfirst, et, op;
+ NodeList *calls, *intypes, *args;
+ Fmt fmt;
+
+ on = nil;
+ op = nn->op;
+ all = nn->list;
+ calls = nil;
+ notfirst = 0;
+ intypes = nil;
+ args = nil;
+
+ memset(&fmt, 0, sizeof fmt);
+ if(defer) {
+ // defer print turns into defer printf with format string
+ fmtstrinit(&fmt);
+ intypes = list(intypes, nod(ODCLFIELD, N, typenod(types[TSTRING])));
+ args = list1(nod(OXXX, N, N));
+ }
+
+ for(l=all; l; l=l->next) {
+ if(notfirst) {
+ if(defer)
+ fmtprint(&fmt, " ");
+ else
+ calls = list(calls, mkcall("printsp", T, init));
+ }
+ notfirst = op == OPRINTN;
+
+ n = l->n;
+ if(n->op == OLITERAL) {
+ switch(n->val.ctype) {
+ case CTINT:
+ defaultlit(&n, types[TINT64]);
+ break;
+ case CTFLT:
+ defaultlit(&n, types[TFLOAT64]);
+ break;
+ }
+ }
+ if(n->op != OLITERAL && n->type && n->type->etype == TIDEAL)
+ defaultlit(&n, types[TINT64]);
+ defaultlit(&n, nil);
+ l->n = n;
+ if(n->type == T || n->type->etype == TFORW)
+ continue;
+
+ t = n->type;
+ et = n->type->etype;
+ if(isinter(n->type)) {
+ if(defer) {
+ if(isnilinter(n->type))
+ fmtprint(&fmt, "%%e");
+ else
+ fmtprint(&fmt, "%%i");
+ } else {
+ if(isnilinter(n->type))
+ on = syslook("printeface", 1);
+ else
+ on = syslook("printiface", 1);
+ argtype(on, n->type); // any-1
+ }
+ } else if(isptr[et] || et == TCHAN || et == TMAP || et == TFUNC || et == TUNSAFEPTR) {
+ if(defer) {
+ fmtprint(&fmt, "%%p");
+ } else {
+ on = syslook("printpointer", 1);
+ argtype(on, n->type); // any-1
+ }
+ } else if(isslice(n->type)) {
+ if(defer) {
+ fmtprint(&fmt, "%%a");
+ } else {
+ on = syslook("printslice", 1);
+ argtype(on, n->type); // any-1
+ }
+ } else if(isint[et]) {
+ if(defer) {
+ if(et == TUINT64)
+ fmtprint(&fmt, "%%U");
+ else {
+ fmtprint(&fmt, "%%D");
+ t = types[TINT64];
+ }
+ } else {
+ if(et == TUINT64)
+ on = syslook("printuint", 0);
+ else
+ on = syslook("printint", 0);
+ }
+ } else if(isfloat[et]) {
+ if(defer) {
+ fmtprint(&fmt, "%%f");
+ t = types[TFLOAT64];
+ } else
+ on = syslook("printfloat", 0);
+ } else if(iscomplex[et]) {
+ if(defer) {
+ fmtprint(&fmt, "%%C");
+ t = types[TCOMPLEX128];
+ } else
+ on = syslook("printcomplex", 0);
+ } else if(et == TBOOL) {
+ if(defer)
+ fmtprint(&fmt, "%%t");
+ else
+ on = syslook("printbool", 0);
+ } else if(et == TSTRING) {
+ if(defer)
+ fmtprint(&fmt, "%%S");
+ else
+ on = syslook("printstring", 0);
+ } else {
+ badtype(OPRINT, n->type, T);
+ continue;
+ }
+
+ if(!defer) {
+ t = *getinarg(on->type);
+ if(t != nil)
+ t = t->type;
+ if(t != nil)
+ t = t->type;
+ }
+
+ if(!eqtype(t, n->type)) {
+ n = nod(OCONV, n, N);
+ n->type = t;
+ }
+
+ if(defer) {
+ intypes = list(intypes, nod(ODCLFIELD, N, typenod(t)));
+ args = list(args, n);
+ } else {
+ r = nod(OCALL, on, N);
+ r->list = list1(n);
+ calls = list(calls, r);
+ }
+ }
+
+ if(defer) {
+ if(op == OPRINTN)
+ fmtprint(&fmt, "\n");
+ on = syslook("goprintf", 1);
+ on->type = functype(nil, intypes, nil);
+ args->n = nod(OLITERAL, N, N);
+ args->n->val.ctype = CTSTR;
+ args->n->val.u.sval = strlit(fmtstrflush(&fmt));
+ r = nod(OCALL, on, N);
+ r->list = args;
+ typecheck(&r, Etop);
+ walkexpr(&r, init);
+ } else {
+ if(op == OPRINTN)
+ calls = list(calls, mkcall("printnl", T, nil));
+ typechecklist(calls, Etop);
+ walkexprlist(calls, init);
+
+ r = nod(OEMPTY, N, N);
+ typecheck(&r, Etop);
+ walkexpr(&r, init);
+ r->ninit = calls;
+ }
+ return r;
+}
+
+Node*
+callnew(Type *t)
+{
+ Node *fn;
+
+ dowidth(t);
+ fn = syslook("new", 1);
+ argtype(fn, t);
+ return mkcall1(fn, ptrto(t), nil, nodintconst(t->width));
+}
+
+static Node*
+convas(Node *n, NodeList **init)
+{
+ Type *lt, *rt;
+
+ if(n->op != OAS)
+ fatal("convas: not OAS %O", n->op);
+
+ n->typecheck = 1;
+
+ if(n->left == N || n->right == N)
+ goto out;
+
+ lt = n->left->type;
+ rt = n->right->type;
+ if(lt == T || rt == T)
+ goto out;
+
+ if(isblank(n->left)) {
+ defaultlit(&n->right, T);
+ goto out;
+ }
+
+ if(n->left->op == OINDEXMAP) {
+ n = mkcall1(mapfn("mapassign1", n->left->left->type), T, init,
+ typename(n->left->left->type),
+ n->left->left, n->left->right, n->right);
+ goto out;
+ }
+
+ if(eqtype(lt, rt))
+ goto out;
+
+ n->right = assignconv(n->right, lt, "assignment");
+ walkexpr(&n->right, init);
+
+out:
+ ullmancalc(n);
+ return n;
+}
+
+/*
+ * from ascompat[te]
+ * evaluating actual function arguments.
+ * f(a,b)
+ * if there is exactly one function expr,
+ * then it is done first. otherwise must
+ * make temp variables
+ */
+NodeList*
+reorder1(NodeList *all)
+{
+ Node *f, *a, *n;
+ NodeList *l, *r, *g;
+ int c, d, t;
+
+ c = 0; // function calls
+ t = 0; // total parameters
+
+ for(l=all; l; l=l->next) {
+ n = l->n;
+ t++;
+ ullmancalc(n);
+ if(n->ullman >= UINF)
+ c++;
+ }
+ if(c == 0 || t == 1)
+ return all;
+
+ g = nil; // fncalls assigned to tempnames
+ f = N; // last fncall assigned to stack
+ r = nil; // non fncalls and tempnames assigned to stack
+ d = 0;
+ for(l=all; l; l=l->next) {
+ n = l->n;
+ if(n->ullman < UINF) {
+ r = list(r, n);
+ continue;
+ }
+ d++;
+ if(d == c) {
+ f = n;
+ continue;
+ }
+
+ // make assignment of fncall to tempname
+ a = nod(OXXX, N, N);
+ tempname(a, n->right->type);
+ a = nod(OAS, a, n->right);
+ g = list(g, a);
+
+ // put normal arg assignment on list
+ // with fncall replaced by tempname
+ n->right = a->left;
+ r = list(r, n);
+ }
+
+ if(f != N)
+ g = list(g, f);
+ return concat(g, r);
+}
+
+/*
+ * from ascompat[ee]
+ * a,b = c,d
+ * simultaneous assignment. there cannot
+ * be later use of an earlier lvalue.
+ */
+
+static int
+vmatch2(Node *l, Node *r)
+{
+ NodeList *ll;
+
+ /*
+ * isolate all right sides
+ */
+ if(r == N)
+ return 0;
+ switch(r->op) {
+ case ONAME:
+ // match each right given left
+ if(l == r)
+ return 1;
+ case OLITERAL:
+ return 0;
+ }
+ if(vmatch2(l, r->left))
+ return 1;
+ if(vmatch2(l, r->right))
+ return 1;
+ for(ll=r->list; ll; ll=ll->next)
+ if(vmatch2(l, ll->n))
+ return 1;
+ return 0;
+}
+
+int
+vmatch1(Node *l, Node *r)
+{
+ NodeList *ll;
+
+ /*
+ * isolate all left sides
+ */
+ if(l == N || r == N)
+ return 0;
+ switch(l->op) {
+ case ONAME:
+ switch(l->class) {
+ case PPARAM:
+ case PPARAMREF:
+ case PAUTO:
+ break;
+ default:
+ // assignment to non-stack variable
+ // must be delayed if right has function calls.
+ if(r->ullman >= UINF)
+ return 1;
+ break;
+ }
+ return vmatch2(l, r);
+ case OLITERAL:
+ return 0;
+ }
+ if(vmatch1(l->left, r))
+ return 1;
+ if(vmatch1(l->right, r))
+ return 1;
+ for(ll=l->list; ll; ll=ll->next)
+ if(vmatch1(ll->n, r))
+ return 1;
+ return 0;
+}
+
+NodeList*
+reorder3(NodeList *all)
+{
+ Node *n1, *n2, *q;
+ int c1, c2;
+ NodeList *l1, *l2, *r;
+
+ r = nil;
+ for(l1=all, c1=0; l1; l1=l1->next, c1++) {
+ n1 = l1->n;
+ for(l2=all, c2=0; l2; l2=l2->next, c2++) {
+ n2 = l2->n;
+ if(c2 > c1) {
+ if(vmatch1(n1->left, n2->right)) {
+ // delay assignment to n1->left
+ q = nod(OXXX, N, N);
+ tempname(q, n1->right->type);
+ q = nod(OAS, n1->left, q);
+ n1->left = q->right;
+ r = list(r, q);
+ break;
+ }
+ }
+ }
+ }
+ return concat(all, r);
+}
+
+/*
+ * walk through argin parameters.
+ * generate and return code to allocate
+ * copies of escaped parameters to the heap.
+ */
+static NodeList*
+paramstoheap(Type **argin, int out)
+{
+ Type *t;
+ Iter savet;
+ Node *v;
+ NodeList *nn;
+
+ nn = nil;
+ for(t = structfirst(&savet, argin); t != T; t = structnext(&savet)) {
+ v = t->nname;
+ if(v == N && out && hasdefer) {
+ // Defer might stop a panic and show the
+ // return values as they exist at the time of panic.
+ // Make sure to zero them on entry to the function.
+ nn = list(nn, nod(OAS, nodarg(t, 1), N));
+ }
+ if(v == N || !(v->class & PHEAP))
+ continue;
+
+ // generate allocation & copying code
+ if(v->alloc == nil)
+ v->alloc = callnew(v->type);
+ nn = list(nn, nod(OAS, v->heapaddr, v->alloc));
+ if((v->class & ~PHEAP) != PPARAMOUT)
+ nn = list(nn, nod(OAS, v, v->stackparam));
+ }
+ return nn;
+}
+
+/*
+ * walk through argout parameters copying back to stack
+ */
+static NodeList*
+returnsfromheap(Type **argin)
+{
+ Type *t;
+ Iter savet;
+ Node *v;
+ NodeList *nn;
+
+ nn = nil;
+ for(t = structfirst(&savet, argin); t != T; t = structnext(&savet)) {
+ v = t->nname;
+ if(v == N || v->class != (PHEAP|PPARAMOUT))
+ continue;
+ nn = list(nn, nod(OAS, v->stackparam, v));
+ }
+ return nn;
+}
+
+/*
+ * take care of migrating any function in/out args
+ * between the stack and the heap. adds code to
+ * curfn's before and after lists.
+ */
+static void
+heapmoves(void)
+{
+ NodeList *nn;
+ int32 lno;
+
+ lno = lineno;
+ lineno = curfn->lineno;
+ nn = paramstoheap(getthis(curfn->type), 0);
+ nn = concat(nn, paramstoheap(getinarg(curfn->type), 0));
+ nn = concat(nn, paramstoheap(getoutarg(curfn->type), 1));
+ curfn->enter = concat(curfn->enter, nn);
+ lineno = curfn->endlineno;
+ curfn->exit = returnsfromheap(getoutarg(curfn->type));
+ lineno = lno;
+}
+
+static Node*
+vmkcall(Node *fn, Type *t, NodeList **init, va_list va)
+{
+ int i, n;
+ Node *r;
+ NodeList *args;
+
+ if(fn->type == T || fn->type->etype != TFUNC)
+ fatal("mkcall %#N %T", fn, fn->type);
+
+ args = nil;
+ n = fn->type->intuple;
+ for(i=0; i<n; i++)
+ args = list(args, va_arg(va, Node*));
+
+ r = nod(OCALL, fn, N);
+ r->list = args;
+ if(fn->type->outtuple > 0)
+ typecheck(&r, Erv | Efnstruct);
+ else
+ typecheck(&r, Etop);
+ walkexpr(&r, init);
+ r->type = t;
+ return r;
+}
+
+Node*
+mkcall(char *name, Type *t, NodeList **init, ...)
+{
+ Node *r;
+ va_list va;
+
+ va_start(va, init);
+ r = vmkcall(syslook(name, 0), t, init, va);
+ va_end(va);
+ return r;
+}
+
+Node*
+mkcall1(Node *fn, Type *t, NodeList **init, ...)
+{
+ Node *r;
+ va_list va;
+
+ va_start(va, init);
+ r = vmkcall(fn, t, init, va);
+ va_end(va);
+ return r;
+}
+
+static Node*
+conv(Node *n, Type *t)
+{
+ if(eqtype(n->type, t))
+ return n;
+ n = nod(OCONV, n, N);
+ n->type = t;
+ typecheck(&n, Erv);
+ return n;
+}
+
+Node*
+chanfn(char *name, int n, Type *t)
+{
+ Node *fn;
+ int i;
+
+ if(t->etype != TCHAN)
+ fatal("chanfn %T", t);
+ fn = syslook(name, 1);
+ for(i=0; i<n; i++)
+ argtype(fn, t->type);
+ return fn;
+}
+
+static Node*
+mapfn(char *name, Type *t)
+{
+ Node *fn;
+
+ if(t->etype != TMAP)
+ fatal("mapfn %T", t);
+ fn = syslook(name, 1);
+ argtype(fn, t->down);
+ argtype(fn, t->type);
+ argtype(fn, t->down);
+ argtype(fn, t->type);
+ return fn;
+}
+
+static Node*
+addstr(Node *n, NodeList **init)
+{
+ Node *r, *cat, *typstr;
+ NodeList *in, *args;
+ int i, count;
+
+ count = 0;
+ for(r=n; r->op == OADDSTR; r=r->left)
+ count++; // r->right
+ count++; // r
+
+ // prepare call of runtime.catstring of type int, string, string, string
+ // with as many strings as we have.
+ cat = syslook("concatstring", 1);
+ cat->type = T;
+ cat->ntype = nod(OTFUNC, N, N);
+ in = list1(nod(ODCLFIELD, N, typenod(types[TINT]))); // count
+ typstr = typenod(types[TSTRING]);
+ for(i=0; i<count; i++)
+ in = list(in, nod(ODCLFIELD, N, typstr));
+ cat->ntype->list = in;
+ cat->ntype->rlist = list1(nod(ODCLFIELD, N, typstr));
+
+ args = nil;
+ for(r=n; r->op == OADDSTR; r=r->left)
+ args = concat(list1(conv(r->right, types[TSTRING])), args);
+ args = concat(list1(conv(r, types[TSTRING])), args);
+ args = concat(list1(nodintconst(count)), args);
+
+ r = nod(OCALL, cat, N);
+ r->list = args;
+ typecheck(&r, Erv);
+ walkexpr(&r, init);
+ r->type = n->type;
+
+ return r;
+}
+
+static Node*
+appendslice(Node *n, NodeList **init)
+{
+ Node *f;
+
+ f = syslook("appendslice", 1);
+ argtype(f, n->type);
+ argtype(f, n->type->type);
+ argtype(f, n->type);
+ return mkcall1(f, n->type, init, typename(n->type), n->list->n, n->list->next->n);
+}
+
+// expand append(src, a [, b]* ) to
+//
+// init {
+// s := src
+// const argc = len(args) - 1
+// if cap(s) - len(s) < argc {
+// s = growslice(s, argc)
+// }
+// n := len(s)
+// s = s[:n+argc]
+// s[n] = a
+// s[n+1] = b
+// ...
+// }
+// s
+static Node*
+append(Node *n, NodeList **init)
+{
+ NodeList *l, *a;
+ Node *nsrc, *ns, *nn, *na, *nx, *fn;
+ int argc;
+
+ walkexprlistsafe(n->list, init);
+
+ nsrc = n->list->n;
+ argc = count(n->list) - 1;
+ if (argc < 1) {
+ return nsrc;
+ }
+
+ l = nil;
+
+ ns = nod(OXXX, N, N); // var s
+ tempname(ns, nsrc->type);
+ l = list(l, nod(OAS, ns, nsrc)); // s = src
+
+ na = nodintconst(argc); // const argc
+ nx = nod(OIF, N, N); // if cap(s) - len(s) < argc
+ nx->ntest = nod(OLT, nod(OSUB, nod(OCAP, ns, N), nod(OLEN, ns, N)), na);
+
+ fn = syslook("growslice", 1); // growslice(<type>, old []T, n int64) (ret []T)
+ argtype(fn, ns->type->type); // 1 old []any
+ argtype(fn, ns->type->type); // 2 ret []any
+
+ nx->nbody = list1(nod(OAS, ns, mkcall1(fn, ns->type, &nx->ninit,
+ typename(ns->type),
+ ns,
+ conv(na, types[TINT64]))));
+ l = list(l, nx);
+
+ nn = nod(OXXX, N, N); // var n
+ tempname(nn, types[TINT]);
+ l = list(l, nod(OAS, nn, nod(OLEN, ns, N))); // n = len(s)
+
+ nx = nod(OSLICE, ns, nod(OKEY, N, nod(OADD, nn, na))); // ...s[:n+argc]
+ nx->etype = 1; // disable bounds check
+ l = list(l, nod(OAS, ns, nx)); // s = s[:n+argc]
+
+ for (a = n->list->next; a != nil; a = a->next) {
+ nx = nod(OINDEX, ns, nn); // s[n] ...
+ nx->etype = 1; // disable bounds check
+ l = list(l, nod(OAS, nx, a->n)); // s[n] = arg
+ if (a->next != nil)
+ l = list(l, nod(OAS, nn, nod(OADD, nn, nodintconst(1)))); // n = n + 1
+ }
+
+ typechecklist(l, Etop);
+ walkstmtlist(l);
+ *init = concat(*init, l);
+ return ns;
+}