gc: new typechecking rules
* Code for assignment, conversions now mirrors spec.
* Changed some snprint -> smprint.
* Renamed runtime functions to separate
interface conversions from type assertions:
convT2I, assertI2T, etc.
* Correct checking of \U sequences.
Fixes #840.
Fixes #830.
Fixes #778.
R=ken2
CC=golang-dev
https://golang.org/cl/1303042
diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c
index 19155f0..d285ad0 100644
--- a/src/cmd/gc/typecheck.c
+++ b/src/cmd/gc/typecheck.c
@@ -21,7 +21,6 @@
static int twoarg(Node*);
static int lookdot(Node*, Type*, int);
static void typecheckaste(int, Type*, NodeList*, char*);
-static int exportassignok(Type*, char*);
static Type* lookdot1(Sym *s, Type *t, Type *f, int);
static int nokeys(NodeList*);
static void typecheckcomplit(Node**);
@@ -32,7 +31,6 @@
static void checklvalue(Node*, char*);
static void checkassign(Node*);
static void checkassignlist(NodeList*);
-static void toslice(Node**);
static void stringtoarraylit(Node**);
void
@@ -57,6 +55,7 @@
Type *t;
Sym *sym;
Val v;
+ char *why;
// cannot type check until all the source has been parsed
if(!typecheckok)
@@ -549,8 +548,8 @@
case TMAP:
n->etype = 0;
defaultlit(&n->right, t->down);
- if(n->right->type != T && !eqtype(n->right->type, t->down))
- yyerror("invalid map index %#N - need type %T", n->right, t->down);
+ if(n->right->type != T)
+ n->right = assignconv(n->right, t->down, "map index");
n->type = t->type;
n->op = OINDEXMAP;
break;
@@ -644,8 +643,6 @@
l = n->left;
if((t = l->type) == T)
goto error;
- // TODO(rsc): 64-bit slice index needs to be checked
- // for overflow in generated code
if(istype(t, TSTRING)) {
n->type = t;
n->op = OSLICESTR;
@@ -866,21 +863,19 @@
typecheck(&n->right, Erv);
if(n->left->type == T || n->right->type == T)
goto error;
- toslice(&n->left);
- toslice(&n->right);
defaultlit(&n->left, T);
defaultlit(&n->right, T);
if(!isslice(n->left->type) || !isslice(n->right->type)) {
if(!isslice(n->left->type) && !isslice(n->right->type))
- yyerror("arguments to copy must be array pointer or slice; have %lT, %lT", n->left->type, n->right->type);
+ yyerror("arguments to copy must be slices; have %lT, %lT", n->left->type, n->right->type);
else if(!isslice(n->left->type))
- yyerror("first argument to copy should be array pointer or slice; have %lT", n->left->type);
+ yyerror("first argument to copy should be slice; have %lT", n->left->type);
else
- yyerror("second argument to copy should be array pointer or slice; have %lT", n->right->type);
+ yyerror("second argument to copy should be slice; have %lT", n->right->type);
goto error;
}
- if(!eqtype(n->left->type, n->right->type)) {
- yyerror("arguments to copy have different element types %lT and %lT", n->left->type, n->right->type);
+ if(!eqtype(n->left->type->type, n->right->type->type)) {
+ yyerror("arguments to copy have different element types: %lT and %lT", n->left->type, n->right->type);
goto error;
}
goto ret;
@@ -892,10 +887,17 @@
convlit1(&n->left, n->type, 1);
if((t = n->left->type) == T || n->type == T)
goto error;
- n = typecheckconv(n, n->left, n->type, 1, "conversion");
- if(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);
+ op = OCONV;
+ }
switch(n->op) {
+ case OCONVNOP:
+ if(n->left->op == OLITERAL) {
+ n->op = OLITERAL;
+ n->val = n->left->val;
+ }
+ break;
case OSTRARRAYBYTE:
case OSTRARRAYRUNE:
if(n->left->op == OLITERAL)
@@ -1031,7 +1033,7 @@
if(onearg(n) < 0)
goto error;
typecheck(&n->left, Erv);
- defaultlit(&n->left, types[TINTER]);
+ defaultlit(&n->left, T);
if(n->left->type == T)
goto error;
goto ret;
@@ -1242,25 +1244,6 @@
*nn = n;
}
-static void
-toslice(Node **nn)
-{
- Node *n;
- Type *t;
-
- n = *nn;
- if(n->type == T)
- return;
- if(isptr[n->type->etype] && isfixedarray(n->type->type)) {
- // convert to slice
- t = typ(TARRAY);
- t->bound = -1;
- t->type = n->type->type->type;
- n = typecheckconv(nil, n, t, 0, "conversion of array pointer to slice");
- *nn = n;
- }
-}
-
static int
onearg(Node *n)
{
@@ -1398,208 +1381,6 @@
}
/*
- * check implicit or explicit conversion from node type nt to type t.
- */
-int
-checkconv(Type *nt, Type *t, int explicit, int *op, int *et, char *desc)
-{
- *op = OCONV;
- *et = 0;
-
- // preexisting error
- if(t == T || t->etype == TFORW)
- return 0;
-
- /*
- * implicit conversions
- */
- if(nt == T)
- return 0;
-
- if(t->etype == TBLANK) {
- *op = OCONVNOP;
- return 0;
- }
-
- if(eqtype(t, nt)) {
- exportassignok(t, desc);
- *op = OCONVNOP;
- if(!explicit || t == nt)
- return 0;
- return 1;
- }
-
- // interfaces are not subject to the name restrictions below.
- // accept anything involving interfaces and let ifacecvt
- // generate a good message. some messages have to be
- // delayed anyway.
- // TODO(rsc): now that everything is delayed for whole-package
- // compilation, the messages could be generated right here.
- if(isnilinter(t) || isnilinter(nt) || isinter(t) || isinter(nt)) {
- *et = ifaceas1(t, nt, 0);
- *op = OCONVIFACE;
- return 1;
- }
-
- // otherwise, if concrete types have names, they must match.
- if(!explicit && t->sym && nt->sym && t != nt)
- return -1;
-
- // channel must not lose directionality
- if(t->etype == TCHAN && nt->etype == TCHAN) {
- if(t->chan & ~nt->chan)
- return -1;
- if(eqtype(t->type, nt->type)) {
- *op = OCONVNOP;
- return 1;
- }
- }
-
- // array to slice
- if(isslice(t) && isptr[nt->etype] && isfixedarray(nt->type)
- && eqtype(t->type, nt->type->type)) {
- *op = OCONVSLICE;
- return 1;
- }
-
- /*
- * explicit conversions
- */
- if(!explicit)
- return -1;
-
- // same representation
- if(cvttype(t, nt)) {
- *op = OCONVNOP;
- return 1;
- }
-
- // simple fix-float
- if(isint[t->etype] || isfloat[t->etype])
- if(isint[nt->etype] || isfloat[nt->etype])
- return 1;
-
- // simple complex-complex
- if(iscomplex[t->etype])
- if(iscomplex[nt->etype])
- return 1;
-
- // to string
- if(istype(t, TSTRING)) {
- // integer rune
- if(isint[nt->etype]) {
- *op = ORUNESTR;
- return 1;
- }
-
- // *[10]byte -> string
- // in preparation for next step
- if(isptr[nt->etype] && isfixedarray(nt->type)) {
- switch(nt->type->type->etype) {
- case TUINT8:
- *op = OARRAYBYTESTR;
- return 1;
- case TINT:
- *op = OARRAYRUNESTR;
- return 1;
- }
- }
-
- // []byte -> string
- if(isslice(nt)) {
- switch(nt->type->etype) {
- case TUINT8:
- *op = OARRAYBYTESTR;
- return 1;
- case TINT:
- *op = OARRAYRUNESTR;
- return 1;
- }
- }
- }
-
- // 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))
- return 1;
-
- // convert from unsafe pointer
- if(isptrto(nt, TANY)
- && (isptr[t->etype] || t->etype == TUINTPTR))
- return 1;
-
- return -1;
-}
-
-Node*
-typecheckconv(Node *nconv, Node *n, Type *t, int explicit, char *desc)
-{
- int et, op;
- Node *n1;
- char *prefix;
-
- convlit1(&n, t, explicit);
- if(n->type == T)
- return n;
-
-
- if(n->op == OLITERAL)
- if(explicit || isideal(n->type))
- if(cvttype(t, n->type)) {
- // can convert literal in place
- // TODO(rsc) is this needed?
- n1 = nod(OXXX, N, N);
- *n1 = *n;
- n1->type = t;
- return n1;
- }
-
- prefix = "";
- if(desc != nil)
- prefix = " in ";
- else
- desc = "";
- switch(checkconv(n->type, t, explicit, &op, &et, desc)) {
- case -1:
- if(explicit)
- yyerror("cannot convert %+N to type %T%s%s", n, t, prefix, desc);
- else
- yyerror("cannot use %+N as type %T%s%s", n, t, prefix, desc);
- return n;
-
- case 0:
- if(nconv) {
- nconv->op = OCONVNOP;
- return nconv;
- }
- return n;
- }
-
- if(op == OCONVIFACE)
- defaultlit(&n, T);
-
- if(nconv == N)
- nconv = nod(OCONV, n, N);
- nconv->op = op;
- nconv->etype = et;
- nconv->type = t;
- nconv->typecheck = 1;
- return nconv;
-}
-
-/*
* typecheck assignment: type list = expression list
*/
static void
@@ -1608,6 +1389,7 @@
Type *t, *tl, *tn;
Node *n;
int lno;
+ char *why;
lno = lineno;
@@ -1619,21 +1401,20 @@
setlineno(n);
tn = n->type->type;
for(tl=tstruct->type; tl; tl=tl->down) {
- int xx, yy;
if(tl->isddd) {
// TODO(rsc): delete if (but not body) in DDD cleanup.
if(tl->type->etype != TINTER)
- for(; tn; tn=tn->down)
- if(checkconv(tn->type, tl->type->type, 0, &xx, &yy, desc) < 0)
- yyerror("cannot use %T as type %T in %s", tn->type, tl->type->type, desc);
+ for(; tn; tn=tn->down)
+ if(assignop(tn->type, tl->type->type, &why) == 0)
+ yyerror("cannot use %T as type %T in %s%s", tn->type, tl->type->type, desc, why);
goto out;
}
if(tn == T) {
yyerror("not enough arguments to %#O", op);
goto out;
}
- if(checkconv(tn->type, tl->type, 0, &xx, &yy, desc) < 0)
- yyerror("cannot use type %T as type %T in %s", tn->type, tl->type, desc);
+ if(assignop(tn->type, tl->type, &why) == 0)
+ yyerror("cannot use %T as type %T in %s%s", tn->type, tl->type, desc, why);
tn = tn->down;
}
if(tn != T)
@@ -1652,13 +1433,12 @@
if(nl != nil && nl->next == nil && nl->n->isddd && eqtype(nl->n->type, t))
goto out;
for(; nl; nl=nl->next) {
- int xx, yy;
setlineno(nl->n);
defaultlit(&nl->n, t->type);
// 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);
+ if(assignop(nl->n->type, t->type, &why) == 0)
+ yyerror("cannot use %+N as type %T in %s%s", nl->n, t->type, desc, why);
}
goto out;
}
@@ -1669,7 +1449,7 @@
n = nl->n;
setlineno(nl->n);
if(n->type != T)
- nl->n = typecheckconv(nil, n, t, 0, desc);
+ nl->n = assignconv(n, t, desc);
nl = nl->next;
}
if(nl != nil) {
@@ -1686,7 +1466,7 @@
* cannot be implicitly assigning to any type with
* an unavailable field.
*/
-static int
+int
exportassignok(Type *t, char *desc)
{
Type *f;
@@ -1882,11 +1662,11 @@
}
typecheck(&l->right, Erv);
defaultlit(&l->right, t->type);
- l->right = typecheckconv(nil, l->right, t->type, 0, "array index");
+ l->right = assignconv(l->right, t->type, "array index");
} else {
typecheck(&ll->n, Erv);
defaultlit(&ll->n, t->type);
- ll->n = typecheckconv(nil, ll->n, t->type, 0, "array index");
+ ll->n = assignconv(ll->n, t->type, "array index");
ll->n = nod(OKEY, nodintconst(i), ll->n);
ll->n->left->type = types[TINT];
ll->n->left->typecheck = 1;
@@ -1922,8 +1702,8 @@
typecheck(&l->right, Erv);
defaultlit(&l->left, t->down);
defaultlit(&l->right, t->type);
- l->left = typecheckconv(nil, l->left, t->down, 0, "map key");
- l->right = typecheckconv(nil, l->right, t->type, 0, "map value");
+ l->left = assignconv(l->left, t->down, "map key");
+ l->right = assignconv(l->right, t->type, "map value");
keydup(l->left, hash, nelem(hash));
}
n->op = OMAPLIT;
@@ -1944,7 +1724,7 @@
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);
- ll->n = typecheckconv(nil, ll->n, f->type, 0, "field value");
+ ll->n = assignconv(ll->n, f->type, "field value");
ll->n = nod(OKEY, newname(f->sym), ll->n);
ll->n->left->typecheck = 1;
f = f->down;
@@ -1977,7 +1757,7 @@
}
s = f->sym;
fielddup(newname(s), hash, nelem(hash));
- l->right = typecheckconv(nil, l->right, f->type, 0, "field value");
+ l->right = assignconv(l->right, f->type, "field value");
}
}
n->op = OSTRUCTLIT;
@@ -2139,8 +1919,8 @@
typecheck(&n->right, Erv);
if(n->right && n->right->type != T) {
if(n->left->type != T)
- n->right = typecheckconv(nil, n->right, n->left->type, 0, "assignment");
- else
+ 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) {
@@ -2157,9 +1937,21 @@
}
static void
+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);
+ return;
+ }
+ exportassignok(dst->type, "multiple assignment");
+}
+
+static void
typecheckas2(Node *n)
{
- int cl, cr, op, et;
+ int cl, cr;
NodeList *ll, *lr;
Node *l, *r;
Iter s;
@@ -2182,7 +1974,7 @@
// easy
for(ll=n->list, lr=n->rlist; ll; ll=ll->next, lr=lr->next) {
if(ll->n->type != T && lr->n->type != T)
- lr->n = typecheckconv(nil, lr->n, ll->n->type, 0, nil);
+ lr->n = assignconv(lr->n, ll->n->type, "assignment");
if(ll->n->defn == n && ll->n->ntype == N) {
defaultlit(&lr->n, T);
ll->n->type = lr->n->type;
@@ -2200,9 +1992,9 @@
if(l->type == T)
goto out;
n->op = OAS2MAPW;
- n->rlist->n = typecheckconv(nil, r, l->type, 0, nil);
+ n->rlist->n = assignconv(r, l->type, "assignment");
r = n->rlist->next->n;
- n->rlist->next->n = typecheckconv(nil, r, types[TBOOL], 0, nil);
+ n->rlist->next->n = assignconv(r, types[TBOOL], "assignment");
goto out;
}
@@ -2223,8 +2015,7 @@
t = structfirst(&s, &r->type);
for(ll=n->list; ll; ll=ll->next) {
if(ll->n->type != T)
- if(checkconv(t->type, ll->n->type, 0, &op, &et, nil) < 0)
- yyerror("cannot assign type %T to %+N", t->type, ll->n);
+ checkassignto(t->type, ll->n);
if(ll->n->defn == n && ll->n->ntype == N)
ll->n->type = t->type;
t = structnext(&s);
@@ -2246,14 +2037,15 @@
goto common;
case ODOTTYPE:
n->op = OAS2DOTTYPE;
+ r->op = ODOTTYPE2;
common:
- if(l->type != T && checkconv(r->type, l->type, 0, &op, &et, nil) < 0)
- yyerror("cannot assign %+N to %+N", r, l);
+ if(l->type != T)
+ checkassignto(r->type, l);
if(l->defn == n)
l->type = r->type;
l = n->list->next->n;
- if(l->type != T && checkconv(types[TBOOL], l->type, 0, &op, &et, nil) < 0)
- yyerror("cannot assign bool value to %+N", l);
+ if(l->type != T)
+ checkassignto(types[TBOOL], l);
if(l->defn == n && l->ntype == N)
l->type = types[TBOOL];
goto out;