]> Cypherpunks repositories - gostls13.git/commitdiff
gc: implement []int(string) and []byte(string)
authorRuss Cox <rsc@golang.org>
Thu, 25 Feb 2010 23:11:07 +0000 (15:11 -0800)
committerRuss Cox <rsc@golang.org>
Thu, 25 Feb 2010 23:11:07 +0000 (15:11 -0800)
R=ken2
CC=golang-dev
https://golang.org/cl/224060

src/cmd/gc/builtin.c.boot
src/cmd/gc/go.h
src/cmd/gc/runtime.go
src/cmd/gc/typecheck.c
src/cmd/gc/walk.c
src/pkg/runtime/string.cgo
test/convlit.go
test/string_lit.go

index 6eed40230d4fc9f0821f43497c21f64a0b2f73d5..3114d75807ebc43af6cb66e65682e33360c28f24 100644 (file)
@@ -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"
index 753de0399a2fa4391b5ffc0d581dc2d9376b796b..cbcdc9c39d27da5b657bffd45e6f40b491500830 100644 (file)
@@ -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,
index e350c282dd08d7574aeb04878b9a20a66b8cf7c8..ca3b6a1bc1ff7f029bb654bfd927b6f2ca3eafc6 100644 (file)
@@ -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
index 158dee673892cc0ed1e3dafd78362a68e39880ec..4c4c9283380e162e837d9836b70f6e66f82d0a97 100644 (file)
@@ -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;
+}
index e902600ba0e09afddbb84f2dfecec8acb6ddc57f..fa63646c50dea21c27afe07c13527ff6bd5fac85 100644 (file)
@@ -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
index 4c85766c2ff94776a64872730b815b50dbba9256..2cb518c6f8f7dddc39ed6710c97cd50d72eee1b0 100644 (file)
@@ -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,
index e65dad3df69c1c8f6ff13394ba4f8c2db52bcec6..22415bb3249d5df150586dd7d1aa38770d04067c 100644 (file)
@@ -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"
index 547be8003a2b0e40d9a94164b65fc0112877aebc..88b5d251ffe64f19d08cb7427463ff26465e79fd 100644 (file)
@@ -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);
 }