diff options
Diffstat (limited to 'src/cmd/gc')
33 files changed, 5969 insertions, 2735 deletions
diff --git a/src/cmd/gc/Makefile b/src/cmd/gc/Makefile index f7e305178..bb0d01637 100644 --- a/src/cmd/gc/Makefile +++ b/src/cmd/gc/Makefile @@ -24,15 +24,17 @@ OFILES=\ dcl.$O\ esc.$O\ export.$O\ + fmt.$O\ gen.$O\ init.$O\ + inl.$O\ lex.$O\ md5.$O\ mparith1.$O\ mparith2.$O\ mparith3.$O\ obj.$O\ - print.$O\ + order.$O\ range.$O\ reflect.$O\ select.$O\ @@ -44,6 +46,8 @@ OFILES=\ walk.$O\ y1.tab.$O\ +HOST_CFLAGS+=-DGOEXPERIMENT='"$(GOEXPERIMENT)"' + NOINSTALL=1 include ../../Make.clib @@ -60,7 +64,7 @@ subr.$O: yerr.h builtin.c: builtin.c.boot cp builtin.c.boot builtin.c -subr.$O: opnames.h +fmt.$O: opnames.h opnames.h: mkopnames go.h ./mkopnames go.h >opnames.h diff --git a/src/cmd/gc/align.c b/src/cmd/gc/align.c index f316c19e0..6982bbe56 100644 --- a/src/cmd/gc/align.c +++ b/src/cmd/gc/align.c @@ -285,6 +285,9 @@ dowidth(Type *t) break; } + if(widthptr == 4 && w != (int32)w) + yyerror("type %T too large", t); + t->width = w; if(t->align == 0) { if(w > 8 || (w&(w-1)) != 0) @@ -491,12 +494,13 @@ typeinit(void) 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 + okforeq[TMAP] = 1; // nil only; refined in typecheck + okforeq[TFUNC] = 1; // nil only; refined in typecheck + okforeq[TARRAY] = 1; // nil slice only; refined in typecheck + okforeq[TSTRUCT] = 1; // it's complicated; refined in typecheck okforcmp[TSTRING] = 1; diff --git a/src/cmd/gc/bisonerrors b/src/cmd/gc/bisonerrors index 5110f5350..0f865d086 100755 --- a/src/cmd/gc/bisonerrors +++ b/src/cmd/gc/bisonerrors @@ -46,24 +46,36 @@ bison && /^state 0/ { grammar = 0; states = 1 } states && /^state / { state = $2 } states { statetext[state] = statetext[state] $0 "\n" } -states && / shift, and go to state/ { +states && / shift/ { n = nshift[state]++ - shift[state,n] = $7 + if($0 ~ /and go to/) + shift[state,n] = $7 # GNU Bison + else + shift[state,n] = $3 # Plan 9 Yacc shifttoken[state,n] = $1 next } -states && / go to state/ { +states && / (go to|goto)/ { n = nshift[state]++ - shift[state,n] = $5 + if($0 ~ /go to/) + shift[state,n] = $5 # GNU Bison + else + shift[state,n] = $3 # Plan 9 Yacc shifttoken[state,n] = $1 next } -states && / reduce using rule/ { +states && / reduce/ { n = nreduce[state]++ - reduce[state,n] = $5 + if($0 ~ /reduce using rule/) + reduce[state,n] = $5 # GNU Bison + else + reduce[state,n] = $3 # Plan 9 yacc reducetoken[state,n] = $1 next -} +} + +# Skip over the summary information printed by Plan 9 yacc. +/nonterminals$/,/^maximum spread/ { next } # First // comment marks the beginning of the pattern file. /^\/\// { bison = 0; grammar = 0; state = 0 } @@ -96,7 +108,8 @@ $1 == "%" { if(found) continue for(j=0; j<nreduce[state]; j++) { - if(reducetoken[state,j] == tok || reducetoken[state,j] == "$default") { + t = reducetoken[state,j] + if(t == tok || t == "$default" || t == ".") { stack[nstack++] = state rule = reduce[state,j] nstack -= rulesize[rule] diff --git a/src/cmd/gc/bits.c b/src/cmd/gc/bits.c index f3b031cc3..c0fd4d85e 100644 --- a/src/cmd/gc/bits.c +++ b/src/cmd/gc/bits.c @@ -151,9 +151,9 @@ Qconv(Fmt *fp) else fmtprint(fp, " "); if(var[i].node == N || var[i].node->sym == S) - fmtprint(fp, "$%lld", var[i].offset); + fmtprint(fp, "$%d", i); else { - fmtprint(fp, var[i].node->sym->name); + fmtprint(fp, "%s", var[i].node->sym->name); if(var[i].offset != 0) fmtprint(fp, "%+lld", (vlong)var[i].offset); } diff --git a/src/cmd/gc/builtin.c.boot b/src/cmd/gc/builtin.c.boot index 190c56008..23d36964a 100644 --- a/src/cmd/gc/builtin.c.boot +++ b/src/cmd/gc/builtin.c.boot @@ -1,114 +1,123 @@ 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" + "func @\"\".new(@\"\".typ *byte) *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 *byte, @\"\".x any, @\"\".y []any) any\n" + "func @\"\".appendstr(@\"\".typ *byte, @\"\".x []byte, @\"\".y string) []byte\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(? []byte) string\n" + "func @\"\".slicerunetostring(? []rune) string\n" + "func @\"\".stringtoslicebyte(? string) []byte\n" + "func @\"\".stringtoslicerune(? string) []rune\n" + "func @\"\".stringiter(? string, ? int) int\n" + "func @\"\".stringiter2(? string, ? int) (@\"\".retk int, @\"\".retv rune)\n" + "func @\"\".copy(@\"\".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 *byte, @\"\".elem any) any\n" + "func @\"\".convT2E(@\"\".typ *byte, @\"\".elem any) any\n" + "func @\"\".convT2I(@\"\".typ *byte, @\"\".typ2 *byte, @\"\".elem any) any\n" + "func @\"\".assertE2E(@\"\".typ *byte, @\"\".iface any) any\n" + "func @\"\".assertE2E2(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any, @\"\".ok bool)\n" + "func @\"\".assertE2I(@\"\".typ *byte, @\"\".iface any) any\n" + "func @\"\".assertE2I2(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any, @\"\".ok bool)\n" + "func @\"\".assertE2T(@\"\".typ *byte, @\"\".iface any) any\n" + "func @\"\".assertE2T2(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any, @\"\".ok bool)\n" + "func @\"\".assertI2E(@\"\".typ *byte, @\"\".iface any) any\n" + "func @\"\".assertI2E2(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any, @\"\".ok bool)\n" + "func @\"\".assertI2I(@\"\".typ *byte, @\"\".iface any) any\n" + "func @\"\".assertI2I2(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any, @\"\".ok bool)\n" + "func @\"\".assertI2T(@\"\".typ *byte, @\"\".iface any) any\n" + "func @\"\".assertI2T2(@\"\".typ *byte, @\"\".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 @\"\".equal(@\"\".typ *byte, @\"\".x1 any, @\"\".x2 any) bool\n" + "func @\"\".makemap(@\"\".mapType *byte, @\"\".hint int64) map[any]any\n" + "func @\"\".mapaccess1(@\"\".mapType *byte, @\"\".hmap map[any]any, @\"\".key any) any\n" + "func @\"\".mapaccess2(@\"\".mapType *byte, @\"\".hmap map[any]any, @\"\".key any) (@\"\".val any, @\"\".pres bool)\n" + "func @\"\".mapassign1(@\"\".mapType *byte, @\"\".hmap map[any]any, @\"\".key any, @\"\".val any)\n" + "func @\"\".mapassign2(@\"\".mapType *byte, @\"\".hmap map[any]any, @\"\".key any, @\"\".val any, @\"\".pres bool)\n" + "func @\"\".mapiterinit(@\"\".mapType *byte, @\"\".hmap map[any]any, @\"\".hiter *any)\n" + "func @\"\".mapdelete(@\"\".mapType *byte, @\"\".hmap map[any]any, @\"\".key 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 *byte, @\"\".hint int64) chan any\n" + "func @\"\".chanrecv1(@\"\".chanType *byte, @\"\".hchan <-chan any) any\n" + "func @\"\".chanrecv2(@\"\".chanType *byte, @\"\".hchan <-chan any) (@\"\".elem any, @\"\".received bool)\n" + "func @\"\".chansend1(@\"\".chanType *byte, @\"\".hchan chan<- any, @\"\".elem any)\n" + "func @\"\".closechan(@\"\".hchan any)\n" + "func @\"\".selectnbsend(@\"\".chanType *byte, @\"\".hchan chan<- any, @\"\".elem any) bool\n" + "func @\"\".selectnbrecv(@\"\".chanType *byte, @\"\".elem *any, @\"\".hchan <-chan any) bool\n" + "func @\"\".selectnbrecv2(@\"\".chanType *byte, @\"\".elem *any, @\"\".received *bool, @\"\".hchan <-chan any) bool\n" + "func @\"\".newselect(@\"\".size int) *byte\n" + "func @\"\".selectsend(@\"\".sel *byte, @\"\".hchan chan<- any, @\"\".elem *any) bool\n" + "func @\"\".selectrecv(@\"\".sel *byte, @\"\".hchan <-chan any, @\"\".elem *any) bool\n" + "func @\"\".selectrecv2(@\"\".sel *byte, @\"\".hchan <-chan any, @\"\".elem *any, @\"\".received *bool) bool\n" + "func @\"\".selectdefault(@\"\".sel *byte) bool\n" + "func @\"\".selectgo(@\"\".sel *byte)\n" + "func @\"\".block()\n" + "func @\"\".makeslice(@\"\".typ *byte, @\"\".nel int64, @\"\".cap int64) []any\n" + "func @\"\".growslice(@\"\".typ *byte, @\"\".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 @\"\".memequal(@\"\".eq *bool, @\"\".size uintptr, @\"\".x *any, @\"\".y *any)\n" + "func @\"\".memequal8(@\"\".eq *bool, @\"\".size uintptr, @\"\".x *any, @\"\".y *any)\n" + "func @\"\".memequal16(@\"\".eq *bool, @\"\".size uintptr, @\"\".x *any, @\"\".y *any)\n" + "func @\"\".memequal32(@\"\".eq *bool, @\"\".size uintptr, @\"\".x *any, @\"\".y *any)\n" + "func @\"\".memequal64(@\"\".eq *bool, @\"\".size uintptr, @\"\".x *any, @\"\".y *any)\n" + "func @\"\".memequal128(@\"\".eq *bool, @\"\".size uintptr, @\"\".x *any, @\"\".y *any)\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" + "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 index d29e8cbc2..fa44e40fa 100644 --- a/src/cmd/gc/closure.c +++ b/src/cmd/gc/closure.c @@ -192,6 +192,10 @@ walkclosure(Node *func, NodeList **init) Node *xtype, *xfunc, *call, *clos; NodeList *l, *in; + // no closure vars, don't bother wrapping + if(func->cvars == nil) + return makeclosure(func, init, 1)->nname; + /* * wrap body in external function * with extra closure parameters. diff --git a/src/cmd/gc/const.c b/src/cmd/gc/const.c index 135a8102e..01c4f15b3 100644 --- a/src/cmd/gc/const.c +++ b/src/cmd/gc/const.c @@ -108,7 +108,7 @@ convlit1(Node **np, Type *t, int explicit) 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); + yyerror("invalid operation: %N (shift of type %T)", n, t); t = T; } n->type = t; @@ -170,6 +170,7 @@ convlit1(Node **np, Type *t, int explicit) break; case CTINT: + case CTRUNE: case CTFLT: case CTCPLX: ct = n->val.ctype; @@ -179,6 +180,7 @@ convlit1(Node **np, Type *t, int explicit) goto bad; case CTCPLX: case CTFLT: + case CTRUNE: n->val = toint(n->val); // flowthrough case CTINT: @@ -192,6 +194,7 @@ convlit1(Node **np, Type *t, int explicit) goto bad; case CTCPLX: case CTINT: + case CTRUNE: n->val = toflt(n->val); // flowthrough case CTFLT: @@ -206,6 +209,7 @@ convlit1(Node **np, Type *t, int explicit) goto bad; case CTFLT: case CTINT: + case CTRUNE: n->val = tocplx(n->val); break; case CTCPLX: @@ -213,7 +217,7 @@ convlit1(Node **np, Type *t, int explicit) break; } } else - if(et == TSTRING && ct == CTINT && explicit) + if(et == TSTRING && (ct == CTINT || ct == CTRUNE) && explicit) n->val = tostr(n->val); else goto bad; @@ -224,7 +228,7 @@ convlit1(Node **np, Type *t, int explicit) bad: if(!n->diag) { - yyerror("cannot convert %#N to type %T", n, t); + yyerror("cannot convert %N to type %T", n, t); n->diag = 1; } if(isideal(n->type)) { @@ -243,6 +247,7 @@ copyval(Val v) switch(v.ctype) { case CTINT: + case CTRUNE: i = mal(sizeof(*i)); mpmovefixfix(i, v.u.xval); v.u.xval = i; @@ -269,6 +274,7 @@ tocplx(Val v) switch(v.ctype) { case CTINT: + case CTRUNE: c = mal(sizeof(*c)); mpmovefixflt(&c->real, v.u.xval); mpmovecflt(&c->imag, 0.0); @@ -293,6 +299,7 @@ toflt(Val v) switch(v.ctype) { case CTINT: + case CTRUNE: f = mal(sizeof(*f)); mpmovefixflt(f, v.u.xval); v.ctype = CTFLT; @@ -316,6 +323,9 @@ toint(Val v) Mpint *i; switch(v.ctype) { + case CTRUNE: + v.ctype = CTINT; + break; case CTFLT: i = mal(sizeof(*i)); if(mpmovefltfix(i, v.u.fval) < 0) @@ -345,6 +355,7 @@ overflow(Val v, Type *t) return; switch(v.ctype) { case CTINT: + case CTRUNE: if(!isint[t->etype]) fatal("overflow: %T integer constant", t); if(mpcmpfixfix(v.u.xval, minintval[t->etype]) < 0 || @@ -379,6 +390,7 @@ tostr(Val v) switch(v.ctype) { case CTINT: + case CTRUNE: if(mpcmpfixfix(v.u.xval, minintval[TINT]) < 0 || mpcmpfixfix(v.u.xval, maxintval[TINT]) > 0) yyerror("overflow in int -> string"); @@ -415,7 +427,12 @@ consttype(Node *n) int isconst(Node *n, int ct) { - return consttype(n) == ct; + int t; + + t = consttype(n); + // If the caller is asking for CTINT, allow CTRUNE too. + // Makes life easier for back ends. + return t == ct || (ct == CTINT && t == CTRUNE); } /* @@ -424,7 +441,7 @@ isconst(Node *n, int ct) void evconst(Node *n) { - Node *nl, *nr; + Node *nl, *nr, *norig; int32 len; Strlit *str; int wl, wr, lno, et; @@ -518,7 +535,8 @@ evconst(Node *n) n->right = nr; if(nr->type && (issigned[nr->type->etype] || !isint[nr->type->etype])) goto illegal; - nl->val = toint(nl->val); + if(nl->val.ctype != CTRUNE) + nl->val = toint(nl->val); nr->val = toint(nr->val); break; } @@ -540,6 +558,17 @@ evconst(Node *n) v = toflt(v); rv = toflt(rv); } + + // Rune and int turns into rune. + if(v.ctype == CTRUNE && rv.ctype == CTINT) + rv.ctype = CTRUNE; + if(v.ctype == CTINT && rv.ctype == CTRUNE) { + if(n->op == OLSH || n->op == ORSH) + rv.ctype = CTINT; + else + v.ctype = CTRUNE; + } + if(v.ctype != rv.ctype) { // Use of undefined name as constant? if((v.ctype == 0 || rv.ctype == 0) && nerrors > 0) @@ -559,15 +588,19 @@ evconst(Node *n) return; case TUP(OADD, CTINT): + case TUP(OADD, CTRUNE): mpaddfixfix(v.u.xval, rv.u.xval); break; case TUP(OSUB, CTINT): + case TUP(OSUB, CTRUNE): mpsubfixfix(v.u.xval, rv.u.xval); break; case TUP(OMUL, CTINT): + case TUP(OMUL, CTRUNE): mpmulfixfix(v.u.xval, rv.u.xval); break; case TUP(ODIV, CTINT): + case TUP(ODIV, CTRUNE): if(mpcmpfixc(rv.u.xval, 0) == 0) { yyerror("division by zero"); mpmovecfix(v.u.xval, 1); @@ -576,6 +609,7 @@ evconst(Node *n) mpdivfixfix(v.u.xval, rv.u.xval); break; case TUP(OMOD, CTINT): + case TUP(OMOD, CTRUNE): if(mpcmpfixc(rv.u.xval, 0) == 0) { yyerror("division by zero"); mpmovecfix(v.u.xval, 1); @@ -585,21 +619,27 @@ evconst(Node *n) break; case TUP(OLSH, CTINT): + case TUP(OLSH, CTRUNE): mplshfixfix(v.u.xval, rv.u.xval); break; case TUP(ORSH, CTINT): + case TUP(ORSH, CTRUNE): mprshfixfix(v.u.xval, rv.u.xval); break; case TUP(OOR, CTINT): + case TUP(OOR, CTRUNE): mporfixfix(v.u.xval, rv.u.xval); break; case TUP(OAND, CTINT): + case TUP(OAND, CTRUNE): mpandfixfix(v.u.xval, rv.u.xval); break; case TUP(OANDNOT, CTINT): + case TUP(OANDNOT, CTRUNE): mpandnotfixfix(v.u.xval, rv.u.xval); break; case TUP(OXOR, CTINT): + case TUP(OXOR, CTRUNE): mpxorfixfix(v.u.xval, rv.u.xval); break; @@ -649,26 +689,32 @@ evconst(Node *n) goto setfalse; case TUP(OEQ, CTINT): + case TUP(OEQ, CTRUNE): if(mpcmpfixfix(v.u.xval, rv.u.xval) == 0) goto settrue; goto setfalse; case TUP(ONE, CTINT): + case TUP(ONE, CTRUNE): if(mpcmpfixfix(v.u.xval, rv.u.xval) != 0) goto settrue; goto setfalse; case TUP(OLT, CTINT): + case TUP(OLT, CTRUNE): if(mpcmpfixfix(v.u.xval, rv.u.xval) < 0) goto settrue; goto setfalse; case TUP(OLE, CTINT): + case TUP(OLE, CTRUNE): if(mpcmpfixfix(v.u.xval, rv.u.xval) <= 0) goto settrue; goto setfalse; case TUP(OGE, CTINT): + case TUP(OGE, CTRUNE): if(mpcmpfixfix(v.u.xval, rv.u.xval) >= 0) goto settrue; goto setfalse; case TUP(OGT, CTINT): + case TUP(OGT, CTRUNE): if(mpcmpfixfix(v.u.xval, rv.u.xval) > 0) goto settrue; goto setfalse; @@ -786,17 +832,21 @@ unary: } // fall through case TUP(OCONV, CTINT): + case TUP(OCONV, CTRUNE): case TUP(OCONV, CTFLT): case TUP(OCONV, CTSTR): convlit1(&nl, n->type, 1); break; case TUP(OPLUS, CTINT): + case TUP(OPLUS, CTRUNE): break; case TUP(OMINUS, CTINT): + case TUP(OMINUS, CTRUNE): mpnegfix(v.u.xval); break; case TUP(OCOM, CTINT): + case TUP(OCOM, CTRUNE): et = Txxx; if(nl->type != T) et = nl->type->etype; @@ -842,8 +892,15 @@ unary: } ret: - // rewrite n in place. + if(n == n->orig) { + // duplicate node for n->orig. + norig = nod(OLITERAL, N, N); + *norig = *n; + } else + norig = n->orig; *n = *nl; + // restore value of n->orig. + n->orig = norig; n->val = v; // check range. @@ -882,6 +939,7 @@ nodlit(Val v) n->type = idealbool; break; case CTINT: + case CTRUNE: case CTFLT: case CTCPLX: n->type = types[TIDEAL]; @@ -939,7 +997,7 @@ defaultlit(Node **np, Type *t) defaultlit(&n->left, t); t = n->left->type; if(t != T && !isint[t->etype]) { - yyerror("invalid operation: %#N (shift of type %T)", n, t); + yyerror("invalid operation: %N (shift of type %T)", n, t); t = T; } n->type = t; @@ -991,7 +1049,7 @@ defaultlit(Node **np, Type *t) n->type = types[TSTRING]; break; } - yyerror("defaultlit: unknown literal: %#N", n); + yyerror("defaultlit: unknown literal: %N", n); break; case CTBOOL: n->type = types[TBOOL]; @@ -1001,6 +1059,9 @@ defaultlit(Node **np, Type *t) case CTINT: n->type = types[TINT]; goto num; + case CTRUNE: + n->type = runetype; + goto num; case CTFLT: n->type = types[TFLOAT64]; goto num; @@ -1065,6 +1126,13 @@ defaultlit2(Node **lp, Node **rp, int force) convlit(rp, types[TFLOAT64]); return; } + + if(isconst(l, CTRUNE) || isconst(r, CTRUNE)) { + convlit(lp, runetype); + convlit(rp, runetype); + return; + } + convlit(lp, types[TINT]); convlit(rp, types[TINT]); } @@ -1101,7 +1169,7 @@ cmpslit(Node *l, Node *r) int smallintconst(Node *n) { - if(n->op == OLITERAL && n->val.ctype == CTINT && n->type != T) + if(n->op == OLITERAL && isconst(n, CTINT) && n->type != T) switch(simtype[n->type->etype]) { case TINT8: case TUINT8: @@ -1203,6 +1271,7 @@ convconst(Node *con, Type *t, Val *val) default: fatal("convconst ctype=%d %lT", val->ctype, t); case CTINT: + case CTRUNE: i = mpgetfix(val->u.xval); break; case CTBOOL: diff --git a/src/cmd/gc/cplx.c b/src/cmd/gc/cplx.c index 52038e71c..dea7bc3bb 100644 --- a/src/cmd/gc/cplx.c +++ b/src/cmd/gc/cplx.c @@ -204,6 +204,8 @@ complexgen(Node *n, Node *res) case OIND: case ONAME: // PHEAP or PPARAMREF var case OCALLFUNC: + case OCALLMETH: + case OCALLINTER: igen(n, &n1, res); complexmove(&n1, res); regfree(&n1); diff --git a/src/cmd/gc/dcl.c b/src/cmd/gc/dcl.c index d8b89b4f3..66edab9b9 100644 --- a/src/cmd/gc/dcl.c +++ b/src/cmd/gc/dcl.c @@ -8,6 +8,7 @@ #include "y.tab.h" static void funcargs(Node*); +static void funcargs2(Type*); static int dflag(void) @@ -174,6 +175,11 @@ declare(Node *n, int ctxt) n->lineno = parserline(); s = n->sym; + + // kludgy: typecheckok means we're past parsing. Eg genwrapper may declare out of package names later. + if(importpkg == nil && !typecheckok && s->pkg != localpkg) + yyerror("cannot declare name %S", s); + gen = 0; if(ctxt == PEXTERN) { externdcl = list(externdcl, n); @@ -415,6 +421,7 @@ oldname(Sym *s) c->funcdepth = funcdepth; c->outer = n->closure; n->closure = c; + n->addrtaken = 1; c->closure = n; c->xoffset = 0; curfn->cvars = list(curfn->cvars, c); @@ -472,7 +479,7 @@ colasdefn(NodeList *left, Node *defn) if(isblank(n)) continue; if(!colasname(n)) { - yyerror("non-name %#N on left side of :=", n); + yyerror("non-name %N on left side of :=", n); nerr++; continue; } @@ -546,13 +553,6 @@ ifacedcl(Node *n) 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"); @@ -563,16 +563,19 @@ funchdr(Node *n) n->outer = curfn; curfn = n; + if(n->nname) funcargs(n->nname->ntype); - else + else if (n->ntype) funcargs(n->ntype); + else + funcargs2(n->type); } static void funcargs(Node *nt) { - Node *n; + Node *n, *nn; NodeList *l; int gen; @@ -581,11 +584,11 @@ funcargs(Node *nt) // 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. + // will not fill in the types until later if(nt->left != N) { n = nt->left; if(n->op != ODCLFIELD) - fatal("funcargs1 %O", n->op); + fatal("funcargs receiver %O", n->op); if(n->left != N) { n->left->op = ONAME; n->left->ntype = n->right; @@ -595,7 +598,7 @@ funcargs(Node *nt) for(l=nt->list; l; l=l->next) { n = l->n; if(n->op != ODCLFIELD) - fatal("funcargs2 %O", n->op); + fatal("funcargs in %O", n->op); if(n->left != N) { n->left->op = ONAME; n->left->ntype = n->right; @@ -608,12 +611,16 @@ funcargs(Node *nt) for(l=nt->rlist; l; l=l->next) { n = l->n; if(n->op != ODCLFIELD) - fatal("funcargs3 %O", n->op); + fatal("funcargs out %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. + // preserve the original in ->orig + nn = nod(OXXX, N, N); + *nn = *n->left; + n->left = nn; snprint(namebuf, sizeof(namebuf), ".anon%d", gen++); n->left->sym = lookup(namebuf); } @@ -623,6 +630,48 @@ funcargs(Node *nt) } /* + * Same as funcargs, except run over an already constructed TFUNC. + * This happens during import, where the hidden_fndcl rule has + * used functype directly to parse the function's type. + */ +static void +funcargs2(Type *t) +{ + Type *ft; + Node *n; + + if(t->etype != TFUNC) + fatal("funcargs2 %T", t); + + if(t->thistuple) + for(ft=getthisx(t)->type; ft; ft=ft->down) { + if(!ft->nname || !ft->nname->sym) + continue; + n = ft->nname; // no need for newname(ft->nname->sym) + n->type = ft->type; + declare(n, PPARAM); + } + + if(t->intuple) + for(ft=getinargx(t)->type; ft; ft=ft->down) { + if(!ft->nname || !ft->nname->sym) + continue; + n = ft->nname; + n->type = ft->type; + declare(n, PPARAM); + } + + if(t->outtuple) + for(ft=getoutargx(t)->type; ft; ft=ft->down) { + if(!ft->nname || !ft->nname->sym) + continue; + n = ft->nname; + n->type = ft->type; + declare(n, PPARAMOUT); + } +} + +/* * finish the body. * called in auto-declaration context. * returns in extern-declaration context. @@ -649,7 +698,7 @@ typedcl0(Sym *s) { Node *n; - n = dclname(s); + n = newname(s); n->op = OTYPE; declare(n, dclcontext); return n; @@ -669,220 +718,265 @@ typedcl1(Node *n, Node *t, int local) } /* - * typedcl1 but during imports + * structs, functions, and methods. + * they don't belong here, but where do they belong? */ -void -typedcl2(Type *pt, Type *t) + +static void +checkembeddedtype(Type *t) { - Node *n; + if (t == T) + return; - // 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(t->sym == S && isptr[t->etype]) { + t = t->type; + if(t->etype == TINTER) + yyerror("embedded type cannot be a pointer to interface"); } + if(isptr[t->etype]) + yyerror("embedded type cannot be a pointer"); + else if(t->etype == TFORW && t->embedlineno == 0) + t->embedlineno = lineno; +} - 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; +static Type* +structfield(Node *n) +{ + Type *f; + int lno; + + lno = lineno; + lineno = n->lineno; + + if(n->op != ODCLFIELD) + fatal("structfield: oops %N\n", n); -ok: - n = pt->nod; - copytype(pt->nod, t); - // unzero nod - pt->nod = n; + f = typ(TFIELD); + f->isddd = n->isddd; - pt->sym->lastlineno = parserline(); - declare(n, PEXTERN); + if(n->right != N) { + typecheck(&n->right, Etype); + n->type = n->right->type; + if(n->left != N) + n->left->type = n->type; + if(n->embedded) + checkembeddedtype(n->type); + } + n->right = N; + + f->type = n->type; + if(f->type == T) + f->broke = 1; + + switch(n->val.ctype) { + case CTSTR: + f->note = n->val.u.sval; + break; + default: + yyerror("field annotation must be string"); + // fallthrough + case CTxxx: + f->note = nil; + break; + } - checkwidth(pt); + if(n->left && n->left->op == ONAME) { + f->nname = n->left; + f->embedded = n->embedded; + f->sym = f->nname->sym; + } + + lineno = lno; + return f; } -/* - * structs, functions, and methods. - * they don't belong here, but where do they belong? - */ +static void +checkdupfields(Type *t, char* what) +{ + Type* t1; + int lno; + lno = lineno; + + for( ; t; t=t->down) + if(t->sym && t->nname && !isblank(t->nname)) + for(t1=t->down; t1; t1=t1->down) + if(t1->sym == t->sym) { + lineno = t->nname->lineno; + yyerror("duplicate %s %s", what, t->sym->name); + break; + } + + lineno = lno; +} /* - * turn a parsed struct into a type + * convert a parsed id/type list into + * a type for struct/interface/arglist */ -static Type** -stotype(NodeList *l, int et, Type **t, int funarg) +Type* +tostruct(NodeList *l) +{ + Type *t, *f, **tp; + t = typ(TSTRUCT); + + for(tp = &t->type; l; l=l->next) { + f = structfield(l->n); + + *tp = f; + tp = &f->down; + } + + for(f=t->type; f && !t->broke; f=f->down) + if(f->broke) + t->broke = 1; + + checkdupfields(t->type, "field"); + + if (!t->broke) + checkwidth(t); + + return t; +} + +static Type* +tofunargs(NodeList *l) +{ + Type *t, *f, **tp; + + t = typ(TSTRUCT); + t->funarg = 1; + + for(tp = &t->type; l; l=l->next) { + f = structfield(l->n); + f->funarg = 1; + + // esc.c needs to find f given a PPARAM to add the tag. + if(l->n->left && l->n->left->class == PPARAM) + l->n->left->paramfld = f; + + *tp = f; + tp = &f->down; + } + + for(f=t->type; f && !t->broke; f=f->down) + if(f->broke) + t->broke = 1; + + checkdupfields(t->type, "argument"); + return t; +} + +static Type* +interfacefield(Node *n) { - Type *f, *t1, *t2, **t0; - Strlit *note; + Type *f; int lno; - Node *n, *left; - char *what; - t0 = t; lno = lineno; - what = "field"; - if(et == TINTER) - what = "method"; + lineno = n->lineno; - for(; l; l=l->next) { - n = l->n; - lineno = n->lineno; + if(n->op != ODCLFIELD) + fatal("interfacefield: oops %N\n", n); - 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->val.ctype != CTxxx) + yyerror("interface method cannot have annotation"); + + f = typ(TFIELD); + f->isddd = n->isddd; + + if(n->right != N) { + if(n->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->left->type = n->type; + queuemethod(n); + + if(n->left->op == ONAME) { + f->nname = n->left; + f->embedded = n->embedded; + f->sym = f->nname->sym; + if(importpkg && !exportname(f->sym->name)) + f->sym = pkglookup(f->sym->name, structpkg); } - } - if(n->type == T) { - // assume error already printed - continue; - } + } else { - 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; - } + typecheck(&n->right, Etype); + n->type = n->right->type; - if(et == TINTER && left == N) { - // embedded interface - inline the methods - if(n->type->etype != TINTER) { - if(n->type->etype == TFORW) + if(n->embedded) + checkembeddedtype(n->type); + + if(n->type) + switch(n->type->etype) { + case TINTER: + break; + case TFORW: yyerror("interface type loop involving %T", n->type); - else + f->broke = 1; + break; + default: 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; - - // esc.c needs to find f given a PPARAM to add the tag. - if(funarg && n->left && n->left->class == PPARAM) - n->left->paramfld = f; - - 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; - } + f->broke = 1; + break; } - } } - - *t = f; - t = &f->down; } - *t = T; + n->right = N; + + f->type = n->type; + if(f->type == T) + f->broke = 1; + lineno = lno; - return t; + return f; } Type* -dostruct(NodeList *l, int et) +tointerface(NodeList *l) { - Type *t; - int funarg; + Type *t, *f, **tp, *t1; - /* - * convert a parsed id/type list into - * a type for struct/interface/arglist - */ + t = typ(TINTER); - 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; + tp = &t->type; + for(; l; l=l->next) { + f = interfacefield(l->n); + + if (l->n->left == N && f->type->etype == TINTER) { + // embedded interface, inline methods + for(t1=f->type->type; t1; t1=t1->down) { + f = typ(TFIELD); + f->type = t1->type; + f->broke = t1->broke; + f->sym = t1->sym; + if(f->sym) + f->nname = newname(f->sym); + *tp = f; + tp = &f->down; + } + } else { + *tp = f; + tp = &f->down; + } } - if(et == TINTER) - t = sortinter(t); - if(!funarg) - checkwidth(t); + + for(f=t->type; f && !t->broke; f=f->down) + if(f->broke) + t->broke = 1; + + checkdupfields(t->type, "method"); + t = sortinter(t); + checkwidth(t); + return t; } - Node* embedded(Sym *s) { @@ -899,7 +993,10 @@ embedded(Sym *s) *utfrune(name, CenterDot) = 0; } - n = newname(lookup(name)); + if(exportname(name) || s->pkg == builtinpkg) // old behaviour, tests pass, but is it correct? + n = newname(lookup(name)); + else + n = newname(pkglookup(name, s->pkg)); n = nod(ODCLFIELD, n, oldname(s)); n->embedded = 1; return n; @@ -964,6 +1061,17 @@ checkarglist(NodeList *all, int input) t = n; n = N; } + + // during import l->n->op is OKEY, but l->n->left->sym == S + // means it was a '?', not that it was + // a lone type This doesn't matter for the exported + // declarations, which are parsed by rules that don't + // use checkargs, but can happen for func literals in + // the inline bodies. + // TODO(rsc) this can go when typefmt case TFIELD in exportmode fmt.c prints _ instead of ? + if(importpkg && n->sym == S) + n = N; + if(n != N && n->sym == S) { t = n; n = N; @@ -1037,9 +1145,12 @@ functype(Node *this, NodeList *in, NodeList *out) 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); + t->type = tofunargs(rcvr); + t->type->down = tofunargs(out); + t->type->down->down = tofunargs(in); + + if (t->type->broke || t->type->down->broke || t->type->down->down->broke) + t->broke = 1; if(this) t->thistuple = 1; @@ -1085,9 +1196,9 @@ methodsym(Sym *nsym, Type *t0, int iface) suffix = "·i"; } if(t0->sym == S && isptr[t0->etype]) - p = smprint("(%#hT).%s%s", t0, nsym->name, suffix); + p = smprint("(%-hT).%s%s", t0, nsym->name, suffix); else - p = smprint("%#hT.%s%s", t0, nsym->name, suffix); + p = smprint("%-hT.%s%s", t0, nsym->name, suffix); s = pkglookup(p, s->pkg); free(p); return s; @@ -1121,11 +1232,16 @@ methodname1(Node *n, Node *t) } 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)); + + if(exportname(t->sym->name)) + n = newname(lookup(p)); + else + n = newname(pkglookup(p, t->sym->pkg)); free(p); return n; } @@ -1164,6 +1280,8 @@ addmethod(Sym *sf, Type *t, int local) t = t->type; } } + if(t->broke) // rely on typecheck having complained before + return; if(t != T) { if(t->sym == S) { yyerror("invalid receiver type %T (%T is an unnamed type)", pa, t); @@ -1185,8 +1303,6 @@ addmethod(Sym *sf, Type *t, int local) } pa = f; - if(importpkg && !exportname(sf->name)) - sf = pkglookup(sf->name, importpkg); n = nod(ODCLFIELD, newname(sf), N); n->type = t; @@ -1209,10 +1325,16 @@ addmethod(Sym *sf, Type *t, int local) return; } + f = structfield(n); + + // during import unexported method names should be in the type's package + if(importpkg && f->sym && !exportname(f->sym->name) && f->sym->pkg != structpkg) + fatal("imported method name %+S in wrong package %s\n", f->sym, structpkg->name); + if(d == T) - stotype(list1(n), 0, &pa->method, 0); + pa->method = f; else - stotype(list1(n), 0, &d->down, 0); + d->down = f; return; } @@ -1254,6 +1376,3 @@ funccompile(Node *n, int isclosure) funcdepth = 0; dclcontext = PEXTERN; } - - - diff --git a/src/cmd/gc/doc.go b/src/cmd/gc/doc.go index 5bb5e0e14..c704011ef 100644 --- a/src/cmd/gc/doc.go +++ b/src/cmd/gc/doc.go @@ -42,6 +42,8 @@ Flags: 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 optimizations -S write assembly language text to standard output -u diff --git a/src/cmd/gc/esc.c b/src/cmd/gc/esc.c index cd1f9770e..7e20457d9 100644 --- a/src/cmd/gc/esc.c +++ b/src/cmd/gc/esc.c @@ -35,6 +35,8 @@ static void escfunc(Node *func); static void esclist(NodeList *l); static void esc(Node *n); +static void escloopdepthlist(NodeList *l); +static void escloopdepth(Node *n); static void escassign(Node *dst, Node *src); static void esccall(Node*); static void escflows(Node *dst, Node *src); @@ -62,6 +64,7 @@ escapes(void) NodeList *l; theSink.op = ONAME; + theSink.orig = &theSink; theSink.class = PEXTERN; theSink.sym = lookup(".sink"); theSink.escloopdepth = -1; @@ -88,7 +91,7 @@ escapes(void) if(debug['m']) { for(l=noesc; l; l=l->next) if(l->n->esc == EscNone) - warnl(l->n->lineno, "%S %#N does not escape", + warnl(l->n->lineno, "%S %hN does not escape", (l->n->curfn && l->n->curfn->nname) ? l->n->curfn->nname->sym : S, l->n); } @@ -138,11 +141,64 @@ escfunc(Node *func) escassign(curfn, n); } + escloopdepthlist(curfn->nbody); esclist(curfn->nbody); curfn = savefn; loopdepth = saveld; } +// Mark labels that have no backjumps to them as not increasing loopdepth. +// Walk hasn't generated (goto|label)->left->sym->label yet, so we'll cheat +// and set it to one of the following two. Then in esc we'll clear it again. +static Label looping; +static Label nonlooping; + +static void +escloopdepthlist(NodeList *l) +{ + for(; l; l=l->next) + escloopdepth(l->n); +} + +static void +escloopdepth(Node *n) +{ + if(n == N) + return; + + escloopdepthlist(n->ninit); + + switch(n->op) { + case OLABEL: + if(!n->left || !n->left->sym) + fatal("esc:label without label: %+N", n); + // Walk will complain about this label being already defined, but that's not until + // after escape analysis. in the future, maybe pull label & goto analysis out of walk and put before esc + // if(n->left->sym->label != nil) + // fatal("escape analysis messed up analyzing label: %+N", n); + n->left->sym->label = &nonlooping; + break; + case OGOTO: + if(!n->left || !n->left->sym) + fatal("esc:goto without label: %+N", n); + // If we come past one that's uninitialized, this must be a (harmless) forward jump + // but if it's set to nonlooping the label must have preceded this goto. + if(n->left->sym->label == &nonlooping) + n->left->sym->label = &looping; + break; + } + + escloopdepth(n->left); + escloopdepth(n->right); + escloopdepthlist(n->list); + escloopdepth(n->ntest); + escloopdepth(n->nincr); + escloopdepthlist(n->nbody); + escloopdepthlist(n->nelse); + escloopdepthlist(n->rlist); + +} + static void esclist(NodeList *l) { @@ -178,7 +234,7 @@ esc(Node *n) loopdepth--; if(debug['m'] > 1) - print("%L:[%d] %#S esc: %#N\n", lineno, loopdepth, + print("%L:[%d] %S esc: %N\n", lineno, loopdepth, (curfn && curfn->nname) ? curfn->nname->sym : S, n); switch(n->op) { @@ -188,9 +244,20 @@ esc(Node *n) n->left->escloopdepth = loopdepth; break; - case OLABEL: // TODO: new loop/scope only if there are backjumps to it. - loopdepth++; - break; + case OLABEL: + if(n->left->sym->label == &nonlooping) { + if(debug['m'] > 1) + print("%L:%N non-looping label\n", lineno, n); + } else if(n->left->sym->label == &looping) { + if(debug['m'] > 1) + print("%L: %N looping label\n", lineno, n); + loopdepth++; + } + // See case OLABEL in escloopdepth above + // else if(n->left->sym->label == nil) + // fatal("escape anaylysis missed or messed up a label: %+N", n); + + n->left->sym->label = nil; case ORANGE: // Everything but fixed array is a dereference. @@ -222,7 +289,6 @@ esc(Node *n) case OAS2RECV: // v, ok = <-ch case OAS2MAPR: // v, ok = m[k] case OAS2DOTTYPE: // v, ok = x.(type) - case OAS2MAPW: // m[k] = x, ok escassign(n->list->n, n->rlist->n); break; @@ -239,6 +305,7 @@ esc(Node *n) case OPROC: // go f(x) - f and x escape escassign(&theSink, n->left->left); + escassign(&theSink, n->left->right); // ODDDARG for call for(ll=n->left->list; ll; ll=ll->next) escassign(&theSink, ll->n); break; @@ -291,6 +358,14 @@ esc(Node *n) for(ll=n->list; ll; ll=ll->next) escassign(n, ll->n->right); break; + + case OPTRLIT: + n->esc = EscNone; // until proven otherwise + noesc = list(noesc, n); + n->escloopdepth = loopdepth; + // Contents make it to memory, lose track. + escassign(&theSink, n->left); + break; case OMAPLIT: n->esc = EscNone; // until proven otherwise @@ -331,7 +406,7 @@ escassign(Node *dst, Node *src) return; if(debug['m'] > 1) - print("%L:[%d] %#S escassign: %hN = %hN\n", lineno, loopdepth, + print("%L:[%d] %S escassign: %hN = %hN\n", lineno, loopdepth, (curfn && curfn->nname) ? curfn->nname->sym : S, dst, src); setlineno(dst); @@ -387,6 +462,7 @@ escassign(Node *dst, Node *src) case ONAME: case OPARAM: case ODDDARG: + case OPTRLIT: case OARRAYLIT: case OMAPLIT: case OSTRUCTLIT: @@ -394,10 +470,14 @@ escassign(Node *dst, Node *src) escflows(dst, src); break; + case ODOT: + // A non-pointer escaping from a struct does not concern us. + if(src->type && !haspointers(src->type)) + break; + // fallthrough case OCONV: case OCONVIFACE: case OCONVNOP: - case ODOT: case ODOTMETH: // treat recv.meth as a value with recv in it, only happens in ODEFER and OPROC // iface.method already leaks iface in esccall, no need to put in extra ODOTINTER edge here case ODOTTYPE: @@ -609,7 +689,7 @@ escflood(Node *dst) } if(debug['m']>1) - print("\nescflood:%d: dst %hN scope:%#S[%d]\n", walkgen, dst, + print("\nescflood:%d: dst %hN scope:%S[%d]\n", walkgen, dst, (dst->curfn && dst->curfn->nname) ? dst->curfn->nname->sym : S, dst->escloopdepth); @@ -630,7 +710,7 @@ escwalk(int level, Node *dst, Node *src) src->walkgen = walkgen; if(debug['m']>1) - print("escwalk: level:%d depth:%d %.*s %hN scope:%#S[%d]\n", + print("escwalk: level:%d depth:%d %.*s %hN scope:%S[%d]\n", level, pdepth, pdepth, "\t\t\t\t\t\t\t\t\t\t", src, (src->curfn && src->curfn->nname) ? src->curfn->nname->sym : S, src->escloopdepth); @@ -647,12 +727,13 @@ escwalk(int level, Node *dst, Node *src) } break; + case OPTRLIT: case OADDR: if(leaks) { src->esc = EscHeap; addrescapes(src->left); if(debug['m']) - warnl(src->lineno, "%#N escapes to heap", src); + warnl(src->lineno, "%hN escapes to heap", src); } escwalk(level-1, dst, src->left); break; @@ -671,7 +752,7 @@ escwalk(int level, Node *dst, Node *src) if(leaks) { src->esc = EscHeap; if(debug['m']) - warnl(src->lineno, "%#N escapes to heap", src); + warnl(src->lineno, "%hN escapes to heap", src); } break; diff --git a/src/cmd/gc/export.c b/src/cmd/gc/export.c index 421afda8b..05fdcbf32 100644 --- a/src/cmd/gc/export.c +++ b/src/cmd/gc/export.c @@ -7,11 +7,9 @@ #include "go.h" #include "y.tab.h" -static void dumpsym(Sym*); -static void dumpexporttype(Sym*); -static void dumpexportvar(Sym*); -static void dumpexportconst(Sym*); +static void dumpexporttype(Type *t); +// Mark n's symbol as exported void exportsym(Node *n) { @@ -27,6 +25,7 @@ exportsym(Node *n) exportlist = list(exportlist, n); } +// Mark n's symbol as package-local static void packagesym(Node *n) { @@ -79,7 +78,7 @@ dumppkg(Pkg *p) { char *suffix; - if(p == nil || p == localpkg || p->exported) + if(p == nil || p == localpkg || p->exported || p == builtinpkg) return; p->exported = 1; suffix = ""; @@ -88,25 +87,84 @@ dumppkg(Pkg *p) Bprint(bout, "\timport %s \"%Z\"%s\n", p->name, p->path, suffix); } +// Look for anything we need for the inline body +static void reexportdep(Node *n); static void -dumpprereq(Type *t) +reexportdeplist(NodeList *ll) { - if(t == T) - return; + for(; ll ;ll=ll->next) + reexportdep(ll->n); +} + +static void +reexportdep(Node *n) +{ + Type *t; - if(t->printed || t == types[t->etype]) + if(!n) return; - t->printed = 1; - if(t->sym != S) { - dumppkg(t->sym->pkg); - if(t->etype != TFIELD) - dumpsym(t->sym); +// print("reexportdep %+hN\n", n); + switch(n->op) { + case ONAME: + switch(n->class&~PHEAP) { + case PFUNC: + // methods will be printed along with their type + if(!n->type || n->type->thistuple > 0) + break; + // fallthrough + case PEXTERN: + if (n->sym && n->sym->pkg != localpkg && n->sym->pkg != builtinpkg) + exportlist = list(exportlist, n); + } + break; + + + case OLITERAL: + t = n->type; + if(t != types[n->type->etype] && t != idealbool && t != idealstring) { + if(isptr[t->etype]) + t = t->type; + if (t && t->sym && t->sym->def && t->sym->pkg != localpkg && t->sym->pkg != builtinpkg) { +// print("reexport literal type %+hN\n", t->sym->def); + exportlist = list(exportlist, t->sym->def); + } + } + // fallthrough + case OTYPE: + if (n->sym && n->sym->pkg != localpkg && n->sym->pkg != builtinpkg) + exportlist = list(exportlist, n); + break; + + // for operations that need a type when rendered, put the type on the export list. + case OCONV: + case OCONVIFACE: + case OCONVNOP: + case ODOTTYPE: + case OSTRUCTLIT: + case OPTRLIT: + t = n->type; + if(!t->sym && t->type) + t = t->type; + if (t && t->sym && t->sym->def && t->sym->pkg != localpkg && t->sym->pkg != builtinpkg) { +// print("reexport convnop %+hN\n", t->sym->def); + exportlist = list(exportlist, t->sym->def); + } + break; } - dumpprereq(t->type); - dumpprereq(t->down); + + reexportdep(n->left); + reexportdep(n->right); + reexportdeplist(n->list); + reexportdeplist(n->rlist); + reexportdeplist(n->ninit); + reexportdep(n->ntest); + reexportdep(n->nincr); + reexportdeplist(n->nbody); + reexportdeplist(n->nelse); } + static void dumpexportconst(Sym *s) { @@ -119,37 +177,12 @@ dumpexportconst(Sym *s) fatal("dumpexportconst: oconst nil: %S", s); t = n->type; // may or may not be specified - if(t != T) - dumpprereq(t); + dumpexporttype(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; - } + Bprint(bout, "\tconst %#S %#T = %#V\n", s, t, &n->val); + else + Bprint(bout, "\tconst %#S = %#V\n", s, &n->val); } static void @@ -159,38 +192,27 @@ dumpexportvar(Sym *s) Type *t; n = s->def; - typecheck(&n, Erv); + typecheck(&n, Erv|Ecall); 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); + dumpexporttype(t); + + if(t->etype == TFUNC && n->class == PFUNC) { + if (n->inl) { + // when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet. + // currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package + if(debug['l'] < 2) + typecheckinl(n); + Bprint(bout, "\tfunc %#S%#hT { %#H }\n", s, t, n->inl); + reexportdeplist(n->inl); + } else + Bprint(bout, "\tfunc %#S%#hT\n", s, t); + } else + Bprint(bout, "\tvar %#S %#T\n", s, t); } static int @@ -204,12 +226,57 @@ methcmp(const void *va, const void *vb) } static void -dumpsym(Sym *s) +dumpexporttype(Type *t) { - Type *f, *t; + Type *f; Type **m; int i, n; + if(t == T) + return; + if(t->printed || t == types[t->etype] || t == bytetype || t == runetype || t == errortype) + return; + t->printed = 1; + + if(t->sym != S && t->etype != TFIELD) + dumppkg(t->sym->pkg); + + dumpexporttype(t->type); + dumpexporttype(t->down); + + if (t->sym == S || t->etype == TFIELD) + return; + + n = 0; + for(f=t->method; f!=T; f=f->down) { + dumpexporttype(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); + + Bprint(bout, "\ttype %#S %#lT\n", t->sym, t); + for(i=0; i<n; i++) { + f = m[i]; + if (f->type->nname && f->type->nname->inl) { // nname was set by caninl + // when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet. + // currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package + if(debug['l'] < 2) + typecheckinl(f->type->nname); + Bprint(bout, "\tfunc (%#T) %#hhS%#hT { %#H }\n", getthisx(f->type)->type, f->sym, f->type, f->type->nname->inl); + reexportdeplist(f->type->nname->inl); + } else + Bprint(bout, "\tfunc (%#T) %#hhS%#hT\n", getthisx(f->type)->type, f->sym, f->type); + } +} + +static void +dumpsym(Sym *s) +{ if(s->flags & SymExported) return; s->flags |= SymExported; @@ -218,56 +285,31 @@ dumpsym(Sym *s) yyerror("unknown export symbol: %S", s); return; } - +// print("dumpsym %O %+S\n", s->def->op, s); 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); - } + if(s->def->type->etype == TFORW) + yyerror("export of incomplete type %S", s); + else + dumpexporttype(s->def->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) { @@ -277,10 +319,7 @@ dumpexport(void) lno = lineno; - packagequotes = 1; - Bprint(bout, "\n$$ // exports\n"); - - Bprint(bout, " package %s", localpkg->name); + Bprint(bout, "\n$$ // exports\n package %s", localpkg->name); if(safemode) Bprint(bout, " safe"); Bprint(bout, "\n"); @@ -295,15 +334,7 @@ dumpexport(void) 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; + Bprint(bout, "\n$$ // local types\n\n$$\n"); // 6l expects this. (see ld/go.c) lineno = lno; } @@ -346,16 +377,29 @@ pkgtype(Sym *s) s->def = typenod(t); } if(s->def->type == T) - yyerror("pkgtype %lS", s); + yyerror("pkgtype %S", s); return s->def->type; } -static int -mypackage(Sym *s) +void +importimport(Sym *s, Strlit *z) { - // we import all definitions for runtime. - // lowercase ones can only be used by the compiler. - return s->pkg == localpkg || s->pkg == runtimepkg; + // Informational: record package name + // associated with import path, for use in + // human-readable messages. + Pkg *p; + + p = mkpkg(z); + if(p->name == nil) { + p->name = s->name; + pkglookup(s->name, nil)->npkg++; + } else if(strcmp(p->name, s->name) != 0) + yyerror("conflicting names %s and %s for package \"%Z\"", p->name, s->name, p->path); + + if(!incannedimport && myimportpath != nil && strcmp(z->s, myimportpath) == 0) { + yyerror("import \"%Z\": package depends on \"%Z\" (import cycle)", importpkg->path, z); + errorexit(); + } } void @@ -363,19 +407,17 @@ 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. + + 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; @@ -389,23 +431,19 @@ importconst(Sym *s, Type *t, Node *n) } void -importvar(Sym *s, Type *t, int ctxt) +importvar(Sym *s, Type *t) { 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); + 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); + declare(n, PEXTERN); if(debug['E']) print("import var %S %lT\n", s, t); @@ -414,17 +452,27 @@ importvar(Sym *s, Type *t, int ctxt) void importtype(Type *pt, Type *t) { - if(pt != T && t != T) - typedcl2(pt, 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) { + n = pt->nod; + copytype(pt->nod, t); + pt->nod = n; // unzero nod + pt->sym->lastlineno = parserline(); + declare(n, PEXTERN); + checkwidth(pt); + } else if(!eqtype(pt->orig, t)) + yyerror("inconsistent definition for type %S during import\n\t%lT\n\t%lT", pt->sym, 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/fmt.c b/src/cmd/gc/fmt.c new file mode 100644 index 000000000..31b0a623f --- /dev/null +++ b/src/cmd/gc/fmt.c @@ -0,0 +1,1626 @@ +// 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 <u.h> +#include <libc.h> +#include "go.h" +#include "opnames.h" + +// +// Format conversions +// %L int Line numbers +// +// %E int etype values (aka 'Kind') +// +// %O int Node Opcodes +// Flags: "%#O": print go syntax. (automatic unless fmtmode == FDbg) +// +// %J Node* Node details +// Flags: "%hJ" supresses things not relevant until walk. +// +// %V Val* Constant values +// +// %S Sym* Symbols +// Flags: +,- #: mode (see below) +// "%hS" unqualified identifier in any mode +// "%hhS" in export mode: unqualified identifier if exported, qualified if not +// +// %T Type* Types +// Flags: +,- #: mode (see below) +// 'l' definition instead of name. +// 'h' omit "func" and receiver in function types +// 'u' (only in -/Sym mode) print type identifiers wit package name instead of prefix. +// +// %N Node* Nodes +// Flags: +,- #: mode (see below) +// 'h' (only in +/debug mode) suppress recursion +// 'l' (only in Error mode) print "foo (type Bar)" +// +// %H NodeList* NodeLists +// Flags: those of %N +// ',' separate items with ',' instead of ';' +// +// %Z Strlit* String literals +// +// In mparith1.c: +// %B Mpint* Big integers +// %F Mpflt* Big floats +// +// %S, %T and %N obey use the following flags to set the format mode: +enum { + FErr, // error mode (default) + FDbg, // "%+N" debug mode + FExp, // "%#N" export mode + FTypeId, // "%-N" turning-types-into-symbols-mode: identical types give identical strings +}; +static int fmtmode; +static int fmtpkgpfx; // %uT stickyness +// +// E.g. for %S: %+S %#S %-S print an identifier properly qualified for debug/export/internal mode. +// +// The mode flags +, - and # are sticky, meaning they persist through +// recursions of %N, %T and %S, but not the h and l flags. The u flag is +// sticky only on %T recursions and only used in %-/Sym mode. + +// +// Useful format combinations: +// +// %+N %+H multiline recursive debug dump of node/nodelist +// %+hN %+hH non recursive debug dump +// +// %#N %#T export format +// %#lT type definition instead of name +// %#hT omit"func" and receiver in function signature +// +// %lN "foo (type Bar)" for error messages +// +// %-T type identifiers +// %-hT type identifiers without "func" and arg names in type signatures (methodsym) +// %-uT type identifiers with package name instead of prefix (typesym, dcommontype, typehash) +// + + +static int +setfmode(unsigned long *flags) +{ + int fm; + + fm = fmtmode; + if(*flags & FmtSign) + fmtmode = FDbg; + else if(*flags & FmtSharp) + fmtmode = FExp; + else if(*flags & FmtLeft) + fmtmode = FTypeId; + + *flags &= ~(FmtSharp|FmtLeft|FmtSign); + return fm; +} + +// Fmt "%L": Linenumbers +static 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'] || (fp->flags&FmtLong)) + 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; +} + +static char* +goopnames[] = +{ + [OADDR] = "&", + [OADD] = "+", + [OADDSTR] = "+", + [OANDAND] = "&&", + [OANDNOT] = "&^", + [OAND] = "&", + [OAPPEND] = "append", + [OAS] = "=", + [OAS2] = "=", + [OBREAK] = "break", + [OCALL] = "function call", // not actual syntax + [OCAP] = "cap", + [OCASE] = "case", + [OCLOSE] = "close", + [OCOMPLEX] = "complex", + [OCOM] = "^", + [OCONTINUE] = "continue", + [OCOPY] = "copy", + [ODEC] = "--", + [ODELETE] = "delete", + [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] = "^", +}; + +// Fmt "%O": Node opcodes +static int +Oconv(Fmt *fp) +{ + int o; + + o = va_arg(fp->args, int); + if((fp->flags & FmtSharp) || fmtmode != FDbg) + if(o >= 0 && o < nelem(goopnames) && goopnames[o] != nil) + return fmtstrcpy(fp, goopnames[o]); + + if(o >= 0 && o < nelem(opnames) && opnames[o] != nil) + return fmtstrcpy(fp, opnames[o]); + + return fmtprint(fp, "O-%d", o); +} + +static const char* classnames[] = { + "Pxxx", + "PEXTERN", + "PAUTO", + "PPARAM", + "PPARAMOUT", + "PPARAMREF", + "PFUNC", +}; + +// Fmt "%J": Node details. +static 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); + + switch(n->esc) { + case EscUnknown: + break; + case EscHeap: + fmtprint(fp, " esc(h)"); + break; + case EscScope: + fmtprint(fp, " esc(s)"); + break; + case EscNone: + fmtprint(fp, " esc(no)"); + break; + case EscNever: + if(!c) + fmtprint(fp, " esc(N)"); + break; + default: + fmtprint(fp, " esc(%d)", n->esc); + break; + } + + if(n->escloopdepth) + fmtprint(fp, " ld(%d)", n->escloopdepth); + + 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(n->embedded != 0) + fmtprint(fp, " embedded(%d)", n->embedded); + + if(!c && n->used != 0) + fmtprint(fp, " used(%d)", n->used); + return 0; +} + +// Fmt "%V": Values +static int +Vconv(Fmt *fp) +{ + Val *v; + vlong x; + + v = va_arg(fp->args, Val*); + + switch(v->ctype) { + case CTINT: + return fmtprint(fp, "%B", v->u.xval); + case CTRUNE: + x = mpgetfix(v->u.xval); + if(' ' <= x && x < 0x80 && x != '\\' && x != '\'') + return fmtprint(fp, "'%c'", (int)x); + if(0 <= x && x < (1<<16)) + return fmtprint(fp, "'\\u%04ux'", (int)x); + if(0 <= x && x <= Runemax) + return fmtprint(fp, "'\\U%08llux'", x); + return fmtprint(fp, "('\\x00' + %B)", v->u.xval); + case CTFLT: + if((fp->flags & FmtSharp) || fmtmode == FExp) + return fmtprint(fp, "%F", v->u.fval); + return fmtprint(fp, "%#F", v->u.fval); + case CTCPLX: + if((fp->flags & FmtSharp) || fmtmode == FExp) + return fmtprint(fp, "(%F+%F)", &v->u.cval->real, &v->u.cval->imag); + return fmtprint(fp, "(%#F + %#Fi)", &v->u.cval->real, &v->u.cval->imag); + case CTSTR: + return fmtprint(fp, "\"%Z\"", v->u.sval); + case CTBOOL: + if( v->u.bval) + return fmtstrcpy(fp, "true"); + return fmtstrcpy(fp, "false"); + case CTNIL: + return fmtstrcpy(fp, "nil"); + } + return fmtprint(fp, "<%d>", v->ctype); +} + +// Fmt "%Z": escaped string literals +static 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; +} + +/* +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", +}; + +// Fmt "%E": etype +static int +Econv(Fmt *fp) +{ + int et; + + et = va_arg(fp->args, int); + if(et >= 0 && et < nelem(etnames) && etnames[et] != nil) + return fmtstrcpy(fp, etnames[et]); + return fmtprint(fp, "E-%d", et); +} + +// Fmt "%S": syms +static int +symfmt(Fmt *fp, Sym *s) +{ + char *p; + + if(s->pkg && !(fp->flags&FmtShort)) { + switch(fmtmode) { + case FErr: // This is for the user + if(s->pkg == localpkg) + return fmtstrcpy(fp, s->name); + // If the name was used by multiple packages, display the full path, + if(s->pkg->name && pkglookup(s->pkg->name, nil)->npkg > 1) + return fmtprint(fp, "\"%Z\".%s", s->pkg->path, s->name); + return fmtprint(fp, "%s.%s", s->pkg->name, s->name); + case FDbg: + return fmtprint(fp, "%s.%s", s->pkg->name, s->name); + case FTypeId: + if(fp->flags&FmtUnsigned) + return fmtprint(fp, "%s.%s", s->pkg->name, s->name); // dcommontype, typehash + return fmtprint(fp, "%s.%s", s->pkg->prefix, s->name); // (methodsym), typesym, weaksym + case FExp: + if(s->pkg != builtinpkg) + return fmtprint(fp, "@\"%Z\".%s", s->pkg->path, s->name); + } + } + + if(fp->flags&FmtByte) { // FmtByte (hh) implies FmtShort (h) + // skip leading "type." in method name + p = utfrrune(s->name, '.'); + if(p) + p++; + else + p = s->name; + + // exportname needs to see the name without the prefix too. + if((fmtmode == FExp && !exportname(p)) || fmtmode == FDbg) + return fmtprint(fp, "@\"%Z\".%s", s->pkg->path, p); + + return fmtstrcpy(fp, p); + } + + return fmtstrcpy(fp, s->name); +} + +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", +}; + +static int +typefmt(Fmt *fp, Type *t) +{ + Type *t1; + Sym *s; + + if(t == T) + return fmtstrcpy(fp, "<T>"); + + if (t == bytetype || t == runetype) { + // in %-T mode collapse rune and byte with their originals. + if(fmtmode != FTypeId) + return fmtprint(fp, "%hS", t->sym); + t = types[t->etype]; + } + + if(t == errortype) + return fmtstrcpy(fp, "error"); + + // Unless the 'l' flag was specified, if the type has a name, just print that name. + if(!(fp->flags&FmtLong) && t->sym && t->etype != TFIELD && t != types[t->etype]) { + switch(fmtmode) { + case FTypeId: + if(fp->flags&FmtShort) + return fmtprint(fp, "%hS", t->sym); + if(fp->flags&FmtUnsigned) + return fmtprint(fp, "%uS", t->sym); + // fallthrough + case FExp: + if(t->sym->pkg == localpkg && t->vargen) + return fmtprint(fp, "%S·%d", t->sym, t->vargen); + break; + } + return fmtprint(fp, "%S", t->sym); + } + + if(t->etype < nelem(basicnames) && basicnames[t->etype] != nil) { + if(fmtmode == FErr && (t == idealbool || t == idealstring)) + fmtstrcpy(fp, "ideal "); + return fmtstrcpy(fp, basicnames[t->etype]); + } + + if(fmtmode == FDbg) + fmtprint(fp, "%E-", t->etype); + + switch(t->etype) { + case TPTR32: + case TPTR64: + if(fmtmode == FTypeId && (fp->flags&FmtShort)) + return fmtprint(fp, "*%hT", t->type); + return fmtprint(fp, "*%T", t->type); + + 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 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 TINTER: + fmtstrcpy(fp, "interface {"); + for(t1=t->type; t1!=T; t1=t1->down) + if(exportname(t1->sym->name)) { + if(t1->down) + fmtprint(fp, " %hS%hT;", t1->sym, t1->type); + else + fmtprint(fp, " %hS%hT ", t1->sym, t1->type); + } else { + // non-exported method names must be qualified + if(t1->down) + fmtprint(fp, " %uS%hT;", t1->sym, t1->type); + else + fmtprint(fp, " %uS%hT ", t1->sym, t1->type); + } + fmtstrcpy(fp, "}"); + return 0; + + case TFUNC: + if(fp->flags & FmtShort) { + fmtprint(fp, "%T", getinargx(t)); + } else { + if(t->thistuple) + fmtprint(fp, "method%T func%T", getthisx(t), getinargx(t)); + else + fmtprint(fp, "func%T", getinargx(t)); + } + switch(t->outtuple) { + case 0: + break; + case 1: + if(fmtmode != FExp) { + fmtprint(fp, " %T", getoutargx(t)->type->type); // struct->field->field's type + break; + } + default: + fmtprint(fp, " %T", getoutargx(t)); + break; + } + return 0; + + case TSTRUCT: + if(t->funarg) { + fmtstrcpy(fp, "("); + if(fmtmode == FTypeId || fmtmode == FErr) { // no argument names on function signature, and no "noescape" tags + for(t1=t->type; t1!=T; t1=t1->down) + if(t1->down) + fmtprint(fp, "%hT, ", t1); + else + fmtprint(fp, "%hT", t1); + } else { + for(t1=t->type; t1!=T; t1=t1->down) + if(t1->down) + fmtprint(fp, "%T, ", t1); + else + fmtprint(fp, "%T", t1); + } + fmtstrcpy(fp, ")"); + } else { + fmtstrcpy(fp, "struct {"); + for(t1=t->type; t1!=T; t1=t1->down) + if(t1->down) + fmtprint(fp, " %lT;", t1); + else + fmtprint(fp, " %lT ", t1); + fmtstrcpy(fp, "}"); + } + return 0; + + case TFIELD: + if(!(fp->flags&FmtShort)) { + s = t->sym; + // Take the name from the original, lest we substituted it with .anon%d + if (t->nname && (fmtmode == FErr || fmtmode == FExp)) + s = t->nname->orig->sym; + + if(s != S && !t->embedded) { + if(fp->flags&FmtLong) + fmtprint(fp, "%hhS ", s); // qualify non-exported names (used on structs, not on funarg) + else + fmtprint(fp, "%S ", s); + } else if(fmtmode == FExp) { + // TODO(rsc) this breaks on the eliding of unused arguments in the backend + // when this is fixed, the special case in dcl.c checkarglist can go. + //if(t->funarg) + // fmtstrcpy(fp, "_ "); + //else + fmtstrcpy(fp, "? "); + } + } + + if(t->isddd) + fmtprint(fp, "...%T", t->type->type); + else + fmtprint(fp, "%T", t->type); + + if(!(fp->flags&FmtShort) && t->note) + fmtprint(fp, " \"%Z\"", t->note); + return 0; + + case TFORW: + if(t->sym) + return fmtprint(fp, "undefined %S", t->sym); + return fmtstrcpy(fp, "undefined"); + + case TUNSAFEPTR: + if(fmtmode == FExp) + return fmtprint(fp, "@\"unsafe\".Pointer"); + return fmtprint(fp, "unsafe.Pointer"); + } + + if(fmtmode == FExp) + fatal("missing %E case during export", t->etype); + // Don't know how to handle - fall back to detailed prints. + return fmtprint(fp, "%E <%S> %T", t->etype, t->sym, t->type); +} + +// Statements which may be rendered with a simplestmt as init. +static int +stmtwithinit(int op) +{ + switch(op) { + case OIF: + case OFOR: + case OSWITCH: + return 1; + } + return 0; +} + +static int +stmtfmt(Fmt *f, Node *n) +{ + int complexinit, simpleinit, extrablock; + + // some statements allow for an init, but at most one, + // but we may have an arbitrary number added, eg by typecheck + // and inlining. If it doesn't fit the syntax, emit an enclosing + // block starting with the init statements. + + // if we can just say "for" n->ninit; ... then do so + simpleinit = n->ninit && !n->ninit->next && !n->ninit->n->ninit && stmtwithinit(n->op); + // otherwise, print the inits as separate statements + complexinit = n->ninit && !simpleinit && (fmtmode != FErr); + // but if it was for if/for/switch, put in an extra surrounding block to limit the scope + extrablock = complexinit && stmtwithinit(n->op); + + if(extrablock) + fmtstrcpy(f, "{"); + + if(complexinit) + fmtprint(f, " %H; ", n->ninit); + + switch(n->op){ + case ODCL: + fmtprint(f, "var %S %T", n->left->sym, n->left->type); + break; + + case ODCLFIELD: + if(n->left) + fmtprint(f, "%N %N", n->left, n->right); + else + fmtprint(f, "%N", n->right); + break; + + case OAS: + if(n->colas && !complexinit) + fmtprint(f, "%N := %N", n->left, n->right); + else + fmtprint(f, "%N = %N", n->left, n->right); + break; + + case OASOP: + fmtprint(f, "%N %#O= %N", n->left, n->etype, n->right); + break; + + case OAS2: + if(n->colas && !complexinit) { + fmtprint(f, "%,H := %,H", n->list, n->rlist); + break; + } + // fallthrough + case OAS2DOTTYPE: + case OAS2FUNC: + case OAS2MAPR: + case OAS2RECV: + fmtprint(f, "%,H = %,H", n->list, n->rlist); + break; + + case ORETURN: + fmtprint(f, "return %,H", n->list); + break; + + case OPROC: + fmtprint(f, "go %N", n->left); + break; + + case ODEFER: + fmtprint(f, "defer %N", n->left); + break; + + case OIF: + if(simpleinit) + fmtprint(f, "if %N; %N { %H }", n->ninit->n, n->ntest, n->nbody); + else + fmtprint(f, "if %N { %H }", n->ntest, n->nbody); + if(n->nelse) + fmtprint(f, " else { %H }", n->nelse); + break; + + case OFOR: + if(fmtmode == FErr) { // TODO maybe only if FmtShort, same below + fmtstrcpy(f, "for loop"); + break; + } + + fmtstrcpy(f, "for"); + if(simpleinit) + fmtprint(f, " %N;", n->ninit->n); + else if(n->nincr) + fmtstrcpy(f, " ;"); + + if(n->ntest) + fmtprint(f, " %N", n->ntest); + + if(n->nincr) + fmtprint(f, "; %N", n->nincr); + else if(simpleinit) + fmtstrcpy(f, ";"); + + + fmtprint(f, " { %H }", n->nbody); + break; + + case ORANGE: + if(fmtmode == FErr) { + fmtstrcpy(f, "for loop"); + break; + } + + fmtprint(f, "for %,H = range %N { %H }", n->list, n->right, n->nbody); + break; + + case OSELECT: + case OSWITCH: + if(fmtmode == FErr) { + fmtprint(f, "%O statement", n->op); + break; + } + + fmtprint(f, "%#O", n->op); + if(simpleinit) + fmtprint(f, " %N;", n->ninit->n); + if(n->ntest) + fmtprint(f, "%N", n->ntest); + + fmtprint(f, " { %H }", n->list); + break; + + case OCASE: + case OXCASE: + if(n->list) + fmtprint(f, "case %,H: %H", n->list, n->nbody); + else + fmtprint(f, "default: %H", n->nbody); + break; + + case OBREAK: + case OCONTINUE: + case OGOTO: + case OFALL: + case OXFALL: + if(n->left) + fmtprint(f, "%#O %N", n->op, n->left); + else + fmtprint(f, "%#O", n->op); + break; + + case OEMPTY: + break; + + case OLABEL: + fmtprint(f, "%N: ", n->left); + break; + + } + + if(extrablock) + fmtstrcpy(f, "}"); + + return 0; +} + + +static int opprec[] = { + [OAPPEND] = 8, + [OARRAYBYTESTR] = 8, + [OARRAYLIT] = 8, + [OARRAYRUNESTR] = 8, + [OCALLFUNC] = 8, + [OCALLINTER] = 8, + [OCALLMETH] = 8, + [OCALL] = 8, + [OCAP] = 8, + [OCLOSE] = 8, + [OCONVIFACE] = 8, + [OCONVNOP] = 8, + [OCONV] = 8, + [OCOPY] = 8, + [ODELETE] = 8, + [OLEN] = 8, + [OLITERAL] = 8, + [OMAKESLICE] = 8, + [OMAKE] = 8, + [OMAPLIT] = 8, + [ONAME] = 8, + [ONEW] = 8, + [ONONAME] = 8, + [OPACK] = 8, + [OPANIC] = 8, + [OPAREN] = 8, + [OPRINTN] = 8, + [OPRINT] = 8, + [ORECV] = 8, + [ORUNESTR] = 8, + [OSTRARRAYBYTE] = 8, + [OSTRARRAYRUNE] = 8, + [OSTRUCTLIT] = 8, + [OTARRAY] = 8, + [OTCHAN] = 8, + [OTFUNC] = 8, + [OTINTER] = 8, + [OTMAP] = 8, + [OTPAREN] = 8, + [OTSTRUCT] = 8, + + [OINDEXMAP] = 8, + [OINDEX] = 8, + [OSLICE] = 8, + [OSLICESTR] = 8, + [OSLICEARR] = 8, + [ODOTINTER] = 8, + [ODOTMETH] = 8, + [ODOTPTR] = 8, + [ODOTTYPE2] = 8, + [ODOTTYPE] = 8, + [ODOT] = 8, + [OXDOT] = 8, + + [OPLUS] = 7, + [ONOT] = 7, + [OCOM] = 7, + [OMINUS] = 7, + [OADDR] = 7, + [OIND] = 7, + + [OMUL] = 6, + [ODIV] = 6, + [OMOD] = 6, + [OLSH] = 6, + [ORSH] = 6, + [OAND] = 6, + [OANDNOT] = 6, + + [OADD] = 5, + [OSUB] = 5, + [OOR] = 5, + [OXOR] = 5, + + [OEQ] = 4, + [OLT] = 4, + [OLE] = 4, + [OGE] = 4, + [OGT] = 4, + [ONE] = 4, + [OCMPSTR] = 4, + [OCMPIFACE] = 4, + + [OSEND] = 3, + [OANDAND] = 2, + [OOROR] = 1, + + // Statements handled by stmtfmt + [OAS] = -1, + [OAS2] = -1, + [OAS2DOTTYPE] = -1, + [OAS2FUNC] = -1, + [OAS2MAPR] = -1, + [OAS2RECV] = -1, + [OASOP] = -1, + [OBREAK] = -1, + [OCASE] = -1, + [OCONTINUE] = -1, + [ODCL] = -1, + [ODCLFIELD] = -1, + [ODEFER] = -1, + [OEMPTY] = -1, + [OFALL] = -1, + [OFOR] = -1, + [OIF] = -1, + [OLABEL] = -1, + [OPROC] = -1, + [ORANGE] = -1, + [ORETURN] = -1, + [OSELECT] = -1, + [OSWITCH] = -1, + [OXCASE] = -1, + [OXFALL] = -1, + + [OEND] = 0 +}; + +static int +exprfmt(Fmt *f, Node *n, int prec) +{ + int nprec; + NodeList *l; + Type *t; + + while(n && n->implicit) + n = n->left; + + if(n == N) + return fmtstrcpy(f, "<N>"); + + nprec = opprec[n->op]; + if(n->op == OTYPE && n->sym != S) + nprec = 8; + + if(prec > nprec) + return fmtprint(f, "(%N)", n); + + switch(n->op) { + case OPAREN: + return fmtprint(f, "(%N)", n->left); + + case ODDDARG: + return fmtprint(f, "... argument"); + + case OREGISTER: + return fmtprint(f, "%R", n->val.u.reg); + + case OLITERAL: // this is a bit of a mess + if(fmtmode == FErr && n->sym != S) + return fmtprint(f, "%S", n->sym); + if(n->val.ctype == CTNIL) + n = n->orig; // if this node was a nil decorated with at type, print the original naked nil + if(n->type != types[n->type->etype] && n->type != idealbool && n->type != idealstring) { + // Need parens when type begins with what might + // be misinterpreted as a unary operator: * or <-. + if(isptr[n->type->etype] || (n->type->etype == TCHAN && n->type->chan == Crecv)) + return fmtprint(f, "(%T)(%V)", n->type, &n->val); + else + return fmtprint(f, "%T(%V)", n->type, &n->val); + } + return fmtprint(f, "%V", &n->val); + + case ONAME: + case OPACK: + case ONONAME: + return fmtprint(f, "%S", n->sym); + + case OTYPE: + if(n->type == T && n->sym != S) + return fmtprint(f, "%S", n->sym); + return fmtprint(f, "%T", n->type); + + case OTARRAY: + if(n->left) + return fmtprint(f, "[]%N", n->left); + return fmtprint(f, "[]%N", n->right); // happens before typecheck + + case OTPAREN: + return fmtprint(f, "(%N)", n->left); + + case OTMAP: + return fmtprint(f, "map[%N]%N", n->left, n->right); + + case OTCHAN: + switch(n->etype) { + case Crecv: + return fmtprint(f, "<-chan %N", n->left); + case Csend: + return fmtprint(f, "chan<- %N", n->left); + default: + if(n->left != N && n->left->op == TCHAN && n->left->sym == S && n->left->etype == Crecv) + return fmtprint(f, "chan (%N)", n->left); + else + return fmtprint(f, "chan %N", n->left); + } + + case OTSTRUCT: + return fmtprint(f, "<struct>"); + + case OTINTER: + return fmtprint(f, "<inter>"); + + case OTFUNC: + return fmtprint(f, "<func>"); + + case OCLOSURE: + if(fmtmode == FErr) + return fmtstrcpy(f, "func literal"); + return fmtprint(f, "%T { %H }", n->type, n->nbody); + + case OCOMPLIT: + if(fmtmode == FErr) + return fmtstrcpy(f, "composite literal"); + return fmtprint(f, "%N{ %,H }", n->right, n->list); + + case OPTRLIT: + return fmtprint(f, "&%N", n->left); + + case OSTRUCTLIT: + if (fmtmode == FExp) { // requires special handling of field names + fmtprint(f, "%T{", n->type); + for(l=n->list; l; l=l->next) { + // another special case: if n->left is an embedded field of builtin type, + // it needs to be non-qualified. Can't figure that out in %S, so do it here + if(l->n->left->type->embedded) { + t = l->n->left->type->type; + if(t->sym == S) + t = t->type; + fmtprint(f, " %T:%N", t, l->n->right); + } else + fmtprint(f, " %hhS:%N", l->n->left->sym, l->n->right); + + if(l->next) + fmtstrcpy(f, ","); + else + fmtstrcpy(f, " "); + } + return fmtstrcpy(f, "}"); + } + // fallthrough + + case OARRAYLIT: + case OMAPLIT: + if(fmtmode == FErr) + return fmtprint(f, "%T literal", n->type); + return fmtprint(f, "%T{ %,H }", n->type, n->list); + + case OKEY: + if(n->left && n->right) + return fmtprint(f, "%N:%N", n->left, n->right); + if(!n->left && n->right) + return fmtprint(f, ":%N", n->right); + if(n->left && !n->right) + return fmtprint(f, "%N:", n->left); + return fmtstrcpy(f, ":"); + + case OXDOT: + case ODOT: + case ODOTPTR: + case ODOTINTER: + case ODOTMETH: + exprfmt(f, n->left, nprec); + if(n->right == N || n->right->sym == S) + fmtstrcpy(f, ".<nil>"); + return fmtprint(f, ".%hhS", n->right->sym); + + case ODOTTYPE: + case ODOTTYPE2: + exprfmt(f, n->left, nprec); + if(n->right != N) + return fmtprint(f, ".(%N)", n->right); + return fmtprint(f, ".(%T)", n->type); + + case OINDEX: + case OINDEXMAP: + case OSLICE: + case OSLICESTR: + case OSLICEARR: + exprfmt(f, n->left, nprec); + return fmtprint(f, "[%N]", n->right); + + case OCOPY: + case OCOMPLEX: + return fmtprint(f, "%#O(%N, %N)", n->op, n->left, n->right); + + case OCONV: + case OCONVIFACE: + case OCONVNOP: + case OARRAYBYTESTR: + case OARRAYRUNESTR: + case OSTRARRAYBYTE: + case OSTRARRAYRUNE: + case ORUNESTR: + if(n->type == T || n->type->sym == S) + return fmtprint(f, "(%T)(%N)", n->type, n->left); + if(n->left) + return fmtprint(f, "%T(%N)", n->type, n->left); + return fmtprint(f, "%T(%,H)", n->type, n->list); + + case OREAL: + case OIMAG: + case OAPPEND: + case OCAP: + case OCLOSE: + case ODELETE: + case OLEN: + case OMAKE: + case ONEW: + case OPANIC: + case OPRINT: + case OPRINTN: + if(n->left) + return fmtprint(f, "%#O(%N)", n->op, n->left); + if(n->isddd) + return fmtprint(f, "%#O(%,H...)", n->op, n->list); + return fmtprint(f, "%#O(%,H)", n->op, n->list); + + case OCALL: + case OCALLFUNC: + case OCALLINTER: + case OCALLMETH: + exprfmt(f, n->left, nprec); + if(n->isddd) + return fmtprint(f, "(%,H...)", n->list); + return fmtprint(f, "(%,H)", n->list); + + case OMAKEMAP: + case OMAKECHAN: + case OMAKESLICE: + if(n->list) // pre-typecheck + return fmtprint(f, "make(%T, %,H)", n->type, n->list); + if(n->right) + return fmtprint(f, "make(%T, %N, %N)", n->type, n->left, n->right); + if(n->left) + return fmtprint(f, "make(%T, %N)", n->type, n->left); + return fmtprint(f, "make(%T)", n->type); + + // Unary + case OPLUS: + case OMINUS: + case OADDR: + case OCOM: + case OIND: + case ONOT: + case ORECV: + if(n->left->op == n->op) + fmtprint(f, "%#O ", n->op); + else + fmtprint(f, "%#O", n->op); + return exprfmt(f, n->left, nprec+1); + + // Binary + case OADD: + case OADDSTR: + case OAND: + 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); + return 0; + + case OCMPSTR: + case OCMPIFACE: + exprfmt(f, n->left, nprec); + fmtprint(f, " %#O ", n->etype); + exprfmt(f, n->right, nprec+1); + return 0; + } + + return fmtprint(f, "<node %O>", n->op); +} + +static int +nodefmt(Fmt *f, Node *n) +{ + Type *t; + + t = n->type; + if(n->orig == N) { + n->orig = n; + fatal("node with no orig %N", n); + } + + // we almost always want the original, except in export mode for literals + // this saves the importer some work, and avoids us having to redo some + // special casing for package unsafe + if(fmtmode != FExp || n->op != OLITERAL) + n = n->orig; + + if(f->flags&FmtLong && t != T) { + if(t->etype == TNIL) + return fmtprint(f, "nil"); + else + return fmtprint(f, "%N (type %T)", n, t); + } + + // TODO inlining produces expressions with ninits. we can't print these yet. + + if(opprec[n->op] < 0) + return stmtfmt(f, n); + + return exprfmt(f, n, 0); +} + +static int dumpdepth; + +static void +indent(Fmt *fp) +{ + int i; + + fmtstrcpy(fp, "\n"); + for(i = 0; i < dumpdepth; ++i) + fmtstrcpy(fp, ". "); +} + +static int +nodedump(Fmt *fp, Node *n) +{ + int recur; + + if(n == N) + return 0; + + recur = !(fp->flags&FmtShort); + + if(recur) { + indent(fp); + if(dumpdepth > 10) + return fmtstrcpy(fp, "..."); + + if(n->ninit != nil) { + fmtprint(fp, "%O-init%H", n->op, n->ninit); + indent(fp); + } + } + +// fmtprint(fp, "[%p]", n); + + switch(n->op) { + default: + fmtprint(fp, "%O%J", n->op, n); + break; + case OREGISTER: + fmtprint(fp, "%O-%R%J", n->op, n->val.u.reg, n); + break; + case OLITERAL: + fmtprint(fp, "%O-%V%J", n->op, &n->val, n); + break; + case ONAME: + case ONONAME: + if(n->sym != S) + fmtprint(fp, "%O-%S%J", n->op, n->sym, n); + else + fmtprint(fp, "%O%J", n->op, n); + break; + case OASOP: + fmtprint(fp, "%O-%O%J", n->op, n->etype, n); + break; + case OTYPE: + fmtprint(fp, "%O %S type=%T", n->op, n->sym, n->type); + if(recur && n->type == T && n->ntype) { + indent(fp); + fmtprint(fp, "%O-ntype%N", n->op, n->ntype); + } + break; + } + + if(n->sym != S && n->op != ONAME) + fmtprint(fp, " %S G%d", n->sym, n->vargen); + + if(n->type != T) + fmtprint(fp, " %T", n->type); + + if(recur) { + if(n->left) + fmtprint(fp, "%N", n->left); + if(n->right) + fmtprint(fp, "%N", n->right); + if(n->list) { + indent(fp); + fmtprint(fp, "%O-list%H", n->op, n->list); + } + if(n->rlist) { + indent(fp); + fmtprint(fp, "%O-rlist%H", n->op, n->rlist); + } + if(n->ntest) { + indent(fp); + fmtprint(fp, "%O-test%N", n->op, n->ntest); + } + if(n->nbody) { + indent(fp); + fmtprint(fp, "%O-body%H", n->op, n->nbody); + } + if(n->nelse) { + indent(fp); + fmtprint(fp, "%O-else%H", n->op, n->nelse); + } + if(n->nincr) { + indent(fp); + fmtprint(fp, "%O-incr%N", n->op, n->nincr); + } + } + + return 0; +} + +// Fmt "%S": syms +// Flags: "%hS" suppresses qualifying with package +static int +Sconv(Fmt *fp) +{ + Sym *s; + int r, sm; + unsigned long sf; + + s = va_arg(fp->args, Sym*); + if(s == S) + return fmtstrcpy(fp, "<S>"); + + if(s->name && s->name[0] == '_' && s->name[1] == '\0') + return fmtstrcpy(fp, "_"); + + sf = fp->flags; + sm = setfmode(&fp->flags); + r = symfmt(fp, s); + fp->flags = sf; + fmtmode = sm; + return r; +} + +// Fmt "%T": types. +// Flags: 'l' print definition, not name +// 'h' omit 'func' and receiver from function types, short type names +// 'u' package name, not prefix (FTypeId mode, sticky) +static int +Tconv(Fmt *fp) +{ + Type *t; + int r, sm; + unsigned long sf; + + t = va_arg(fp->args, Type*); + if(t == T) + return fmtstrcpy(fp, "<T>"); + + if(t->trecur > 4) + return fmtstrcpy(fp, "<...>"); + + t->trecur++; + sf = fp->flags; + sm = setfmode(&fp->flags); + + if(fmtmode == FTypeId && (sf&FmtUnsigned)) + fmtpkgpfx++; + if(fmtpkgpfx) + fp->flags |= FmtUnsigned; + + r = typefmt(fp, t); + + if(fmtmode == FTypeId && (sf&FmtUnsigned)) + fmtpkgpfx--; + + fp->flags = sf; + fmtmode = sm; + t->trecur--; + return r; +} + +// Fmt '%N': Nodes. +// Flags: 'l' suffix with "(type %T)" where possible +// '+h' in debug mode, don't recurse, no multiline output +static int +Nconv(Fmt *fp) +{ + Node *n; + int r, sm; + unsigned long sf; + + n = va_arg(fp->args, Node*); + if(n == N) + return fmtstrcpy(fp, "<N>"); + sf = fp->flags; + sm = setfmode(&fp->flags); + + r = -1; + switch(fmtmode) { + case FErr: + case FExp: + r = nodefmt(fp, n); + break; + case FDbg: + dumpdepth++; + r = nodedump(fp, n); + dumpdepth--; + break; + default: + fatal("unhandled %%N mode"); + } + + fp->flags = sf; + fmtmode = sm; + return r; +} + +// Fmt '%H': NodeList. +// Flags: all those of %N plus ',': separate with comma's instead of semicolons. +static int +Hconv(Fmt *fp) +{ + NodeList *l; + int r, sm; + unsigned long sf; + char *sep; + + l = va_arg(fp->args, NodeList*); + + if(l == nil && fmtmode == FDbg) + return fmtstrcpy(fp, "<nil>"); + + sf = fp->flags; + sm = setfmode(&fp->flags); + r = 0; + sep = "; "; + if(fmtmode == FDbg) + sep = "\n"; + else if(fp->flags & FmtComma) + sep = ", "; + + for(;l; l=l->next) { + r += fmtprint(fp, "%N", l->n); + if(l->next) + r += fmtstrcpy(fp, sep); + } + + fp->flags = sf; + fmtmode = sm; + return r; +} + +void +fmtinstallgo(void) +{ + fmtmode = FErr; + fmtinstall('E', Econv); // etype opcodes + fmtinstall('J', Jconv); // all the node flags + fmtinstall('H', Hconv); // node lists + fmtinstall('L', Lconv); // line number + fmtinstall('N', Nconv); // node pointer + fmtinstall('O', Oconv); // node opcodes + fmtinstall('S', Sconv); // sym pointer + fmtinstall('T', Tconv); // type pointer + fmtinstall('V', Vconv); // val pointer + fmtinstall('Z', Zconv); // escaped string + + // These are in mparith1.c + fmtinstall('B', Bconv); // big numbers + fmtinstall('F', Fconv); // big float numbers + +} + +void +dumplist(char *s, NodeList *l) +{ + print("%s\n%+H\n", s, l); +} + +void +dump(char *s, Node *n) +{ + print("%s [%p]\n%+N\n", s, n, n); +} diff --git a/src/cmd/gc/gen.c b/src/cmd/gc/gen.c index a818dbc19..694a10ab5 100644 --- a/src/cmd/gc/gen.c +++ b/src/cmd/gc/gen.c @@ -54,7 +54,7 @@ addrescapes(Node *n) if(n->class == PAUTO && n->esc == EscNever) break; - if(debug['s'] && n->esc != EscUnknown) + if(debug['N'] && n->esc != EscUnknown) fatal("without escape analysis, only PAUTO's should have esc: %N", n); switch(n->class) { @@ -91,10 +91,10 @@ addrescapes(Node *n) snprint(buf, sizeof buf, "&%S", n->sym); n->heapaddr->sym = lookup(buf); n->heapaddr->orig->sym = n->heapaddr->sym; - if(!debug['s']) + if(!debug['N']) n->esc = EscHeap; if(debug['m']) - print("%L: moved to heap: %hN\n", n->lineno, n); + print("%L: moved to heap: %N\n", n->lineno, n); curfn = oldfn; break; } @@ -805,6 +805,7 @@ tempname(Node *nn, Type *t) s = lookup(namebuf); n = nod(ONAME, N, N); n->sym = s; + s->def = n; n->type = t; n->class = PAUTO; n->addable = 1; @@ -825,5 +826,6 @@ temp(Type *t) n = nod(OXXX, N, N); tempname(n, t); + n->sym->def->used = 1; return n; } diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h index f72799420..9584bb744 100644 --- a/src/cmd/gc/go.h +++ b/src/cmd/gc/go.h @@ -16,6 +16,12 @@ #undef BUFSIZ +// The parser's maximum stack size. +// We have to use a #define macro here since yacc +// or bison will check for its definition and use +// a potentially smaller value if it is undefined. +#define YYMAXDEPTH 500 + enum { NHUNK = 50000, @@ -23,7 +29,6 @@ enum NSYMB = 500, NHASH = 1024, STRINGSZ = 200, - YYMAXDEPTH = 500, MAXALIGN = 7, UINF = 100, HISTSZ = 10, @@ -32,23 +37,30 @@ enum AUNK = 100, - // these values are known by runtime + // These values are known by runtime. + // The MEMx and NOEQx values must run in parallel. See algtype. AMEM = 0, - ANOEQ, - ASTRING, - AINTER, - ANILINTER, - ASLICE, + AMEM0, AMEM8, AMEM16, AMEM32, AMEM64, AMEM128, + ANOEQ, + ANOEQ0, ANOEQ8, ANOEQ16, ANOEQ32, ANOEQ64, ANOEQ128, + ASTRING, + AINTER, + ANILINTER, + ASLICE, + AFLOAT32, + AFLOAT64, + ACPLX64, + ACPLX128, BADWIDTH = -1000000000, }; @@ -67,33 +79,6 @@ struct Strlit 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 @@ -135,7 +120,7 @@ struct Val { short reg; // OREGISTER short bval; // bool value CTBOOL - Mpint* xval; // int CTINT + Mpint* xval; // int CTINT, rune CTRUNE Mpflt* fval; // float CTFLT Mpcplx* cval; // float CTCPLX Strlit* sval; // string CTSTR @@ -157,12 +142,12 @@ struct Type uchar printed; uchar embedded; // TFIELD embedded type uchar siggen; - uchar funarg; + uchar funarg; // on TSTRUCT and TFIELD uchar copyany; uchar local; // created in this file uchar deferwidth; uchar broke; - uchar isddd; // TFIELD is ... argument + uchar isddd; // TFIELD is ... argument uchar align; Node* nod; // canonical OTYPE node @@ -266,6 +251,8 @@ struct Node uchar isddd; uchar readonly; uchar implicit; // don't show in printout + uchar addrtaken; // address taken, even if not moved to heap + uchar dupok; // duplicate definitions ok (for func) // most nodes Type* type; @@ -279,6 +266,7 @@ struct Node NodeList* exit; NodeList* cvars; // closure params NodeList* dcl; // autodcl for this func/closure + NodeList* inl; // copy of the body for use in inlining // OLITERAL/OREGISTER Val val; @@ -299,6 +287,9 @@ struct Node Node* outer; // outer PPARAMREF in nested closure Node* closure; // ONAME/PHEAP <-> ONAME/PPARAMREF + // ONAME substitute while inlining + Node* inlvar; + // OPACK Pkg* pkg; @@ -346,9 +337,9 @@ struct NodeList enum { - SymExport = 1<<0, + SymExport = 1<<0, // to be exported SymPackage = 1<<1, - SymExported = 1<<2, + SymExported = 1<<2, // already written out by export SymUniq = 1<<3, SymSiggen = 1<<4, }; @@ -375,10 +366,10 @@ EXTERN Sym* dclstack; struct Pkg { - char* name; - Strlit* path; + char* name; // package name + Strlit* path; // string literal used in import statement Sym* pathsym; - char* prefix; + char* prefix; // escaped path for use in symbol table Pkg* link; char exported; // import line written in export data char direct; // imported directly @@ -422,17 +413,19 @@ enum OAPPEND, OARRAYBYTESTR, OARRAYRUNESTR, OSTRARRAYBYTE, OSTRARRAYRUNE, - OAS, OAS2, OAS2MAPW, OAS2FUNC, OAS2RECV, OAS2MAPR, OAS2DOTTYPE, OASOP, + OAS, OAS2, OAS2FUNC, OAS2RECV, OAS2MAPR, OAS2DOTTYPE, + OASOP, OBAD, OCALL, OCALLFUNC, OCALLMETH, OCALLINTER, OCAP, OCLOSE, OCLOSURE, OCMPIFACE, OCMPSTR, - OCOMPLIT, OMAPLIT, OSTRUCTLIT, OARRAYLIT, + OCOMPLIT, OMAPLIT, OSTRUCTLIT, OARRAYLIT, OPTRLIT, OCONV, OCONVIFACE, OCONVNOP, OCOPY, ODCL, ODCLFUNC, ODCLFIELD, ODCLCONST, ODCLTYPE, + ODELETE, ODOT, ODOTPTR, ODOTMETH, ODOTINTER, OXDOT, ODOTTYPE, ODOTTYPE2, @@ -490,6 +483,7 @@ enum // misc ODDD, ODDDARG, + OINLCALL, // intermediary representation of an inlined call // for back ends OCMP, ODEC, OEXTEND, OINC, OREGISTER, OINDREG, @@ -546,6 +540,7 @@ enum CTxxx, CTINT, + CTRUNE, CTFLT, CTCPLX, CTSTR, @@ -779,6 +774,9 @@ EXTERN Idir* idirs; EXTERN Type* types[NTYPE]; EXTERN Type* idealstring; EXTERN Type* idealbool; +EXTERN Type* bytetype; +EXTERN Type* runetype; +EXTERN Type* errortype; EXTERN uchar simtype[NTYPE]; EXTERN uchar isptr[NTYPE]; EXTERN uchar isforw[NTYPE]; @@ -810,7 +808,7 @@ EXTERN NodeList* xtop; EXTERN NodeList* externdcl; EXTERN NodeList* closures; EXTERN NodeList* exportlist; -EXTERN NodeList* typelist; +EXTERN NodeList* importlist; // imported functions and methods with inlinable bodies EXTERN int dclcontext; // PEXTERN/PAUTO EXTERN int incannedimport; EXTERN int statuniqgen; // name generator for static temps @@ -834,18 +832,13 @@ 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; /* @@ -930,7 +923,6 @@ 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); @@ -951,9 +943,10 @@ void popdcl(void); void poptodcl(void); void redeclare(Sym *s, char *where); void testdclstack(void); +Type* tointerface(NodeList *l); +Type* tostruct(NodeList *l); 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); @@ -969,14 +962,21 @@ 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); +void importconst(Sym *s, Type *t, Node *n); +void importimport(Sym *s, Strlit *z); +Sym* importsym(Sym *s, int op); +void importtype(Type *pt, Type *t); +void importvar(Sym *s, Type *t); Type* pkgtype(Sym *s); /* + * fmt.c + */ +void fmtinstallgo(void); +void dump(char *s, Node *n); +void dumplist(char *s, NodeList *l); + +/* * gen.c */ void addrescapes(Node *n); @@ -995,7 +995,14 @@ Node* temp(Type*); * init.c */ void fninit(NodeList *n); -Node* renameinit(Node *n); +Sym* renameinit(void); + +/* + * inl.c + */ +void caninl(Node *fn); +void inlcalls(Node *fn); +void typecheckinl(Node *fn); /* * lex.c @@ -1003,6 +1010,7 @@ Node* renameinit(Node *n); void cannedimports(char *file, char *cp); void importfile(Val *f, int line); char* lexname(int lex); +char* expstring(void); void mkpackage(char* pkgname); void unimportfile(void); int32 yylex(void); @@ -1085,10 +1093,9 @@ void ieeedtod(uint64 *ieee, double native); Sym* stringsym(char*, int); /* - * print.c + * order.c */ -void exprfmt(Fmt *f, Node *n, int prec); -void exprlistfmt(Fmt *f, NodeList *l); +void order(Node *fn); /* * range.c @@ -1103,6 +1110,7 @@ void dumptypestructs(void); Type* methodfunc(Type *f, Type*); Node* typename(Type *t); Sym* typesym(Type *t); +Sym* typesymprefix(char *prefix, Type *t); int haspointers(Type *t); /* @@ -1123,19 +1131,12 @@ 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); +void addinit(Node**, NodeList*); Type* aindex(Node *b, Type *t); int algtype(Type *t); +int algtype1(Type *t, Type **bad); void argtype(Node *on, Type *t); Node* assignconv(Node *n, Type *t, char *context); int assignop(Type *src, Type *dst, char **why); @@ -1144,10 +1145,9 @@ int brcom(int a); int brrev(int a); NodeList* concat(NodeList *a, NodeList *b); int convertop(Type *src, Type *dst, char **why); +Node* copyexpr(Node*, Type*, NodeList**); 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); @@ -1158,6 +1158,8 @@ void frame(int context); Type* funcfirst(Iter *s, Type *t); Type* funcnext(Iter *s); void genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface); +void genhash(Sym *sym, Type *t); +void geneq(Sym *sym, Type *t); Type** getinarg(Type *t); Type* getinargx(Type *t); Type** getoutarg(Type *t); @@ -1237,7 +1239,6 @@ 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); @@ -1250,6 +1251,7 @@ void queuemethod(Node *n); /* * unsafe.c */ +int isunsafebuiltin(Node *n); Node* unsafenmagic(Node *n); /* @@ -1266,6 +1268,7 @@ void walkexprlist(NodeList *l, NodeList **init); void walkexprlistsafe(NodeList *l, NodeList **init); void walkstmt(Node **np); void walkstmtlist(NodeList *l); +Node* conv(Node*, Type*); /* * arch-specific ggen.c/gsubr.c/gobj.c/pgen.c @@ -1335,11 +1338,16 @@ void zname(Biobuf *b, Sym *s, int t); #pragma varargck type "D" Addr* #pragma varargck type "lD" Addr* #pragma varargck type "E" int +#pragma varargck type "E" uint #pragma varargck type "F" Mpflt* +#pragma varargck type "H" NodeList* #pragma varargck type "J" Node* +#pragma varargck type "lL" int +#pragma varargck type "lL" uint #pragma varargck type "L" int #pragma varargck type "L" uint #pragma varargck type "N" Node* +#pragma varargck type "lN" Node* #pragma varargck type "O" uint #pragma varargck type "P" Prog* #pragma varargck type "Q" Bits @@ -1348,5 +1356,6 @@ void zname(Biobuf *b, Sym *s, int t); #pragma varargck type "lS" Sym* #pragma varargck type "T" Type* #pragma varargck type "lT" Type* +#pragma varargck type "V" Val* #pragma varargck type "Y" char* #pragma varargck type "Z" Strlit* diff --git a/src/cmd/gc/go.y b/src/cmd/gc/go.y index 0c007f5f0..de0735425 100644 --- a/src/cmd/gc/go.y +++ b/src/cmd/gc/go.y @@ -31,13 +31,13 @@ static void fixlbrace(int); Type* type; Sym* sym; struct Val val; - int lint; + int i; } // |sed 's/.* //' |9 fmt -l1 |sort |9 fmt -l50 | sed 's/^/%xxx /' %token <val> LLITERAL -%token <lint> LASOP +%token <i> 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 @@ -47,7 +47,7 @@ static void fixlbrace(int); %token LANDAND LANDNOT LBODY LCOMM LDEC LEQ LGE LGT %token LIGNORE LINC LLE LLSH LLT LNE LOROR LRSH -%type <lint> lbrace import_here +%type <i> lbrace import_here %type <sym> sym packname %type <val> oliteral @@ -56,7 +56,7 @@ static void fixlbrace(int); %type <node> case caseblock %type <node> compound_stmt dotname embed expr complitexpr %type <node> expr_or_type -%type <node> fndcl fnliteral +%type <node> fndcl hidden_fndcl fnliteral %type <node> for_body for_header for_stmt if_header if_stmt else non_dcl_stmt %type <node> interfacedcl keyval labelname name %type <node> name_or_type non_expr_type @@ -66,7 +66,7 @@ static void fixlbrace(int); %type <node> pseudocall range_stmt select_stmt %type <node> simple_stmt %type <node> switch_stmt uexpr -%type <node> xfndcl typedcl +%type <node> xfndcl typedcl start_complit %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 @@ -78,12 +78,10 @@ static void fixlbrace(int); %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 <node> hidden_constant hidden_literal hidden_funarg +%type <node> hidden_interfacedcl hidden_structdcl %type <list> hidden_funres %type <list> ohidden_funres @@ -237,7 +235,7 @@ import_here: } import_package: - LPACKAGE sym import_safety ';' + LPACKAGE LNAME import_safety ';' { if(importpkg->name == nil) { importpkg->name = $2->name; @@ -420,18 +418,15 @@ simple_stmt: | expr_list LCOLAS expr_list { if($3->n->op == OTYPESW) { - Node *n; - - n = N; + $$ = nod(OTYPESW, N, $3->n->right); 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); + yyerror("invalid variable name %N in type switch", $1->n); else - n = $1->n; - $$ = nod(OTYPESW, n, $3->n->right); + $$->left = dclname($1->n->sym); // it's a colas, so must not re-use an oldname. break; } $$ = colas($1, $3); @@ -450,7 +445,7 @@ simple_stmt: case: LCASE expr_or_type_list ':' { - Node *n; + Node *n, *nn; // will be converted to OCASE // right will point to next case @@ -460,12 +455,13 @@ case: $$->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; + nn = newname(n->sym); + declare(nn, dclcontext); + $$->nname = nn; + + // keep track of the instances for reporting unused + nn->defn = typesw->right; } - break; } | LCASE expr_or_type_list '=' expr ':' { @@ -496,16 +492,18 @@ case: } | LDEFAULT ':' { - Node *n; + Node *n, *nn; 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; + nn = newname(n->sym); + declare(nn, dclcontext); + $$->nname = nn; + + // keep track of the instances for reporting unused + nn->defn = typesw->right; } } @@ -806,7 +804,14 @@ uexpr: } | '&' uexpr { - $$ = nod(OADDR, $2, N); + if($2->op == OCOMPLIT) { + // Special case for &T{...}: turn into (*T){...}. + $$ = $2; + $$->right = nod(OIND, $$->right, N); + $$->right->implicit = 1; + } else { + $$ = nod(OADDR, $2, N); + } } | '+' uexpr { @@ -895,29 +900,35 @@ pexpr_no_paren: $$ = nod(OCALL, $1, N); $$->list = list1($3); } -| comptype lbrace braced_keyval_list '}' +| comptype lbrace start_complit braced_keyval_list '}' { - // composite expression - $$ = nod(OCOMPLIT, N, $1); - $$->list = $3; - + $$ = $3; + $$->right = $1; + $$->list = $4; fixlbrace($2); } -| pexpr_no_paren '{' braced_keyval_list '}' +| pexpr_no_paren '{' start_complit braced_keyval_list '}' { - // composite expression - $$ = nod(OCOMPLIT, N, $1); - $$->list = $3; + $$ = $3; + $$->right = $1; + $$->list = $4; } -| '(' expr_or_type ')' '{' braced_keyval_list '}' +| '(' expr_or_type ')' '{' start_complit braced_keyval_list '}' { yyerror("cannot parenthesize type in composite literal"); - // composite expression - $$ = nod(OCOMPLIT, N, $2); - $$->list = $5; + $$ = $5; + $$->right = $2; + $$->list = $6; } | fnliteral +start_complit: + { + // composite expression. + // make node early so we get the right line number. + $$ = nod(OCOMPLIT, N, N); + } + keyval: expr ':' complitexpr { @@ -926,10 +937,10 @@ keyval: complitexpr: expr -| '{' braced_keyval_list '}' +| '{' start_complit braced_keyval_list '}' { - $$ = nod(OCOMPLIT, N, N); - $$->list = $2; + $$ = $2; + $$->list = $3; } pexpr: @@ -993,6 +1004,26 @@ onew_name: sym: LNAME + { + $$ = $1; + // during imports, unqualified non-exported identifiers are from builtinpkg + if(importpkg != nil && !exportname($1->name)) + $$ = pkglookup($1->name, builtinpkg); + } +| hidden_importsym +| '?' + { + $$ = S; + } + +hidden_importsym: + '@' LLITERAL '.' LNAME + { + if($2.u.sval->len == 0) + $$ = pkglookup($4->name, importpkg); + else + $$ = pkglookup($4->name, mkpkg($2.u.sval)); + } name: sym %prec NotParen @@ -1165,38 +1196,43 @@ xfndcl: } fndcl: - dcl_name '(' oarg_type_list_ocomma ')' fnres + sym '(' oarg_type_list_ocomma ')' fnres { - Node *n; + Node *t; + $$ = 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(strcmp($1->name, "init") == 0) { + $1 = renameinit(); 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(strcmp(localpkg->name, "main") == 0 && strcmp($1->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; + + t = nod(OTFUNC, N, N); + t->list = $3; + t->rlist = $5; + + $$ = nod(ODCLFUNC, N, N); + $$->nname = newname($1); + $$->nname->defn = $$; + $$->nname->ntype = t; // TODO: check if nname already has an ntype + declare($$->nname, PFUNC); + funchdr($$); } | '(' oarg_type_list_ocomma ')' sym '(' oarg_type_list_ocomma ')' fnres { Node *rcvr, *t; - Node *name; - - name = newname($4); + + $$ = N; $2 = checkarglist($2, 0); $6 = checkarglist($6, 1); - $$ = N; + if($2 == nil) { yyerror("method has no receiver"); break; @@ -1213,15 +1249,59 @@ fndcl: 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; + + $$ = nod(ODCLFUNC, N, N); + $$->shortname = newname($4); + $$->nname = methodname1($$->shortname, rcvr->right); + $$->nname->defn = $$; $$->nname->ntype = t; - $$->shortname = name; + declare($$->nname, PFUNC); + + funchdr($$); + } + +hidden_fndcl: + hidden_pkg_importsym '(' ohidden_funarg_list ')' ohidden_funres + { + Sym *s; + Type *t; + + $$ = N; + + s = $1; + t = functype(N, $3, $5); + + importsym(s, ONAME); + if(s->def != N && s->def->op == ONAME) { + if(eqtype(t, s->def->type)) + break; + yyerror("inconsistent definition for func %S during import\n\t%T\n\t%T", s, s->def->type, t); + } + + $$ = newname(s); + $$->type = t; + declare($$, PFUNC); + funchdr($$); } +| '(' hidden_funarg_list ')' sym '(' ohidden_funarg_list ')' ohidden_funres + { + $$ = methodname1(newname($4), $2->n->right); + $$->type = functype($2->n, $6, $8); + + checkwidth($$->type); + addmethod($4, $$->type, 0); + funchdr($$); + + // inl.c's inlnode in on a dotmeth node expects to find the inlineable body as + // (dotmeth's type)->nname->inl, and dotmeth's type has been pulled + // out by typecheck's lookdot as this $$->ttype. So by providing + // this back link here we avoid special casing there. + $$->type->nname = $$; + } fntype: LFUNC '(' oarg_type_list_ocomma ')' fnres @@ -1538,6 +1618,18 @@ non_dcl_stmt: { $$ = nod(ORETURN, N, N); $$->list = $2; + if($$->list == nil && curfn != N) { + NodeList *l; + + for(l=curfn->dcl; l; l=l->next) { + if(l->n->class == PPARAM) + continue; + if(l->n->class != PPARAMOUT) + break; + if(l->n->sym->def != l->n) + yyerror("%s is shadowed during return", l->n->sym->name); + } + } } stmt_list: @@ -1676,31 +1768,16 @@ oliteral: | LLITERAL /* - * import syntax from header of - * an output package + * import syntax from package header */ hidden_import: - LIMPORT sym LLITERAL ';' + LIMPORT LNAME 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); - if(!incannedimport && myimportpath != nil && strcmp($3.u.sval->s, myimportpath) == 0) { - yyerror("import \"%Z\": package depends on \"%Z\" (import cycle)", importpkg->path, $3.u.sval); - errorexit(); - } + importimport($2, $3.u.sval); } | LVAR hidden_pkg_importsym hidden_type ';' { - importvar($2, $3, PEXTERN); + importvar($2, $3); } | LCONST hidden_pkg_importsym '=' hidden_constant ';' { @@ -1714,17 +1791,28 @@ hidden_import: { importtype($2, $3); } -| LFUNC hidden_pkg_importsym '(' ohidden_funarg_list ')' ohidden_funres ';' +| LFUNC hidden_fndcl fnbody ';' { - importvar($2, functype(N, $4, $6), PFUNC); + if($2 == N) + break; + + $2->inl = $3; + + funcbody($2); + importlist = list(importlist, $2); + + if(debug['E']) { + print("import [%Z] func %lN \n", importpkg->path, $2); + if(debug['l'] > 2 && $2->inl) + print("inl body:%+H\n", $2->inl); + } } -| LFUNC '(' hidden_funarg_list ')' sym '(' ohidden_funarg_list ')' ohidden_funres ';' + +hidden_pkg_importsym: + hidden_importsym { - if($3->next != nil || $3->n->op != ODCLFIELD) { - yyerror("bad receiver in method"); - YYERROR; - } - importmethod($5, functype($3->n, $7, $9)); + $$ = $1; + structpkg = $$->pkg; } hidden_pkgtype: @@ -1734,6 +1822,10 @@ hidden_pkgtype: importsym($1, OTYPE); } +/* + * importing types + */ + hidden_type: hidden_type_misc | hidden_type_recv_chan @@ -1772,11 +1864,11 @@ hidden_type_misc: } | LSTRUCT '{' ohidden_structdcl_list '}' { - $$ = dostruct($3, TSTRUCT); + $$ = tostruct($3); } | LINTERFACE '{' ohidden_interfacedcl_list '}' { - $$ = dostruct($3, TINTER); + $$ = tointerface($3); } | '*' hidden_type { @@ -1815,61 +1907,45 @@ hidden_type_func: $$ = functype(nil, $3, $5); } -hidden_opt_sym: - sym - { - $$ = newname($1); - } -| '?' - { - $$ = N; - } - -hidden_dcl: - hidden_opt_sym hidden_type hidden_tag +hidden_funarg: + sym hidden_type oliteral { - $$ = nod(ODCLFIELD, $1, typenod($2)); + $$ = nod(ODCLFIELD, N, typenod($2)); + if($1) + $$->left = newname($1); $$->val = $3; } -| hidden_opt_sym LDDD hidden_type hidden_tag +| sym LDDD hidden_type oliteral { Type *t; - + t = typ(TARRAY); t->bound = -1; t->type = $3; - $$ = nod(ODCLFIELD, $1, typenod(t)); + + $$ = nod(ODCLFIELD, N, typenod(t)); + if($1) + $$->left = newname($1); $$->isddd = 1; $$->val = $4; } hidden_structdcl: - sym hidden_type hidden_tag - { - $$ = nod(ODCLFIELD, newname($1), typenod($2)); - $$->val = $3; - } -| '?' hidden_type hidden_tag + sym hidden_type oliteral { 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; + if($1 != S) { + $$ = nod(ODCLFIELD, newname($1), typenod($2)); + $$->val = $3; + } else { + s = $2->sym; + if(s == S && isptr[$2->etype]) + s = $2->type->sym; + $$ = embedded(s); + $$->right = typenod($2); + $$->val = $3; + } } hidden_interfacedcl: @@ -1877,9 +1953,9 @@ hidden_interfacedcl: { $$ = nod(ODCLFIELD, newname($1), typenod(functype(fakethis(), $3, $5))); } -| hidden_importsym '(' ohidden_funarg_list ')' ohidden_funres +| hidden_type { - $$ = nod(ODCLFIELD, newname($1), typenod(functype(fakethis(), $3, $5))); + $$ = nod(ODCLFIELD, N, typenod($1)); } ohidden_funres: @@ -1898,6 +1974,10 @@ hidden_funres: $$ = list1(nod(ODCLFIELD, N, typenod($1))); } +/* + * importing constants + */ + hidden_literal: LLITERAL { @@ -1908,6 +1988,7 @@ hidden_literal: $$ = nodlit($2); switch($$->val.ctype){ case CTINT: + case CTRUNE: mpnegfix($$->val.u.xval); break; case CTFLT: @@ -1928,37 +2009,23 @@ hidden_constant: hidden_literal | '(' hidden_literal '+' hidden_literal ')' { + if($2->val.ctype == CTRUNE && $4->val.ctype == CTINT) { + $$ = $2; + mpaddfixfix($2->val.u.xval, $4->val.u.xval); + break; + } $$ = 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 + hidden_funarg { $$ = list1($1); } -| hidden_funarg_list ',' hidden_dcl +| hidden_funarg_list ',' hidden_funarg { $$ = list($1, $3); } diff --git a/src/cmd/gc/init.c b/src/cmd/gc/init.c index da69e41ae..be402cc0c 100644 --- a/src/cmd/gc/init.c +++ b/src/cmd/gc/init.c @@ -13,21 +13,13 @@ * package and also uncallable, the name, * normally "pkg.init", is altered to "pkg.init·1". */ -Node* -renameinit(Node *n) +Sym* +renameinit(void) { - 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); + return lookup(namebuf); } /* @@ -125,7 +117,9 @@ fninit(NodeList *n) fn = nod(ODCLFUNC, N, N); initsym = lookup(namebuf); fn->nname = newname(initsym); + fn->nname->defn = fn; fn->nname->ntype = nod(OTFUNC, N, N); + declare(fn->nname, PFUNC); funchdr(fn); // (3) diff --git a/src/cmd/gc/inl.c b/src/cmd/gc/inl.c new file mode 100644 index 000000000..ed7a7eb95 --- /dev/null +++ b/src/cmd/gc/inl.c @@ -0,0 +1,780 @@ +// 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. +// +// The inlining facility makes 2 passes: first caninl determines which +// functions are suitable for inlining, and for those that are it +// saves a copy of the body. Then inlcalls walks each function body to +// expand calls to inlinable functions. +// +// The debug['l'] flag controls the agressiveness. Note that main() swaps level 0 and 1, +// making 1 the default and -l disable. -ll and more is useful to flush out bugs. +// These additional levels (beyond -l) may be buggy and are not supported. +// 0: disabled +// 1: 40-nodes leaf functions, oneliners, lazy typechecking (default) +// 2: early typechecking of all imported bodies +// 3: +// 4: allow non-leaf functions , (breaks runtime.Caller) +// 5: transitive inlining +// +// At some point this may get another default and become switch-offable with -N. +// +// The debug['m'] flag enables diagnostic output. a single -m is useful for verifying +// which calls get inlined or not, more is for debugging, and may go away at any point. +// +// TODO: +// - inline functions with ... args +// - handle T.meth(f()) with func f() (t T, arg, arg, ) + +#include <u.h> +#include <libc.h> +#include "go.h" + +// Used by caninl. +static Node* inlcopy(Node *n); +static NodeList* inlcopylist(NodeList *ll); +static int ishairy(Node *n, int *budget); +static int ishairylist(NodeList *ll, int *budget); + +// Used by inlcalls +static void inlnodelist(NodeList *l); +static void inlnode(Node **np); +static void mkinlcall(Node **np, Node *fn); +static Node* inlvar(Node *n); +static Node* retvar(Type *n, int i); +static Node* newlabel(void); +static Node* inlsubst(Node *n); +static NodeList* inlsubstlist(NodeList *l); + +static void setlno(Node*, int); + +// Used during inlsubst[list] +static Node *inlfn; // function currently being inlined +static Node *inlretlabel; // target of the goto substituted in place of a return +static NodeList *inlretvars; // temp out variables + +// Lazy typechecking of imported bodies. +// TODO avoid redoing local functions (imporpkg would be wrong) +void +typecheckinl(Node *fn) +{ + Node *savefn; + + if (debug['m']>2) + print("typecheck import [%S] %lN { %#H }\n", fn->sym, fn, fn->inl); + + savefn = curfn; + curfn = fn; + importpkg = fn->sym->pkg; + typechecklist(fn->inl, Etop); + importpkg = nil; + curfn = savefn; +} + +// Caninl determines whether fn is inlineable. Currently that means: +// fn is exactly 1 statement, either a return or an assignment, and +// some temporary constraints marked TODO. If fn is inlineable, saves +// fn->nbody in fn->inl and substitutes it with a copy. +void +caninl(Node *fn) +{ + Node *savefn; + Type *t; + int budget; + + if(fn->op != ODCLFUNC) + fatal("caninl %N", fn); + if(!fn->nname) + fatal("caninl no nname %+N", fn); + + // If fn has no body (is defined outside of Go), cannot inline it. + if(fn->nbody == nil) + return; + + // can't handle ... args yet + for(t=fn->type->type->down->down->type; t; t=t->down) + if(t->isddd) + return; + + budget = 40; // allowed hairyness + if(ishairylist(fn->nbody, &budget)) + return; + + savefn = curfn; + curfn = fn; + + fn->nname->inl = fn->nbody; + fn->nbody = inlcopylist(fn->nname->inl); + + // hack, TODO, check for better way to link method nodes back to the thing with the ->inl + // this is so export can find the body of a method + fn->type->nname = fn->nname; + + if(debug['m'] > 1) + print("%L: can inline %#N as: %#T { %#H }\n", fn->lineno, fn->nname, fn->type, fn->nname->inl); + else if(debug['m']) + print("%L: can inline %N\n", fn->lineno, fn->nname); + + curfn = savefn; +} + +// Look for anything we want to punt on. +static int +ishairylist(NodeList *ll, int* budget) +{ + for(;ll;ll=ll->next) + if(ishairy(ll->n, budget)) + return 1; + return 0; +} + +static int +ishairy(Node *n, int *budget) +{ + if(!n) + return 0; + + // Things that are too hairy, irrespective of the budget + switch(n->op) { + case OCALL: + case OCALLFUNC: + case OCALLINTER: + case OCALLMETH: + if(debug['l'] < 4) + return 1; + break; + + case OCLOSURE: + case ORANGE: + case OFOR: + case OSELECT: + case OSWITCH: + case OPROC: + case ODEFER: + case ODCL: // declares locals as globals b/c of @"". qualification + case ODCLTYPE: // can't print yet + case ODCLCONST: // can't print yet + return 1; + + break; + case OAS: + // x = <N> zero initializing assignments aren't representible in export yet. + // alternatively we may just skip them in printing and hope their DCL printed + // as a var will regenerate it + if(n->right == N) + return 1; + break; + } + + (*budget)--; + + return *budget < 0 || + ishairy(n->left, budget) || + ishairy(n->right, budget) || + ishairylist(n->list, budget) || + ishairylist(n->rlist, budget) || + ishairylist(n->ninit, budget) || + ishairy(n->ntest, budget) || + ishairy(n->nincr, budget) || + ishairylist(n->nbody, budget) || + ishairylist(n->nelse, budget); +} + +// Inlcopy and inlcopylist recursively copy the body of a function. +// Any name-like node of non-local class is marked for re-export by adding it to +// the exportlist. +static NodeList* +inlcopylist(NodeList *ll) +{ + NodeList *l; + + l = nil; + for(; ll; ll=ll->next) + l = list(l, inlcopy(ll->n)); + return l; +} + +static Node* +inlcopy(Node *n) +{ + Node *m; + + if(n == N) + return N; + + switch(n->op) { + case ONAME: + case OTYPE: + case OLITERAL: + return n; + } + + m = nod(OXXX, N, N); + *m = *n; + m->inl = nil; + m->left = inlcopy(n->left); + m->right = inlcopy(n->right); + m->list = inlcopylist(n->list); + m->rlist = inlcopylist(n->rlist); + m->ninit = inlcopylist(n->ninit); + m->ntest = inlcopy(n->ntest); + m->nincr = inlcopy(n->nincr); + m->nbody = inlcopylist(n->nbody); + m->nelse = inlcopylist(n->nelse); + + return m; +} + + +// Inlcalls/nodelist/node walks fn's statements and expressions and substitutes any +// calls made to inlineable functions. This is the external entry point. +void +inlcalls(Node *fn) +{ + Node *savefn; + + savefn = curfn; + curfn = fn; + inlnode(&fn); + if(fn != curfn) + fatal("inlnode replaced curfn"); + curfn = savefn; +} + +// Turn an OINLCALL into a statement. +static void +inlconv2stmt(Node *n) +{ + n->op = OBLOCK; + // n->ninit stays + n->list = n->nbody; + n->nbody = nil; + n->rlist = nil; +} + +// Turn an OINLCALL into a single valued expression. +static void +inlconv2expr(Node **np) +{ + Node *n, *r; + n = *np; + r = n->rlist->n; + addinit(&r, concat(n->ninit, n->nbody)); + *np = r; +} + +// Turn the rlist (with the return values) of the OINLCALL in +// n into an expression list lumping the ninit and body +// containing the inlined statements on the first list element so +// order will be preserved Used in return, oas2func and call +// statements. +static NodeList* +inlconv2list(Node *n) +{ + NodeList *l; + + if(n->op != OINLCALL || n->rlist == nil) + fatal("inlconv2list %+N\n", n); + + l = n->rlist; + addinit(&l->n, concat(n->ninit, n->nbody)); + return l; +} + +static void +inlnodelist(NodeList *l) +{ + for(; l; l=l->next) + inlnode(&l->n); +} + +// inlnode recurses over the tree to find inlineable calls, which will +// be turned into OINLCALLs by mkinlcall. When the recursion comes +// back up will examine left, right, list, rlist, ninit, ntest, nincr, +// nbody and nelse and use one of the 4 inlconv/glue functions above +// to turn the OINLCALL into an expression, a statement, or patch it +// in to this nodes list or rlist as appropriate. +// NOTE it makes no sense to pass the glue functions down the +// recursion to the level where the OINLCALL gets created because they +// have to edit /this/ n, so you'd have to push that one down as well, +// but then you may as well do it here. so this is cleaner and +// shorter and less complicated. +static void +inlnode(Node **np) +{ + Node *n; + NodeList *l; + int lno; + + if(*np == nil) + return; + + n = *np; + + switch(n->op) { + case ODEFER: + case OPROC: + // inhibit inlining of their argument + switch(n->left->op) { + case OCALLFUNC: + case OCALLMETH: + n->left->etype = n->op; + } + + case OCLOSURE: + // TODO do them here instead of in lex.c phase 6b, so escape analysis + // can avoid more heapmoves. + return; + } + + lno = setlineno(n); + + inlnodelist(n->ninit); + for(l=n->ninit; l; l=l->next) + if(l->n->op == OINLCALL) + inlconv2stmt(l->n); + + inlnode(&n->left); + if(n->left && n->left->op == OINLCALL) + inlconv2expr(&n->left); + + inlnode(&n->right); + if(n->right && n->right->op == OINLCALL) + inlconv2expr(&n->right); + + inlnodelist(n->list); + switch(n->op) { + case OBLOCK: + for(l=n->list; l; l=l->next) + if(l->n->op == OINLCALL) + inlconv2stmt(l->n); + break; + + case ORETURN: + case OCALLFUNC: + case OCALLMETH: + case OCALLINTER: + // if we just replaced arg in f(arg()) or return arg with an inlined call + // and arg returns multiple values, glue as list + if(count(n->list) == 1 && n->list->n->op == OINLCALL && count(n->list->n->rlist) > 1) { + n->list = inlconv2list(n->list->n); + break; + } + + // fallthrough + default: + for(l=n->list; l; l=l->next) + if(l->n->op == OINLCALL) + inlconv2expr(&l->n); + } + + inlnodelist(n->rlist); + switch(n->op) { + case OAS2FUNC: + if(n->rlist->n->op == OINLCALL) { + n->rlist = inlconv2list(n->rlist->n); + n->op = OAS2; + n->typecheck = 0; + typecheck(np, Etop); + break; + } + + // fallthrough + default: + for(l=n->rlist; l; l=l->next) + if(l->n->op == OINLCALL) + inlconv2expr(&l->n); + + } + + inlnode(&n->ntest); + if(n->ntest && n->ntest->op == OINLCALL) + inlconv2expr(&n->ntest); + + inlnode(&n->nincr); + if(n->nincr && n->nincr->op == OINLCALL) + inlconv2stmt(n->nincr); + + inlnodelist(n->nbody); + for(l=n->nbody; l; l=l->next) + if(l->n->op == OINLCALL) + inlconv2stmt(l->n); + + inlnodelist(n->nelse); + for(l=n->nelse; l; l=l->next) + if(l->n->op == OINLCALL) + inlconv2stmt(l->n); + + // with all the branches out of the way, it is now time to + // transmogrify this node itself unless inhibited by the + // switch at the top of this function. + switch(n->op) { + case OCALLFUNC: + case OCALLMETH: + if (n->etype == OPROC || n->etype == ODEFER) + return; + } + + switch(n->op) { + case OCALLFUNC: + if(debug['m']>3) + print("%L:call to func %+N\n", n->lineno, n->left); + if(n->left->inl) // normal case + mkinlcall(np, n->left); + else if(n->left->op == ONAME && n->left->left && n->left->left->op == OTYPE && n->left->right && n->left->right->op == ONAME) // methods called as functions + if(n->left->sym->def) + mkinlcall(np, n->left->sym->def); + break; + + case OCALLMETH: + if(debug['m']>3) + print("%L:call to meth %lN\n", n->lineno, n->left->right); + // typecheck should have resolved ODOTMETH->type, whose nname points to the actual function. + if(n->left->type == T) + fatal("no function type for [%p] %+N\n", n->left, n->left); + + if(n->left->type->nname == N) + fatal("no function definition for [%p] %+T\n", n->left->type, n->left->type); + + mkinlcall(np, n->left->type->nname); + + break; + } + + lineno = lno; +} + +// if *np is a call, and fn is a function with an inlinable body, substitute *np with an OINLCALL. +// On return ninit has the parameter assignments, the nbody is the +// inlined function body and list, rlist contain the input, output +// parameters. +static void +mkinlcall(Node **np, Node *fn) +{ + int i; + Node *n, *call, *saveinlfn, *as, *m; + NodeList *dcl, *ll, *ninit, *body; + Type *t; + + if (fn->inl == nil) + return; + + if (fn == curfn || fn->defn == curfn) + return; + + if(debug['l']<2) + typecheckinl(fn); + + n = *np; + + // Bingo, we have a function node, and it has an inlineable body + if(debug['m']>1) + print("%L: inlining call to %S %#T { %#H }\n", n->lineno, fn->sym, fn->type, fn->inl); + else if(debug['m']) + print("%L: inlining call to %N\n", n->lineno, fn); + + if(debug['m']>2) + print("%L: Before inlining: %+N\n", n->lineno, n); + + saveinlfn = inlfn; + inlfn = fn; + + ninit = n->ninit; + + if (fn->defn) // local function + dcl = fn->defn->dcl; + else // imported function + dcl = fn->dcl; + + inlretvars = nil; + i = 0; + // Make temp names to use instead of the originals + for(ll = dcl; ll; ll=ll->next) + if(ll->n->op == ONAME) { + ll->n->inlvar = inlvar(ll->n); + ninit = list(ninit, nod(ODCL, ll->n->inlvar, N)); // otherwise gen won't emit the allocations for heapallocs + if (ll->n->class == PPARAMOUT) // we rely on the order being correct here + inlretvars = list(inlretvars, ll->n->inlvar); + } + + // anonymous return values, synthesize names for use in assignment that replaces return + if(inlretvars == nil && fn->type->outtuple > 0) + for(t = getoutargx(fn->type)->type; t; t = t->down) { + m = retvar(t, i++); + ninit = list(ninit, nod(ODCL, m, N)); + inlretvars = list(inlretvars, m); + } + + // assign arguments to the parameters' temp names + as = N; + if(fn->type->thistuple) { + t = getthisx(fn->type)->type; + if(t != T && t->nname != N && !isblank(t->nname) && !t->nname->inlvar) + fatal("missing inlvar for %N\n", t->nname); + + if(n->left->op == ODOTMETH) { + if(!n->left->left) + fatal("method call without receiver: %+N", n); + if(t == T) + fatal("method call unknown receiver type: %+N", n); + if(t->nname != N && !isblank(t->nname)) + as = nod(OAS, t->nname->inlvar, n->left->left); + else + as = nod(OAS, temp(t->type), n->left->left); + } else { // non-method call to method + if (!n->list) + fatal("non-method call to method without first arg: %+N", n); + if(t != T && t->nname != N && !isblank(t->nname)) + as = nod(OAS, t->nname->inlvar, n->list->n); + } + + if(as != N) { + typecheck(&as, Etop); + ninit = list(ninit, as); + } + } + + as = nod(OAS2, N, N); + if(fn->type->intuple > 1 && n->list && !n->list->next) { + // TODO check that n->list->n is a call? + // TODO: non-method call to T.meth(f()) where f returns t, args... + as->rlist = n->list; + for(t = getinargx(fn->type)->type; t; t=t->down) { + if(t->nname && !isblank(t->nname)) { + if(!t->nname->inlvar) + fatal("missing inlvar for %N\n", t->nname); + as->list = list(as->list, t->nname->inlvar); + } else { + as->list = list(as->list, temp(t->type)); + } + } + } else { + ll = n->list; + if(fn->type->thistuple && n->left->op != ODOTMETH) // non method call to method + ll=ll->next; // was handled above in if(thistuple) + + for(t = getinargx(fn->type)->type; t && ll; t=t->down) { + if(t->nname && !isblank(t->nname)) { + if(!t->nname->inlvar) + fatal("missing inlvar for %N\n", t->nname); + as->list = list(as->list, t->nname->inlvar); + as->rlist = list(as->rlist, ll->n); + } + ll=ll->next; + } + if(ll || t) + fatal("arg count mismatch: %#T vs %,H\n", getinargx(fn->type), n->list); + } + + if (as->rlist) { + typecheck(&as, Etop); + ninit = list(ninit, as); + } + + // zero the outparams + for(ll = inlretvars; ll; ll=ll->next) { + as = nod(OAS, ll->n, N); + typecheck(&as, Etop); + ninit = list(ninit, as); + } + + inlretlabel = newlabel(); + body = inlsubstlist(fn->inl); + + body = list(body, nod(OGOTO, inlretlabel, N)); // avoid 'not used' when function doesnt have return + body = list(body, nod(OLABEL, inlretlabel, N)); + + typechecklist(body, Etop); + + call = nod(OINLCALL, N, N); + call->ninit = ninit; + call->nbody = body; + call->rlist = inlretvars; + call->type = n->type; + call->typecheck = 1; + + setlno(call, n->lineno); + + *np = call; + + inlfn = saveinlfn; + + // transitive inlining + // TODO do this pre-expansion on fn->inl directly. requires + // either supporting exporting statemetns with complex ninits + // or saving inl and making inlinl + if(debug['l'] >= 5) { + body = fn->inl; + fn->inl = nil; // prevent infinite recursion + inlnodelist(call->nbody); + for(ll=call->nbody; ll; ll=ll->next) + if(ll->n->op == OINLCALL) + inlconv2stmt(ll->n); + fn->inl = body; + } + + if(debug['m']>2) + print("%L: After inlining %+N\n\n", n->lineno, *np); + +} + +// Every time we expand a function we generate a new set of tmpnames, +// PAUTO's in the calling functions, and link them off of the +// PPARAM's, PAUTOS and PPARAMOUTs of the called function. +static Node* +inlvar(Node *var) +{ + Node *n; + + if(debug['m']>3) + print("inlvar %+N\n", var); + + n = newname(var->sym); + n->type = var->type; + n->class = PAUTO; + n->used = 1; + n->curfn = curfn; // the calling function, not the called one + curfn->dcl = list(curfn->dcl, n); + return n; +} + +// Synthesize a variable to store the inlined function's results in. +static Node* +retvar(Type *t, int i) +{ + Node *n; + + snprint(namebuf, sizeof(namebuf), ".r%d", i); + n = newname(lookup(namebuf)); + n->type = t->type; + n->class = PAUTO; + n->used = 1; + n->curfn = curfn; // the calling function, not the called one + curfn->dcl = list(curfn->dcl, n); + return n; +} + +static Node* +newlabel(void) +{ + Node *n; + static int label; + + label++; + snprint(namebuf, sizeof(namebuf), ".inlret%.6d", label); + n = newname(lookup(namebuf)); + n->etype = 1; // flag 'safe' for escape analysis (no backjumps) + return n; +} + +// inlsubst and inlsubstlist recursively copy the body of the saved +// pristine ->inl body of the function while substituting references +// to input/output parameters with ones to the tmpnames, and +// substituting returns with assignments to the output. +static NodeList* +inlsubstlist(NodeList *ll) +{ + NodeList *l; + + l = nil; + for(; ll; ll=ll->next) + l = list(l, inlsubst(ll->n)); + return l; +} + +static Node* +inlsubst(Node *n) +{ + Node *m, *as; + NodeList *ll; + + if(n == N) + return N; + + switch(n->op) { + case ONAME: + if(n->inlvar) { // These will be set during inlnode + if (debug['m']>2) + print ("substituting name %+N -> %+N\n", n, n->inlvar); + return n->inlvar; + } + if (debug['m']>2) + print ("not substituting name %+N\n", n); + return n; + + case OLITERAL: + case OTYPE: + return n; + + case ORETURN: + // Since we don't handle bodies with closures, this return is guaranteed to belong to the current inlined function. + +// dump("Return before substitution", n); + m = nod(OGOTO, inlretlabel, N); + m->ninit = inlsubstlist(n->ninit); + + if(inlretvars && n->list) { + as = nod(OAS2, N, N); + // shallow copy or OINLCALL->rlist will be the same list, and later walk and typecheck may clobber that. + for(ll=inlretvars; ll; ll=ll->next) + as->list = list(as->list, ll->n); + as->rlist = inlsubstlist(n->list); + typecheck(&as, Etop); + m->ninit = list(m->ninit, as); + } + + typechecklist(m->ninit, Etop); + typecheck(&m, Etop); +// dump("Return after substitution", m); + return m; + } + + + m = nod(OXXX, N, N); + *m = *n; + m->ninit = nil; + + if(n->op == OCLOSURE) + fatal("cannot inline function containing closure: %+N", n); + + m->left = inlsubst(n->left); + m->right = inlsubst(n->right); + m->list = inlsubstlist(n->list); + m->rlist = inlsubstlist(n->rlist); + m->ninit = concat(m->ninit, inlsubstlist(n->ninit)); + m->ntest = inlsubst(n->ntest); + m->nincr = inlsubst(n->nincr); + m->nbody = inlsubstlist(n->nbody); + m->nelse = inlsubstlist(n->nelse); + + return m; +} + +// Plaster over linenumbers +static void +setlnolist(NodeList *ll, int lno) +{ + for(;ll;ll=ll->next) + setlno(ll->n, lno); +} + +static void +setlno(Node *n, int lno) +{ + if(!n) + return; + + // don't clobber names, unless they're freshly synthesized + if(n->op != ONAME || n->lineno == 0) + n->lineno = lno; + + setlno(n->left, lno); + setlno(n->right, lno); + setlnolist(n->list, lno); + setlnolist(n->rlist, lno); + setlnolist(n->ninit, lno); + setlno(n->ntest, lno); + setlno(n->nincr, lno); + setlnolist(n->nbody, lno); + setlnolist(n->nelse, lno); +} diff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c index 0290fb131..db6dfc3e1 100644 --- a/src/cmd/gc/lex.c +++ b/src/cmd/gc/lex.c @@ -19,6 +19,7 @@ int yyprev; int yylast; static void lexinit(void); +static void lexinit1(void); static void lexfini(void); static void yytinit(void); static int getc(void); @@ -29,6 +30,61 @@ static void addidir(char*); static int getlinepragma(void); static char *goos, *goarch, *goroot; +// Compiler experiments. +// These are controlled by the GOEXPERIMENT environment +// variable recorded when the compiler is built. +static struct { + char *name; + int *val; +} exper[] = { +// {"rune32", &rune32}, + {nil, nil}, +}; + +static void +addexp(char *s) +{ + int i; + + for(i=0; exper[i].name != nil; i++) { + if(strcmp(exper[i].name, s) == 0) { + *exper[i].val = 1; + return; + } + } + + print("unknown experiment %s\n", s); + exits("unknown experiment"); +} + +static void +setexp(void) +{ + char *f[20]; + int i, nf; + + // The makefile #defines GOEXPERIMENT for us. + nf = getfields(GOEXPERIMENT, f, nelem(f), 1, ","); + for(i=0; i<nf; i++) + addexp(f[i]); +} + +char* +expstring(void) +{ + int i; + static char buf[512]; + + strcpy(buf, "X"); + for(i=0; exper[i].name != nil; i++) + if(*exper[i].val) + seprint(buf+strlen(buf), buf+sizeof buf, ",%s", exper[i].name); + if(strlen(buf) == 1) + strcpy(buf, "X,none"); + buf[1] = ':'; + return buf; +} + // Our own isdigit, isspace, isalpha, isalnum that take care // of EOF and other out of range arguments. static int @@ -82,6 +138,7 @@ usage(void) print(" -N disable optimizer\n"); print(" -S print the assembly language\n"); print(" -V print the compiler version\n"); + print(" -W print the parse tree after typing\n"); print(" -d print declarations\n"); print(" -e no limit on number of errors printed\n"); print(" -f print stack frame structure\n"); @@ -91,9 +148,9 @@ usage(void) print(" -p assumed import path for this code\n"); print(" -s disable escape analysis\n"); print(" -u disable package unsafe\n"); - print(" -w print the parse tree after typing\n"); + print(" -w print type checking details\n"); print(" -x print lex tokens\n"); - exits(0); + exits("usage"); } void @@ -143,6 +200,8 @@ main(int argc, char *argv[]) goroot = getgoroot(); goos = getgoos(); goarch = thestring; + + setexp(); outfile = nil; ARGBEGIN { @@ -169,9 +228,19 @@ main(int argc, char *argv[]) break; case 'V': - print("%cg version %s\n", thechar, getgoversion()); + p = expstring(); + if(strcmp(p, "X:none") == 0) + p = ""; + print("%cg version %s%s%s\n", thechar, getgoversion(), *p ? " " : "", p); exits(0); } ARGEND + + // enable inlining. for now: + // default: inlining on. (debug['l'] == 1) + // -l: inlining off (debug['l'] == 0) + // -ll, -lll: inlining on again, with extra debugging (debug['l'] > 1) + if(debug['l'] <= 1) + debug['l'] = 1 - debug['l']; if(argc < 1) usage(); @@ -193,23 +262,14 @@ main(int argc, char *argv[]) *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 - + fmtinstallgo(); betypeinit(); if(widthptr == 0) fatal("betypeinit failed"); lexinit(); typeinit(); + lexinit1(); yytinit(); blockgen = 1; @@ -283,11 +343,37 @@ main(int argc, char *argv[]) if(nsavederrors+nerrors) errorexit(); - // Phase 3b: escape analysis. - if(!debug['s']) + // Phase 4: Inlining + if (debug['l'] > 1) { + // Typecheck imported function bodies if debug['l'] > 1, + // otherwise lazily when used or re-exported. + for(l=importlist; l; l=l->next) + if (l->n->inl) { + saveerrors(); + typecheckinl(l->n); + } + + if(nsavederrors+nerrors) + errorexit(); + } + + if (debug['l']) { + // Find functions that can be inlined and clone them before walk expands them. + for(l=xtop; l; l=l->next) + if(l->n->op == ODCLFUNC) + caninl(l->n); + + // Expand inlineable calls in all functions + for(l=xtop; l; l=l->next) + if(l->n->op == ODCLFUNC) + inlcalls(l->n); + } + + // Phase 5: escape analysis. + if(!debug['N']) escapes(); - // Phase 4: Compile function bodies. + // Phase 6: Compile top level functions. for(l=xtop; l; l=l->next) if(l->n->op == ODCLFUNC) funccompile(l->n, 0); @@ -295,16 +381,18 @@ main(int argc, char *argv[]) if(nsavederrors+nerrors == 0) fninit(xtop); - // Phase 4b: Compile all closures. + // Phase 6b: Compile all closures. while(closures) { l = closures; closures = nil; for(; l; l=l->next) { + if (debug['l']) + inlcalls(l->n); funccompile(l->n, 1); } } - // Phase 5: check external declarations. + // Phase 7: check external declarations. for(l=externdcl; l; l=l->next) if(l->n->op == ONAME) typecheck(&l->n, Erv); @@ -329,18 +417,30 @@ saveerrors(void) nerrors = 0; } +/* + * macro to portably read/write archive header. + * 'cmd' is read/write/Bread/Bwrite, etc. + */ +#define HEADER_IO(cmd, f, h) cmd(f, h.name, sizeof(h.name)) != sizeof(h.name)\ + || cmd(f, h.date, sizeof(h.date)) != sizeof(h.date)\ + || cmd(f, h.uid, sizeof(h.uid)) != sizeof(h.uid)\ + || cmd(f, h.gid, sizeof(h.gid)) != sizeof(h.gid)\ + || cmd(f, h.mode, sizeof(h.mode)) != sizeof(h.mode)\ + || cmd(f, h.size, sizeof(h.size)) != sizeof(h.size)\ + || cmd(f, h.fmag, sizeof(h.fmag)) != sizeof(h.fmag) + static int arsize(Biobuf *b, char *name) { - struct ar_hdr *a; + struct ar_hdr a; - if((a = Brdline(b, '\n')) == nil) - return -1; - if(Blinelen(b) != sizeof(struct ar_hdr)) + if (HEADER_IO(Bread, b, a)) return -1; - if(strncmp(a->name, name, strlen(name)) != 0) + + if(strncmp(a.name, name, strlen(name)) != 0) return -1; - return atoi(a->size); + + return atoi(a.size); } static int @@ -537,7 +637,7 @@ importfile(Val *f, int line) yyerror("import %s: not a go object file", file); errorexit(); } - q = smprint("%s %s %s", getgoos(), thestring, getgoversion()); + q = smprint("%s %s %s %s", getgoos(), thestring, getgoversion(), expstring()); if(strcmp(p+10, q) != 0) { yyerror("import %s: object is [%s] expected [%s]", file, p+10, q); errorexit(); @@ -744,6 +844,8 @@ l0: ncp += ncp; } c = getr(); + if(c == '\r') + continue; if(c == EOF) { yyerror("eof in string"); break; @@ -777,7 +879,7 @@ l0: } yylval.val.u.xval = mal(sizeof(*yylval.val.u.xval)); mpmovecfix(yylval.val.u.xval, v); - yylval.val.ctype = CTINT; + yylval.val.ctype = CTRUNE; DBG("lex: codepoint literal\n"); strcpy(litbuf, "string literal"); return LLITERAL; @@ -1035,7 +1137,7 @@ lx: return c; asop: - yylval.lint = c; // rathole to hold which asop + yylval.i = c; // rathole to hold which asop DBG("lex: TOKEN ASOP %c\n", c); return LASOP; @@ -1273,7 +1375,7 @@ static int getlinepragma(void) { int i, c, n; - char *cp, *ep; + char *cp, *ep, *linep; Hist *h; for(i=0; i<5; i++) { @@ -1284,32 +1386,36 @@ getlinepragma(void) cp = lexbuf; ep = lexbuf+sizeof(lexbuf)-5; + linep = nil; for(;;) { c = getr(); - if(c == '\n' || c == EOF) + if(c == EOF) goto out; + if(c == '\n') + break; if(c == ' ') continue; if(c == ':') - break; + linep = cp; if(cp < ep) *cp++ = c; } *cp = 0; + if(linep == nil || linep >= ep) + goto out; + *linep++ = '\0'; n = 0; - for(;;) { - c = getr(); - if(!yy_isdigit(c)) - break; - n = n*10 + (c-'0'); + for(cp=linep; *cp; cp++) { + if(*cp < '0' || *cp > '9') + goto out; + n = n*10 + *cp - '0'; if(n > 1e8) { yyerror("line number out of range"); errorexit(); } } - - if(c != '\n' || n <= 0) + if(n <= 0) goto out; // try to avoid allocating file name over and over @@ -1360,7 +1466,7 @@ yylex(void) // Track last two tokens returned by yylex. yyprev = yylast; yylast = lx; - return lx; + return lx; } static int @@ -1587,7 +1693,6 @@ static struct "complex128", LNAME, TCOMPLEX128, OXXX, "bool", LNAME, TBOOL, OXXX, - "byte", LNAME, TUINT8, OXXX, "string", LNAME, TSTRING, OXXX, "any", LNAME, TANY, OXXX, @@ -1618,11 +1723,12 @@ static struct "type", LTYPE, Txxx, OXXX, "var", LVAR, Txxx, OXXX, - "append", LNAME, Txxx, OAPPEND, + "append", LNAME, Txxx, OAPPEND, "cap", LNAME, Txxx, OCAP, "close", LNAME, Txxx, OCLOSE, "complex", LNAME, Txxx, OCOMPLEX, "copy", LNAME, Txxx, OCOPY, + "delete", LNAME, Txxx, ODELETE, "imag", LNAME, Txxx, OIMAG, "len", LNAME, Txxx, OLEN, "make", LNAME, Txxx, OMAKE, @@ -1647,6 +1753,7 @@ lexinit(void) Sym *s, *s1; Type *t; int etype; + Val v; /* * initialize basic types array @@ -1675,6 +1782,16 @@ lexinit(void) s1->def = typenod(t); continue; } + + etype = syms[i].op; + if(etype != OXXX) { + s1 = pkglookup(syms[i].name, builtinpkg); + s1->lexical = LNAME; + s1->def = nod(ONAME, N, N); + s1->def->sym = s1; + s1->def->etype = etype; + s1->def->builtin = 1; + } } // logically, the type of a string literal. @@ -1702,6 +1819,77 @@ lexinit(void) types[TBLANK] = typ(TBLANK); s->def->type = types[TBLANK]; nblank = s->def; + + s = pkglookup("_", builtinpkg); + s->block = -100; + s->def = nod(ONAME, N, N); + s->def->sym = s; + types[TBLANK] = typ(TBLANK); + s->def->type = types[TBLANK]; + + types[TNIL] = typ(TNIL); + s = pkglookup("nil", builtinpkg); + v.ctype = CTNIL; + s->def = nodlit(v); + s->def->sym = s; +} + +static void +lexinit1(void) +{ + Sym *s, *s1; + Type *t, *f, *rcvr, *in, *out; + + // t = interface { Error() string } + rcvr = typ(TSTRUCT); + rcvr->type = typ(TFIELD); + rcvr->type->type = ptrto(typ(TSTRUCT)); + rcvr->funarg = 1; + in = typ(TSTRUCT); + in->funarg = 1; + out = typ(TSTRUCT); + out->type = typ(TFIELD); + out->type->type = types[TSTRING]; + out->funarg = 1; + f = typ(TFUNC); + *getthis(f) = rcvr; + *getoutarg(f) = out; + *getinarg(f) = in; + f->thistuple = 1; + f->intuple = 0; + f->outnamed = 0; + f->outtuple = 1; + t = typ(TINTER); + t->type = typ(TFIELD); + t->type->sym = lookup("Error"); + t->type->type = f; + + // error type + s = lookup("error"); + s->lexical = LNAME; + errortype = t; + errortype->sym = s; + s1 = pkglookup("error", builtinpkg); + s1->lexical = LNAME; + s1->def = typenod(errortype); + + // byte alias + s = lookup("byte"); + s->lexical = LNAME; + bytetype = typ(TUINT8); + bytetype->sym = s; + s1 = pkglookup("byte", builtinpkg); + s1->lexical = LNAME; + s1->def = typenod(bytetype); + + // rune alias + s = lookup("rune"); + s->lexical = LNAME; + runetype = typ(TINT32); + runetype->sym = s; + s1 = pkglookup("rune", builtinpkg); + s1->lexical = LNAME; + s1->def = typenod(runetype); } static void @@ -1739,7 +1927,18 @@ lexfini(void) // there's only so much table-driven we can handle. // these are special cases. - types[TNIL] = typ(TNIL); + s = lookup("byte"); + if(s->def == N) + s->def = typenod(bytetype); + + s = lookup("error"); + if(s->def == N) + s->def = typenod(errortype); + + s = lookup("rune"); + if(s->def == N) + s->def = typenod(runetype); + s = lookup("nil"); if(s->def == N) { v.ctype = CTNIL; diff --git a/src/cmd/gc/obj.c b/src/cmd/gc/obj.c index 730b42671..aae566dbb 100644 --- a/src/cmd/gc/obj.c +++ b/src/cmd/gc/obj.c @@ -23,7 +23,7 @@ dumpobj(void) errorexit(); } - Bprint(bout, "go object %s %s %s\n", getgoos(), thestring, getgoversion()); + Bprint(bout, "go object %s %s %s %s\n", getgoos(), thestring, getgoversion(), expstring()); Bprint(bout, " exports automatically generated from\n"); Bprint(bout, " %s in package \"%s\"\n", curio.infile, localpkg->name); dumpexport(); @@ -52,7 +52,7 @@ dumpglobls(void) continue; if(n->type == T) - fatal("external %#N nil type\n", n); + fatal("external %N nil type\n", n); if(n->class == PFUNC) continue; if(n->sym->pkg != localpkg) @@ -267,6 +267,7 @@ stringsym(char *s, int len) if(sym->flags & SymUniq) return sym; sym->flags |= SymUniq; + sym->def = newname(sym); off = 0; diff --git a/src/cmd/gc/order.c b/src/cmd/gc/order.c new file mode 100644 index 000000000..2cab5fb95 --- /dev/null +++ b/src/cmd/gc/order.c @@ -0,0 +1,358 @@ +// Copyright 2012 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. + +// Rewrite tree to use separate statements to enforce +// order of evaluation. Makes walk easier, because it +// can (after this runs) reorder at will within an expression. + +#include <u.h> +#include <libc.h> +#include "go.h" + +static void orderstmt(Node*, NodeList**); +static void orderstmtlist(NodeList*, NodeList**); +static void orderblock(NodeList **l); +static void orderexpr(Node**, NodeList**); +static void orderexprlist(NodeList*, NodeList**); + +void +order(Node *fn) +{ + orderblock(&fn->nbody); +} + +static void +orderstmtlist(NodeList *l, NodeList **out) +{ + for(; l; l=l->next) + orderstmt(l->n, out); +} + +// Order the block of statements *l onto a new list, +// and then replace *l with that list. +static void +orderblock(NodeList **l) +{ + NodeList *out; + + out = nil; + orderstmtlist(*l, &out); + *l = out; +} + +// Order the side effects in *np and leave them as +// the init list of the final *np. +static void +orderexprinplace(Node **np) +{ + Node *n; + NodeList *out; + + n = *np; + out = nil; + orderexpr(&n, &out); + addinit(&n, out); + *np = n; +} + +// Like orderblock, but applied to a single statement. +static void +orderstmtinplace(Node **np) +{ + Node *n; + NodeList *out; + + n = *np; + out = nil; + orderstmt(n, &out); + *np = liststmt(out); +} + +// Move n's init list to *out. +static void +orderinit(Node *n, NodeList **out) +{ + orderstmtlist(n->ninit, out); + n->ninit = nil; +} + +// Is the list l actually just f() for a multi-value function? +static int +ismulticall(NodeList *l) +{ + Node *n; + + // one arg only + if(l == nil || l->next != nil) + return 0; + n = l->n; + + // must be call + switch(n->op) { + default: + return 0; + case OCALLFUNC: + case OCALLMETH: + case OCALLINTER: + break; + } + + // call must return multiple values + return n->left->type->outtuple > 1; +} + +// n is a multi-value function call. Add t1, t2, .. = n to out +// and return the list t1, t2, ... +static NodeList* +copyret(Node *n, NodeList **out) +{ + Type *t; + Node *tmp, *as; + NodeList *l1, *l2; + Iter tl; + + if(n->type->etype != TSTRUCT || !n->type->funarg) + fatal("copyret %T %d", n->type, n->left->type->outtuple); + + l1 = nil; + l2 = nil; + for(t=structfirst(&tl, &n->type); t; t=structnext(&tl)) { + tmp = temp(t->type); + l1 = list(l1, tmp); + l2 = list(l2, tmp); + } + + as = nod(OAS2, N, N); + as->list = l1; + as->rlist = list1(n); + typecheck(&as, Etop); + orderstmt(as, out); + + return l2; +} + +static void +ordercallargs(NodeList **l, NodeList **out) +{ + if(ismulticall(*l)) { + // return f() where f() is multiple values. + *l = copyret((*l)->n, out); + } else { + orderexprlist(*l, out); + } +} + +static void +ordercall(Node *n, NodeList **out) +{ + orderexpr(&n->left, out); + ordercallargs(&n->list, out); +} + +static void +orderstmt(Node *n, NodeList **out) +{ + int lno; + NodeList *l; + Node *r; + + if(n == N) + return; + + lno = setlineno(n); + + orderinit(n, out); + + switch(n->op) { + default: + fatal("orderstmt %O", n->op); + + case OAS2: + case OAS2DOTTYPE: + case OAS2MAPR: + case OAS: + case OASOP: + case OCLOSE: + case OCOPY: + case ODELETE: + case OPANIC: + case OPRINT: + case OPRINTN: + case ORECOVER: + case ORECV: + case OSEND: + orderexpr(&n->left, out); + orderexpr(&n->right, out); + orderexprlist(n->list, out); + orderexprlist(n->rlist, out); + *out = list(*out, n); + break; + + case OAS2FUNC: + // Special: avoid copy of func call n->rlist->n. + orderexprlist(n->list, out); + ordercall(n->rlist->n, out); + *out = list(*out, n); + break; + + case OAS2RECV: + // Special: avoid copy of receive. + orderexprlist(n->list, out); + orderexpr(&n->rlist->n->left, out); // arg to recv + *out = list(*out, n); + break; + + case OBLOCK: + case OEMPTY: + // Special: does not save n onto out. + orderstmtlist(n->list, out); + break; + + case OBREAK: + case OCONTINUE: + case ODCL: + case ODCLCONST: + case ODCLTYPE: + case OFALL: + case_OFALL: + case OGOTO: + case OLABEL: + // Special: n->left is not an expression; save as is. + *out = list(*out, n); + break; + + case OCALLFUNC: + case OCALLINTER: + case OCALLMETH: + // Special: handle call arguments. + ordercall(n, out); + *out = list(*out, n); + break; + + case ODEFER: + case OPROC: + // Special: order arguments to inner call but not call itself. + ordercall(n->left, out); + *out = list(*out, n); + break; + + case OFOR: + orderexprinplace(&n->ntest); + orderstmtinplace(&n->nincr); + orderblock(&n->nbody); + *out = list(*out, n); + break; + + case OIF: + orderexprinplace(&n->ntest); + orderblock(&n->nbody); + orderblock(&n->nelse); + *out = list(*out, n); + break; + + case ORANGE: + orderexpr(&n->right, out); + for(l=n->list; l; l=l->next) + orderexprinplace(&l->n); + orderblock(&n->nbody); + *out = list(*out, n); + break; + + case ORETURN: + ordercallargs(&n->list, out); + *out = list(*out, n); + break; + + case OSELECT: + for(l=n->list; l; l=l->next) { + if(l->n->op != OXCASE) + fatal("order select case %O", l->n->op); + r = l->n->left; + if(r == nil) + continue; + switch(r->op) { + case OSELRECV: + case OSELRECV2: + orderexprinplace(&r->left); + orderexprinplace(&r->ntest); + orderexpr(&r->right->left, out); + break; + case OSEND: + orderexpr(&r->left, out); + orderexpr(&r->right, out); + break; + } + } + *out = list(*out, n); + break; + + case OSWITCH: + orderexpr(&n->ntest, out); + for(l=n->list; l; l=l->next) { + if(l->n->op != OXCASE) + fatal("order switch case %O", l->n->op); + orderexpr(&l->n->left, &l->n->ninit); + } + *out = list(*out, n); + break; + + case OXFALL: + yyerror("fallthrough statement out of place"); + n->op = OFALL; + goto case_OFALL; + } + + lineno = lno; +} + +static void +orderexprlist(NodeList *l, NodeList **out) +{ + for(; l; l=l->next) + orderexpr(&l->n, out); +} + +static void +orderexpr(Node **np, NodeList **out) +{ + Node *n; + int lno; + + n = *np; + if(n == N) + return; + + lno = setlineno(n); + orderinit(n, out); + + switch(n->op) { + default: + orderexpr(&n->left, out); + orderexpr(&n->right, out); + orderexprlist(n->list, out); + orderexprlist(n->rlist, out); + break; + + case OANDAND: + case OOROR: + orderexpr(&n->left, out); + orderexprinplace(&n->right); + break; + + case OCALLFUNC: + case OCALLMETH: + case OCALLINTER: + ordercall(n, out); + n = copyexpr(n, n->type, out); + break; + + case ORECV: + n = copyexpr(n, n->type, out); + break; + } + + lineno = lno; + + *np = n; +} diff --git a/src/cmd/gc/pgen.c b/src/cmd/gc/pgen.c index d16481b66..8e65ba22d 100644 --- a/src/cmd/gc/pgen.c +++ b/src/cmd/gc/pgen.c @@ -54,7 +54,11 @@ compile(Node *fn) t = structnext(&save); } } - + + order(curfn); + if(nerrors != 0) + goto ret; + hasdefer = 0; walk(curfn); if(nerrors != 0) @@ -70,6 +74,8 @@ compile(Node *fn) nodconst(&nod1, types[TINT32], 0); ptxt = gins(ATEXT, isblank(curfn->nname) ? N : curfn->nname, &nod1); + if(fn->dupok) + ptxt->TEXTFLAG = DUPOK; afunclit(&ptxt->from); ginit(); @@ -117,6 +123,10 @@ compile(Node *fn) if(0) print("allocauto: %lld to %lld\n", oldstksize, (vlong)stksize); + setlineno(curfn); + if(stksize+maxarg > (1ULL<<31)) + yyerror("stack frame too large (>2GB)"); + defframe(ptxt); if(0) diff --git a/src/cmd/gc/print.c b/src/cmd/gc/print.c deleted file mode 100644 index 37e3e7ac0..000000000 --- a/src/cmd/gc/print.c +++ /dev/null @@ -1,486 +0,0 @@ -// 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 "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 ODDDARG: - fmtprint(f, "... argument"); - 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 index 5cbafd895..9bcd833a7 100644 --- a/src/cmd/gc/range.c +++ b/src/cmd/gc/range.c @@ -32,7 +32,7 @@ typecheckrange(Node *n) switch(t->etype) { default: - yyerror("cannot range over %+N", n->right); + yyerror("cannot range over %lN", n->right); goto out; case TARRAY: @@ -46,6 +46,10 @@ typecheckrange(Node *n) break; case TCHAN: + if(!(t->chan & Crecv)) { + yyerror("invalid operation: range %N (receive from send-only type %T)", n->right, n->right->type); + goto out; + } t1 = t->type; t2 = nil; if(count(n->list) == 2) @@ -54,7 +58,7 @@ typecheckrange(Node *n) case TSTRING: t1 = types[TINT]; - t2 = types[TINT]; + t2 = runetype; break; } @@ -71,12 +75,12 @@ typecheckrange(Node *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); + yyerror("cannot assign type %T to %lN 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); + yyerror("cannot assign type %T to %lN in range%s", t2, v2, why); } out: @@ -163,7 +167,9 @@ walkrange(Node *n) case TMAP: th = typ(TARRAY); th->type = ptrto(types[TUINT8]); - th->bound = (sizeof(struct Hiter) + widthptr - 1) / widthptr; + // see ../../pkg/runtime/hashmap.h:/hash_iter + // Size in words. + th->bound = 5 + 4*3 + 4*4/widthptr; hit = temp(th); fn = syslook("mapiterinit", 1); @@ -216,7 +222,7 @@ walkrange(Node *n) if(v2 == N) a = nod(OAS, hv1, mkcall("stringiter", types[TINT], nil, ha, hv1)); else { - hv2 = temp(types[TINT]); + hv2 = temp(runetype); a = nod(OAS2, N, N); a->list = list(list1(hv1), hv2); fn = syslook("stringiter2", 0); diff --git a/src/cmd/gc/reflect.c b/src/cmd/gc/reflect.c index ca7d08e51..49aca0906 100644 --- a/src/cmd/gc/reflect.c +++ b/src/cmd/gc/reflect.c @@ -13,6 +13,7 @@ static NodeList* signatlist; static Sym* dtypesym(Type*); static Sym* weaktypesym(Type*); +static Sym* dalgsym(Type*); static int sigcmp(Sig *a, Sig *b) @@ -158,10 +159,13 @@ methods(Type *t) // generating code if necessary. a = nil; for(f=mt->xmethod; f; f=f->down) { - if(f->type->etype != TFUNC) - continue; if(f->etype != TFIELD) - fatal("methods: not field"); + fatal("methods: not field %T", f); + if (f->type->etype != TFUNC || f->type->thistuple == 0) + fatal("non-method on %T method %S %T\n", mt, f->sym, f); + if (!getthisx(f->type)->type) + fatal("receiver with no type on %T method %S %T\n", mt, f->sym, f); + method = f->sym; if(method == nil) continue; @@ -353,7 +357,7 @@ dextratype(Sym *sym, int off, Type *t, int ptroff) s = sym; if(t->sym) { ot = dgostringptr(s, ot, t->sym->name); - if(t != types[t->etype]) + if(t != types[t->etype] && t != errortype) ot = dgopkgpath(s, ot, t->sym->pkg); else ot = dgostringptr(s, ot, nil); @@ -550,12 +554,20 @@ haspointers(Type *t) static int dcommontype(Sym *s, int ot, Type *t) { - int i; - Sym *sptr; + int i, alg, sizeofAlg; + Sym *sptr, *algsym; + static Sym *algarray; char *p; + sizeofAlg = 4*widthptr; + if(algarray == nil) + algarray = pkglookup("algarray", runtimepkg); + alg = algtype(t); + algsym = S; + if(alg < 0) + algsym = dalgsym(t); + dowidth(t); - if(t->sym != nil && !isptr[t->etype]) sptr = dtypesym(ptrto(t)); else @@ -583,7 +595,7 @@ dcommontype(Sym *s, int ot, Type *t) // } ot = duintptr(s, ot, t->width); ot = duint32(s, ot, typehash(t)); - ot = duint8(s, ot, algtype(t)); + ot = duint8(s, ot, 0); // unused ot = duint8(s, ot, t->align); // align ot = duint8(s, ot, t->align); // fieldAlign i = kinds[t->etype]; @@ -592,9 +604,12 @@ dcommontype(Sym *s, int ot, Type *t) if(!haspointers(t)) i |= KindNoPointers; ot = duint8(s, ot, i); // kind - longsymnames = 1; - p = smprint("%-T", t); - longsymnames = 0; + if(alg >= 0) + ot = dsymptr(s, ot, algarray, alg*sizeofAlg); + else + ot = dsymptr(s, ot, algsym, 0); + p = smprint("%-uT", t); + //print("dcommontype: %s\n", p); ot = dgostringptr(s, ot, p); // string free(p); @@ -614,8 +629,22 @@ typesym(Type *t) char *p; Sym *s; - p = smprint("%#-T", t); + p = smprint("%-T", t); + s = pkglookup(p, typepkg); + //print("typesym: %s -> %+S\n", p, s); + free(p); + return s; +} + +Sym* +typesymprefix(char *prefix, Type *t) +{ + char *p; + Sym *s; + + p = smprint("%s.%-T", prefix, t); s = pkglookup(p, typepkg); + //print("algsym: %s -> %+S\n", p, s); free(p); return s; } @@ -662,8 +691,9 @@ weaktypesym(Type *t) weak->prefix = "weak.type"; // not weak%2etype } - p = smprint("%#-T", t); + p = smprint("%-T", t); s = pkglookup(p, weak); + //print("weaktypesym: %s -> %+S\n", p, s); free(p); return s; } @@ -692,8 +722,13 @@ dtypesym(Type *t) tbase = t->type; dupok = tbase->sym == S; - if(compiling_runtime && tbase == types[tbase->etype]) // int, float, etc + if(compiling_runtime && + (tbase == types[tbase->etype] || + tbase == bytetype || + tbase == runetype || + tbase == errortype)) { // int, float, etc goto ok; + } // named types from other files are defined only by those files if(tbase->sym && !tbase->local) @@ -902,9 +937,56 @@ dumptypestructs(void) dtypesym(ptrto(types[i])); dtypesym(ptrto(types[TSTRING])); dtypesym(ptrto(types[TUNSAFEPTR])); + + // emit type structs for error and func(error) string. + // The latter is the type of an auto-generated wrapper. + dtypesym(ptrto(errortype)); + dtypesym(functype(nil, + list1(nod(ODCLFIELD, N, typenod(errortype))), + list1(nod(ODCLFIELD, N, typenod(types[TSTRING]))))); // add paths for runtime and main, which 6l imports implicitly. dimportpath(runtimepkg); dimportpath(mkpkg(strlit("main"))); } } + +Sym* +dalgsym(Type *t) +{ + int ot; + Sym *s, *hash, *eq; + char buf[100]; + + // dalgsym is only called for a type that needs an algorithm table, + // which implies that the type is comparable (or else it would use ANOEQ). + + s = typesymprefix(".alg", t); + hash = typesymprefix(".hash", t); + genhash(hash, t); + eq = typesymprefix(".eq", t); + geneq(eq, t); + + // ../../pkg/runtime/runtime.h:/Alg + ot = 0; + ot = dsymptr(s, ot, hash, 0); + ot = dsymptr(s, ot, eq, 0); + ot = dsymptr(s, ot, pkglookup("memprint", runtimepkg), 0); + switch(t->width) { + default: + ot = dsymptr(s, ot, pkglookup("memcopy", runtimepkg), 0); + break; + case 1: + case 2: + case 4: + case 8: + case 16: + snprint(buf, sizeof buf, "memcopy%d", (int)t->width*8); + ot = dsymptr(s, ot, pkglookup(buf, runtimepkg), 0); + break; + } + + ggloblsym(s, ot, 1); + return s; +} + diff --git a/src/cmd/gc/runtime.go b/src/cmd/gc/runtime.go index 549f7abe3..000b2328f 100644 --- a/src/cmd/gc/runtime.go +++ b/src/cmd/gc/runtime.go @@ -6,11 +6,13 @@ // to update builtin.c.boot. This is not done automatically // to avoid depending on having a working compiler binary. +// +build ignore + package PACKAGE // emitted by compiler, not referred to by go programs -func new(int32) *any +func new(typ *byte) *any func panicindex() func panicslice() func throwreturn() @@ -40,18 +42,19 @@ func concatstring() // filled in by compiler: Type*, int n, Slice, ... func append() func appendslice(typ *byte, x any, y []any) any +func appendstr(typ *byte, x []byte, y string) []byte 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 slicerunetostring([]rune) string func stringtoslicebyte(string) []byte -func stringtosliceint(string) []int +func stringtoslicerune(string) []rune func stringiter(string, int) int -func stringiter2(string, int) (retk int, retv int) -func slicecopy(to any, fr any, wid uint32) int +func stringiter2(string, int) (retk int, retv rune) +func copy(to any, fr any, wid uint32) int func slicestringcopy(to any, fr any) int // interface conversions @@ -79,6 +82,8 @@ func efaceeq(i1 any, i2 any) (ret bool) func ifacethash(i1 any) (ret uint32) func efacethash(i1 any) (ret uint32) +func equal(typ *byte, x1, x2 any) (ret bool) + // *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) @@ -86,6 +91,7 @@ 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 mapdelete(mapType *byte, hmap map[any]any, key any) func mapiternext(hiter *any) func mapiter1(hiter *any) (key any) func mapiter2(hiter *any) (key any, val any) @@ -117,6 +123,13 @@ func slicearray(old *any, nel uint64, lb uint64, hb uint64, width uint64) (ary [ func closure() // has args, but compiler fills in +func memequal(eq *bool, size uintptr, x, y *any) +func memequal8(eq *bool, size uintptr, x, y *any) +func memequal16(eq *bool, size uintptr, x, y *any) +func memequal32(eq *bool, size uintptr, x, y *any) +func memequal64(eq *bool, size uintptr, x, y *any) +func memequal128(eq *bool, size uintptr, x, y *any) + // only used on 32-bit func int64div(int64, int64) int64 func uint64div(uint64, uint64) uint64 diff --git a/src/cmd/gc/sinit.c b/src/cmd/gc/sinit.c index 4550577a4..0cf21e2bb 100644 --- a/src/cmd/gc/sinit.c +++ b/src/cmd/gc/sinit.c @@ -154,6 +154,10 @@ init2(Node *n, NodeList **out) { if(n == N || n->initorder == InitDone) return; + + if(n->op == ONAME && n->ninit) + fatal("name %S with ninit: %+N\n", n->sym, n); + init1(n, out); init2(n->left, out); init2(n->right, out); @@ -262,6 +266,14 @@ staticcopy(Node *l, Node *r, NodeList **out) case ONAME: gdata(l, r, l->type->width); return 1; + } + break; + + case OPTRLIT: + switch(r->left->op) { + default: + //dump("not static addr", r); + break; case OARRAYLIT: case OSTRUCTLIT: case OMAPLIT: @@ -294,18 +306,18 @@ staticcopy(Node *l, Node *r, NodeList **out) n1.type = e->expr->type; if(e->expr->op == OLITERAL) gdata(&n1, e->expr, n1.type->width); - else if(staticassign(&n1, e->expr, out)) { - // Done - } else { - // Requires computation, but we're - // copying someone else's computation. + else { ll = nod(OXXX, N, N); *ll = n1; - rr = nod(OXXX, N, N); - *rr = *orig; - rr->type = ll->type; - rr->xoffset += e->xoffset; - *out = list(*out, nod(OAS, ll, rr)); + if(!staticassign(ll, e->expr, out)) { + // Requires computation, but we're + // copying someone else's computation. + rr = nod(OXXX, N, N); + *rr = *orig; + rr->type = ll->type; + rr->xoffset += e->xoffset; + *out = list(*out, nod(OAS, ll, rr)); + } } } return 1; @@ -347,7 +359,14 @@ staticassign(Node *l, Node *r, NodeList **out) case ONAME: gdata(l, r, l->type->width); return 1; - + } + + case OPTRLIT: + switch(r->left->op) { + default: + //dump("not static ptrlit", r); + break; + case OARRAYLIT: case OMAPLIT: case OSTRUCTLIT: @@ -392,12 +411,11 @@ staticassign(Node *l, Node *r, NodeList **out) n1.type = e->expr->type; if(e->expr->op == OLITERAL) gdata(&n1, e->expr, n1.type->width); - else if(staticassign(&n1, e->expr, out)) { - // done - } else { + else { a = nod(OXXX, N, N); *a = n1; - *out = list(*out, nod(OAS, a, e->expr)); + if(!staticassign(a, e->expr, out)) + *out = list(*out, nod(OAS, a, e->expr)); } } return 1; @@ -693,9 +711,10 @@ slicelit(int ctxt, Node *n, Node *var, NodeList **init) // set auto to point at new temp or heap (3 assign) if(n->esc == EscNone) { - a = temp(t); - *init = list(*init, nod(OAS, a, N)); // zero new temp - a = nod(OADDR, a, N); + a = nod(OAS, temp(t), N); + typecheck(&a, Etop); + *init = list(*init, a); // zero new temp + a = nod(OADDR, a->left, N); } else { a = nod(ONEW, N, N); a->list = list1(typenod(t)); @@ -918,6 +937,19 @@ anylit(int ctxt, Node *n, Node *var, NodeList **init) default: fatal("anylit: not lit"); + case OPTRLIT: + if(!isptr[t->etype]) + fatal("anylit: not ptr"); + + a = nod(OAS, var, callnew(t->type)); + typecheck(&a, Etop); + *init = list(*init, a); + + var = nod(OIND, var, N); + typecheck(&var, Erv | Easgn); + anylit(ctxt, n->left, var, init); + break; + case OSTRUCTLIT: if(t->etype != TSTRUCT) fatal("anylit: not struct"); @@ -1313,6 +1345,7 @@ iszero(Node *n) return n->val.u.bval == 0; case CTINT: + case CTRUNE: return mpcmpfixc(n->val.u.xval, 0) == 0; case CTFLT: diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c index b450b9b0e..64a007077 100644 --- a/src/cmd/gc/subr.c +++ b/src/cmd/gc/subr.c @@ -7,11 +7,8 @@ #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 { @@ -47,12 +44,10 @@ 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) @@ -124,7 +119,7 @@ yyerrorl(int line, char *fmt, ...) hcrash(); nerrors++; - if(nerrors >= 10 && !debug['e']) { + if(nsavederrors+nerrors >= 10 && !debug['e']) { flusherrors(); print("%L: too many errors\n", line); errorexit(); @@ -192,7 +187,7 @@ yyerror(char *fmt, ...) hcrash(); nerrors++; - if(nerrors >= 10 && !debug['e']) { + if(nsavederrors+nerrors >= 10 && !debug['e']) { flusherrors(); print("%L: too many errors\n", parserline()); errorexit(); @@ -500,45 +495,118 @@ nod(int op, Node *nleft, Node *nright) } int +algtype1(Type *t, Type **bad) +{ + int a, ret; + Type *t1; + + if(bad) + *bad = T; + + switch(t->etype) { + case TINT8: + case TUINT8: + case TINT16: + case TUINT16: + case TINT32: + case TUINT32: + case TINT64: + case TUINT64: + case TINT: + case TUINT: + case TUINTPTR: + case TBOOL: + case TPTR32: + case TPTR64: + case TCHAN: + case TUNSAFEPTR: + return AMEM; + + case TFUNC: + case TMAP: + if(bad) + *bad = t; + return ANOEQ; + + case TFLOAT32: + return AFLOAT32; + + case TFLOAT64: + return AFLOAT64; + + case TCOMPLEX64: + return ACPLX64; + + case TCOMPLEX128: + return ACPLX128; + + case TSTRING: + return ASTRING; + + case TINTER: + if(isnilinter(t)) + return ANILINTER; + return AINTER; + + case TARRAY: + if(isslice(t)) { + if(bad) + *bad = t; + return ANOEQ; + } + if(t->bound == 0) + return AMEM; + a = algtype1(t->type, bad); + if(a == ANOEQ || a == AMEM) { + if(a == ANOEQ && bad) + *bad = t; + return a; + } + return -1; // needs special compare + + case TSTRUCT: + if(t->type != T && t->type->down == T) { + // One-field struct is same as that one field alone. + return algtype1(t->type->type, bad); + } + ret = AMEM; + for(t1=t->type; t1!=T; t1=t1->down) { + a = algtype1(t1->type, bad); + if(a == ANOEQ) + return ANOEQ; // not comparable + if(a != AMEM) + ret = -1; // needs special compare + } + return ret; + } + + fatal("algtype1: unexpected type %T", t); + return 0; +} + +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 + + a = algtype1(t, nil); + if(a == AMEM || a == ANOEQ) { + if(isslice(t)) + return ASLICE; + switch(t->width) { + case 0: + return a + AMEM0 - AMEM; + case 1: + return a + AMEM8 - AMEM; + case 2: + return a + AMEM16 - AMEM; + case 4: + return a + AMEM32 - AMEM; + case 8: + return a + AMEM64 - AMEM; + case 16: + return a + AMEM128 - AMEM; + } } return a; } @@ -550,9 +618,12 @@ maptype(Type *key, Type *val) if(key != nil) { switch(key->etype) { - case TARRAY: - case TSTRUCT: - yyerror("invalid map key type %T", key); + default: + if(algtype1(key, nil) == ANOEQ) + yyerror("invalid map key type %T", key); + break; + case TANY: + // will be resolved later. break; case TFORW: // map[key] used during definition of key. @@ -713,6 +784,7 @@ aindex(Node *b, Type *t) yyerror("array bound must be an integer expression"); break; case CTINT: + case CTRUNE: bound = mpgetfix(b->val.u.xval); if(bound < 0) yyerror("array bound must be non negative"); @@ -727,910 +799,6 @@ aindex(Node *b, Type *t) 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); - - switch(n->esc) { - case EscUnknown: - break; - case EscHeap: - fmtprint(fp, " esc(h)"); - break; - case EscScope: - fmtprint(fp, " esc(s)"); - break; - case EscNone: - fmtprint(fp, " esc(no)"); - break; - case EscNever: - if(!c) - fmtprint(fp, " esc(N)"); - break; - default: - fmtprint(fp, " esc(%d)", n->esc); - break; - } - - if(n->escloopdepth) - fmtprint(fp, " ld(%d)", n->escloopdepth); - - 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->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) { @@ -1671,52 +839,6 @@ treecopy(Node *n) 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) @@ -1872,6 +994,25 @@ eqnote(Strlit *a, Strlit *b) return memcmp(a->s, b->s, a->len) == 0; } +typedef struct TypePairList TypePairList; +struct TypePairList +{ + Type *t1; + Type *t2; + TypePairList *next; +}; + +static int +onlist(TypePairList *l, Type *t1, Type *t2) +{ + for(; l; l=l->next) + if((l->t1 == t1 && l->t2 == t2) || (l->t1 == t2 && l->t2 == t1)) + return 1; + return 0; +} + +static int eqtype1(Type*, Type*, TypePairList*); + // 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 @@ -1881,10 +1022,40 @@ eqnote(Strlit *a, Strlit *b) int eqtype(Type *t1, Type *t2) { + return eqtype1(t1, t2, nil); +} + +static int +eqtype1(Type *t1, Type *t2, TypePairList *assumed_equal) +{ + TypePairList l; + if(t1 == t2) return 1; - if(t1 == T || t2 == T || t1->etype != t2->etype || t1->sym || t2->sym) + if(t1 == T || t2 == T || t1->etype != t2->etype) return 0; + if(t1->sym || t2->sym) { + // Special case: we keep byte and uint8 separate + // for error messages. Treat them as equal. + switch(t1->etype) { + case TUINT8: + if((t1 == types[TUINT8] || t1 == bytetype) && (t2 == types[TUINT8] || t2 == bytetype)) + return 1; + break; + case TINT: + case TINT32: + if((t1 == types[runetype->etype] || t1 == runetype) && (t2 == types[runetype->etype] || t2 == runetype)) + return 1; + break; + } + return 0; + } + + if(onlist(assumed_equal, t1, t2)) + return 1; + l.next = assumed_equal; + l.t1 = t1; + l.t2 = t2; switch(t1->etype) { case TINTER: @@ -1892,10 +1063,12 @@ eqtype(Type *t1, Type *t2) 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; + if(t1->sym != t2->sym || t1->embedded != t2->embedded || !eqtype1(t1->type, t2->type, &l) || !eqnote(t1->note, t2->note)) + goto no; } - return t1 == T && t2 == T; + if(t1 == T && t2 == T) + goto yes; + goto no; case TFUNC: // Loop over structs: receiver, in, out. @@ -1909,26 +1082,38 @@ eqtype(Type *t1, Type *t2) 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->isddd != tb->isddd || !eqtype1(ta->type, tb->type, &l)) + goto no; } if(ta != T || tb != T) - return 0; + goto no; } - return t1 == T && t2 == T; + if(t1 == T && t2 == T) + goto yes; + goto no; case TARRAY: if(t1->bound != t2->bound) - return 0; + goto no; break; case TCHAN: if(t1->chan != t2->chan) - return 0; + goto no; break; } - return eqtype(t1->down, t2->down) && eqtype(t1->type, t2->type); + if(eqtype1(t1->down, t2->down, &l) && eqtype1(t1->type, t2->type, &l)) + goto yes; + goto no; + +yes: + assumed_equal = l.next; + return 1; + +no: + assumed_equal = l.next; + return 0; } // Are t1 and t2 equal struct types when field names are ignored? @@ -1955,9 +1140,6 @@ eqtypenoname(Type *t1, Type *t2) // 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) { @@ -1967,7 +1149,9 @@ assignop(Type *src, Type *dst, char **why) if(why != nil) *why = ""; - if(safemode && src != T && src->etype == TUNSAFEPTR) { + // TODO(rsc,lvd): This behaves poorly in the presence of inlining. + // https://code.google.com/p/go/issues/detail?id=2795 + if(safemode && importpkg == nil && src != T && src->etype == TUNSAFEPTR) { yyerror("cannot use unsafe.Pointer"); errorexit(); } @@ -1991,6 +1175,11 @@ assignop(Type *src, Type *dst, char **why) if(dst->etype == TINTER && src->etype != TNIL) { if(implements(src, dst, &missing, &have, &ptr)) return OCONVIFACE; + + // we'll have complained about this method anyway, supress spurious messages. + if(have && have->sym == missing->sym && (have->type->broke || missing->type->broke)) + return OCONVIFACE; + if(why != nil) { if(isptrto(src, TINTER)) *why = smprint(":\n\t%T is pointer to interface, not interface", src); @@ -2107,29 +1296,25 @@ convertop(Type *src, Type *dst, char **why) return OCONV; } - // 6. src is an integer or has type []byte or []int + // 6. src is an integer or has type []byte or []rune // 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: + if(isslice(src) && dst->etype == TSTRING) { + if(src->type->etype == bytetype->etype) return OARRAYBYTESTR; - case TINT: + if(src->type->etype == runetype->etype) return OARRAYRUNESTR; - } } - // 7. src is a string and dst is []byte or []int. + // 7. src is a string and dst is []byte or []rune. // String to slice. - if(src->etype == TSTRING && isslice(dst) && dst->sym == nil && dst->type == types[dst->type->etype]) { - switch(dst->type->etype) { - case TUINT8: + if(src->etype == TSTRING && isslice(dst)) { + if(dst->type->etype == bytetype->etype) return OSTRARRAYBYTE; - case TINT: + if(dst->type->etype == runetype->etype) return OSTRARRAYRUNE; - } } // 8. src is a pointer or uintptr and dst is unsafe.Pointer. @@ -2161,13 +1346,12 @@ assignconv(Node *n, Type *t, char *context) 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); + yyerror("cannot use %lN as type %T in %s%s", n, t, context, why); op = OCONV; } @@ -2392,7 +1576,7 @@ syslook(char *name, int copy) * 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 + * %T already contains * all the necessary logic to generate a representation * of the type that completely describes it. * using smprint here avoids duplicating that code. @@ -2406,15 +1590,14 @@ typehash(Type *t) char *p; MD5 d; - longsymnames = 1; if(t->thistuple) { // hide method receiver from Tpretty t->thistuple = 0; - p = smprint("%-T", t); + p = smprint("%-uT", t); t->thistuple = 1; - }else - p = smprint("%-T", t); - longsymnames = 0; + } else + p = smprint("%-uT", t); + //print("typehash: %s\n", p); md5reset(&d); md5write(&d, (uchar*)p, strlen(p)); free(p); @@ -2427,7 +1610,7 @@ ptrto(Type *t) Type *t1; if(tptr == 0) - fatal("ptrto: nil"); + fatal("ptrto: no tptr"); t1 = typ(tptr); t1->type = t; t1->width = widthptr; @@ -2784,7 +1967,7 @@ safeexpr(Node *n, NodeList **init) return cheapexpr(n, init); } -static Node* +Node* copyexpr(Node *n, Type *t, NodeList **init) { Node *a, *l; @@ -2905,7 +2088,7 @@ lookdot0(Sym *s, Type *t, Type **save, int ignorecase) return c; } -// search depth d -- +// search depth d for field/method s -- // return count of fields+methods // found at search depth. // answer is in dotlist array and @@ -2983,7 +2166,7 @@ adddot(Node *n) out: if(c > 1) - yyerror("ambiguous DOT reference %T.%S", t, s); + yyerror("ambiguous selector %T.%S", t, s); // rebuild elided dots for(c=d-1; c>=0; c--) @@ -3028,8 +2211,6 @@ expand0(Type *t, int followptr) 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; @@ -3045,8 +2226,6 @@ expand0(Type *t, int followptr) 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; @@ -3122,8 +2301,11 @@ expandmeth(Sym *s, Type *t) if(c == 0) continue; if(c == 1) { - sl->good = 1; - sl->field = f; + // addot1 may have dug out arbitrary fields, we only want methods. + if(f->type->etype == TFUNC && f->type->thistuple > 0) { + sl->good = 1; + sl->field = f; + } } break; } @@ -3164,13 +2346,12 @@ structargs(Type **tl, int mustname) 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 + if(mustname && (t->sym == nil || strcmp(t->sym->name, "_") == 0)) { + // invent a name so that we can refer to it in the trampoline snprint(buf, sizeof buf, ".anon%d", gen++); n = newname(lookup(buf)); - } + } else if(t->sym) + n = newname(t->sym); a = nod(ODCLFIELD, n, typenod(t->type)); a->isddd = t->isddd; if(n != N) @@ -3212,7 +2393,7 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface) int isddd; Val v; - if(0 && debug['r']) + if(debug['r']) print("genwrapper rcvrtype=%T method=%T newnam=%S\n", rcvr, method, newnam); @@ -3226,8 +2407,6 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface) 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) { @@ -3244,7 +2423,12 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface) } t->list = concat(l, in); t->rlist = out; + + fn = nod(ODCLFUNC, N, N); + fn->nname = newname(newnam); + fn->nname->defn = fn; fn->nname->ntype = t; + declare(fn->nname, PFUNC); funchdr(fn); // arg list @@ -3298,6 +2482,443 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface) funccompile(fn, 0); } +static Node* +hashmem(Type *t, vlong width) +{ + Node *tfn, *n; + Sym *sym; + + sym = pkglookup("memhash", runtimepkg); + + n = newname(sym); + n->class = PFUNC; + tfn = nod(OTFUNC, N, N); + tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(ptrto(types[TUINTPTR])))); + tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR]))); + tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(ptrto(t)))); + typecheck(&tfn, Etype); + n->type = tfn->type; + return n; +} + +static Node* +hashfor(Type *t) +{ + int a; + Sym *sym; + Node *tfn, *n; + + a = algtype1(t, nil); + switch(a) { + case AMEM: + return hashmem(t, t->width); + case AINTER: + sym = pkglookup("interhash", runtimepkg); + break; + case ANILINTER: + sym = pkglookup("nilinterhash", runtimepkg); + break; + case ASTRING: + sym = pkglookup("strhash", runtimepkg); + break; + case AFLOAT32: + sym = pkglookup("f32hash", runtimepkg); + break; + case AFLOAT64: + sym = pkglookup("f64hash", runtimepkg); + break; + case ACPLX64: + sym = pkglookup("c64hash", runtimepkg); + break; + case ACPLX128: + sym = pkglookup("c128hash", runtimepkg); + break; + default: + sym = typesymprefix(".hash", t); + break; + } + + n = newname(sym); + n->class = PFUNC; + tfn = nod(OTFUNC, N, N); + tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(ptrto(types[TUINTPTR])))); + tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR]))); + tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(ptrto(t)))); + typecheck(&tfn, Etype); + n->type = tfn->type; + return n; +} + +/* + * Generate a helper function to compute the hash of a value of type t. + */ +void +genhash(Sym *sym, Type *t) +{ + Node *n, *fn, *np, *nh, *ni, *call, *nx, *na, *tfn; + Node *hashel; + Type *first, *t1; + int old_safemode; + int64 size, mul; + + if(debug['r']) + print("genhash %S %T\n", sym, t); + + lineno = 1; // less confusing than end of input + dclcontext = PEXTERN; + markdcl(); + + // func sym(h *uintptr, s uintptr, p *T) + fn = nod(ODCLFUNC, N, N); + fn->nname = newname(sym); + fn->nname->class = PFUNC; + tfn = nod(OTFUNC, N, N); + fn->nname->ntype = tfn; + + n = nod(ODCLFIELD, newname(lookup("h")), typenod(ptrto(types[TUINTPTR]))); + tfn->list = list(tfn->list, n); + nh = n->left; + n = nod(ODCLFIELD, newname(lookup("s")), typenod(types[TUINTPTR])); + tfn->list = list(tfn->list, n); + n = nod(ODCLFIELD, newname(lookup("p")), typenod(ptrto(t))); + tfn->list = list(tfn->list, n); + np = n->left; + + funchdr(fn); + typecheck(&fn->nname->ntype, Etype); + + // genhash is only called for types that have equality but + // cannot be handled by the standard algorithms, + // so t must be either an array or a struct. + switch(t->etype) { + default: + fatal("genhash %T", t); + case TARRAY: + if(isslice(t)) + fatal("genhash %T", t); + // An array of pure memory would be handled by the + // standard algorithm, so the element type must not be + // pure memory. + hashel = hashfor(t->type); + n = nod(ORANGE, N, nod(OIND, np, N)); + ni = newname(lookup("i")); + ni->type = types[TINT]; + n->list = list1(ni); + n->colas = 1; + colasdefn(n->list, n); + ni = n->list->n; + + // *h = *h<<3 | *h>>61 + n->nbody = list(n->nbody, + nod(OAS, + nod(OIND, nh, N), + nod(OOR, + nod(OLSH, nod(OIND, nh, N), nodintconst(3)), + nod(ORSH, nod(OIND, nh, N), nodintconst(widthptr*8-3))))); + + // *h *= mul + // Same multipliers as in runtime.memhash. + if(widthptr == 4) + mul = 3267000013LL; + else + mul = 23344194077549503LL; + n->nbody = list(n->nbody, + nod(OAS, + nod(OIND, nh, N), + nod(OMUL, nod(OIND, nh, N), nodintconst(mul)))); + + // hashel(h, sizeof(p[i]), &p[i]) + call = nod(OCALL, hashel, N); + call->list = list(call->list, nh); + call->list = list(call->list, nodintconst(t->type->width)); + nx = nod(OINDEX, np, ni); + nx->etype = 1; // no bounds check + na = nod(OADDR, nx, N); + na->etype = 1; // no escape to heap + call->list = list(call->list, na); + n->nbody = list(n->nbody, call); + + fn->nbody = list(fn->nbody, n); + break; + + case TSTRUCT: + // Walk the struct using memhash for runs of AMEM + // and calling specific hash functions for the others. + first = T; + for(t1=t->type;; t1=t1->down) { + if(t1 != T && algtype1(t1->type, nil) == AMEM) { + if(first == T) + first = t1; + continue; + } + // Run memhash for fields up to this one. + if(first != T) { + if(first->down == t1) + size = first->type->width; + else if(t1 == T) + size = t->width - first->width; // first->width is offset + else + size = t1->width - first->width; // both are offsets + hashel = hashmem(first->type, size); + // hashel(h, size, &p.first) + call = nod(OCALL, hashel, N); + call->list = list(call->list, nh); + call->list = list(call->list, nodintconst(size)); + nx = nod(OXDOT, np, newname(first->sym)); // TODO: fields from other packages? + na = nod(OADDR, nx, N); + na->etype = 1; // no escape to heap + call->list = list(call->list, na); + fn->nbody = list(fn->nbody, call); + + first = T; + } + if(t1 == T) + break; + + // Run hash for this field. + hashel = hashfor(t1->type); + // hashel(h, size, &p.t1) + call = nod(OCALL, hashel, N); + call->list = list(call->list, nh); + call->list = list(call->list, nodintconst(t1->type->width)); + nx = nod(OXDOT, np, newname(t1->sym)); // TODO: fields from other packages? + na = nod(OADDR, nx, N); + na->etype = 1; // no escape to heap + call->list = list(call->list, na); + fn->nbody = list(fn->nbody, call); + } + break; + } + + if(debug['r']) + dumplist("genhash body", fn->nbody); + + funcbody(fn); + curfn = fn; + fn->dupok = 1; + typecheck(&fn, Etop); + typechecklist(fn->nbody, Etop); + curfn = nil; + + // Disable safemode while compiling this code: the code we + // generate internally can refer to unsafe.Pointer. + // In this case it can happen if we need to generate an == + // for a struct containing a reflect.Value, which itself has + // an unexported field of type unsafe.Pointer. + old_safemode = safemode; + safemode = 0; + funccompile(fn, 0); + safemode = old_safemode; +} + +// Return node for +// if p.field != q.field { *eq = false; return } +static Node* +eqfield(Node *p, Node *q, Node *field, Node *eq) +{ + Node *nif, *nx, *ny; + + nx = nod(OXDOT, p, field); + ny = nod(OXDOT, q, field); + nif = nod(OIF, N, N); + nif->ntest = nod(ONE, nx, ny); + nif->nbody = list(nif->nbody, nod(OAS, nod(OIND, eq, N), nodbool(0))); + nif->nbody = list(nif->nbody, nod(ORETURN, N, N)); + return nif; +} + +static Node* +eqmemfunc(vlong size, Type *type) +{ + char buf[30]; + Node *fn; + + switch(size) { + default: + fn = syslook("memequal", 1); + break; + case 1: + case 2: + case 4: + case 8: + case 16: + snprint(buf, sizeof buf, "memequal%d", (int)size*8); + fn = syslook(buf, 1); + break; + } + argtype(fn, type); + argtype(fn, type); + return fn; +} + +// Return node for +// if memequal(size, &p.field, &q.field, eq); !*eq { return } +static Node* +eqmem(Node *p, Node *q, Node *field, vlong size, Node *eq) +{ + Node *nif, *nx, *ny, *call; + + nx = nod(OADDR, nod(OXDOT, p, field), N); + nx->etype = 1; // does not escape + ny = nod(OADDR, nod(OXDOT, q, field), N); + ny->etype = 1; // does not escape + typecheck(&nx, Erv); + typecheck(&ny, Erv); + + call = nod(OCALL, eqmemfunc(size, nx->type->type), N); + call->list = list(call->list, eq); + call->list = list(call->list, nodintconst(size)); + call->list = list(call->list, nx); + call->list = list(call->list, ny); + + nif = nod(OIF, N, N); + nif->ninit = list(nif->ninit, call); + nif->ntest = nod(ONOT, nod(OIND, eq, N), N); + nif->nbody = list(nif->nbody, nod(ORETURN, N, N)); + return nif; +} + +/* + * Generate a helper function to check equality of two values of type t. + */ +void +geneq(Sym *sym, Type *t) +{ + Node *n, *fn, *np, *neq, *nq, *tfn, *nif, *ni, *nx, *ny, *nrange; + Type *t1, *first; + int old_safemode; + int64 size; + + if(debug['r']) + print("geneq %S %T\n", sym, t); + + lineno = 1; // less confusing than end of input + dclcontext = PEXTERN; + markdcl(); + + // func sym(eq *bool, s uintptr, p, q *T) + fn = nod(ODCLFUNC, N, N); + fn->nname = newname(sym); + fn->nname->class = PFUNC; + tfn = nod(OTFUNC, N, N); + fn->nname->ntype = tfn; + + n = nod(ODCLFIELD, newname(lookup("eq")), typenod(ptrto(types[TBOOL]))); + tfn->list = list(tfn->list, n); + neq = n->left; + n = nod(ODCLFIELD, newname(lookup("s")), typenod(types[TUINTPTR])); + tfn->list = list(tfn->list, n); + n = nod(ODCLFIELD, newname(lookup("p")), typenod(ptrto(t))); + tfn->list = list(tfn->list, n); + np = n->left; + n = nod(ODCLFIELD, newname(lookup("q")), typenod(ptrto(t))); + tfn->list = list(tfn->list, n); + nq = n->left; + + funchdr(fn); + + // geneq is only called for types that have equality but + // cannot be handled by the standard algorithms, + // so t must be either an array or a struct. + switch(t->etype) { + default: + fatal("geneq %T", t); + case TARRAY: + if(isslice(t)) + fatal("geneq %T", t); + // An array of pure memory would be handled by the + // standard memequal, so the element type must not be + // pure memory. Even if we unrolled the range loop, + // each iteration would be a function call, so don't bother + // unrolling. + nrange = nod(ORANGE, N, nod(OIND, np, N)); + ni = newname(lookup("i")); + ni->type = types[TINT]; + nrange->list = list1(ni); + nrange->colas = 1; + colasdefn(nrange->list, nrange); + ni = nrange->list->n; + + // if p[i] != q[i] { *eq = false; return } + nx = nod(OINDEX, np, ni); + nx->etype = 1; // no bounds check + ny = nod(OINDEX, nq, ni); + ny->etype = 1; // no bounds check + + nif = nod(OIF, N, N); + nif->ntest = nod(ONE, nx, ny); + nif->nbody = list(nif->nbody, nod(OAS, nod(OIND, neq, N), nodbool(0))); + nif->nbody = list(nif->nbody, nod(ORETURN, N, N)); + nrange->nbody = list(nrange->nbody, nif); + fn->nbody = list(fn->nbody, nrange); + + // *eq = true; + fn->nbody = list(fn->nbody, nod(OAS, nod(OIND, neq, N), nodbool(1))); + break; + + case TSTRUCT: + // Walk the struct using memequal for runs of AMEM + // and calling specific equality tests for the others. + first = T; + for(t1=t->type;; t1=t1->down) { + if(t1 != T && algtype1(t1->type, nil) == AMEM) { + if(first == T) + first = t1; + continue; + } + // Run memequal for fields up to this one. + // TODO(rsc): All the calls to newname are wrong for + // cross-package unexported fields. + if(first != T) { + if(first->down == t1) { + fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym), neq)); + } else if(first->down->down == t1) { + fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym), neq)); + first = first->down; + fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym), neq)); + } else { + // More than two fields: use memequal. + if(t1 == T) + size = t->width - first->width; // first->width is offset + else + size = t1->width - first->width; // both are offsets + fn->nbody = list(fn->nbody, eqmem(np, nq, newname(first->sym), size, neq)); + } + first = T; + } + if(t1 == T) + break; + + // Check this field, which is not just memory. + fn->nbody = list(fn->nbody, eqfield(np, nq, newname(t1->sym), neq)); + } + + // *eq = true; + fn->nbody = list(fn->nbody, nod(OAS, nod(OIND, neq, N), nodbool(1))); + break; + } + + if(debug['r']) + dumplist("geneq body", fn->nbody); + + funcbody(fn); + curfn = fn; + fn->dupok = 1; + typecheck(&fn, Etop); + typechecklist(fn->nbody, Etop); + curfn = nil; + + // Disable safemode while compiling this code: the code we + // generate internally can refer to unsafe.Pointer. + // In this case it can happen if we need to generate an == + // for a struct containing a reflect.Value, which itself has + // an unexported field of type unsafe.Pointer. + old_safemode = safemode; + safemode = 0; + funccompile(fn, 0); + safemode = old_safemode; +} + static Type* ifacelookdot(Sym *s, Type *t, int *followptr, int ignorecase) { @@ -3855,21 +3476,31 @@ ngotype(Node *n) } /* - * 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. + * Convert raw string to the prefix that will be used in the symbol + * table. All control characters, space, '%' and '"', as well as + * non-7-bit clean bytes turn into %xx. The period needs escaping + * only in the last segment of the path, and it makes for happier + * users if we escape that as little as possible. + * + * If you edit this, edit ../ld/lib.c:/^pathtoprefix copy too. */ static char* pathtoprefix(char *s) { static char hex[] = "0123456789abcdef"; - char *p, *r, *w; + char *p, *r, *w, *l; int n; + // find first character past the last slash, if any. + l = s; + for(r=s; *r; r++) + if(*r == '/') + l = r+1; + // check for chars that need escaping n = 0; for(r=s; *r; r++) - if(*r <= ' ' || *r == '.' || *r == '%' || *r == '"') + if(*r <= ' ' || (*r == '.' && r >= l) || *r == '%' || *r == '"' || *r >= 0x7f) n++; // quick exit @@ -3879,7 +3510,7 @@ pathtoprefix(char *s) // escape p = mal((r-s)+1+2*n); for(r=s, w=p; *r; r++) { - if(*r <= ' ' || *r == '.' || *r == '%' || *r == '"') { + if(*r <= ' ' || (*r == '.' && r >= l) || *r == '%' || *r == '"' || *r >= 0x7f) { *w++ = '%'; *w++ = hex[(*r>>4)&0xF]; *w++ = hex[*r&0xF]; @@ -3924,3 +3555,26 @@ strlit(char *s) t->len = strlen(s); return t; } + +void +addinit(Node **np, NodeList *init) +{ + Node *n; + + if(init == nil) + return; + + n = *np; + switch(n->op) { + case ONAME: + case OLITERAL: + // There may be multiple refs to this node; + // introduce OCONVNOP to hold init list. + n = nod(OCONVNOP, n, N); + n->type = n->left->type; + n->typecheck = 1; + *np = n; + break; + } + n->ninit = concat(init, n->ninit); +} diff --git a/src/cmd/gc/swt.c b/src/cmd/gc/swt.c index 0381132d0..f1a95587f 100644 --- a/src/cmd/gc/swt.c +++ b/src/cmd/gc/swt.c @@ -132,6 +132,7 @@ exprcmp(Case *c1, Case *c2) n = mpcmpfltflt(n1->val.u.fval, n2->val.u.fval); break; case CTINT: + case CTRUNE: n = mpcmpfixfix(n1->val.u.xval, n2->val.u.xval); break; case CTSTR: @@ -380,6 +381,7 @@ mkcaselist(Node *sw, int arg) switch(consttype(n->left)) { case CTFLT: case CTINT: + case CTRUNE: case CTSTR: c->type = Texprconst; } @@ -538,7 +540,7 @@ loop: } // deal with the variables one-at-a-time - if(c0->type != Texprconst) { + if(!okforcmp[t->etype] || c0->type != Texprconst) { a = exprbsw(c0, 1, arg); cas = list(cas, a); c0 = c0->link; @@ -790,7 +792,6 @@ walkswitch(Node *sw) * 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); @@ -810,14 +811,16 @@ walkswitch(Node *sw) void typecheckswitch(Node *n) { - int top, lno; - Type *t; + int top, lno, ptr; + char *nilonly; + Type *t, *missing, *have; NodeList *l, *ll; Node *ncase, *nvar; Node *def; lno = lineno; typechecklist(n->ninit, Etop); + nilonly = nil; if(n->ntest != N && n->ntest->op == OTYPESW) { // type switch @@ -825,7 +828,7 @@ typecheckswitch(Node *n) 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); + yyerror("cannot type switch on non-interface value %lN", n->ntest->right); } else { // value switch top = Erv; @@ -835,6 +838,16 @@ typecheckswitch(Node *n) t = n->ntest->type; } else t = types[TBOOL]; + if(t) { + if(!okforeq[t->etype] || isfixedarray(t)) + yyerror("cannot switch on %lN", n->ntest); + else if(t->etype == TARRAY) + nilonly = "slice"; + else if(t->etype == TFUNC) + nilonly = "func"; + else if(t->etype == TMAP) + nilonly = "map"; + } } n->type = t; @@ -854,21 +867,37 @@ typecheckswitch(Node *n) typecheck(&ll->n, Erv | Etype); if(ll->n->type == T || t == T) continue; + setlineno(ncase); 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); + else if(ll->n->type != T && !assignop(ll->n->type, t, nil) && !assignop(t, ll->n->type, nil)) { + if(n->ntest) + yyerror("invalid case %N in switch on %N (mismatched types %T and %T)", ll->n, n->ntest, ll->n->type, t); + else + yyerror("invalid case %N in switch (mismatched types %T and bool)", ll->n, ll->n->type); + } else if(nilonly && !isconst(ll->n, CTNIL)) { + yyerror("invalid case %N in switch (can only compare %s %N to nil)", ll->n, nilonly, n->ntest); + } 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); + } else if(ll->n->op != OTYPE && ll->n->type != T) { // should this be ||? + yyerror("%lN is not a type", ll->n); // reset to original type ll->n = n->ntest->right; + } else if(ll->n->type->etype != TINTER && !implements(ll->n->type, t, &missing, &have, &ptr)) { + if(have && !missing->broke && !have->broke) + yyerror("impossible type switch case: %lN cannot have dynamic type %T" + " (wrong type for %S method)\n\thave %S%hT\n\twant %S%hT", + n->ntest->right, ll->n->type, missing->sym, have->sym, have->type, + missing->sym, missing->type); + else if(!missing->broke) + yyerror("impossible type switch case: %lN cannot have dynamic type %T" + " (missing %S method)", n->ntest->right, ll->n->type, missing->sym); } break; } diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c index b9c302ce8..2e8c3b1e2 100644 --- a/src/cmd/gc/typecheck.c +++ b/src/cmd/gc/typecheck.c @@ -43,7 +43,7 @@ resolve(Node *n) { Node *r; - if(n != N && n->op == ONONAME && (r = n->sym->def) != N) { + if(n != N && n->op == ONONAME && n->sym != S && (r = n->sym->def) != N) { if(r->op != OIOTA) n = r; else if(n->iota >= 0) @@ -79,6 +79,7 @@ static char* _typekind[] = { [TSTRING] = "string", [TPTR32] = "pointer", [TPTR64] = "pointer", + [TUNSAFEPTR] = "unsafe.Pointer", [TSTRUCT] = "struct", [TINTER] = "interface", [TCHAN] = "chan", @@ -90,11 +91,15 @@ static char* _typekind[] = { }; static char* -typekind(int et) +typekind(Type *t) { + int et; static char buf[50]; char *s; + if(isslice(t)) + return "slice"; + et = t->etype; if(0 <= et && et < nelem(_typekind) && (s=_typekind[et]) != nil) return s; snprint(buf, sizeof buf, "etype=%d", et); @@ -113,8 +118,7 @@ typecheck(Node **np, int top) Node *n, *l, *r; NodeList *args; int lno, ok, ntop; - Type *t, *tp, *ft, *missing, *have; - Sym *sym; + Type *t, *tp, *ft, *missing, *have, *badtype; Val v; char *why; @@ -153,7 +157,7 @@ typecheck(Node **np, int top) } if(n->typecheck == 2) { - yyerror("typechecking loop involving %#N", n); + yyerror("typechecking loop involving %N", n); lineno = lno; return n; } @@ -210,6 +214,10 @@ reswitch: } n->used = 1; } + if(!(top &Ecall) && isunsafebuiltin(n)) { + yyerror("%N is not an expression, must be called", n); + goto error; + } ok |= Erv; goto ret; @@ -254,13 +262,14 @@ reswitch: l = typecheck(&n->left, Erv); switch(consttype(l)) { case CTINT: + case CTRUNE: v = l->val; break; case CTFLT: v = toint(l->val); break; default: - yyerror("invalid array bound %#N", l); + yyerror("invalid array bound %N", l); goto error; } t->bound = mpgetfix(v.u.xval); @@ -311,7 +320,7 @@ reswitch: case OTSTRUCT: ok |= Etype; n->op = OTYPE; - n->type = dostruct(n->list, TSTRUCT); + n->type = tostruct(n->list); if(n->type == T) goto error; n->list = nil; @@ -320,7 +329,7 @@ reswitch: case OTINTER: ok |= Etype; n->op = OTYPE; - n->type = dostruct(n->list, TINTER); + n->type = tointerface(n->list); if(n->type == T) goto error; break; @@ -340,6 +349,7 @@ reswitch: ntop = Erv | Etype; if(!(top & Eaddr)) // The *x in &*x is not an indirect. ntop |= Eindir; + ntop |= top & Ecomplit; l = typecheck(&n->left, ntop); if((t = l->type) == T) goto error; @@ -351,7 +361,7 @@ reswitch: goto ret; } if(!isptr[t->etype]) { - yyerror("invalid indirect of %+N", n->left); + yyerror("invalid indirect of %lN", n->left); goto error; } ok |= Erv; @@ -414,15 +424,25 @@ reswitch: 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.) + // the same type. + // + // the only conversion that isn't a no-op is concrete == interface. + // in that case, check comparability of the concrete type. if(r->type->etype != TBLANK && (aop = assignop(l->type, r->type, nil)) != 0) { + if(isinter(r->type) && !isinter(l->type) && algtype1(l->type, nil) == ANOEQ) { + yyerror("invalid operation: %N (operator %O not defined on %s)", n, op, typekind(l->type)); + goto error; + } 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) { + if(isinter(l->type) && !isinter(r->type) && algtype1(r->type, nil) == ANOEQ) { + yyerror("invalid operation: %N (operator %O not defined on %s)", n, op, typekind(r->type)); + goto error; + } r = nod(aop, r, N); r->type = l->type; r->typecheck = 1; @@ -433,24 +453,36 @@ reswitch: } 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); + 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)); + yyerror("invalid operation: %N (operator %O not defined on %s)", n, op, typekind(t)); + goto error; + } + // okfor allows any array == array, map == map, func == func. + // restrict to slice/map/func == nil and nil == slice/map/func. + if(isfixedarray(l->type) && algtype1(l->type, nil) == ANOEQ) { + yyerror("invalid operation: %N (%T cannot be compared)", n, l->type); 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); + yyerror("invalid operation: %N (slice can only be compared to nil)", n); goto error; } + if(l->type->etype == TMAP && !isnil(l) && !isnil(r)) { + yyerror("invalid operation: %N (map can only be compared to nil)", n); + goto error; + } + if(l->type->etype == TFUNC && !isnil(l) && !isnil(r)) { + yyerror("invalid operation: %N (func can only be compared to nil)", n); + goto error; + } + if(l->type->etype == TSTRUCT && algtype1(l->type, &badtype) == ANOEQ) { + yyerror("invalid operation: %N (struct containing %T cannot be compared)", n, badtype); + goto error; + } + t = l->type; if(iscmp[n->op]) { evconst(n); @@ -488,12 +520,12 @@ reswitch: 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); + 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); + yyerror("invalid operation: %N (shift of type %T)", n, t); goto error; } // no defaultlit for left @@ -510,7 +542,7 @@ reswitch: if((t = l->type) == T) goto error; if(!okfor[n->op][t->etype]) { - yyerror("invalid operation: %#O %T", n->op, t); + yyerror("invalid operation: %O %T", n->op, t); goto error; } n->type = t; @@ -524,21 +556,17 @@ reswitch: 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"); - } + checklvalue(n->left, "take the address of"); + for(l=n->left; l->op == ODOT; l=l->left) + l->addrtaken = 1; + l->addrtaken = 1; defaultlit(&n->left, T); l = n->left; if((t = l->type) == T) goto error; // top&Eindir means this is &x in *&x. (or the arg to built-in print) // n->etype means code generator flagged it as non-escaping. - if(debug['s'] && !(top & Eindir) && !n->etype) + if(debug['N'] && !(top & Eindir) && !n->etype) addrescapes(n->left); n->type = ptrto(t); goto ret; @@ -557,36 +585,34 @@ reswitch: case ODOT: typecheck(&n->left, Erv|Etype); defaultlit(&n->left, T); - l = n->left; - if((t = l->type) == T) + if((t = n->left->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(n->left->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); + 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); + 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); + yyerror("type %T has no method %hS", n->left->type, n->right->sym); n->type = T; goto error; } n->op = ONAME; - n->sym = methodsym(sym, l->type, 0); - n->type = methodfunc(n->type, l->type); + n->sym = n->right->sym; + n->type = methodfunc(n->type, n->left->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) @@ -596,9 +622,9 @@ reswitch: } 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); + 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); + yyerror("%N undefined (type %T has no field or method %S)", n, n->left->type, n->right->sym); goto error; } switch(n->op) { @@ -620,7 +646,7 @@ reswitch: if((t = l->type) == T) goto error; if(!isinter(t)) { - yyerror("invalid type assertion: %#N (non-interface type %T on left)", n, t); + yyerror("invalid type assertion: %N (non-interface type %T on left)", n, t); goto error; } if(n->right != N) { @@ -633,12 +659,12 @@ reswitch: 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", + yyerror("impossible type assertion: %lN cannot have dynamic type %T" + " (wrong type for %S method)\n\thave %S%hT\n\twant %S%hT", l, n->type, missing->sym, have->sym, have->type, missing->sym, missing->type); else - yyerror("impossible type assertion: %+N cannot have dynamic type %T" + yyerror("impossible type assertion: %lN cannot have dynamic type %T" " (missing %S method)", l, n->type, missing->sym); goto error; } @@ -656,13 +682,13 @@ reswitch: goto error; switch(t->etype) { default: - yyerror("invalid operation: %#N (index of type %T)", n, t); + 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); + yyerror("non-integer array index %N", n->right); n->type = t->type; break; @@ -678,7 +704,7 @@ reswitch: 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); + yyerror("non-integer string index %N", n->right); n->type = types[TUINT8]; break; } @@ -692,11 +718,11 @@ reswitch: if((t = l->type) == T) goto error; if(t->etype != TCHAN) { - yyerror("invalid operation: %#N (receive from non-chan type %T)", n, t); + 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); + yyerror("invalid operation: %N (receive from send-only type %T)", n, t); goto error; } n->type = t->type; @@ -704,7 +730,7 @@ reswitch: case OSEND: if(top & Erv) { - yyerror("send statement %#N used as value; use select for non-blocking send", n); + yyerror("send statement %N used as value; use select for non-blocking send", n); goto error; } ok |= Etop | Erv; @@ -715,11 +741,11 @@ reswitch: if((t = l->type) == T) goto error; if(t->etype != TCHAN) { - yyerror("invalid operation: %#N (send to non-chan type %T)", n, t); + 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); + yyerror("invalid operation: %N (send to receive-only type %T)", n, t); goto error; } defaultlit(&n->right, t->type); @@ -741,14 +767,19 @@ reswitch: defaultlit(&n->right->left, T); defaultlit(&n->right->right, T); if(isfixedarray(n->left->type)) { + if(!islvalue(n->left)) { + yyerror("invalid operation %N (slice of unaddressable value)", n); + goto error; + } n->left = nod(OADDR, n->left, N); - typecheck(&n->left, top); + n->left->implicit = 1; + typecheck(&n->left, Erv); } 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); + yyerror("invalid slice index %N (type %T)", n->right->left, t); goto error; } } @@ -756,7 +787,7 @@ reswitch: if((t = n->right->right->type) == T) goto error; if(!isint[t->etype]) { - yyerror("invalid slice index %#N (type %T)", n->right->right, t); + yyerror("invalid slice index %N (type %T)", n->right->right, t); goto error; } } @@ -780,7 +811,7 @@ reswitch: n->type = t; goto ret; } - yyerror("cannot slice %#N (type %T)", l, t); + yyerror("cannot slice %N (type %T)", l, t); goto error; /* @@ -790,7 +821,7 @@ reswitch: l = n->left; if(l->op == ONAME && (r = unsafenmagic(n)) != N) { if(n->isddd) - yyerror("invalid use of ... with builtin %#N", l); + yyerror("invalid use of ... with builtin %N", l); n = r; goto reswitch; } @@ -798,7 +829,7 @@ reswitch: l = n->left; if(l->op == ONAME && l->etype != 0) { if(n->isddd && l->etype != OAPPEND) - yyerror("invalid use of ... with builtin %#N", l); + yyerror("invalid use of ... with builtin %N", l); // builtin: OLEN, OCAP, etc. n->op = l->etype; n->left = n->right; @@ -848,7 +879,7 @@ reswitch: default: n->op = OCALLFUNC; if(t->etype != TFUNC) { - yyerror("cannot call non-function %#N (type %T)", l, t); + yyerror("cannot call non-function %N (type %T)", l, t); goto error; } break; @@ -869,7 +900,7 @@ reswitch: } // multiple return if(!(top & (Efnstruct | Etop))) { - yyerror("multiple-value %#N() in single-value context", l); + yyerror("multiple-value %N() in single-value context", l); goto ret; } n->type = getoutargx(l->type); @@ -880,7 +911,7 @@ reswitch: case OREAL: case OIMAG: ok |= Erv; - if(onearg(n, "%#O", n->op) < 0) + if(onearg(n, "%O", n->op) < 0) goto error; typecheck(&n->left, Erv); defaultlit(&n->left, T); @@ -946,7 +977,7 @@ reswitch: 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); + yyerror("invalid operation: %N (complex of types %T, %T)", n, l->type, r->type); goto error; } switch(l->type->etype) { @@ -970,7 +1001,7 @@ reswitch: goto ret; case OCLOSE: - if(onearg(n, "%#O", n->op) < 0) + if(onearg(n, "%O", n->op) < 0) goto error; typecheck(&n->left, Erv); defaultlit(&n->left, T); @@ -978,10 +1009,39 @@ reswitch: if((t = l->type) == T) goto error; if(t->etype != TCHAN) { - yyerror("invalid operation: %#N (non-chan type %T)", n, t); + yyerror("invalid operation: %N (non-chan type %T)", n, t); + goto error; + } + if(!(t->chan & Csend)) { + yyerror("invalid operation: %N (cannot close receive-only channel)", n); + goto error; + } + ok |= Etop; + goto ret; + + case ODELETE: + args = n->list; + if(args == nil) { + yyerror("missing arguments to delete"); + goto error; + } + if(args->next == nil) { + yyerror("missing second (key) argument to delete"); + goto error; + } + if(args->next->next != nil) { + yyerror("too many arguments to delete"); goto error; } ok |= Etop; + typechecklist(args, Erv); + l = args->n; + r = args->next->n; + if(l->type != T && l->type->etype != TMAP) { + yyerror("first argument to delete must be map; have %lT", l->type); + goto error; + } + args->next->n = assignconv(r, l->type->down, "delete"); goto ret; case OAPPEND: @@ -999,6 +1059,7 @@ reswitch: 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"); @@ -1008,6 +1069,10 @@ reswitch: yyerror("too many arguments to append"); goto error; } + if(istype(t->type, TUINT8) && istype(args->next->n->type, TSTRING)) { + defaultlit(&args->next->n, types[TSTRING]); + goto ret; + } args->next->n = assignconv(args->next->n, t->orig, "append"); goto ret; } @@ -1039,15 +1104,15 @@ reswitch: 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]) + if(eqtype(n->left->type->type, bytetype)) 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); @@ -1071,7 +1136,7 @@ reswitch: 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); + yyerror("cannot convert %lN to type %T%s", n->left, n->type, why); n->op = OCONV; } switch(n->op) { @@ -1096,6 +1161,7 @@ reswitch: yyerror("missing argument to make"); goto error; } + n->list = nil; l = args->n; args = args->next; typecheck(&l, Etype); @@ -1275,7 +1341,7 @@ reswitch: 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); + yyerror("non-bool %lN used as for condition", n->ntest); typecheck(&n->nincr, Etop); typechecklist(n->nbody, Etop); goto ret; @@ -1285,7 +1351,7 @@ reswitch: 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); + yyerror("non-bool %lN used as if condition", n->ntest); typechecklist(n->nbody, Etop); typechecklist(n->nelse, Etop); goto ret; @@ -1372,21 +1438,21 @@ ret: goto error; } if((top & (Erv|Etype)) == Etype && n->op != OTYPE) { - yyerror("%#N is not a type", n); + 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); + 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); + 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); + yyerror("%N not used", n); n->diag = 1; } goto error; @@ -1399,7 +1465,7 @@ ret: goto out; badcall1: - yyerror("invalid argument %#N (type %T) for %#O", n->left, n->left->type, n->op); + yyerror("invalid argument %lN for %O", n->left, n->op); goto error; error: @@ -1445,14 +1511,14 @@ onearg(Node *n, char *f, ...) va_start(arg, f); p = vsmprint(f, arg); va_end(arg); - yyerror("missing argument to %s: %#N", p, n); + 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); + yyerror("too many arguments to %s: %N", p, n); n->left = n->list->n; n->list = nil; return -1; @@ -1468,17 +1534,17 @@ twoarg(Node *n) if(n->left != N) return 0; if(n->list == nil) { - yyerror("missing argument to %#O - %#N", n->op, n); + 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); + 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); + yyerror("too many arguments to %O - %N", n->op, n); n->list = nil; return -1; } @@ -1499,7 +1565,7 @@ lookdot1(Sym *s, Type *t, Type *f, int dostrcmp) if(f->sym != s) continue; if(r != T) { - yyerror("ambiguous DOT reference %T.%S", t, s); + yyerror("ambiguous selector %T.%S", t, s); break; } r = f; @@ -1547,7 +1613,7 @@ looktypedot(Node *n, Type *t, int dostrcmp) && !isptr[t->etype] && f2->embedded != 2 && !isifacemethod(f2->type)) { - yyerror("invalid method expression %#N (needs pointer receiver: (*%T).%s)", n, t, f2->sym->name); + yyerror("invalid method expression %N (needs pointer receiver: (*%T).%hS)", n, t, f2->sym); return 0; } @@ -1558,6 +1624,14 @@ looktypedot(Node *n, Type *t, int dostrcmp) return 1; } +static Type* +derefall(Type* t) +{ + while(t && t->etype == tptr) + t = t->type; + return t; +} + static int lookdot(Node *n, Type *t, int dostrcmp) { @@ -1583,7 +1657,7 @@ lookdot(Node *n, Type *t, int dostrcmp) if(f1 != T) { if(f2 != T) - yyerror("ambiguous DOT reference %S as both field and method", + yyerror("%S is both field and method", n->right->sym); if(f1->width == BADWIDTH) fatal("lookdot badwidth %T %p", f1, f1); @@ -1606,7 +1680,7 @@ lookdot(Node *n, Type *t, int dostrcmp) if(!eqtype(rcvr, tt)) { if(rcvr->etype == tptr && eqtype(rcvr->type, tt)) { checklvalue(n->left, "call pointer method on"); - if(debug['s']) + if(debug['N']) addrescapes(n->left); n->left = nod(OADDR, n->left, N); n->left->implicit = 1; @@ -1615,14 +1689,22 @@ lookdot(Node *n, Type *t, int dostrcmp) n->left = nod(OIND, n->left, N); n->left->implicit = 1; typecheck(&n->left, Etype|Erv); + } else if(tt->etype == tptr && tt->type->etype == tptr && eqtype(derefall(tt), rcvr)) { + yyerror("calling method %N with receiver %lN requires explicit dereference", n->right, n->left); + while(tt->etype == tptr) { + n->left = nod(OIND, n->left, N); + n->left->implicit = 1; + typecheck(&n->left, Etype|Erv); + tt = tt->type; + } } 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; +// print("lookdot found [%p] %T\n", f2->type, f2->type); n->op = ODOTMETH; return 1; } @@ -1661,22 +1743,20 @@ typecheckaste(int op, Node *call, int isddd, Type *tstruct, NodeList *nl, char * 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); + 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->type, desc, why); + yyerror("cannot use %T as type %T in %s%s", tn->type, tl->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); + 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); } @@ -1721,9 +1801,9 @@ typecheckaste(int op, Node *call, int isddd, Type *tstruct, NodeList *nl, char * goto toomany; if(isddd) { if(call != N) - yyerror("invalid use of ... in call to %#N", call); + yyerror("invalid use of ... in call to %N", call); else - yyerror("invalid use of ... in %#O", op); + yyerror("invalid use of ... in %O", op); } out: @@ -1732,80 +1812,20 @@ out: notenough: if(call != N) - yyerror("not enough arguments in call to %#N", call); + yyerror("not enough arguments in call to %N", call); else - yyerror("not enough arguments to %#O", op); + yyerror("not enough arguments to %O", op); goto out; toomany: if(call != N) - yyerror("too many arguments in call to %#N", call); + yyerror("too many arguments in call to %N", call); else - yyerror("too many arguments to %#O", op); + 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 */ @@ -1850,6 +1870,7 @@ keydup(Node *n, Node *hash[], ulong nhash) b = 23; break; case CTINT: + case CTRUNE: b = mpgetfix(n->val.u.xval); break; case CTFLT: @@ -1958,13 +1979,51 @@ inithash(Node *n, Node ***hash, Node **autohash, ulong nautohash) return h; } +static int +iscomptype(Type *t) +{ + switch(t->etype) { + case TARRAY: + case TSTRUCT: + case TMAP: + return 1; + case TPTR32: + case TPTR64: + switch(t->type->etype) { + case TARRAY: + case TSTRUCT: + case TMAP: + return 1; + } + break; + } + return 0; +} + +static void +pushtype(Node *n, Type *t) +{ + if(n == N || n->op != OCOMPLIT || !iscomptype(t)) + return; + + if(n->right == N) { + n->right = typenod(t); + n->right->implicit = 1; + } + else if(debug['s']) { + typecheck(&n->right, Etype); + if(n->right->type != T && eqtype(n->right->type, t)) + print("%lL: redundant type: %T\n", n->lineno, t); + } +} + static void typecheckcomplit(Node **np) { int bad, i, len, nerr; - Node *l, *n, **hash; + Node *l, *n, *r, **hash; NodeList *ll; - Type *t, *f, *pushtype; + Type *t, *f; Sym *s; int32 lno; ulong nhash; @@ -1979,30 +2038,29 @@ typecheckcomplit(Node **np) 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; - } + n->type = t; + + if(isptr[t->etype]) { + // For better or worse, we don't allow pointers as + // the composite literal type, except when using + // the &T syntax, which sets implicit. + if(!n->right->implicit) { + yyerror("invalid pointer type %T for composite literal (use &%T instead)", t, t->type); + goto error; + } + + // Also, the underlying type must be a struct, map, slice, or array. + if(!iscomptype(t)) { + yyerror("invalid pointer type %T for composite literal", t); + goto error; } + t = t->type; } switch(t->etype) { @@ -2045,11 +2103,11 @@ typecheckcomplit(Node **np) } } - 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"); + r = l->right; + pushtype(r, t->type); + typecheck(&r, Erv); + defaultlit(&r, t->type); + l->right = assignconv(r, t->type, "array element"); } if(t->bound == -100) t->bound = len; @@ -2073,13 +2131,14 @@ typecheckcomplit(Node **np) 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->left->op != OCONV) + 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"); + r = l->right; + pushtype(r, t->type); + typecheck(&r, Erv); + defaultlit(&r, t->type); + l->right = assignconv(r, t->type, "map value"); } n->op = OMAPLIT; break; @@ -2100,6 +2159,7 @@ typecheckcomplit(Node **np) 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); + // No pushtype allowed here. Must name fields for that. ll->n = assignconv(ll->n, f->type, "field value"); ll->n = nod(OKEY, newname(f->sym), ll->n); ll->n->left->type = f; @@ -2123,19 +2183,20 @@ typecheckcomplit(Node **np) } s = l->left->sym; if(s == S) { - yyerror("invalid field name %#N in struct initializer", l->left); + 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) + if(s->pkg != localpkg && exportname(s->name)) s = lookup(s->name); + 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); + yyerror("unknown %T field '%S' in struct literal", t, s); continue; } l->left = newname(s); @@ -2143,7 +2204,10 @@ typecheckcomplit(Node **np) l->left->type = f; s = f->sym; fielddup(newname(s), hash, nhash); - l->right = assignconv(l->right, f->type, "field value"); + r = l->right; + // No pushtype allowed here. Tried and rejected. + typecheck(&r, Erv); + l->right = assignconv(r, f->type, "field value"); } } n->op = OSTRUCTLIT; @@ -2151,7 +2215,14 @@ typecheckcomplit(Node **np) } if(nerr != nerrors) goto error; - n->type = t; + + if(isptr[n->type->etype]) { + n = nod(OPTRLIT, n, N); + n->typecheck = 1; + n->type = n->left->type; + n->left->type = t; + n->left->typecheck = 1; + } *np = n; lineno = lno; @@ -2193,7 +2264,7 @@ static void checklvalue(Node *n, char *verb) { if(!islvalue(n)) - yyerror("cannot %s %#N", verb, n); + yyerror("cannot %s %N", verb, n); } static void @@ -2205,7 +2276,7 @@ checkassign(Node *n) n->etype = 1; return; } - yyerror("cannot assign to %#N", n); + yyerror("cannot assign to %N", n); } static void @@ -2240,8 +2311,6 @@ typecheckas(Node *n) 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); @@ -2262,10 +2331,9 @@ 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); + yyerror("cannot assign %T to %lN in multiple assignment%s", src, dst, why); return; } - exportassignok(dst->type, "multiple assignment"); } static void @@ -2312,10 +2380,7 @@ typecheckas2(Node *n) 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"); + yyerror("assignment count mismatch: %d = %d (use delete)", cl, cr); goto out; } @@ -2397,7 +2462,7 @@ typecheckfunc(Node *n) if((t = n->nname->type) == T) return; n->type = t; - + t->nname = n->nname; rcvr = getthisx(t)->type; if(rcvr != nil && n->shortname != N && !isblank(n->shortname)) addmethod(n->shortname->sym, t, 1); @@ -2427,7 +2492,7 @@ stringtoarraylit(Node **np) while(p < ep) l = list(l, nod(OKEY, nodintconst(i++), nodintconst((uchar)*p++))); } else { - // utf-8 []int + // utf-8 []rune while(p < ep) { p += chartorune(&r, p); l = list(l, nod(OKEY, nodintconst(i++), nodintconst(r))); @@ -2467,6 +2532,7 @@ static void domethod(Node *n) { Node *nt; + Type *t; nt = n->type->nname; typecheck(&nt, Etype); @@ -2476,6 +2542,20 @@ domethod(Node *n) n->type->nod = N; return; } + + // If we have + // type I interface { + // M(_ int) + // } + // then even though I.M looks like it doesn't care about the + // value of its argument, a specific implementation of I may + // care. The _ would suppress the assignment to that argument + // while generating a call, so remove it. + for(t=getinargx(nt->type)->type; t; t=t->down) { + if(t->sym != nil && strcmp(t->sym->name, "_") == 0) + t->sym = nil; + } + *n->type = *nt->type; n->type->nod = N; checkwidth(n->type); @@ -2532,6 +2612,7 @@ copytype(Node *n, Type *t) t->vargen = n->vargen; t->siggen = 0; t->method = nil; + t->xmethod = nil; t->nod = N; t->printed = 0; t->deferwidth = 0; @@ -2689,7 +2770,7 @@ typecheckdef(Node *n) goto ret; } if(!isideal(e->type) && !eqtype(t, e->type)) { - yyerror("cannot use %+N as type %T in const initializer", e, t); + yyerror("cannot use %lN as type %T in const initializer", e, t); goto ret; } convlit(&e, t); diff --git a/src/cmd/gc/unsafe.c b/src/cmd/gc/unsafe.c index 6435492e0..95200ad41 100644 --- a/src/cmd/gc/unsafe.c +++ b/src/cmd/gc/unsafe.c @@ -10,6 +10,7 @@ * look for * unsafe.Sizeof * unsafe.Offsetof + * unsafe.Alignof * rewrite with a constant */ Node* @@ -22,7 +23,7 @@ unsafenmagic(Node *nn) Val val; Node *fn; NodeList *args; - + fn = nn->left; args = nn->list; @@ -80,10 +81,10 @@ no: return N; bad: - yyerror("invalid expression %#N", nn); + yyerror("invalid expression %N", nn); v = 0; goto ret; - + yes: if(args->next != nil) yyerror("extra arguments for %S", s); @@ -93,7 +94,23 @@ ret: val.u.xval = mal(sizeof(*n->val.u.xval)); mpmovecfix(val.u.xval, v); n = nod(OLITERAL, N, N); + n->orig = nn; n->val = val; n->type = types[TUINTPTR]; + nn->type = types[TUINTPTR]; return n; } + +int +isunsafebuiltin(Node *n) +{ + if(n == N || n->op != ONAME || n->sym == S || n->sym->pkg != unsafepkg) + return 0; + if(strcmp(n->sym->name, "Sizeof") == 0) + return 1; + if(strcmp(n->sym->name, "Offsetof") == 0) + return 1; + if(strcmp(n->sym->name, "Alignof") == 0) + return 1; + return 0; +} diff --git a/src/cmd/gc/unsafe.go b/src/cmd/gc/unsafe.go index db27d7425..c7b48a8b0 100644 --- a/src/cmd/gc/unsafe.go +++ b/src/cmd/gc/unsafe.go @@ -6,6 +6,8 @@ // to update builtin.c.boot. This is not done automatically // to avoid depending on having a working compiler binary. +// +build ignore + package PACKAGE type Pointer uintptr // not really; filled in by compiler diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c index 8a84956a6..53040fe93 100644 --- a/src/cmd/gc/walk.c +++ b/src/cmd/gc/walk.c @@ -7,9 +7,8 @@ #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* mapfndel(char*, Type*); static Node* ascompatee1(int, Node*, Node*, NodeList**); static NodeList* ascompatee(int, NodeList*, NodeList*, NodeList**); static NodeList* ascompatet(int, NodeList*, Type**, int, NodeList**); @@ -22,6 +21,7 @@ static NodeList* reorder3(NodeList*); static Node* addstr(Node*, NodeList**); static Node* appendslice(Node*, NodeList**); static Node* append(Node*, NodeList**); +static void walkcompare(Node**, NodeList**); // can this code branch reach the end // without an unconditional RETURN @@ -62,7 +62,6 @@ walk(Node *fn) { char s[50]; NodeList *l; - Node *n; int lno; curfn = fn; @@ -76,15 +75,33 @@ walk(Node *fn) yyerror("function ends without a return statement"); lno = lineno; + + // Final typecheck for any unused variables. + // It's hard to be on the heap when not-used, but best to be consistent about &~PHEAP here and below. + for(l=fn->dcl; l; l=l->next) + if(l->n->op == ONAME && (l->n->class&~PHEAP) == PAUTO) + typecheck(&l->n, Erv | Easgn); + + // Propagate the used flag for typeswitch variables up to the NONAME in it's definition. + for(l=fn->dcl; l; l=l->next) + if(l->n->op == ONAME && (l->n->class&~PHEAP) == PAUTO && l->n->defn && l->n->defn->op == OTYPESW && l->n->used) + l->n->defn->left->used++; + for(l=fn->dcl; l; l=l->next) { - n = l->n; - if(n->op != ONAME || n->class != PAUTO) + if(l->n->op != ONAME || (l->n->class&~PHEAP) != PAUTO || l->n->sym->name[0] == '&' || l->n->used) 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); - } + if(l->n->defn && l->n->defn->op == OTYPESW) { + if(l->n->defn->left->used) + continue; + lineno = l->n->defn->left->lineno; + yyerror("%S declared and not used", l->n->sym); + l->n->defn->left->used = 1; // suppress repeats + } else { + lineno = l->n->lineno; + yyerror("%S declared and not used", l->n->sym); + } + } + lineno = lno; if(nerrors != 0) return; @@ -121,11 +138,12 @@ static int paramoutheap(Node *fn) { NodeList *l; - + for(l=fn->dcl; l; l=l->next) { switch(l->n->class) { + case PPARAMOUT: case PPARAMOUT|PHEAP: - return 1; + return l->n->addrtaken; case PAUTO: case PAUTO|PHEAP: // stop early - parameters are over @@ -149,6 +167,8 @@ walkstmt(Node **np) setlineno(n); + walkstmtlist(n->ninit); + switch(n->op) { default: if(n->op == ONAME) @@ -164,7 +184,6 @@ walkstmt(Node **np) case OAS2DOTTYPE: case OAS2RECV: case OAS2FUNC: - case OAS2MAPW: case OAS2MAPR: case OCLOSE: case OCOPY: @@ -172,6 +191,7 @@ walkstmt(Node **np) case OCALLINTER: case OCALL: case OCALLFUNC: + case ODELETE: case OSEND: case ORECV: case OPRINT: @@ -179,14 +199,12 @@ walkstmt(Node **np) case OPANIC: case OEMPTY: case ORECOVER: - if(n->typecheck == 0) { - dump("missing typecheck:", n); - fatal("missing typecheck"); - } + if(n->typecheck == 0) + fatal("missing typecheck: %+N", n); init = n->ninit; n->ninit = nil; walkexpr(&n, &init); - n->ninit = concat(init, n->ninit); + addinit(&n, init); break; case OBREAK: @@ -225,20 +243,18 @@ walkstmt(Node **np) 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); + addinit(&n->ntest, init); } walkstmt(&n->nincr); walkstmtlist(n->nbody); break; case OIF: - walkstmtlist(n->ninit); walkexpr(&n->ntest, &n->ninit); walkstmtlist(n->nbody); walkstmtlist(n->nelse); @@ -281,10 +297,13 @@ walkstmt(Node **np) // 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); + fatal("expected return of call, have %N", f); n->list = concat(list1(f), ascompatet(n->op, rl, &f->type, 0, &n->ninit)); break; } + + // move function calls out, to make reorder3's job easier. + walkexprlistsafe(n->list, &n->ninit); ll = ascompatee(n->op, rl, n->list, &n->ninit); n->list = reorder3(ll); break; @@ -311,6 +330,9 @@ walkstmt(Node **np) break; } + if(n->op == ONAME) + fatal("walkstmt ended up with name: %+N", n); + *np = n; } @@ -363,6 +385,12 @@ walkexpr(Node **np, NodeList **init) fatal("walkexpr init == &n->ninit"); } + if(n->ninit != nil) { + walkstmtlist(n->ninit); + *init = concat(*init, n->ninit); + n->ninit = nil; + } + // annoying case - not typechecked if(n->op == OKEY) { walkexpr(&n->left, init); @@ -375,10 +403,8 @@ walkexpr(Node **np, NodeList **init) if(debug['w'] > 1) dump("walk-before", n); - if(n->typecheck != 1) { - dump("missed typecheck", n); - fatal("missed typecheck"); - } + if(n->typecheck != 1) + fatal("missed typecheck: %+N\n", n); switch(n->op) { default: @@ -409,7 +435,7 @@ walkexpr(Node **np, NodeList **init) 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; @@ -421,7 +447,7 @@ walkexpr(Node **np, NodeList **init) n->typecheck = 1; } goto ret; - + case OLSH: case ORSH: case OAND: @@ -429,8 +455,6 @@ walkexpr(Node **np, NodeList **init) case OXOR: case OSUB: case OMUL: - case OEQ: - case ONE: case OLT: case OLE: case OGE: @@ -440,7 +464,14 @@ walkexpr(Node **np, NodeList **init) walkexpr(&n->left, init); walkexpr(&n->right, init); goto ret; - + + case OEQ: + case ONE: + walkexpr(&n->left, init); + walkexpr(&n->right, init); + walkcompare(&n, init); + goto ret; + case OANDAND: case OOROR: walkexpr(&n->left, init); @@ -449,7 +480,7 @@ walkexpr(Node **np, NodeList **init) // save elsewhere and store on the eventual n->right. ll = nil; walkexpr(&n->right, &ll); - n->right->ninit = concat(n->right->ninit, ll); + addinit(&n->right, ll); goto ret; case OPRINT: @@ -553,7 +584,7 @@ walkexpr(Node **np, NodeList **init) 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 @@ -606,15 +637,19 @@ walkexpr(Node **np, NodeList **init) n->op = OAS2FUNC; goto as2func; - case OAS2MAPW: - // map[] = a,b - mapassign2 - // a,b = m[i]; + case ODELETE: *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); + r = n->list->next->n; + if(n->right != N) { + // TODO: Remove once two-element map assigment is gone. + l = safeexpr(l, init); + r = safeexpr(r, init); + safeexpr(n->right, init); // cause side effects from n->right + } + t = l->type; + n = mkcall1(mapfndel("mapdelete", t), t->down, init, typename(t), l, r); goto ret; case OAS2DOTTYPE: @@ -648,7 +683,7 @@ walkexpr(Node **np, NodeList **init) if(n->op == ODOTTYPE2) *p++ = '2'; *p = '\0'; - + fn = syslook(buf, 1); ll = list1(typename(n->type)); ll = list(ll, n->left); @@ -679,7 +714,7 @@ walkexpr(Node **np, NodeList **init) else *p++ = 'I'; *p = '\0'; - + fn = syslook(buf, 1); ll = nil; if(!isinter(n->left->type)) @@ -840,6 +875,7 @@ walkexpr(Node **np, NodeList **init) // delayed until now because "abc"[2] is not // an ideal constant. nodconst(n, n->type, n->left->val.u.sval->s[v]); + n->typecheck = 1; } } goto ret; @@ -894,7 +930,7 @@ walkexpr(Node **np, NodeList **init) } if(v1 >= 0 && v2 >= 0 && v1 > v2) yyerror("inverted slice range"); - + if(n->op == OSLICEARR) goto slicearray; @@ -925,7 +961,7 @@ walkexpr(Node **np, NodeList **init) l, nodintconst(t->type->width)); } - n->etype = et; // preserve no-typecheck flag from OSLICE to the slice* call. + n->etype = et; // preserve no-typecheck flag from OSLICE to the slice* call. goto ret; slicearray: @@ -950,32 +986,17 @@ walkexpr(Node **np, NodeList **init) 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; - } - + case OADDR: walkexpr(&n->left, init); goto ret; case ONEW: if(n->esc == EscNone && n->type->type->width < (1<<16)) { r = temp(n->type->type); - *init = list(*init, nod(OAS, r, N)); // zero temp - r = nod(OADDR, r, N); + r = nod(OAS, r, N); // zero temp + typecheck(&r, Etop); + *init = list(*init, r); + r = nod(OADDR, r->left, N); typecheck(&r, Erv); n = r; } else { @@ -1054,10 +1075,14 @@ walkexpr(Node **np, NodeList **init) l); } goto ret; - + case OAPPEND: - if(n->isddd) - n = appendslice(n, init); + if(n->isddd) { + if(istype(n->type->type, TUINT8) && istype(n->list->next->n->type, TSTRING)) + n = mkcall("appendstr", n->type, init, typename(n->type), n->list->n, n->list->next->n); + else + n = appendslice(n, init); + } else n = append(n, init); goto ret; @@ -1066,7 +1091,7 @@ walkexpr(Node **np, NodeList **init) if(n->right->type->etype == TSTRING) fn = syslook("slicestringcopy", 1); else - fn = syslook("slicecopy", 1); + fn = syslook("copy", 1); argtype(fn, n->left->type); argtype(fn, n->right->type); n = mkcall1(fn, n->type, init, @@ -1126,8 +1151,8 @@ walkexpr(Node **np, NodeList **init) goto ret; case OARRAYRUNESTR: - // sliceinttostring([]int) string; - n = mkcall("sliceinttostring", n->type, init, n->left); + // slicerunetostring([]rune) string; + n = mkcall("slicerunetostring", n->type, init, n->left); goto ret; case OSTRARRAYBYTE: @@ -1136,8 +1161,8 @@ walkexpr(Node **np, NodeList **init) goto ret; case OSTRARRAYRUNE: - // stringtosliceint(string) []int - n = mkcall("stringtosliceint", n->type, init, n->left); + // stringtoslicerune(string) []rune + n = mkcall("stringtoslicerune", n->type, init, n->left); goto ret; case OCMPIFACE: @@ -1161,9 +1186,10 @@ walkexpr(Node **np, NodeList **init) case OARRAYLIT: case OMAPLIT: case OSTRUCTLIT: - nvar = temp(n->type); - anylit(0, n, nvar, init); - n = nvar; + case OPTRLIT: + var = temp(n->type); + anylit(0, n, var, init); + n = var; goto ret; case OSEND: @@ -1186,22 +1212,6 @@ ret: } static Node* -makenewvar(Type *t, NodeList **init, Node **nstar) -{ - Node *nvar, *nas; - - nvar = temp(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) { USED(op); @@ -1232,7 +1242,7 @@ ascompatee(int op, NodeList *nl, NodeList *nr, NodeList **init) // cannot happen: caller checked that lists had same length if(ll || lr) - yyerror("error in shape across %O", op); + yyerror("error in shape across %+H %O %+H", nl, op, nr); return nn; } @@ -1304,10 +1314,11 @@ ascompatet(int op, NodeList *nl, Type **nr, int fp, NodeList **init) } if(ll != nil || r != T) - yyerror("assignment count mismatch: %d = %d", + yyerror("ascompatet: assignment count mismatch: %d = %d", count(nl), structcount(*nr)); + if(ucount) - fatal("reorder2: too many function calls evaluating parameters"); + fatal("ascompatet: too many function calls evaluating parameters"); return concat(nn, mm); } @@ -1319,7 +1330,7 @@ mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init, int { Node *a, *n; Type *tslice; - + tslice = typ(TARRAY); tslice->type = l->type->type; tslice->bound = -1; @@ -1413,7 +1424,7 @@ ascompatte(int op, Node *call, int isddd, Type **nl, NodeList *lr, int fp, NodeL 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 @@ -1423,7 +1434,7 @@ ascompatte(int op, Node *call, int isddd, Type **nl, NodeList *lr, int fp, NodeL nn = list1(convas(nod(OAS, a, r), init)); goto ret; } - + // conversions involved. // copy into temporaries. alist = nil; @@ -1540,6 +1551,9 @@ walkprint(Node *nn, NodeList **init, int defer) n = l->n; if(n->op == OLITERAL) { switch(n->val.ctype) { + case CTRUNE: + defaultlit(&n, runetype); + break; case CTINT: defaultlit(&n, types[TINT64]); break; @@ -1682,7 +1696,7 @@ callnew(Type *t) dowidth(t); fn = syslook("new", 1); argtype(fn, t); - return mkcall1(fn, ptrto(t), nil, nodintconst(t->width)); + return mkcall1(fn, ptrto(t), nil, typename(t)); } static Node* @@ -1714,10 +1728,10 @@ convas(Node *n, NodeList **init) 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); @@ -1786,28 +1800,242 @@ reorder1(NodeList *all) return concat(g, r); } +static void reorder3save(Node**, NodeList*, NodeList*, NodeList**); +static int aliased(Node*, NodeList*, NodeList*); + /* * from ascompat[ee] * a,b = c,d * simultaneous assignment. there cannot * be later use of an earlier lvalue. + * + * function calls have been removed. + */ +static NodeList* +reorder3(NodeList *all) +{ + NodeList *list, *early; + Node *l; + + // If a needed expression may be affected by an + // earlier assignment, make an early copy of that + // expression and use the copy instead. + early = nil; + for(list=all; list; list=list->next) { + l = list->n->left; + + // Save subexpressions needed on left side. + // Drill through non-dereferences. + for(;;) { + if(l->op == ODOT || l->op == OPAREN) { + l = l->left; + continue; + } + if(l->op == OINDEX && isfixedarray(l->left->type)) { + reorder3save(&l->right, all, list, &early); + l = l->left; + continue; + } + break; + } + switch(l->op) { + default: + fatal("reorder3 unexpected lvalue %#O", l->op); + case ONAME: + break; + case OINDEX: + reorder3save(&l->left, all, list, &early); + reorder3save(&l->right, all, list, &early); + break; + case OIND: + case ODOTPTR: + reorder3save(&l->left, all, list, &early); + } + + // Save expression on right side. + reorder3save(&list->n->right, all, list, &early); + } + + return concat(early, all); +} + +static int vmatch2(Node*, Node*); +static int varexpr(Node*); + +/* + * if the evaluation of *np would be affected by the + * assignments in all up to but not including stop, + * copy into a temporary during *early and + * replace *np with that temp. + */ +static void +reorder3save(Node **np, NodeList *all, NodeList *stop, NodeList **early) +{ + Node *n, *q; + + n = *np; + if(!aliased(n, all, stop)) + return; + + q = temp(n->type); + q = nod(OAS, q, n); + typecheck(&q, Etop); + *early = list(*early, q); + *np = q->left; +} + +/* + * what's the outer value that a write to n affects? + * outer value means containing struct or array. + */ +static Node* +outervalue(Node *n) +{ + for(;;) { + if(n->op == ODOT || n->op == OPAREN) { + n = n->left; + continue; + } + if(n->op == OINDEX && isfixedarray(n->left->type)) { + n = n->left; + continue; + } + break; + } + return n; +} + +/* + * Is it possible that the computation of n might be + * affected by writes in as up to but not including stop? + */ +static int +aliased(Node *n, NodeList *all, NodeList *stop) +{ + int memwrite, varwrite; + Node *a; + NodeList *l; + + if(n == N) + return 0; + + // Look for obvious aliasing: a variable being assigned + // during the all list and appearing in n. + // Also record whether there are any writes to main memory. + // Also record whether there are any writes to variables + // whose addresses have been taken. + memwrite = 0; + varwrite = 0; + for(l=all; l!=stop; l=l->next) { + a = outervalue(l->n->left); + if(a->op != ONAME) { + memwrite = 1; + continue; + } + switch(n->class) { + default: + varwrite = 1; + continue; + case PAUTO: + case PPARAM: + case PPARAMOUT: + if(n->addrtaken) { + varwrite = 1; + continue; + } + if(vmatch2(a, n)) { + // Direct hit. + return 1; + } + } + } + + // The variables being written do not appear in n. + // However, n might refer to computed addresses + // that are being written. + + // If no computed addresses are affected by the writes, no aliasing. + if(!memwrite && !varwrite) + return 0; + + // If n does not refer to computed addresses + // (that is, if n only refers to variables whose addresses + // have not been taken), no aliasing. + if(varexpr(n)) + return 0; + + // Otherwise, both the writes and n refer to computed memory addresses. + // Assume that they might conflict. + return 1; +} + +/* + * does the evaluation of n only refer to variables + * whose addresses have not been taken? + * (and no other memory) */ +static int +varexpr(Node *n) +{ + if(n == N) + return 1; + + switch(n->op) { + case OLITERAL: + return 1; + case ONAME: + switch(n->class) { + case PAUTO: + case PPARAM: + case PPARAMOUT: + if(!n->addrtaken) + return 1; + } + return 0; + case OADD: + case OSUB: + case OOR: + case OXOR: + case OMUL: + case ODIV: + case OMOD: + case OLSH: + case ORSH: + case OAND: + case OANDNOT: + case OPLUS: + case OMINUS: + case OCOM: + case OPAREN: + case OANDAND: + case OOROR: + case ODOT: // but not ODOTPTR + case OCONV: + case OCONVNOP: + case OCONVIFACE: + case ODOTTYPE: + return varexpr(n->left) && varexpr(n->right); + } + + // Be conservative. + return 0; +} + +/* + * is the name l mentioned in r? + */ 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; + return l == r; case OLITERAL: return 0; } @@ -1821,6 +2049,10 @@ vmatch2(Node *l, Node *r) return 0; } +/* + * is any name mentioned in l also mentioned in r? + * called by sinit.c + */ int vmatch1(Node *l, Node *r) { @@ -1859,33 +2091,6 @@ vmatch1(Node *l, Node *r) return 0; } -static 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 = temp(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 @@ -1952,7 +2157,7 @@ heapmoves(void) { NodeList *nn; int32 lno; - + lno = lineno; lineno = curfn->lineno; nn = paramstoheap(getthis(curfn->type), 0); @@ -1972,7 +2177,7 @@ vmkcall(Node *fn, Type *t, NodeList **init, va_list va) NodeList *args; if(fn->type == T || fn->type->etype != TFUNC) - fatal("mkcall %#N %T", fn, fn->type); + fatal("mkcall %N %T", fn, fn->type); args = nil; n = fn->type->intuple; @@ -2014,7 +2219,7 @@ mkcall1(Node *fn, Type *t, NodeList **init, ...) return r; } -static Node* +Node* conv(Node *n, Type *t) { if(eqtype(n->type, t)) @@ -2055,12 +2260,26 @@ mapfn(char *name, Type *t) } static Node* +mapfndel(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); + 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 @@ -2089,7 +2308,7 @@ addstr(Node *n, NodeList **init) typecheck(&r, Erv); walkexpr(&r, init); r->type = n->type; - + return r; } @@ -2097,7 +2316,7 @@ static Node* appendslice(Node *n, NodeList **init) { Node *f; - + f = syslook("appendslice", 1); argtype(f, n->type); argtype(f, n->type->type); @@ -2111,7 +2330,7 @@ appendslice(Node *n, NodeList **init) // s := src // const argc = len(args) - 1 // if cap(s) - len(s) < argc { -// s = growslice(s, argc) +// s = growslice(s, argc) // } // n := len(s) // s = s[:n+argc] @@ -2140,13 +2359,13 @@ append(Node *n, NodeList **init) ns = temp(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 + 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 + 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), @@ -2155,16 +2374,16 @@ append(Node *n, NodeList **init) l = list(l, nx); nn = temp(types[TINT]); - l = list(l, nod(OAS, nn, nod(OLEN, ns, N))); // n = len(s) + 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] + 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 + 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 } @@ -2174,3 +2393,186 @@ append(Node *n, NodeList **init) *init = concat(*init, l); return ns; } + +static Node* +eqfor(Type *t) +{ + int a; + Node *n; + Node *ntype; + Sym *sym; + + // Should only arrive here with large memory or + // a struct/array containing a non-memory field/element. + // Small memory is handled inline, and single non-memory + // is handled during type check (OCMPSTR etc). + a = algtype1(t, nil); + if(a != AMEM && a != -1) + fatal("eqfor %T", t); + + if(a == AMEM) { + n = syslook("memequal", 1); + argtype(n, t); + argtype(n, t); + return n; + } + + sym = typesymprefix(".eq", t); + n = newname(sym); + n->class = PFUNC; + ntype = nod(OTFUNC, N, N); + ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(ptrto(types[TBOOL])))); + ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR]))); + ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(ptrto(t)))); + ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(ptrto(t)))); + typecheck(&ntype, Etype); + n->type = ntype->type; + return n; +} + +static int +countfield(Type *t) +{ + Type *t1; + int n; + + n = 0; + for(t1=t->type; t1!=T; t1=t1->down) + n++; + return n; +} + +static void +walkcompare(Node **np, NodeList **init) +{ + Node *n, *l, *r, *fn, *call, *a, *li, *ri, *expr; + int andor, i; + Type *t, *t1; + static Node *tempbool; + + n = *np; + + // Must be comparison of array or struct. + // Otherwise back end handles it. + t = n->left->type; + switch(t->etype) { + default: + return; + case TARRAY: + if(isslice(t)) + return; + break; + case TSTRUCT: + break; + } + + if(!islvalue(n->left) || !islvalue(n->right)) + goto hard; + + l = temp(ptrto(t)); + a = nod(OAS, l, nod(OADDR, n->left, N)); + a->right->etype = 1; // addr does not escape + typecheck(&a, Etop); + *init = list(*init, a); + + r = temp(ptrto(t)); + a = nod(OAS, r, nod(OADDR, n->right, N)); + a->right->etype = 1; // addr does not escape + typecheck(&a, Etop); + *init = list(*init, a); + + expr = N; + andor = OANDAND; + if(n->op == ONE) + andor = OOROR; + + if(t->etype == TARRAY && + t->bound <= 4 && + issimple[t->type->etype]) { + // Four or fewer elements of a basic type. + // Unroll comparisons. + for(i=0; i<t->bound; i++) { + li = nod(OINDEX, l, nodintconst(i)); + ri = nod(OINDEX, r, nodintconst(i)); + a = nod(n->op, li, ri); + if(expr == N) + expr = a; + else + expr = nod(andor, expr, a); + } + if(expr == N) + expr = nodbool(n->op == OEQ); + typecheck(&expr, Erv); + walkexpr(&expr, init); + *np = expr; + return; + } + + if(t->etype == TSTRUCT && countfield(t) <= 4) { + // Struct of four or fewer fields. + // Inline comparisons. + for(t1=t->type; t1; t1=t1->down) { + li = nod(OXDOT, l, newname(t1->sym)); + ri = nod(OXDOT, r, newname(t1->sym)); + a = nod(n->op, li, ri); + if(expr == N) + expr = a; + else + expr = nod(andor, expr, a); + } + if(expr == N) + expr = nodbool(n->op == OEQ); + typecheck(&expr, Erv); + walkexpr(&expr, init); + *np = expr; + return; + } + + // Chose not to inline, but still have addresses. + // Call equality function directly. + // The equality function requires a bool pointer for + // storing its address, because it has to be callable + // from C, and C can't access an ordinary Go return value. + // To avoid creating many temporaries, cache one per function. + if(tempbool == N || tempbool->curfn != curfn) + tempbool = temp(types[TBOOL]); + + call = nod(OCALL, eqfor(t), N); + a = nod(OADDR, tempbool, N); + a->etype = 1; // does not escape + call->list = list(call->list, a); + call->list = list(call->list, nodintconst(t->width)); + call->list = list(call->list, l); + call->list = list(call->list, r); + typecheck(&call, Etop); + walkstmt(&call); + *init = list(*init, call); + + if(n->op == OEQ) + r = tempbool; + else + r = nod(ONOT, tempbool, N); + typecheck(&r, Erv); + walkexpr(&r, init); + *np = r; + return; + +hard: + // Cannot take address of one or both of the operands. + // Instead, pass directly to runtime helper function. + // Easier on the stack than passing the address + // of temporary variables, because we are better at reusing + // the argument space than temporary variable space. + fn = syslook("equal", 1); + l = n->left; + r = n->right; + argtype(fn, n->left->type); + argtype(fn, n->left->type); + r = mkcall1(fn, n->type, init, typename(n->left->type), l, r); + if(n->op == ONE) { + r = nod(ONOT, r, N); + typecheck(&r, Erv); + } + *np = r; + return; +} |
