summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2010-02-25 15:11:07 -0800
committerRuss Cox <rsc@golang.org>2010-02-25 15:11:07 -0800
commit3910161307f79fd821148652fb2a77872e7efd52 (patch)
treeed1675a302b1008c7a13de6f750f25e73a3b8d9a
parentb86c0b0c4a69aaca1bd748fb2969f90cb2a28310 (diff)
downloadgo-git-3910161307f79fd821148652fb2a77872e7efd52.tar.gz
gc: implement []int(string) and []byte(string)
R=ken2 CC=golang-dev https://golang.org/cl/224060
-rw-r--r--src/cmd/gc/builtin.c.boot2
-rw-r--r--src/cmd/gc/go.h5
-rw-r--r--src/cmd/gc/runtime.go2
-rw-r--r--src/cmd/gc/typecheck.c60
-rw-r--r--src/cmd/gc/walk.c32
-rw-r--r--src/pkg/runtime/string.cgo31
-rw-r--r--test/convlit.go27
-rw-r--r--test/string_lit.go30
8 files changed, 175 insertions, 14 deletions
diff --git a/src/cmd/gc/builtin.c.boot b/src/cmd/gc/builtin.c.boot
index 6eed40230d..3114d75807 100644
--- a/src/cmd/gc/builtin.c.boot
+++ b/src/cmd/gc/builtin.c.boot
@@ -26,6 +26,8 @@ char *runtimeimport =
"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"
diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h
index 753de0399a..cbcdc9c39d 100644
--- a/src/cmd/gc/go.h
+++ b/src/cmd/gc/go.h
@@ -351,6 +351,7 @@ enum
OAPPENDSTR,
OARRAY,
OARRAYBYTESTR, OARRAYRUNESTR,
+ OSTRARRAYBYTE, OSTRARRAYRUNE,
OAS, OAS2, OAS2MAPW, OAS2FUNC, OAS2RECV, OAS2MAPR, OAS2DOTTYPE, OASOP,
OBAD,
OCALL, OCALLFUNC, OCALLMETH, OCALLINTER,
@@ -411,7 +412,7 @@ enum
OTINTER,
OTFUNC,
OTARRAY,
-
+
// misc
ODDD,
@@ -458,7 +459,7 @@ enum
TIDEAL, // 32
TNIL,
TBLANK,
-
+
// pseudo-type for frame layout
TFUNCARGS,
TCHANARGS,
diff --git a/src/cmd/gc/runtime.go b/src/cmd/gc/runtime.go
index e350c282dd..ca3b6a1bc1 100644
--- a/src/cmd/gc/runtime.go
+++ b/src/cmd/gc/runtime.go
@@ -38,6 +38,8 @@ func indexstring(string, int) byte
func intstring(int64) string
func slicebytetostring([]byte) string
func sliceinttostring([]int) string
+func stringtoslicebyte(string) []byte
+func stringtosliceint(string) []int
func stringiter(string, int) int
func stringiter2(string, int) (retk int, retv int)
func slicecopy(to any, fr any, wid uint32) int
diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c
index 158dee6738..4c4c928338 100644
--- a/src/cmd/gc/typecheck.c
+++ b/src/cmd/gc/typecheck.c
@@ -32,6 +32,7 @@ static void checklvalue(Node*, char*);
static void checkassign(Node*);
static void checkassignlist(NodeList*);
static void toslice(Node**);
+static void stringtoarraylit(Node**);
void
typechecklist(NodeList *l, int top)
@@ -835,6 +836,13 @@ reswitch:
n = typecheckconv(n, n->left, n->type, 1, "conversion");
if(n->type == T)
goto error;
+ switch(n->op) {
+ case OSTRARRAYBYTE:
+ case OSTRARRAYRUNE:
+ if(n->left->op == OLITERAL)
+ stringtoarraylit(&n);
+ break;
+ }
goto ret;
case OMAKE:
@@ -1406,6 +1414,18 @@ checkconv(Type *nt, Type *t, int explicit, int *op, int *et, char *desc)
}
}
+ // from string
+ if(istype(nt, TSTRING) && isslice(t) && t->sym == S) {
+ switch(t->type->etype) {
+ case TUINT8:
+ *op = OSTRARRAYBYTE;
+ return 1;
+ case TINT:
+ *op = OSTRARRAYRUNE;
+ return 1;
+ }
+ }
+
// convert to unsafe pointer
if(isptrto(t, TANY)
&& (isptr[nt->etype] || nt->etype == TUINTPTR))
@@ -1534,7 +1554,7 @@ typecheckaste(int op, Type *tstruct, NodeList *nl, char *desc)
// TODO(rsc): drop first if in DDD cleanup
if(t->etype != TINTER)
if(checkconv(nl->n->type, t->type, 0, &xx, &yy, desc) < 0)
- yyerror("cannot use %+N as type %T in %s", nl->n, t->type, desc);
+ yyerror("cannot use %+N as type %T in %s", nl->n, t->type, desc);
}
goto out;
}
@@ -1587,7 +1607,7 @@ exportassignok(Type *t, char *desc)
// 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 ";
@@ -2164,3 +2184,39 @@ typecheckfunc(Node *n)
if(rcvr != nil && n->shortname != N && !isblank(n->shortname))
addmethod(n->shortname->sym, t, 1);
}
+
+static void
+stringtoarraylit(Node **np)
+{
+ int32 i;
+ NodeList *l;
+ Strlit *s;
+ char *p, *ep;
+ Rune r;
+ Node *nn, *n;
+
+ n = *np;
+ if(n->left->op != OLITERAL || n->left->val.ctype != CTSTR)
+ fatal("stringtoarraylit %N", n);
+
+ s = n->left->val.u.sval;
+ l = nil;
+ p = s->s;
+ ep = s->s + s->len;
+ i = 0;
+ if(n->type->type->etype == TUINT8) {
+ // raw []byte
+ while(p < ep)
+ l = list(l, nod(OKEY, nodintconst(i++), nodintconst((uchar)*p++)));
+ } else {
+ // utf-8 []int
+ while(p < ep) {
+ p += chartorune(&r, p);
+ l = list(l, nod(OKEY, nodintconst(i++), nodintconst(r)));
+ }
+ }
+ nn = nod(OCOMPLIT, N, typenod(n->type));
+ nn->list = l;
+ typecheck(&nn, Erv);
+ *np = nn;
+}
diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c
index e902600ba0..fa63646c50 100644
--- a/src/cmd/gc/walk.c
+++ b/src/cmd/gc/walk.c
@@ -8,6 +8,7 @@ static Node* walkprint(Node*, NodeList**, int);
static Node* conv(Node*, Type*);
static Node* mapfn(char*, Type*);
static Node* makenewvar(Type*, NodeList**, Node**);
+
enum
{
Inone,
@@ -122,7 +123,7 @@ static void
domethod(Node *n)
{
Node *nt;
-
+
nt = n->type->nname;
typecheck(&nt, Etype);
if(nt->type == T) {
@@ -142,7 +143,7 @@ walkdeftype(Node *n)
int maplineno, embedlineno, lno;
Type *t;
NodeList *l;
-
+
nwalkdeftype++;
lno = lineno;
setlineno(n);
@@ -183,7 +184,7 @@ walkdeftype(Node *n)
ret:
lineno = lno;
-
+
// if there are no type definitions going on, it's safe to
// try to resolve the method types for the interfaces
// we just read.
@@ -868,7 +869,7 @@ walkexpr(Node **np, NodeList **init)
case OINDEX:
walkexpr(&n->left, init);
walkexpr(&n->right, init);
-
+
// if range of type cannot exceed static array bound,
// disable bounds check
if(!isslice(n->left->type))
@@ -1092,10 +1093,20 @@ walkexpr(Node **np, NodeList **init)
goto ret;
case OARRAYRUNESTR:
- // sliceinttostring([]byte) string;
+ // sliceinttostring([]int) string;
n = mkcall("sliceinttostring", n->type, init, n->left);
goto ret;
+ case OSTRARRAYBYTE:
+ // stringtoslicebyte(string) []byte;
+ n = mkcall("stringtoslicebyte", n->type, init, n->left);
+ goto ret;
+
+ case OSTRARRAYRUNE:
+ // stringtosliceint(string) []int
+ n = mkcall("stringtosliceint", n->type, init, n->left);
+ goto ret;
+
case OCMPIFACE:
// ifaceeq(i1 any-1, i2 any-2) (ret bool);
if(!eqtype(n->left->type, n->right->type))
@@ -1117,6 +1128,7 @@ walkexpr(Node **np, NodeList **init)
case OARRAYLIT:
case OMAPLIT:
case OSTRUCTLIT:
+ arraylit:
nvar = nod(OXXX, N, N);
tempname(nvar, n->type);
anylit(n, nvar, init);
@@ -1448,18 +1460,18 @@ mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init)
{
Node *a, *n;
Type *tslice;
-
+
tslice = typ(TARRAY);
tslice->type = l->type->type;
tslice->bound = -1;
-
+
n = nod(OCOMPLIT, N, typenod(tslice));
n->list = lr0;
typecheck(&n, Erv);
if(n->type == T)
fatal("mkdotargslice: typecheck failed");
walkexpr(&n, init);
-
+
a = nod(OAS, nodarg(l, fp), n);
nn = list(nn, convas(a, init));
return nn;
@@ -1758,7 +1770,7 @@ walkprint(Node *nn, NodeList **init, int defer)
n = nod(OCONV, n, N);
n->type = t;
}
-
+
if(defer) {
intypes = list(intypes, nod(ODCLFIELD, N, typenod(t)));
args = list(args, n);
@@ -1788,7 +1800,7 @@ walkprint(Node *nn, NodeList **init, int defer)
calls = list(calls, mkcall("printnl", T, nil));
typechecklist(calls, Etop);
walkexprlist(calls, init);
-
+
if(op == OPANIC || op == OPANICN)
r = mkcall("panicl", T, nil);
else
diff --git a/src/pkg/runtime/string.cgo b/src/pkg/runtime/string.cgo
index 4c85766c2f..2cb518c6f8 100644
--- a/src/pkg/runtime/string.cgo
+++ b/src/pkg/runtime/string.cgo
@@ -4,6 +4,7 @@
package runtime
#include "runtime.h"
+#include "malloc.h"
String emptystring;
@@ -210,6 +211,12 @@ func slicebytetostring(b Slice) (s String) {
mcpy(s.str, b.array, s.len);
}
+func stringtoslicebyte(s String) (b Slice) {
+ b.array = mallocgc(s.len, RefNoPointers, 1, 1);
+ b.len = s.len;
+ b.cap = s.len;
+ mcpy(b.array, s.str, s.len);
+}
func sliceinttostring(b Slice) (s String) {
int32 siz1, siz2, i;
@@ -233,6 +240,30 @@ func sliceinttostring(b Slice) (s String) {
s.len = siz2;
}
+func stringtosliceint(s String) (b Slice) {
+ int32 n;
+ int32 dum, *r;
+ uint8 *p, *ep;
+
+ // two passes.
+ // unlike sliceinttostring, no race because strings are immutable.
+ p = s.str;
+ ep = s.str+s.len;
+ n = 0;
+ while(p < ep) {
+ p += charntorune(&dum, p, ep-p);
+ n++;
+ }
+
+ b.array = mallocgc(n*sizeof(r[0]), RefNoPointers, 1, 1);
+ b.len = n;
+ b.cap = n;
+ p = s.str;
+ r = (int32*)b.array;
+ while(p < ep)
+ p += charntorune(r++, p, ep-p);
+}
+
enum
{
Runeself = 0x80,
diff --git a/test/convlit.go b/test/convlit.go
index e65dad3df6..22415bb324 100644
--- a/test/convlit.go
+++ b/test/convlit.go
@@ -35,3 +35,30 @@ var good2 int = 1.0;
var good3 int = 1e9;
var good4 float = 1e20;
+// explicit conversion of string is okay
+var _ = []int("abc")
+var _ = []byte("abc")
+
+// implicit is not
+var _ []int = "abc" // ERROR "cannot use|incompatible|invalid"
+var _ []byte = "abc" // ERROR "cannot use|incompatible|invalid"
+
+// named string is okay
+type Tstring string
+var ss Tstring = "abc"
+var _ = []int(ss)
+var _ = []byte(ss)
+
+// implicit is still not
+var _ []int = ss // ERROR "cannot use|incompatible|invalid"
+var _ []byte = ss // ERROR "cannot use|incompatible|invalid"
+
+// named slice is not
+type Tint []int
+type Tbyte []byte
+var _ = Tint("abc") // ERROR "convert|incompatible|invalid"
+var _ = Tbyte("abc") // ERROR "convert|incompatible|invalid"
+
+// implicit is still not
+var _ Tint = "abc" // ERROR "cannot use|incompatible|invalid"
+var _ Tbyte = "abc" // ERROR "cannot use|incompatible|invalid"
diff --git a/test/string_lit.go b/test/string_lit.go
index 547be8003a..88b5d251ff 100644
--- a/test/string_lit.go
+++ b/test/string_lit.go
@@ -34,6 +34,19 @@ func assert(a, b, c string) {
}
}
+const (
+ gx1 = "aä本☺"
+ gx2 = "aä\xFF\xFF本☺"
+ gx2fix = "aä\uFFFD\uFFFD本☺"
+)
+
+var (
+ gr1 = []int(gx1)
+ gr2 = []int(gx2)
+ gb1 = []byte(gx1)
+ gb2 = []byte(gx2)
+)
+
func main() {
ecode = 0;
s :=
@@ -86,5 +99,22 @@ func main() {
r = 0x10ffff + 1;
s = string(r);
assert(s, "\xef\xbf\xbd", "too-large rune");
+
+ assert(string(gr1), gx1, "global ->[]int")
+ assert(string(gr2), gx2fix, "global invalid ->[]int")
+ assert(string(gb1), gx1, "->[]byte")
+ assert(string(gb2), gx2, "global invalid ->[]byte")
+
+ var (
+ r1 = []int(gx1)
+ r2 = []int(gx2)
+ b1 = []byte(gx1)
+ b2 = []byte(gx2)
+ )
+ assert(string(r1), gx1, "->[]int")
+ assert(string(r2), gx2fix, "invalid ->[]int")
+ assert(string(b1), gx1, "->[]byte")
+ assert(string(b2), gx2, "invalid ->[]byte")
+
os.Exit(ecode);
}