gc: implement new len spec, range bug fix, optimization

Fixes #885.

R=ken2
CC=golang-dev
https://golang.org/cl/1680048
This commit is contained in:
Russ Cox 2010-07-01 18:04:25 -07:00
parent f4429181df
commit 81c3e8cabc
4 changed files with 227 additions and 9 deletions

View file

@ -24,6 +24,8 @@ typecheckrange(Node *n)
typecheck(&n->right, Erv);
if((t = n->right->type) == T)
goto out;
if(isptr[t->etype] && isfixedarray(t->type))
t = t->type;
n->type = t;
switch(t->etype) {
@ -104,9 +106,6 @@ walkrange(Node *n)
a = nod(OCONV, n->right, N);
a->type = types[TSTRING];
}
ha = nod(OXXX, N, N);
tempname(ha, a->type);
init = list(init, nod(OAS, ha, a));
v1 = n->list->n;
hv1 = N;
@ -116,6 +115,16 @@ walkrange(Node *n)
v2 = n->list->next->n;
hv2 = N;
if(v2 == N && t->etype == TARRAY) {
// will have just one reference to argument.
// no need to make a potentially expensive copy.
ha = a;
} else {
ha = nod(OXXX, N, N);
tempname(ha, a->type);
init = list(init, nod(OAS, ha, a));
}
switch(t->etype) {
default:
fatal("walkrange");
@ -131,7 +140,7 @@ walkrange(Node *n)
init = list(init, nod(OAS, hn, nod(OLEN, ha, N)));
if(v2) {
hp = nod(OXXX, N, N);
tempname(hp, ptrto(a->type->type));
tempname(hp, ptrto(n->type->type));
tmp = nod(OINDEX, ha, nodintconst(0));
tmp->etype = 1; // no bounds check
init = list(init, nod(OAS, hp, nod(OADDR, tmp, N)));

View file

@ -802,7 +802,7 @@ reswitch:
nodconst(n, types[TINT], l->val.u.sval->len);
break;
case TARRAY:
if(t->bound >= 0)
if(t->bound >= 0 && l->op == ONAME)
nodconst(n, types[TINT], t->bound);
break;
}

View file

@ -594,8 +594,6 @@ walkexpr(Node **np, NodeList **init)
case OMINUS:
case OPLUS:
case OCOM:
case OLEN:
case OCAP:
case OREAL:
case OIMAG:
case ODOT:
@ -606,6 +604,22 @@ walkexpr(Node **np, NodeList **init)
walkexpr(&n->left, init);
goto ret;
case OLEN:
case OCAP:
walkexpr(&n->left, init);
// replace len(*[10]int) with 10.
// delayed until now to preserve side effects.
t = n->left->type;
if(isptr[t->etype])
t = t->type;
if(isfixedarray(t)) {
safeexpr(n->left, init);
nodconst(n, n->type, t->bound);
n->typecheck = 1;
}
goto ret;
case OLSH:
case ORSH:
case OAND:

View file

@ -32,18 +32,59 @@ func testchan() {
}
}
// test that range over array only evaluates
// test that range over slice only evaluates
// the expression after "range" once.
var nmake = 0
func makearray() []int {
func makeslice() []int {
nmake++
return []int{1, 2, 3, 4, 5}
}
func testslice() {
s := 0
nmake = 0
for _, v := range makeslice() {
s += v
}
if nmake != 1 {
println("range called makeslice", nmake, "times")
panic("fail")
}
if s != 15 {
println("wrong sum ranging over makeslice")
panic("fail")
}
}
func testslice1() {
s := 0
nmake = 0
for i := range makeslice() {
s += i
}
if nmake != 1 {
println("range called makeslice", nmake, "times")
panic("fail")
}
if s != 10 {
println("wrong sum ranging over makeslice")
panic("fail")
}
}
// test that range over array only evaluates
// the expression after "range" once.
func makearray() [5]int {
nmake++
return [5]int{1, 2, 3, 4, 5}
}
func testarray() {
s := 0
nmake = 0
for _, v := range makearray() {
s += v
}
@ -57,6 +98,151 @@ func testarray() {
}
}
func testarray1() {
s := 0
nmake = 0
for i := range makearray() {
s += i
}
if nmake != 1 {
println("range called makearray", nmake, "times")
panic("fail")
}
if s != 10 {
println("wrong sum ranging over makearray")
panic("fail")
}
}
func makearrayptr() *[5]int {
nmake++
return &[5]int{1, 2, 3, 4, 5}
}
func testarrayptr() {
nmake = 0
x := len(makearrayptr())
if x != 5 || nmake != 1 {
println("len called makearrayptr", nmake, "times and got len", x)
panic("fail")
}
nmake = 0
x = cap(makearrayptr())
if x != 5 || nmake != 1 {
println("cap called makearrayptr", nmake, "times and got len", x)
panic("fail")
}
s := 0
nmake = 0
for _, v := range makearrayptr() {
s += v
}
if nmake != 1 {
println("range called makearrayptr", nmake, "times")
panic("fail")
}
if s != 15 {
println("wrong sum ranging over makearrayptr")
panic("fail")
}
}
func testarrayptr1() {
s := 0
nmake = 0
for i := range makearrayptr() {
s += i
}
if nmake != 1 {
println("range called makearrayptr", nmake, "times")
panic("fail")
}
if s != 10 {
println("wrong sum ranging over makearrayptr")
panic("fail")
}
}
// test that range over string only evaluates
// the expression after "range" once.
func makestring() string {
nmake++
return "abcd☺"
}
func teststring() {
s := 0
nmake = 0
for _, v := range makestring() {
s += v
}
if nmake != 1 {
println("range called makestring", nmake, "times")
panic("fail")
}
if s != 'a'+'b'+'c'+'d'+'☺' {
println("wrong sum ranging over makestring")
panic("fail")
}
}
func teststring1() {
s := 0
nmake = 0
for i := range makestring() {
s += i
}
if nmake != 1 {
println("range called makestring", nmake, "times")
panic("fail")
}
if s != 10 {
println("wrong sum ranging over makestring")
panic("fail")
}
}
// test that range over map only evaluates
// the expression after "range" once.
func makemap() map[int]int {
nmake++
return map[int]int{0:'a', 1:'b', 2:'c', 3:'d', 4:'☺'}
}
func testmap() {
s := 0
nmake = 0
for _, v := range makemap() {
s += v
}
if nmake != 1 {
println("range called makemap", nmake, "times")
panic("fail")
}
if s != 'a'+'b'+'c'+'d'+'☺' {
println("wrong sum ranging over makemap")
panic("fail")
}
}
func testmap1() {
s := 0
nmake = 0
for i := range makemap() {
s += i
}
if nmake != 1 {
println("range called makemap", nmake, "times")
panic("fail")
}
if s != 10 {
println("wrong sum ranging over makemap")
panic("fail")
}
}
// test that range evaluates the index and value expressions
// exactly once per iteration.
@ -98,5 +284,14 @@ func testcalls() {
func main() {
testchan()
testarray()
testarray1()
testarrayptr()
testarrayptr1()
testslice()
testslice1()
teststring()
teststring1()
testmap()
testmap1()
testcalls()
}