Commit 39101613 authored by Russ Cox's avatar Russ Cox

gc: implement []int(string) and []byte(string)

R=ken2
CC=golang-dev
https://golang.org/cl/224060
parent b86c0b0c
...@@ -26,6 +26,8 @@ char *runtimeimport = ...@@ -26,6 +26,8 @@ char *runtimeimport =
"func \"\".intstring (? int64) string\n" "func \"\".intstring (? int64) string\n"
"func \"\".slicebytetostring (? []uint8) string\n" "func \"\".slicebytetostring (? []uint8) string\n"
"func \"\".sliceinttostring (? []int) string\n" "func \"\".sliceinttostring (? []int) string\n"
"func \"\".stringtoslicebyte (? string) []uint8\n"
"func \"\".stringtosliceint (? string) []int\n"
"func \"\".stringiter (? string, ? int) int\n" "func \"\".stringiter (? string, ? int) int\n"
"func \"\".stringiter2 (? string, ? int) (retk int, retv int)\n" "func \"\".stringiter2 (? string, ? int) (retk int, retv int)\n"
"func \"\".slicecopy (to any, fr any, wid uint32) int\n" "func \"\".slicecopy (to any, fr any, wid uint32) int\n"
......
...@@ -351,6 +351,7 @@ enum ...@@ -351,6 +351,7 @@ enum
OAPPENDSTR, OAPPENDSTR,
OARRAY, OARRAY,
OARRAYBYTESTR, OARRAYRUNESTR, OARRAYBYTESTR, OARRAYRUNESTR,
OSTRARRAYBYTE, OSTRARRAYRUNE,
OAS, OAS2, OAS2MAPW, OAS2FUNC, OAS2RECV, OAS2MAPR, OAS2DOTTYPE, OASOP, OAS, OAS2, OAS2MAPW, OAS2FUNC, OAS2RECV, OAS2MAPR, OAS2DOTTYPE, OASOP,
OBAD, OBAD,
OCALL, OCALLFUNC, OCALLMETH, OCALLINTER, OCALL, OCALLFUNC, OCALLMETH, OCALLINTER,
...@@ -411,7 +412,7 @@ enum ...@@ -411,7 +412,7 @@ enum
OTINTER, OTINTER,
OTFUNC, OTFUNC,
OTARRAY, OTARRAY,
// misc // misc
ODDD, ODDD,
...@@ -458,7 +459,7 @@ enum ...@@ -458,7 +459,7 @@ enum
TIDEAL, // 32 TIDEAL, // 32
TNIL, TNIL,
TBLANK, TBLANK,
// pseudo-type for frame layout // pseudo-type for frame layout
TFUNCARGS, TFUNCARGS,
TCHANARGS, TCHANARGS,
......
...@@ -38,6 +38,8 @@ func indexstring(string, int) byte ...@@ -38,6 +38,8 @@ func indexstring(string, int) byte
func intstring(int64) string func intstring(int64) string
func slicebytetostring([]byte) string func slicebytetostring([]byte) string
func sliceinttostring([]int) string func sliceinttostring([]int) string
func stringtoslicebyte(string) []byte
func stringtosliceint(string) []int
func stringiter(string, int) int func stringiter(string, int) int
func stringiter2(string, int) (retk int, retv int) func stringiter2(string, int) (retk int, retv int)
func slicecopy(to any, fr any, wid uint32) int func slicecopy(to any, fr any, wid uint32) int
......
...@@ -32,6 +32,7 @@ static void checklvalue(Node*, char*); ...@@ -32,6 +32,7 @@ static void checklvalue(Node*, char*);
static void checkassign(Node*); static void checkassign(Node*);
static void checkassignlist(NodeList*); static void checkassignlist(NodeList*);
static void toslice(Node**); static void toslice(Node**);
static void stringtoarraylit(Node**);
void void
typechecklist(NodeList *l, int top) typechecklist(NodeList *l, int top)
...@@ -835,6 +836,13 @@ reswitch: ...@@ -835,6 +836,13 @@ reswitch:
n = typecheckconv(n, n->left, n->type, 1, "conversion"); n = typecheckconv(n, n->left, n->type, 1, "conversion");
if(n->type == T) if(n->type == T)
goto error; goto error;
switch(n->op) {
case OSTRARRAYBYTE:
case OSTRARRAYRUNE:
if(n->left->op == OLITERAL)
stringtoarraylit(&n);
break;
}
goto ret; goto ret;
case OMAKE: case OMAKE:
...@@ -1406,6 +1414,18 @@ checkconv(Type *nt, Type *t, int explicit, int *op, int *et, char *desc) ...@@ -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 // convert to unsafe pointer
if(isptrto(t, TANY) if(isptrto(t, TANY)
&& (isptr[nt->etype] || nt->etype == TUINTPTR)) && (isptr[nt->etype] || nt->etype == TUINTPTR))
...@@ -1534,7 +1554,7 @@ typecheckaste(int op, Type *tstruct, NodeList *nl, char *desc) ...@@ -1534,7 +1554,7 @@ typecheckaste(int op, Type *tstruct, NodeList *nl, char *desc)
// TODO(rsc): drop first if in DDD cleanup // TODO(rsc): drop first if in DDD cleanup
if(t->etype != TINTER) if(t->etype != TINTER)
if(checkconv(nl->n->type, t->type, 0, &xx, &yy, desc) < 0) 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; goto out;
} }
...@@ -1587,7 +1607,7 @@ exportassignok(Type *t, char *desc) ...@@ -1587,7 +1607,7 @@ exportassignok(Type *t, char *desc)
// it only happens for fields in a ... struct. // it only happens for fields in a ... struct.
if(s != nil && !exportname(s->name) && s->pkg != localpkg) { if(s != nil && !exportname(s->name) && s->pkg != localpkg) {
char *prefix; char *prefix;
prefix = ""; prefix = "";
if(desc != nil) if(desc != nil)
prefix = " in "; prefix = " in ";
...@@ -2164,3 +2184,39 @@ typecheckfunc(Node *n) ...@@ -2164,3 +2184,39 @@ typecheckfunc(Node *n)
if(rcvr != nil && n->shortname != N && !isblank(n->shortname)) if(rcvr != nil && n->shortname != N && !isblank(n->shortname))
addmethod(n->shortname->sym, t, 1); 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;
}
...@@ -8,6 +8,7 @@ static Node* walkprint(Node*, NodeList**, int); ...@@ -8,6 +8,7 @@ static Node* walkprint(Node*, NodeList**, int);
static Node* conv(Node*, Type*); static Node* conv(Node*, Type*);
static Node* mapfn(char*, Type*); static Node* mapfn(char*, Type*);
static Node* makenewvar(Type*, NodeList**, Node**); static Node* makenewvar(Type*, NodeList**, Node**);
enum enum
{ {
Inone, Inone,
...@@ -122,7 +123,7 @@ static void ...@@ -122,7 +123,7 @@ static void
domethod(Node *n) domethod(Node *n)
{ {
Node *nt; Node *nt;
nt = n->type->nname; nt = n->type->nname;
typecheck(&nt, Etype); typecheck(&nt, Etype);
if(nt->type == T) { if(nt->type == T) {
...@@ -142,7 +143,7 @@ walkdeftype(Node *n) ...@@ -142,7 +143,7 @@ walkdeftype(Node *n)
int maplineno, embedlineno, lno; int maplineno, embedlineno, lno;
Type *t; Type *t;
NodeList *l; NodeList *l;
nwalkdeftype++; nwalkdeftype++;
lno = lineno; lno = lineno;
setlineno(n); setlineno(n);
...@@ -183,7 +184,7 @@ walkdeftype(Node *n) ...@@ -183,7 +184,7 @@ walkdeftype(Node *n)
ret: ret:
lineno = lno; lineno = lno;
// if there are no type definitions going on, it's safe to // if there are no type definitions going on, it's safe to
// try to resolve the method types for the interfaces // try to resolve the method types for the interfaces
// we just read. // we just read.
...@@ -868,7 +869,7 @@ walkexpr(Node **np, NodeList **init) ...@@ -868,7 +869,7 @@ walkexpr(Node **np, NodeList **init)
case OINDEX: case OINDEX:
walkexpr(&n->left, init); walkexpr(&n->left, init);
walkexpr(&n->right, init); walkexpr(&n->right, init);
// if range of type cannot exceed static array bound, // if range of type cannot exceed static array bound,
// disable bounds check // disable bounds check
if(!isslice(n->left->type)) if(!isslice(n->left->type))
...@@ -1092,10 +1093,20 @@ walkexpr(Node **np, NodeList **init) ...@@ -1092,10 +1093,20 @@ walkexpr(Node **np, NodeList **init)
goto ret; goto ret;
case OARRAYRUNESTR: case OARRAYRUNESTR:
// sliceinttostring([]byte) string; // sliceinttostring([]int) string;
n = mkcall("sliceinttostring", n->type, init, n->left); n = mkcall("sliceinttostring", n->type, init, n->left);
goto ret; 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: case OCMPIFACE:
// ifaceeq(i1 any-1, i2 any-2) (ret bool); // ifaceeq(i1 any-1, i2 any-2) (ret bool);
if(!eqtype(n->left->type, n->right->type)) if(!eqtype(n->left->type, n->right->type))
...@@ -1117,6 +1128,7 @@ walkexpr(Node **np, NodeList **init) ...@@ -1117,6 +1128,7 @@ walkexpr(Node **np, NodeList **init)
case OARRAYLIT: case OARRAYLIT:
case OMAPLIT: case OMAPLIT:
case OSTRUCTLIT: case OSTRUCTLIT:
arraylit:
nvar = nod(OXXX, N, N); nvar = nod(OXXX, N, N);
tempname(nvar, n->type); tempname(nvar, n->type);
anylit(n, nvar, init); anylit(n, nvar, init);
...@@ -1448,18 +1460,18 @@ mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init) ...@@ -1448,18 +1460,18 @@ mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init)
{ {
Node *a, *n; Node *a, *n;
Type *tslice; Type *tslice;
tslice = typ(TARRAY); tslice = typ(TARRAY);
tslice->type = l->type->type; tslice->type = l->type->type;
tslice->bound = -1; tslice->bound = -1;
n = nod(OCOMPLIT, N, typenod(tslice)); n = nod(OCOMPLIT, N, typenod(tslice));
n->list = lr0; n->list = lr0;
typecheck(&n, Erv); typecheck(&n, Erv);
if(n->type == T) if(n->type == T)
fatal("mkdotargslice: typecheck failed"); fatal("mkdotargslice: typecheck failed");
walkexpr(&n, init); walkexpr(&n, init);
a = nod(OAS, nodarg(l, fp), n); a = nod(OAS, nodarg(l, fp), n);
nn = list(nn, convas(a, init)); nn = list(nn, convas(a, init));
return nn; return nn;
...@@ -1758,7 +1770,7 @@ walkprint(Node *nn, NodeList **init, int defer) ...@@ -1758,7 +1770,7 @@ walkprint(Node *nn, NodeList **init, int defer)
n = nod(OCONV, n, N); n = nod(OCONV, n, N);
n->type = t; n->type = t;
} }
if(defer) { if(defer) {
intypes = list(intypes, nod(ODCLFIELD, N, typenod(t))); intypes = list(intypes, nod(ODCLFIELD, N, typenod(t)));
args = list(args, n); args = list(args, n);
...@@ -1788,7 +1800,7 @@ walkprint(Node *nn, NodeList **init, int defer) ...@@ -1788,7 +1800,7 @@ walkprint(Node *nn, NodeList **init, int defer)
calls = list(calls, mkcall("printnl", T, nil)); calls = list(calls, mkcall("printnl", T, nil));
typechecklist(calls, Etop); typechecklist(calls, Etop);
walkexprlist(calls, init); walkexprlist(calls, init);
if(op == OPANIC || op == OPANICN) if(op == OPANIC || op == OPANICN)
r = mkcall("panicl", T, nil); r = mkcall("panicl", T, nil);
else else
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
package runtime package runtime
#include "runtime.h" #include "runtime.h"
#include "malloc.h"
String emptystring; String emptystring;
...@@ -210,6 +211,12 @@ func slicebytetostring(b Slice) (s String) { ...@@ -210,6 +211,12 @@ func slicebytetostring(b Slice) (s String) {
mcpy(s.str, b.array, s.len); 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) { func sliceinttostring(b Slice) (s String) {
int32 siz1, siz2, i; int32 siz1, siz2, i;
...@@ -233,6 +240,30 @@ func sliceinttostring(b Slice) (s String) { ...@@ -233,6 +240,30 @@ func sliceinttostring(b Slice) (s String) {
s.len = siz2; 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 enum
{ {
Runeself = 0x80, Runeself = 0x80,
......
...@@ -35,3 +35,30 @@ var good2 int = 1.0; ...@@ -35,3 +35,30 @@ var good2 int = 1.0;
var good3 int = 1e9; var good3 int = 1e9;
var good4 float = 1e20; 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"
...@@ -34,6 +34,19 @@ func assert(a, b, c string) { ...@@ -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() { func main() {
ecode = 0; ecode = 0;
s := s :=
...@@ -86,5 +99,22 @@ func main() { ...@@ -86,5 +99,22 @@ func main() {
r = 0x10ffff + 1; r = 0x10ffff + 1;
s = string(r); s = string(r);
assert(s, "\xef\xbf\xbd", "too-large rune"); 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); os.Exit(ecode);
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment