summaryrefslogtreecommitdiff
path: root/src/cmd/gc
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/gc')
-rw-r--r--src/cmd/gc/Makefile8
-rw-r--r--src/cmd/gc/align.c10
-rwxr-xr-xsrc/cmd/gc/bisonerrors29
-rw-r--r--src/cmd/gc/bits.c4
-rw-r--r--src/cmd/gc/builtin.c.boot217
-rw-r--r--src/cmd/gc/closure.c4
-rw-r--r--src/cmd/gc/const.c89
-rw-r--r--src/cmd/gc/cplx.c2
-rw-r--r--src/cmd/gc/dcl.c523
-rw-r--r--src/cmd/gc/doc.go2
-rw-r--r--src/cmd/gc/esc.c105
-rw-r--r--src/cmd/gc/export.c342
-rw-r--r--src/cmd/gc/fmt.c1626
-rw-r--r--src/cmd/gc/gen.c8
-rw-r--r--src/cmd/gc/go.h157
-rw-r--r--src/cmd/gc/go.y375
-rw-r--r--src/cmd/gc/init.c16
-rw-r--r--src/cmd/gc/inl.c780
-rw-r--r--src/cmd/gc/lex.c283
-rw-r--r--src/cmd/gc/obj.c5
-rw-r--r--src/cmd/gc/order.c358
-rw-r--r--src/cmd/gc/pgen.c12
-rw-r--r--src/cmd/gc/print.c486
-rw-r--r--src/cmd/gc/range.c18
-rw-r--r--src/cmd/gc/reflect.c110
-rw-r--r--src/cmd/gc/runtime.go23
-rw-r--r--src/cmd/gc/sinit.c69
-rw-r--r--src/cmd/gc/subr.c1764
-rw-r--r--src/cmd/gc/swt.c47
-rw-r--r--src/cmd/gc/typecheck.c503
-rw-r--r--src/cmd/gc/unsafe.c23
-rw-r--r--src/cmd/gc/unsafe.go2
-rw-r--r--src/cmd/gc/walk.c704
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;
+}