blob: 8a3b486bd6686013229c3a0acb9ab570ee5ba662 [file] [log] [blame]
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
* type check the whole tree of an expression.
* calculates expression types.
* evaluates compile time constants.
* marks variables that escape the local frame.
* rewrites n->op to be more specific in some cases.
*/
#include <u.h>
#include <libc.h>
#include "go.h"
static void implicitstar(Node**);
static int onearg(Node*, char*, ...);
static int twoarg(Node*);
static int lookdot(Node*, Type*, int);
static int looktypedot(Node*, Type*, int);
static void typecheckaste(int, Node*, int, Type*, NodeList*, char*);
static Type* lookdot1(Node*, Sym *s, Type *t, Type *f, int);
static int nokeys(NodeList*);
static void typecheckcomplit(Node**);
static void typecheckas2(Node*);
static void typecheckas(Node*);
static void typecheckfunc(Node*);
static void checklvalue(Node*, char*);
static void checkassign(Node*);
static void checkassignlist(NodeList*);
static void stringtoarraylit(Node**);
static Node* resolve(Node*);
static void checkdefergo(Node*);
static int checkmake(Type*, char*, Node*);
static int checksliceindex(Node*, Node*, Type*);
static int checksliceconst(Node*, Node*);
static NodeList* typecheckdefstack;
/*
* resolve ONONAME to definition, if any.
*/
static Node*
resolve(Node *n)
{
Node *r;
if(n != N && n->op == ONONAME && n->sym != S && (r = n->sym->def) != N) {
if(r->op != OIOTA)
n = r;
else if(n->iota >= 0)
n = nodintconst(n->iota);
}
return n;
}
void
typechecklist(NodeList *l, int top)
{
for(; l; l=l->next)
typecheck(&l->n, top);
}
static char* _typekind[] = {
[TINT] = "int",
[TUINT] = "uint",
[TINT8] = "int8",
[TUINT8] = "uint8",
[TINT16] = "int16",
[TUINT16] = "uint16",
[TINT32] = "int32",
[TUINT32] = "uint32",
[TINT64] = "int64",
[TUINT64] = "uint64",
[TUINTPTR] = "uintptr",
[TCOMPLEX64] = "complex64",
[TCOMPLEX128] = "complex128",
[TFLOAT32] = "float32",
[TFLOAT64] = "float64",
[TBOOL] = "bool",
[TSTRING] = "string",
[TPTR32] = "pointer",
[TPTR64] = "pointer",
[TUNSAFEPTR] = "unsafe.Pointer",
[TSTRUCT] = "struct",
[TINTER] = "interface",
[TCHAN] = "chan",
[TMAP] = "map",
[TARRAY] = "array",
[TFUNC] = "func",
[TNIL] = "nil",
[TIDEAL] = "untyped number",
};
static char*
typekind(Type *t)
{
int et;
static char buf[50];
char *s;
if(isslice(t))
return "slice";
et = t->etype;
if(0 <= et && et < nelem(_typekind) && (s=_typekind[et]) != nil)
return s;
snprint(buf, sizeof buf, "etype=%d", et);
return buf;
}
/*
* sprint_depchain prints a dependency chain
* of nodes into fmt.
* It is used by typecheck in the case of OLITERAL nodes
* to print constant definition loops.
*/
static void
sprint_depchain(Fmt *fmt, NodeList *stack, Node *cur, Node *first)
{
NodeList *l;
for(l = stack; l; l=l->next) {
if(l->n->op == cur->op) {
if(l->n != first)
sprint_depchain(fmt, l->next, l->n, first);
fmtprint(fmt, "\n\t%L: %N uses %N", l->n->lineno, l->n, cur);
return;
}
}
}
/*
* type check node *np.
* replaces *np with a new pointer in some cases.
* returns the final value of *np as a convenience.
*/
static void typecheck1(Node **, int);
Node*
typecheck(Node **np, int top)
{
Node *n;
int lno;
Fmt fmt;
NodeList *l;
static NodeList *tcstack, *tcfree;
// cannot type check until all the source has been parsed
if(!typecheckok)
fatal("early typecheck");
n = *np;
if(n == N)
return N;
lno = setlineno(n);
// Skip over parens.
while(n->op == OPAREN)
n = n->left;
// Resolve definition of name and value of iota lazily.
n = resolve(n);
*np = n;
// Skip typecheck if already done.
// But re-typecheck ONAME/OTYPE/OLITERAL/OPACK node in case context has changed.
if(n->typecheck == 1) {
switch(n->op) {
case ONAME:
case OTYPE:
case OLITERAL:
case OPACK:
break;
default:
lineno = lno;
return n;
}
}
if(n->typecheck == 2) {
// Typechecking loop. Trying printing a meaningful message,
// otherwise a stack trace of typechecking.
switch(n->op) {
case ONAME:
// We can already diagnose variables used as types.
if((top & (Erv|Etype)) == Etype)
yyerror("%N is not a type", n);
break;
case OLITERAL:
if((top & (Erv|Etype)) == Etype) {
yyerror("%N is not a type", n);
break;
}
fmtstrinit(&fmt);
sprint_depchain(&fmt, tcstack, n, n);
yyerrorl(n->lineno, "constant definition loop%s", fmtstrflush(&fmt));
break;
}
if(nsavederrors+nerrors == 0) {
fmtstrinit(&fmt);
for(l=tcstack; l; l=l->next)
fmtprint(&fmt, "\n\t%L %N", l->n->lineno, l->n);
yyerror("typechecking loop involving %N%s", n, fmtstrflush(&fmt));
}
lineno = lno;
return n;
}
n->typecheck = 2;
if(tcfree != nil) {
l = tcfree;
tcfree = l->next;
} else
l = mal(sizeof *l);
l->next = tcstack;
l->n = n;
tcstack = l;
typecheck1(&n, top);
*np = n;
n->typecheck = 1;
if(tcstack != l)
fatal("typecheck stack out of sync");
tcstack = l->next;
l->next = tcfree;
tcfree = l;
lineno = lno;
return n;
}
/*
* does n contain a call or receive operation?
*/
static int callrecvlist(NodeList*);
static int
callrecv(Node *n)
{
if(n == nil)
return 0;
switch(n->op) {
case OCALL:
case OCALLMETH:
case OCALLINTER:
case OCALLFUNC:
case ORECV:
case OCAP:
case OLEN:
case OCOPY:
case ONEW:
case OAPPEND:
case ODELETE:
return 1;
}
return callrecv(n->left) ||
callrecv(n->right) ||
callrecv(n->ntest) ||
callrecv(n->nincr) ||
callrecvlist(n->ninit) ||
callrecvlist(n->nbody) ||
callrecvlist(n->nelse) ||
callrecvlist(n->list) ||
callrecvlist(n->rlist);
}
static int
callrecvlist(NodeList *l)
{
for(; l; l=l->next)
if(callrecv(l->n))
return 1;
return 0;
}
// indexlit implements typechecking of untyped values as
// array/slice indexes. It is equivalent to defaultlit
// except for constants of numerical kind, which are acceptable
// whenever they can be represented by a value of type int.
static void
indexlit(Node **np)
{
Node *n;
n = *np;
if(n == N || !isideal(n->type))
return;
switch(consttype(n)) {
case CTINT:
case CTRUNE:
case CTFLT:
case CTCPLX:
defaultlit(np, types[TINT]);
break;
}
defaultlit(np, T);
}
static void
typecheck1(Node **np, int top)
{
int et, aop, op, ptr;
Node *n, *l, *r, *lo, *mid, *hi;
NodeList *args;
int ok, ntop;
Type *t, *tp, *missing, *have, *badtype;
Val v;
char *why, *desc, descbuf[64];
vlong x;
n = *np;
if(n->sym) {
if(n->op == ONAME && n->etype != 0 && !(top & Ecall)) {
yyerror("use of builtin %S not in function call", n->sym);
goto error;
}
typecheckdef(n);
if(n->op == ONONAME)
goto error;
}
*np = n;
reswitch:
ok = 0;
switch(n->op) {
default:
// until typecheck is complete, do nothing.
dump("typecheck", n);
fatal("typecheck %O", n->op);
/*
* names
*/
case OLITERAL:
ok |= Erv;
if(n->type == T && n->val.ctype == CTSTR)
n->type = idealstring;
goto ret;
case ONONAME:
ok |= Erv;
goto ret;
case ONAME:
if(n->etype != 0) {
ok |= Ecall;
goto ret;
}
if(!(top & Easgn)) {
// not a write to the variable
if(isblank(n)) {
yyerror("cannot use _ as value");
goto error;
}
n->used = 1;
}
if(!(top &Ecall) && isunsafebuiltin(n)) {
yyerror("%N is not an expression, must be called", n);
goto error;
}
ok |= Erv;
goto ret;
case OPACK:
yyerror("use of package %S without selector", n->sym);
goto error;
case ODDD:
break;
/*
* types (OIND is with exprs)
*/
case OTYPE:
ok |= Etype;
if(n->type == T)
goto error;
break;
case OTARRAY:
ok |= Etype;
t = typ(TARRAY);
l = n->left;
r = n->right;
if(l == nil) {
t->bound = -1; // slice
} else if(l->op == ODDD) {
t->bound = -100; // to be filled in
if(!(top&Ecomplit) && !n->diag) {
t->broke = 1;
n->diag = 1;
yyerror("use of [...] array outside of array literal");
}
} else {
l = typecheck(&n->left, Erv);
switch(consttype(l)) {
case CTINT:
case CTRUNE:
v = l->val;
break;
case CTFLT:
v = toint(l->val);
break;
default:
if(l->type != T && isint[l->type->etype] && l->op != OLITERAL)
yyerror("non-constant array bound %N", l);
else
yyerror("invalid array bound %N", l);
goto error;
}
t->bound = mpgetfix(v.u.xval);
if(doesoverflow(v, types[TINT])) {
yyerror("array bound is too large");
goto error;
} else if(t->bound < 0) {
yyerror("array bound must be non-negative");
goto error;
}
}
typecheck(&r, Etype);
if(r->type == T)
goto error;
t->type = r->type;
n->op = OTYPE;
n->type = t;
n->left = N;
n->right = N;
if(t->bound != -100)
checkwidth(t);
break;
case OTMAP:
ok |= Etype;
l = typecheck(&n->left, Etype);
r = typecheck(&n->right, Etype);
if(l->type == T || r->type == T)
goto error;
n->op = OTYPE;
n->type = maptype(l->type, r->type);
n->left = N;
n->right = N;
break;
case OTCHAN:
ok |= Etype;
l = typecheck(&n->left, Etype);
if(l->type == T)
goto error;
t = typ(TCHAN);
t->type = l->type;
t->chan = n->etype;
n->op = OTYPE;
n->type = t;
n->left = N;
n->etype = 0;
break;
case OTSTRUCT:
ok |= Etype;
n->op = OTYPE;
n->type = tostruct(n->list);
if(n->type == T || n->type->broke)
goto error;
n->list = nil;
break;
case OTINTER:
ok |= Etype;
n->op = OTYPE;
n->type = tointerface(n->list);
if(n->type == T)
goto error;
break;
case OTFUNC:
ok |= Etype;
n->op = OTYPE;
n->type = functype(n->left, n->list, n->rlist);
if(n->type == T)
goto error;
break;
/*
* type or expr
*/
case OIND:
ntop = Erv | Etype;
if(!(top & Eaddr)) // The *x in &*x is not an indirect.
ntop |= Eindir;
ntop |= top & Ecomplit;
l = typecheck(&n->left, ntop);
if((t = l->type) == T)
goto error;
if(l->op == OTYPE) {
ok |= Etype;
n->op = OTYPE;
n->type = ptrto(l->type);
n->left = N;
goto ret;
}
if(!isptr[t->etype]) {
if(top & (Erv | Etop)) {
yyerror("invalid indirect of %lN", n->left);
goto error;
}
goto ret;
}
ok |= Erv;
n->type = t->type;
goto ret;
/*
* arithmetic exprs
*/
case OASOP:
ok |= Etop;
l = typecheck(&n->left, Erv);
checkassign(n->left);
r = typecheck(&n->right, Erv);
if(l->type == T || r->type == T)
goto error;
op = n->etype;
goto arith;
case OADD:
case OAND:
case OANDAND:
case OANDNOT:
case ODIV:
case OEQ:
case OGE:
case OGT:
case OLE:
case OLT:
case OLSH:
case ORSH:
case OMOD:
case OMUL:
case ONE:
case OOR:
case OOROR:
case OSUB:
case OXOR:
ok |= Erv;
l = typecheck(&n->left, Erv | (top & Eiota));
r = typecheck(&n->right, Erv | (top & Eiota));
if(l->type == T || r->type == T)
goto error;
op = n->op;
arith:
if(op == OLSH || op == ORSH)
goto shift;
// ideal mixed with non-ideal
defaultlit2(&l, &r, 0);
n->left = l;
n->right = r;
if(l->type == T || r->type == T)
goto error;
t = l->type;
if(t->etype == TIDEAL)
t = r->type;
et = t->etype;
if(et == TIDEAL)
et = TINT;
if(iscmp[n->op] && t->etype != TIDEAL && !eqtype(l->type, r->type)) {
// comparison is okay as long as one side is
// assignable to the other. convert so they have
// the same type.
//
// the only conversion that isn't a no-op is concrete == interface.
// in that case, check comparability of the concrete type.
if(r->type->etype != TBLANK && (aop = assignop(l->type, r->type, nil)) != 0) {
if(isinter(r->type) && !isinter(l->type) && algtype1(l->type, nil) == ANOEQ) {
yyerror("invalid operation: %N (operator %O not defined on %s)", n, op, typekind(l->type));
goto error;
}
l = nod(aop, l, N);
l->type = r->type;
l->typecheck = 1;
n->left = l;
t = l->type;
} else if(l->type->etype != TBLANK && (aop = assignop(r->type, l->type, nil)) != 0) {
if(isinter(l->type) && !isinter(r->type) && algtype1(r->type, nil) == ANOEQ) {
yyerror("invalid operation: %N (operator %O not defined on %s)", n, op, typekind(r->type));
goto error;
}
r = nod(aop, r, N);
r->type = l->type;
r->typecheck = 1;
n->right = r;
t = r->type;
}
et = t->etype;
}
if(t->etype != TIDEAL && !eqtype(l->type, r->type)) {
defaultlit2(&l, &r, 1);
if(n->op == OASOP && n->implicit) {
yyerror("invalid operation: %N (non-numeric type %T)", n, l->type);
goto error;
}
yyerror("invalid operation: %N (mismatched types %T and %T)", n, l->type, r->type);
goto error;
}
if(!okfor[op][et]) {
yyerror("invalid operation: %N (operator %O not defined on %s)", n, op, typekind(t));
goto error;
}
// okfor allows any array == array, map == map, func == func.
// restrict to slice/map/func == nil and nil == slice/map/func.
if(isfixedarray(l->type) && algtype1(l->type, nil) == ANOEQ) {
yyerror("invalid operation: %N (%T cannot be compared)", n, l->type);
goto error;
}
if(isslice(l->type) && !isnil(l) && !isnil(r)) {
yyerror("invalid operation: %N (slice can only be compared to nil)", n);
goto error;
}
if(l->type->etype == TMAP && !isnil(l) && !isnil(r)) {
yyerror("invalid operation: %N (map can only be compared to nil)", n);
goto error;
}
if(l->type->etype == TFUNC && !isnil(l) && !isnil(r)) {
yyerror("invalid operation: %N (func can only be compared to nil)", n);
goto error;
}
if(l->type->etype == TSTRUCT && algtype1(l->type, &badtype) == ANOEQ) {
yyerror("invalid operation: %N (struct containing %T cannot be compared)", n, badtype);
goto error;
}
t = l->type;
if(iscmp[n->op]) {
evconst(n);
t = idealbool;
if(n->op != OLITERAL) {
defaultlit2(&l, &r, 1);
n->left = l;
n->right = r;
}
} else if(n->op == OANDAND || n->op == OOROR) {
if(l->type == r->type)
t = l->type;
else if(l->type == idealbool)
t = r->type;
else if(r->type == idealbool)
t = l->type;
// non-comparison operators on ideal bools should make them lose their ideal-ness
} else if(t == idealbool)
t = types[TBOOL];
if(et == TSTRING) {
if(iscmp[n->op]) {
n->etype = n->op;
n->op = OCMPSTR;
} else if(n->op == OADD) {
// create OADDSTR node with list of strings in x + y + z + (w + v) + ...
n->op = OADDSTR;
if(l->op == OADDSTR)
n->list = l->list;
else
n->list = list1(l);
if(r->op == OADDSTR)
n->list = concat(n->list, r->list);
else
n->list = list(n->list, r);
n->left = N;
n->right = N;
}
}
if(et == TINTER) {
if(l->op == OLITERAL && l->val.ctype == CTNIL) {
// swap for back end
n->left = r;
n->right = l;
} else if(r->op == OLITERAL && r->val.ctype == CTNIL) {
// leave alone for back end
} else {
n->etype = n->op;
n->op = OCMPIFACE;
}
}
if((op == ODIV || op == OMOD) && isconst(r, CTINT))
if(mpcmpfixc(r->val.u.xval, 0) == 0) {
yyerror("division by zero");
goto error;
}
n->type = t;
goto ret;
shift:
defaultlit(&r, types[TUINT]);
n->right = r;
t = r->type;
if(!isint[t->etype] || issigned[t->etype]) {
yyerror("invalid operation: %N (shift count type %T, must be unsigned integer)", n, r->type);
goto error;
}
t = l->type;
if(t != T && t->etype != TIDEAL && !isint[t->etype]) {
yyerror("invalid operation: %N (shift of type %T)", n, t);
goto error;
}
// no defaultlit for left
// the outer context gives the type
n->type = l->type;
goto ret;
case OCOM:
case OMINUS:
case ONOT:
case OPLUS:
ok |= Erv;
l = typecheck(&n->left, Erv | (top & Eiota));
if((t = l->type) == T)
goto error;
if(!okfor[n->op][t->etype]) {
yyerror("invalid operation: %O %T", n->op, t);
goto error;
}
n->type = t;
goto ret;
/*
* exprs
*/
case OADDR:
ok |= Erv;
typecheck(&n->left, Erv | Eaddr);
if(n->left->type == T)
goto error;
checklvalue(n->left, "take the address of");
r = outervalue(n->left);
for(l = n->left; l != r; l = l->left)
l->addrtaken = 1;
if(l->orig != l && l->op == ONAME)
fatal("found non-orig name node %N", l);
l->addrtaken = 1;
defaultlit(&n->left, T);
l = n->left;
if((t = l->type) == T)
goto error;
n->type = ptrto(t);
goto ret;
case OCOMPLIT:
ok |= Erv;
typecheckcomplit(&n);
if(n->type == T)
goto error;
goto ret;
case OXDOT:
n = adddot(n);
n->op = ODOT;
if(n->left == N)
goto error;
// fall through
case ODOT:
typecheck(&n->left, Erv|Etype);
defaultlit(&n->left, T);
if((t = n->left->type) == T)
goto error;
if(n->right->op != ONAME) {
yyerror("rhs of . must be a name"); // impossible
goto error;
}
r = n->right;
if(n->left->op == OTYPE) {
if(!looktypedot(n, t, 0)) {
if(looktypedot(n, t, 1))
yyerror("%N undefined (cannot refer to unexported method %S)", n, n->right->sym);
else
yyerror("%N undefined (type %T has no method %S)", n, t, n->right->sym);
goto error;
}
if(n->type->etype != TFUNC || n->type->thistuple != 1) {
yyerror("type %T has no method %hS", n->left->type, n->right->sym);
n->type = T;
goto error;
}
n->op = ONAME;
n->sym = n->right->sym;
n->type = methodfunc(n->type, n->left->type);
n->xoffset = 0;
n->class = PFUNC;
ok = Erv;
goto ret;
}
if(isptr[t->etype] && t->type->etype != TINTER) {
t = t->type;
if(t == T)
goto error;
n->op = ODOTPTR;
checkwidth(t);
}
if(isblank(n->right)) {
yyerror("cannot refer to blank field or method");
goto error;
}
if(!lookdot(n, t, 0)) {
if(lookdot(n, t, 1))
yyerror("%N undefined (cannot refer to unexported field or method %S)", n, n->right->sym);
else
yyerror("%N undefined (type %T has no field or method %S)", n, n->left->type, n->right->sym);
goto error;
}
switch(n->op) {
case ODOTINTER:
case ODOTMETH:
if(top&Ecall)
ok |= Ecall;
else {
typecheckpartialcall(n, r);
ok |= Erv;
}
break;
default:
ok |= Erv;
break;
}
goto ret;
case ODOTTYPE:
ok |= Erv;
typecheck(&n->left, Erv);
defaultlit(&n->left, T);
l = n->left;
if((t = l->type) == T)
goto error;
if(!isinter(t)) {
yyerror("invalid type assertion: %N (non-interface type %T on left)", n, t);
goto error;
}
if(n->right != N) {
typecheck(&n->right, Etype);
n->type = n->right->type;
n->right = N;
if(n->type == T)
goto error;
}
if(n->type != T && n->type->etype != TINTER)
if(!implements(n->type, t, &missing, &have, &ptr)) {
if(have && have->sym == missing->sym)
yyerror("impossible type assertion:\n\t%T does not implement %T (wrong type for %S method)\n"
"\t\thave %S%hhT\n\t\twant %S%hhT", n->type, t, missing->sym,
have->sym, have->type, missing->sym, missing->type);
else if(ptr)
yyerror("impossible type assertion:\n\t%T does not implement %T (%S method has pointer receiver)",
n->type, t, missing->sym);
else if(have)
yyerror("impossible type assertion:\n\t%T does not implement %T (missing %S method)\n"
"\t\thave %S%hhT\n\t\twant %S%hhT", n->type, t, missing->sym,
have->sym, have->type, missing->sym, missing->type);
else
yyerror("impossible type assertion:\n\t%T does not implement %T (missing %S method)",
n->type, t, missing->sym);
goto error;
}
goto ret;
case OINDEX:
ok |= Erv;
typecheck(&n->left, Erv);
defaultlit(&n->left, T);
implicitstar(&n->left);
l = n->left;
typecheck(&n->right, Erv);
r = n->right;
if((t = l->type) == T || r->type == T)
goto error;
switch(t->etype) {
default:
yyerror("invalid operation: %N (type %T does not support indexing)", n, t);
goto error;
case TSTRING:
case TARRAY:
indexlit(&n->right);
if(t->etype == TSTRING)
n->type = types[TUINT8];
else
n->type = t->type;
why = "string";
if(t->etype == TARRAY) {
if(isfixedarray(t))
why = "array";
else
why = "slice";
}
if(n->right->type != T && !isint[n->right->type->etype]) {
yyerror("non-integer %s index %N", why, n->right);
break;
}
if(isconst(n->right, CTINT)) {
x = mpgetfix(n->right->val.u.xval);
if(x < 0)
yyerror("invalid %s index %N (index must be non-negative)", why, n->right);
else if(isfixedarray(t) && t->bound > 0 && x >= t->bound)
yyerror("invalid array index %N (out of bounds for %d-element array)", n->right, t->bound);
else if(isconst(n->left, CTSTR) && x >= n->left->val.u.sval->len)
yyerror("invalid string index %N (out of bounds for %d-byte string)", n->right, n->left->val.u.sval->len);
else if(mpcmpfixfix(n->right->val.u.xval, maxintval[TINT]) > 0)
yyerror("invalid %s index %N (index too large)", why, n->right);
}
break;
case TMAP:
n->etype = 0;
defaultlit(&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;
}
goto ret;
case ORECV:
ok |= Etop | Erv;
typecheck(&n->left, Erv);
defaultlit(&n->left, T);
l = n->left;
if((t = l->type) == T)
goto error;
if(t->etype != TCHAN) {
yyerror("invalid operation: %N (receive from non-chan type %T)", n, t);
goto error;
}
if(!(t->chan & Crecv)) {
yyerror("invalid operation: %N (receive from send-only type %T)", n, t);
goto error;
}
n->type = t->type;
goto ret;
case OSEND:
ok |= Etop;
l = typecheck(&n->left, Erv);
typecheck(&n->right, Erv);
defaultlit(&n->left, T);
l = n->left;
if((t = l->type) == T)
goto error;
if(t->etype != TCHAN) {
yyerror("invalid operation: %N (send to non-chan type %T)", n, t);
goto error;
}
if(!(t->chan & Csend)) {
yyerror("invalid operation: %N (send to receive-only type %T)", n, t);
goto error;
}
defaultlit(&n->right, t->type);
r = n->right;
if(r->type == T)
goto error;
n->right = assignconv(r, l->type->type, "send");
// TODO: more aggressive
n->etype = 0;
n->type = T;
goto ret;
case OSLICE:
ok |= Erv;
typecheck(&n->left, top);
typecheck(&n->right->left, Erv);
typecheck(&n->right->right, Erv);
defaultlit(&n->left, T);
indexlit(&n->right->left);
indexlit(&n->right->right);
l = n->left;
if(isfixedarray(l->type)) {
if(!islvalue(n->left)) {
yyerror("invalid operation %N (slice of unaddressable value)", n);
goto error;
}
n->left = nod(OADDR, n->left, N);
n->left->implicit = 1;
typecheck(&n->left, Erv);
l = n->left;
}
if((t = l->type) == T)
goto error;
tp = nil;
if(istype(t, TSTRING)) {
n->type = t;
n->op = OSLICESTR;
} else if(isptr[t->etype] && isfixedarray(t->type)) {
tp = t->type;
n->type = typ(TARRAY);
n->type->type = tp->type;
n->type->bound = -1;
dowidth(n->type);
n->op = OSLICEARR;
} else if(isslice(t)) {
n->type = t;
} else {
yyerror("cannot slice %N (type %T)", l, t);
goto error;
}
if((lo = n->right->left) != N && checksliceindex(l, lo, tp) < 0)
goto error;
if((hi = n->right->right) != N && checksliceindex(l, hi, tp) < 0)
goto error;
if(checksliceconst(lo, hi) < 0)
goto error;
goto ret;
case OSLICE3:
ok |= Erv;
typecheck(&n->left, top);
typecheck(&n->right->left, Erv);
typecheck(&n->right->right->left, Erv);
typecheck(&n->right->right->right, Erv);
defaultlit(&n->left, T);
indexlit(&n->right->left);
indexlit(&n->right->right->left);
indexlit(&n->right->right->right);
l = n->left;
if(isfixedarray(l->type)) {
if(!islvalue(n->left)) {
yyerror("invalid operation %N (slice of unaddressable value)", n);
goto error;
}
n->left = nod(OADDR, n->left, N);
n->left->implicit = 1;
typecheck(&n->left, Erv);
l = n->left;
}
if((t = l->type) == T)
goto error;
tp = nil;
if(istype(t, TSTRING)) {
yyerror("invalid operation %N (3-index slice of string)", n);
goto error;
}
if(isptr[t->etype] && isfixedarray(t->type)) {
tp = t->type;
n->type = typ(TARRAY);
n->type->type = tp->type;
n->type->bound = -1;
dowidth(n->type);
n->op = OSLICE3ARR;
} else if(isslice(t)) {
n->type = t;
} else {
yyerror("cannot slice %N (type %T)", l, t);
goto error;
}
if((lo = n->right->left) != N && checksliceindex(l, lo, tp) < 0)
goto error;
if((mid = n->right->right->left) != N && checksliceindex(l, mid, tp) < 0)
goto error;
if((hi = n->right->right->right) != N && checksliceindex(l, hi, tp) < 0)
goto error;
if(checksliceconst(lo, hi) < 0 || checksliceconst(lo, mid) < 0 || checksliceconst(mid, hi) < 0)
goto error;
goto ret;
/*
* call and call like
*/
case OCALL:
l = n->left;
if(l->op == ONAME && (r = unsafenmagic(n)) != N) {
if(n->isddd)
yyerror("invalid use of ... with builtin %N", l);
n = r;
goto reswitch;
}
typecheck(&n->left, Erv | Etype | Ecall |(top&Eproc));
n->diag |= n->left->diag;
l = n->left;
if(l->op == ONAME && l->etype != 0) {
if(n->isddd && l->etype != OAPPEND)
yyerror("invalid use of ... with builtin %N", l);
// builtin: OLEN, OCAP, etc.
n->op = l->etype;
n->left = n->right;
n->right = N;
goto reswitch;
}
defaultlit(&n->left, T);
l = n->left;
if(l->op == OTYPE) {
if(n->isddd || l->type->bound == -100) {
if(!l->type->broke)
yyerror("invalid use of ... in type conversion", l);
n->diag = 1;
}
// pick off before type-checking arguments
ok |= Erv;
// turn CALL(type, arg) into CONV(arg) w/ type
n->left = N;
n->op = OCONV;
n->type = l->type;
if(onearg(n, "conversion to %T", l->type) < 0)
goto error;
goto doconv;
}
if(count(n->list) == 1 && !n->isddd)
typecheck(&n->list->n, Erv | Efnstruct);
else
typechecklist(n->list, Erv);
if((t = l->type) == T)
goto error;
checkwidth(t);
switch(l->op) {
case ODOTINTER:
n->op = OCALLINTER;
break;
case ODOTMETH:
n->op = OCALLMETH;
// typecheckaste was used here but there wasn't enough
// information further down the call chain to know if we
// were testing a method receiver for unexported fields.
// It isn't necessary, so just do a sanity check.
tp = getthisx(t)->type->type;
if(l->left == N || !eqtype(l->left->type, tp))
fatal("method receiver");
break;
default:
n->op = OCALLFUNC;
if(t->etype != TFUNC) {
yyerror("cannot call non-function %N (type %T)", l, t);
goto error;
}
break;
}
if(snprint(descbuf, sizeof descbuf, "argument to %N", n->left) < sizeof descbuf)
desc = descbuf;
else
desc = "function argument";
typecheckaste(OCALL, n->left, n->isddd, getinargx(t), n->list, desc);
ok |= Etop;
if(t->outtuple == 0)
goto ret;
ok |= Erv;
if(t->outtuple == 1) {
t = getoutargx(l->type)->type;
if(t == T)
goto error;
if(t->etype == TFIELD)
t = t->type;
n->type = t;
goto ret;
}
// multiple return
if(!(top & (Efnstruct | Etop))) {
yyerror("multiple-value %N() in single-value context", l);
goto ret;
}
n->type = getoutargx(l->type);
goto ret;
case OCAP:
case OLEN:
case OREAL:
case OIMAG:
ok |= Erv;
if(onearg(n, "%O", n->op) < 0)
goto error;
typecheck(&n->left, Erv);
defaultlit(&n->left, T);
implicitstar(&n->left);
l = n->left;
t = l->type;
if(t == T)
goto error;
switch(n->op) {
case OCAP:
if(!okforcap[t->etype])
goto badcall1;
break;
case OLEN:
if(!okforlen[t->etype])
goto badcall1;
break;
case OREAL:
case OIMAG:
if(!iscomplex[t->etype])
goto badcall1;
if(isconst(l, CTCPLX)){
r = n;
if(n->op == OREAL)
n = nodfltconst(&l->val.u.cval->real);
else
n = nodfltconst(&l->val.u.cval->imag);
n->orig = r;
}
n->type = types[cplxsubtype(t->etype)];
goto ret;
}
// might be constant
switch(t->etype) {
case TSTRING:
if(isconst(l, CTSTR)) {
r = nod(OXXX, N, N);
nodconst(r, types[TINT], l->val.u.sval->len);
r->orig = n;
n = r;
}
break;
case TARRAY:
if(t->bound < 0) // slice
break;
if(callrecv(l)) // has call or receive
break;
r = nod(OXXX, N, N);
nodconst(r, types[TINT], t->bound);
r->orig = n;
n = r;
break;
}
n->type = types[TINT];
goto ret;
case OCOMPLEX:
ok |= Erv;
if(count(n->list) == 1) {
typechecklist(n->list, Efnstruct);
if(n->list->n->op != OCALLFUNC && n->list->n->op != OCALLMETH) {
yyerror("invalid operation: complex expects two arguments");
goto error;
}
t = n->list->n->left->type;
if(t->outtuple != 2) {
yyerror("invalid operation: complex expects two arguments, %N returns %d results", n->list->n, t->outtuple);
goto error;
}
t = n->list->n->type->type;
l = t->nname;
r = t->down->nname;
} else {
if(twoarg(n) < 0)
goto error;
l = typecheck(&n->left, Erv | (top & Eiota));
r = typecheck(&n->right, Erv | (top & Eiota));
if(l->type == T || r->type == T)
goto error;
defaultlit2(&l, &r, 0);
if(l->type == T || r->type == T)
goto error;
n->left = l;
n->right = r;
}
if(!eqtype(l->type, r->type)) {
yyerror("invalid operation: %N (mismatched types %T and %T)", n, l->type, r->type);
goto error;
}
switch(l->type->etype) {
default:
yyerror("invalid operation: %N (arguments have type %T, expected floating-point)", n, l->type, r->type);
goto error;
case TIDEAL:
t = types[TIDEAL];
break;
case TFLOAT32:
t = types[TCOMPLEX64];
break;
case TFLOAT64:
t = types[TCOMPLEX128];
break;
}
if(l->op == OLITERAL && r->op == OLITERAL) {
// make it a complex literal
r = nodcplxlit(l->val, r->val);
r->orig = n;
n = r;
}
n->type = t;
goto ret;
case OCLOSE:
if(onearg(n, "%O", n->op) < 0)
goto error;
typecheck(&n->left, Erv);
defaultlit(&n->left, T);
l = n->left;
if((t = l->type) == T)
goto error;
if(t->etype != TCHAN) {
yyerror("invalid operation: %N (non-chan type %T)", n, t);
goto error;
}
if(!(t->chan & Csend)) {
yyerror("invalid operation: %N (cannot close receive-only channel)", n);
goto error;
}
ok |= Etop;
goto ret;
case ODELETE:
args = n->list;
if(args == nil) {
yyerror("missing arguments to delete");
goto error;
}
if(args->next == nil) {
yyerror("missing second (key) argument to delete");
goto error;
}
if(args->next->next != nil) {
yyerror("too many arguments to delete");
goto error;
}
ok |= Etop;
typechecklist(args, Erv);
l = args->n;
r = args->next->n;
if(l->type != T && l->type->etype != TMAP) {
yyerror("first argument to delete must be map; have %lT", l->type);
goto error;
}
args->next->n = assignconv(r, l->type->down, "delete");
goto ret;
case OAPPEND:
ok |= Erv;
args = n->list;
if(args == nil) {
yyerror("missing arguments to append");
goto error;
}
if(count(args) == 1 && !n->isddd)
typecheck(&args->n, Erv | Efnstruct);
else
typechecklist(args, Erv);
if((t = args->n->type) == T)
goto error;
// Unpack multiple-return result before type-checking.
if(istype(t, TSTRUCT)) {
t = t->type;
if(istype(t, TFIELD))
t = t->type;
}
n->type = t;
if(!isslice(t)) {
if(isconst(args->n, CTNIL)) {
yyerror("first argument to append must be typed slice; have untyped nil", t);
goto error;
}
yyerror("first argument to append must be slice; have %lT", t);
goto error;
}
if(n->isddd) {
if(args->next == nil) {
yyerror("cannot use ... on first argument to append");
goto error;
}
if(args->next->next != nil) {
yyerror("too many arguments to append");
goto error;
}
if(istype(t->type, TUINT8) && istype(args->next->n->type, TSTRING)) {
defaultlit(&args->next->n, types[TSTRING]);
goto ret;
}
args->next->n = assignconv(args->next->n, t->orig, "append");
goto ret;
}
for(args=args->next; args != nil; args=args->next) {
if(args->n->type == T)
continue;
args->n = assignconv(args->n, t->type, "append");
}
goto ret;
case OCOPY:
ok |= Etop|Erv;
args = n->list;
if(args == nil || args->next == nil) {
yyerror("missing arguments to copy");
goto error;
}
if(args->next->next != nil) {
yyerror("too many arguments to copy");
goto error;
}
n->left = args->n;
n->right = args->next->n;
n->list = nil;
n->type = types[TINT];
typecheck(&n->left, Erv);
typecheck(&n->right, Erv);
if(n->left->type == T || n->right->type == T)
goto error;
defaultlit(&n->left, T);
defaultlit(&n->right, T);
if(n->left->type == T || n->right->type == T)
goto error;
// copy([]byte, string)
if(isslice(n->left->type) && n->right->type->etype == TSTRING) {
if(eqtype(n->left->type->type, bytetype))
goto ret;
yyerror("arguments to copy have different element types: %lT and string", n->left->type);
goto error;
}
if(!isslice(n->left->type) || !isslice(n->right->type)) {
if(!isslice(n->left->type) && !isslice(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 slice; have %lT", n->left->type);
else
yyerror("second argument to copy should be slice or string; have %lT", n->right->type);
goto error;
}
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;
case OCONV:
doconv:
ok |= Erv;
saveorignode(n);
typecheck(&n->left, Erv | (top & (Eindir | Eiota)));
convlit1(&n->left, n->type, 1);
if((t = n->left->type) == T || n->type == T)
goto error;
if((n->op = convertop(t, n->type, &why)) == 0) {
if(!n->diag && !n->type->broke) {
yyerror("cannot convert %lN to type %T%s", n->left, n->type, why);
n->diag = 1;
}
n->op = OCONV;
}
switch(n->op) {
case OCONVNOP:
if(n->left->op == OLITERAL && n->type != types[TBOOL]) {
r = nod(OXXX, N, N);
n->op = OCONV;
n->orig = r;
*r = *n;
n->op = OLITERAL;
n->val = n->left->val;
}
break;
case OSTRARRAYBYTE:
// do not use stringtoarraylit.
// generated code and compiler memory footprint is better without it.
break;
case OSTRARRAYRUNE:
if(n->left->op == OLITERAL)
stringtoarraylit(&n);
break;
}
goto ret;
case OMAKE:
ok |= Erv;
args = n->list;
if(args == nil) {
yyerror("missing argument to make");
goto error;
}
n->list = nil;
l = args->n;
args = args->next;
typecheck(&l, Etype);
if((t = l->type) == T)
goto error;
switch(t->etype) {
default:
badmake:
yyerror("cannot make type %T", t);
goto error;
case TARRAY:
if(!isslice(t))
goto badmake;
if(args == nil) {
yyerror("missing len argument to make(%T)", t);
goto error;
}
l = args->n;
args = args->next;
typecheck(&l, Erv);
r = N;
if(args != nil) {
r = args->n;
args = args->next;
typecheck(&r, Erv);
}
if(l->type == T || (r && r->type == T))
goto error;
et = checkmake(t, "len", l) < 0;
et |= r && checkmake(t, "cap", r) < 0;
if(et)
goto error;
if(isconst(l, CTINT) && r && isconst(r, CTINT) && mpcmpfixfix(l->val.u.xval, r->val.u.xval) > 0) {
yyerror("len larger than cap in make(%T)", t);
goto error;
}
n->left = l;
n->right = r;
n->op = OMAKESLICE;
break;
case TMAP:
if(args != nil) {
l = args->n;
args = args->next;
typecheck(&l, Erv);
defaultlit(&l, types[TINT]);
if(l->type == T)
goto error;
if(checkmake(t, "size", l) < 0)
goto error;
n->left = l;
} else
n->left = nodintconst(0);
n->op = OMAKEMAP;
break;
case TCHAN:
l = N;
if(args != nil) {
l = args->n;
args = args->next;
typecheck(&l, Erv);
defaultlit(&l, types[TINT]);
if(l->type == T)
goto error;
if(checkmake(t, "buffer", l) < 0)
goto error;
n->left = l;
} else
n->left = nodintconst(0);
n->op = OMAKECHAN;
break;
}
if(args != nil) {
yyerror("too many arguments to make(%T)", t);
n->op = OMAKE;
goto error;
}
n->type = t;
goto ret;
case ONEW:
ok |= Erv;
args = n->list;
if(args == nil) {
yyerror("missing argument to new");
goto error;
}
l = args->n;
typecheck(&l, Etype);
if((t = l->type) == T)
goto error;
if(args->next != nil) {
yyerror("too many arguments to new(%T)", t);
goto error;
}
n->left = l;
n->type = ptrto(t);
goto ret;
case OPRINT:
case OPRINTN:
ok |= Etop;
typechecklist(n->list, Erv | Eindir); // Eindir: address does not escape
for(args=n->list; args; args=args->next) {
// Special case for print: int constant is int64, not int.
if(isconst(args->n, CTINT))
defaultlit(&args->n, types[TINT64]);
else
defaultlit(&args->n, T);
}
goto ret;
case OPANIC:
ok |= Etop;
if(onearg(n, "panic") < 0)
goto error;
typecheck(&n->left, Erv);
defaultlit(&n->left, types[TINTER]);
if(n->left->type == T)
goto error;
goto ret;
case ORECOVER:
ok |= Erv|Etop;
if(n->list != nil) {
yyerror("too many arguments to recover");
goto error;
}
n->type = types[TINTER];
goto ret;
case OCLOSURE:
ok |= Erv;
typecheckclosure(n, top);
if(n->type == T)
goto error;
goto ret;
case OITAB:
ok |= Erv;
typecheck(&n->left, Erv);
if((t = n->left->type) == T)
goto error;
if(t->etype != TINTER)
fatal("OITAB of %T", t);
n->type = ptrto(types[TUINTPTR]);
goto ret;
case OSPTR:
ok |= Erv;
typecheck(&n->left, Erv);
if((t = n->left->type) == T)
goto error;
if(!isslice(t) && t->etype != TSTRING)
fatal("OSPTR of %T", t);
if(t->etype == TSTRING)
n->type = ptrto(types[TUINT8]);
else
n->type = ptrto(t->type);
goto ret;
case OCLOSUREVAR:
ok |= Erv;
goto ret;
case OCFUNC:
ok |= Erv;
typecheck(&n->left, Erv);
n->type = types[TUINTPTR];
goto ret;
case OCONVNOP:
ok |= Erv;
typecheck(&n->left, Erv);
goto ret;
/*
* statements
*/
case OAS:
ok |= Etop;
typecheckas(n);
goto ret;
case OAS2:
ok |= Etop;
typecheckas2(n);
goto ret;
case OBREAK:
case OCONTINUE:
case ODCL:
case OEMPTY:
case OGOTO:
case OLABEL:
case OXFALL:
case OVARKILL:
ok |= Etop;
goto ret;
case ODEFER:
ok |= Etop;
typecheck(&n->left, Etop|Erv);
if(!n->left->diag)
checkdefergo(n);
goto ret;
case OPROC:
ok |= Etop;
typecheck(&n->left, Etop|Eproc|Erv);
checkdefergo(n);
goto ret;
case OFOR:
ok |= Etop;
typechecklist(n->ninit, Etop);
typecheck(&n->ntest, Erv);
if(n->ntest != N && (t = n->ntest->type) != T && t->etype != TBOOL)
yyerror("non-bool %lN used as for condition", n->ntest);
typecheck(&n->nincr, Etop);
typechecklist(n->nbody, Etop);
goto ret;
case OIF:
ok |= Etop;
typechecklist(n->ninit, Etop);
typecheck(&n->ntest, Erv);
if(n->ntest != N && (t = n->ntest->type) != T && t->etype != TBOOL)
yyerror("non-bool %lN used as if condition", n->ntest);
typechecklist(n->nbody, Etop);
typechecklist(n->nelse, Etop);
goto ret;
case ORETURN:
ok |= Etop;
if(count(n->list) == 1)
typechecklist(n->list, Erv | Efnstruct);
else
typechecklist(n->list, Erv);
if(curfn == N) {
yyerror("return outside function");
goto error;
}
if(curfn->type->outnamed && n->list == nil)
goto ret;
typecheckaste(ORETURN, nil, 0, getoutargx(curfn->type), n->list, "return argument");
goto ret;
case ORETJMP:
ok |= Etop;
goto ret;
case OSELECT:
ok |= Etop;
typecheckselect(n);
goto ret;
case OSWITCH:
ok |= Etop;
typecheckswitch(n);
goto ret;
case ORANGE:
ok |= Etop;
typecheckrange(n);
goto ret;
case OTYPESW:
yyerror("use of .(type) outside type switch");
goto error;
case OXCASE:
ok |= Etop;
typechecklist(n->list, Erv);
typechecklist(n->nbody, Etop);
goto ret;
case ODCLFUNC:
ok |= Etop;
typecheckfunc(n);
goto ret;
case ODCLCONST:
ok |= Etop;
typecheck(&n->left, Erv);
goto ret;
case ODCLTYPE:
ok |= Etop;
typecheck(&n->left, Etype);
if(!incannedimport)
checkwidth(n->left->type);
goto ret;
}
ret:
t = n->type;
if(t && !t->funarg && n->op != OTYPE) {
switch(t->etype) {
case TFUNC: // might have TANY; wait until its called
case TANY:
case TFORW:
case TIDEAL:
case TNIL:
case TBLANK:
break;
default:
checkwidth(t);
}
}
if(safemode && !incannedimport && !importpkg && !compiling_wrappers && t && t->etype == TUNSAFEPTR)
yyerror("cannot use unsafe.Pointer");
evconst(n);
if(n->op == OTYPE && !(top & Etype)) {
yyerror("type %T is not an expression", n->type);
goto error;
}
if((top & (Erv|Etype)) == Etype && n->op != OTYPE) {
yyerror("%N is not a type", n);
goto error;
}
// TODO(rsc): simplify
if((top & (Ecall|Erv|Etype)) && !(top & Etop) && !(ok & (Erv|Etype|Ecall))) {
yyerror("%N used as value", n);
goto error;
}
if((top & Etop) && !(top & (Ecall|Erv|Etype)) && !(ok & Etop)) {
if(n->diag == 0) {
yyerror("%N evaluated but not used", n);
n->diag = 1;
}
goto error;
}
/* TODO
if(n->type == T)
fatal("typecheck nil type");
*/
goto out;
badcall1:
yyerror("invalid argument %lN for %O", n->left, n->op);
goto error;
error:
n->type = T;
out:
*np = n;
}
static int
checksliceindex(Node *l, Node *r, Type *tp)
{
Type *t;
if((t = r->type) == T)
return -1;
if(!isint[t->etype]) {
yyerror("invalid slice index %N (type %T)", r, t);
return -1;
}
if(r->op == OLITERAL) {
if(mpgetfix(r->val.u.xval) < 0) {
yyerror("invalid slice index %N (index must be non-negative)", r);
return -1;
} else if(tp != nil && tp->bound > 0 && mpgetfix(r->val.u.xval) > tp->bound) {
yyerror("invalid slice index %N (out of bounds for %d-element array)", r, tp->bound);
return -1;
} else if(isconst(l, CTSTR) && mpgetfix(r->val.u.xval) > l->val.u.sval->len) {
yyerror("invalid slice index %N (out of bounds for %d-byte string)", r, l->val.u.sval->len);
return -1;
} else if(mpcmpfixfix(r->val.u.xval, maxintval[TINT]) > 0) {
yyerror("invalid slice index %N (index too large)", r);
return -1;
}
}
return 0;
}
static int
checksliceconst(Node *lo, Node *hi)
{
if(lo != N && hi != N && lo->op == OLITERAL && hi->op == OLITERAL
&& mpcmpfixfix(lo->val.u.xval, hi->val.u.xval) > 0) {
yyerror("invalid slice index: %N > %N", lo, hi);
return -1;
}
return 0;
}
static void
checkdefergo(Node *n)
{
char *what;
what = "defer";
if(n->op == OPROC)
what = "go";
switch(n->left->op) {
case OCALLINTER:
case OCALLMETH:
case OCALLFUNC:
case OCLOSE:
case OCOPY:
case ODELETE:
case OPANIC:
case OPRINT:
case OPRINTN:
case ORECOVER:
// ok
break;
case OAPPEND:
case OCAP:
case OCOMPLEX:
case OIMAG:
case OLEN:
case OMAKE:
case OMAKESLICE:
case OMAKECHAN:
case OMAKEMAP:
case ONEW:
case OREAL:
case OLITERAL: // conversion or unsafe.Alignof, Offsetof, Sizeof
if(n->left->orig != N && n->left->orig->op == OCONV)
goto conv;
yyerror("%s discards result of %N", what, n->left);
break;
default:
conv:
// type is broken or missing, most likely a method call on a broken type
// we will warn about the broken type elsewhere. no need to emit a potentially confusing error
if(n->left->type == T || n->left->type->broke)
break;
if(!n->diag) {
// The syntax made sure it was a call, so this must be
// a conversion.
n->diag = 1;
yyerror("%s requires function call, not conversion", what);
}
break;
}
}
static void
implicitstar(Node **nn)
{
Type *t;
Node *n;
// insert implicit * if needed for fixed array
n = *nn;
t = n->type;
if(t == T || !isptr[t->etype])
return;
t = t->type;
if(t == T)
return;
if(!isfixedarray(t))
return;
n = nod(OIND, n, N);
n->implicit = 1;
typecheck(&n, Erv);
*nn = n;
}
static int
onearg(Node *n, char *f, ...)
{
va_list arg;
char *p;
if(n->left != N)
return 0;
if(n->list == nil) {
va_start(arg, f);
p = vsmprint(f, arg);
va_end(arg);
yyerror("missing argument to %s: %N", p, n);
return -1;
}
if(n->list->next != nil) {
va_start(arg, f);
p = vsmprint(f, arg);
va_end(arg);
yyerror("too many arguments to %s: %N", p, n);
n->left = n->list->n;
n->list = nil;
return -1;
}
n->left = n->list->n;
n->list = nil;
return 0;
}
static int
twoarg(Node *n)
{
if(n->left != N)
return 0;
if(n->list == nil) {
yyerror("missing argument to %O - %N", n->op, n);
return -1;
}
n->left = n->list->n;
if(n->list->next == nil) {
yyerror("missing argument to %O - %N", n->op, n);
n->list = nil;
return -1;
}
if(n->list->next->next != nil) {
yyerror("too many arguments to %O - %N", n->op, n);
n->list = nil;
return -1;
}
n->right = n->list->next->n;
n->list = nil;
return 0;
}
static Type*
lookdot1(Node *errnode, Sym *s, Type *t, Type *f, int dostrcmp)
{
Type *r;
r = T;
for(; f!=T; f=f->down) {
if(dostrcmp && strcmp(f->sym->name, s->name) == 0)
return f;
if(f->sym != s)
continue;
if(r != T) {
if(errnode)
yyerror("ambiguous selector %N", errnode);
else if(isptr[t->etype])
yyerror("ambiguous selector (%T).%S", t, s);
else
yyerror("ambiguous selector %T.%S", t, s);
break;
}
r = f;
}
return r;
}
static int
looktypedot(Node *n, Type *t, int dostrcmp)
{
Type *f1, *f2;
Sym *s;
s = n->right->sym;
if(t->etype == TINTER) {
f1 = lookdot1(n, s, t, t->type, dostrcmp);
if(f1 == T)
return 0;
n->right = methodname(n->right, t);
n->xoffset = f1->width;
n->type = f1->type;
n->op = ODOTINTER;
return 1;
}
// Find the base type: methtype will fail if t
// is not of the form T or *T.
f2 = methtype(t, 0);
if(f2 == T)
return 0;
expandmeth(f2);
f2 = lookdot1(n, s, f2, f2->xmethod, dostrcmp);
if(f2 == T)
return 0;
// disallow T.m if m requires *T receiver
if(isptr[getthisx(f2->type)->type->type->etype]
&& !isptr[t->etype]
&& f2->embedded != 2
&& !isifacemethod(f2->type)) {
yyerror("invalid method expression %N (needs pointer receiver: (*%T).%hS)", n, t, f2->sym);
return 0;
}
n->right = methodname(n->right, t);
n->xoffset = f2->width;
n->type = f2->type;
n->op = ODOTMETH;
return 1;
}
static Type*
derefall(Type* t)
{
while(t && t->etype == tptr)
t = t->type;
return t;
}
static int
lookdot(Node *n, Type *t, int dostrcmp)
{
Type *f1, *f2, *tt, *rcvr;
Sym *s;
s = n->right->sym;
dowidth(t);
f1 = T;
if(t->etype == TSTRUCT || t->etype == TINTER)
f1 = lookdot1(n, s, t, t->type, dostrcmp);
f2 = T;
if(n->left->type == t || n->left->type->sym == S) {
f2 = methtype(t, 0);
if(f2 != T) {
// Use f2->method, not f2->xmethod: adddot has
// already inserted all the necessary embedded dots.
f2 = lookdot1(n, s, f2, f2->method, dostrcmp);
}
}
if(f1 != T) {
if(f2 != T)
yyerror("%S is both field and method",
n->right->sym);
if(f1->width == BADWIDTH)
fatal("lookdot badwidth %T %p", f1, f1);
n->xoffset = f1->width;
n->type = f1->type;
n->paramfld = f1;
if(t->etype == TINTER) {
if(isptr[n->left->type->etype]) {
n->left = nod(OIND, n->left, N); // implicitstar
n->left->implicit = 1;
typecheck(&n->left, Erv);
}
n->op = ODOTINTER;
}
return 1;
}
if(f2 != T) {
tt = n->left->type;
dowidth(tt);
rcvr = getthisx(f2->type)->type->type;
if(!eqtype(rcvr, tt)) {
if(rcvr->etype == tptr && eqtype(rcvr->type, tt)) {
checklvalue(n->left, "call pointer method on");
n->left = nod(OADDR, n->left, N);
n->left->implicit = 1;
typecheck(&n->left, Etype|Erv);
} else if(tt->etype == tptr && rcvr->etype != tptr && eqtype(tt->type, rcvr)) {
n->left = nod(OIND, n->left, N);
n->left->implicit = 1;
typecheck(&n->left, Etype|Erv);
} else if(tt->etype == tptr && tt->type->etype == tptr && eqtype(derefall(tt), derefall(rcvr))) {
yyerror("calling method %N with receiver %lN requires explicit dereference", n->right, n->left);
while(tt->etype == tptr) {
// Stop one level early for method with pointer receiver.
if(rcvr->etype == tptr && tt->type->etype != tptr)
break;
n->left = nod(OIND, n->left, N);
n->left->implicit = 1;
typecheck(&n->left, Etype|Erv);
tt = tt->type;
}
} else {
fatal("method mismatch: %T for %T", rcvr, tt);
}
}
n->right = methodname(n->right, n->left->type);
n->xoffset = f2->width;
n->type = f2->type;
// print("lookdot found [%p] %T\n", f2->type, f2->type);
n->op = ODOTMETH;
return 1;
}
return 0;
}
static int
nokeys(NodeList *l)
{
for(; l; l=l->next)
if(l->n->op == OKEY)
return 0;
return 1;
}
static int
hasddd(Type *t)
{
Type *tl;
for(tl=t->type; tl; tl=tl->down) {
if(tl->isddd)
return 1;
}
return 0;
}
static int
downcount(Type *t)
{
Type *tl;
int n;
n = 0;
for(tl=t->type; tl; tl=tl->down) {
n++;
}
return n;
}
/*
* typecheck assignment: type list = expression list
*/
static void
typecheckaste(int op, Node *call, int isddd, Type *tstruct, NodeList *nl, char *desc)
{
Type *t, *tl, *tn;
Node *n;
int lno;
char *why;
int n1, n2;
lno = lineno;
if(tstruct->broke)
goto out;
n = N;
if(nl != nil && nl->next == nil && (n = nl->n)->type != T)
if(n->type->etype == TSTRUCT && n->type->funarg) {
if(!hasddd(tstruct)) {
n1 = downcount(tstruct);
n2 = downcount(n->type);
if(n2 > n1)
goto toomany;
if(n2 < n1)
goto notenough;
}
tn = n->type->type;
for(tl=tstruct->type; tl; tl=tl->down) {
if(tl->isddd) {
for(; tn; tn=tn->down) {
if(assignop(tn->type, tl->type->type, &why) == 0) {
if(call != N)
yyerror("cannot use %T as type %T in argument to %N%s", tn->type, tl->type->type, call, why);
else
yyerror("cannot use %T as type %T in %s%s", tn->type, tl->type->type, desc, why);
}
}
goto out;
}
if(tn == T)
goto notenough;
if(assignop(tn->type, tl->type, &why) == 0) {
if(call != N)
yyerror("cannot use %T as type %T in argument to %N%s", tn->type, tl->type, call, why);
else
yyerror("cannot use %T as type %T in %s%s", tn->type, tl->type, desc, why);
}
tn = tn->down;
}
if(tn != T)
goto toomany;
goto out;
}
n1 = downcount(tstruct);
n2 = count(nl);
if(!hasddd(tstruct)) {
if(n2 > n1)
goto toomany;
if(n2 < n1)
goto notenough;
}
else {
if(!isddd) {
if(n2 < n1-1)
goto notenough;
} else {
if(n2 > n1)
goto toomany;
if(n2 < n1)
goto notenough;
}
}
for(tl=tstruct->type; tl; tl=tl->down) {
t = tl->type;
if(tl->isddd) {
if(isddd) {
if(nl == nil)
goto notenough;
if(nl->next != nil)
goto toomany;
n = nl->n;
setlineno(n);
if(n->type != T)
nl->n = assignconv(n, t, desc);
goto out;
}
for(; nl; nl=nl->next) {
n = nl->n;
setlineno(nl->n);
if(n->type != T)
nl->n = assignconv(n, t->type, desc);
}
goto out;
}
if(nl == nil)
goto notenough;
n = nl->n;
setlineno(n);
if(n->type != T)
nl->n = assignconv(n, t, desc);
nl = nl->next;
}
if(nl != nil)
goto toomany;
if(isddd) {
if(call != N)
yyerror("invalid use of ... in call to %N", call);
else
yyerror("invalid use of ... in %O", op);
}
out:
lineno = lno;
return;
notenough:
if(n == N || !n->diag) {
if(call != N)
yyerror("not enough arguments in call to %N", call);
else
yyerror("not enough arguments to %O", op);
if(n != N)
n->diag = 1;
}
goto out;
toomany:
if(call != N)
yyerror("too many arguments in call to %N", call);
else
yyerror("too many arguments to %O", op);
goto out;
}
/*
* type check composite
*/
static void
fielddup(Node *n, Node *hash[], ulong nhash)
{
uint h;
char *s;
Node *a;
if(n->op != ONAME)
fatal("fielddup: not ONAME");
s = n->sym->name;
h = stringhash(s)%nhash;
for(a=hash[h]; a!=N; a=a->ntest) {
if(strcmp(a->sym->name, s) == 0) {
yyerror("duplicate field name in struct literal: %s", s);
return;
}
}
n->ntest = hash[h];
hash[h] = n;
}
static void
keydup(Node *n, Node *hash[], ulong nhash)
{
uint h;
ulong b;
double d;
int i;
Node *a, *orign;
Node cmp;
char *s;
orign = n;
if(n->op == OCONVIFACE)
n = n->left;
evconst(n);
if(n->op != OLITERAL)
return; // we dont check variables
switch(n->val.ctype) {
default: // unknown, bool, nil
b = 23;
break;
case CTINT:
case CTRUNE:
b = mpgetfix(n->val.u.xval);
break;
case CTFLT:
d = mpgetflt(n->val.u.fval);
s = (char*)&d;
b = 0;
for(i=sizeof(d); i>0; i--)
b = b*PRIME1 + *s++;
break;
case CTSTR:
b = 0;
s = n->val.u.sval->s;
for(i=n->val.u.sval->len; i>0; i--)
b = b*PRIME1 + *s++;
break;
}
h = b%nhash;
memset(&cmp, 0, sizeof(cmp));
for(a=hash[h]; a!=N; a=a->ntest) {
cmp.op = OEQ;
cmp.left = n;
b = 0;
if(a->op == OCONVIFACE && orign->op == OCONVIFACE) {
if(eqtype(a->left->type, n->type)) {
cmp.right = a->left;
evconst(&cmp);
b = cmp.val.u.bval;
}
} else if(eqtype(a->type, n->type)) {
cmp.right = a;
evconst(&cmp);
b = cmp.val.u.bval;
}
if(b) {
yyerror("duplicate key %N in map literal", n);
return;
}
}
orign->ntest = hash[h];
hash[h] = orign;
}
static void
indexdup(Node *n, Node *hash[], ulong nhash)
{
uint h;
Node *a;
ulong b, c;
if(n->op != OLITERAL)
fatal("indexdup: not OLITERAL");
b = mpgetfix(n->val.u.xval);
h = b%nhash;
for(a=hash[h]; a!=N; a=a->ntest) {
c = mpgetfix(a->val.u.xval);
if(b == c) {
yyerror("duplicate index in array literal: %ld", b);
return;
}
}
n->ntest = hash[h];
hash[h] = n;
}
static int
prime(ulong h, ulong sr)
{
ulong n;
for(n=3; n<=sr; n+=2)
if(h%n == 0)
return 0;
return 1;
}
static ulong
inithash(Node *n, Node ***hash, Node **autohash, ulong nautohash)
{
ulong h, sr;
NodeList *ll;
int i;
// count the number of entries
h = 0;
for(ll=n->list; ll; ll=ll->next)
h++;
// if the auto hash table is
// large enough use it.
if(h <= nautohash) {
*hash = autohash;
memset(*hash, 0, nautohash * sizeof(**hash));
return nautohash;
}
// make hash size odd and 12% larger than entries
h += h/8;
h |= 1;
// calculate sqrt of h
sr = h/2;
for(i=0; i<5; i++)
sr = (sr + h/sr)/2;
// check for primeality
while(!prime(h, sr))
h += 2;
// build and return a throw-away hash table
*hash = mal(h * sizeof(**hash));
memset(*hash, 0, h * sizeof(**hash));
return h;
}
static int
iscomptype(Type *t)
{
switch(t->etype) {
case TARRAY:
case TSTRUCT:
case TMAP:
return 1;
case TPTR32:
case TPTR64:
switch(t->type->etype) {
case TARRAY:
case TSTRUCT:
case TMAP:
return 1;
}
break;
}
return 0;
}
static void
pushtype(Node *n, Type *t)
{
if(n == N || n->op != OCOMPLIT || !iscomptype(t))
return;
if(n->right == N) {
n->right = typenod(t);
n->implicit = 1; // don't print
n->right->implicit = 1; // * is okay
}
else if(debug['s']) {
typecheck(&n->right, Etype);
if(n->right->type != T && eqtype(n->right->type, t))
print("%L: redundant type: %T\n", n->lineno, t);
}
}
static void
typecheckcomplit(Node **np)
{
int bad, i, nerr;
int64 len;
Node *l, *n, *norig, *r, **hash;
NodeList *ll;
Type *t, *f;
Sym *s, *s1;
int32 lno;
ulong nhash;
Node *autohash[101];
n = *np;
lno = lineno;
if(n->right == N) {
if(n->list != nil)
setlineno(n->list->n);
yyerror("missing type in composite literal");
goto error;
}
// Save original node (including n->right)
norig = nod(n->op, N, N);
*norig = *n;
setlineno(n->right);
l = typecheck(&n->right /* sic */, Etype|Ecomplit);
if((t = l->type) == T)
goto error;
nerr = nerrors;
n->type = t;
if(isptr[t->etype]) {
// For better or worse, we don't allow pointers as the composite literal type,
// except when using the &T syntax, which sets implicit on the OIND.
if(!n->right->implicit) {
yyerror("invalid pointer type %T for composite literal (use &%T instead)", t, t->type);
goto error;
}
// Also, the underlying type must be a struct, map, slice, or array.
if(!iscomptype(t)) {
yyerror("invalid pointer type %T for composite literal", t);
goto error;
}
t = t->type;
}
switch(t->etype) {
default:
yyerror("invalid type for composite literal: %T", t);
n->type = T;
break;
case TARRAY:
nhash = inithash(n, &hash, autohash, nelem(autohash));
len = 0;
i = 0;
for(ll=n->list; ll; ll=ll->next) {
l = ll->n;
setlineno(l);
if(l->op != OKEY) {
l = nod(OKEY, nodintconst(i), l);
l->left->type = types[TINT];
l->left->typecheck = 1;
ll->n = l;
}
typecheck(&l->left, Erv);
evconst(l->left);
i = nonnegconst(l->left);
if(i < 0 && !l->left->diag) {
yyerror("array index must be non-negative integer constant");
l->left->diag = 1;
i = -(1<<30); // stay negative for a while
}
if(i >= 0)
indexdup(l->left, hash, nhash);
i++;
if(i > len) {
len = i;
if(t->bound >= 0 && len > t->bound) {
setlineno(l);
yyerror("array index %lld out of bounds [0:%lld]", len-1, t->bound);
t->bound = -1; // no more errors
}
}
r = l->right;
pushtype(r, t->type);
typecheck(&r, Erv);
defaultlit(&r, t->type);
l->right = assignconv(r, t->type, "array element");
}
if(t->bound == -100)
t->bound = len;
if(t->bound < 0)
n->right = nodintconst(len);
n->op = OARRAYLIT;
break;
case TMAP:
nhash = inithash(n, &hash, autohash, nelem(autohash));
for(ll=n->list; ll; ll=ll->next) {
l = ll->n;
setlineno(l);
if(l->op != OKEY) {
typecheck(&ll->n, Erv);
yyerror("missing key in map literal");
continue;
}
typecheck(&l->left, Erv);
defaultlit(&l->left, t->down);
l->left = assignconv(l->left, t->down, "map key");
if (l->left->op != OCONV)
keydup(l->left, hash, nhash);
r = l->right;
pushtype(r, t->type);
typecheck(&r, Erv);
defaultlit(&r, t->type);
l->right = assignconv(r, t->type, "map value");
}
n->op = OMAPLIT;
break;
case TSTRUCT:
bad = 0;
if(n->list != nil && nokeys(n->list)) {
// simple list of variables
f = t->type;
for(ll=n->list; ll; ll=ll->next) {
setlineno(ll->n);
typecheck(&ll->n, Erv);
if(f == nil) {
if(!bad++)
yyerror("too many values in struct initializer");
continue;
}
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);
// No pushtype allowed here. Must name fields for that.
ll->n = assignconv(ll->n, f->type, "field value");
ll->n = nod(OKEY, newname(f->sym), ll->n);
ll->n->left->type = f;
ll->n->left->typecheck = 1;
f = f->down;
}
if(f != nil)
yyerror("too few values in struct initializer");
} else {
nhash = inithash(n, &hash, autohash, nelem(autohash));
// keyed list
for(ll=n->list; ll; ll=ll->next) {
l = ll->n;
setlineno(l);
if(l->op != OKEY) {
if(!bad++)
yyerror("mixture of field:value and value initializers");
typecheck(&ll->n, Erv);
continue;
}
s = l->left->sym;
if(s == S) {
yyerror("invalid field name %N in struct initializer", l->left);
typecheck(&l->right, Erv);
continue;
}
// Sym might have resolved to name in other top-level
// package, because of import dot. Redirect to correct sym
// before we do the lookup.
if(s->pkg != localpkg && exportname(s->name)) {
s1 = lookup(s->name);
if(s1->origpkg == s->pkg)
s = s1;
}
f = lookdot1(nil, s, t, t->type, 0);
if(f == nil) {
yyerror("unknown %T field '%S' in struct literal", t, s);
continue;
}
l->left = newname(s);
l->left->typecheck = 1;
l->left->type = f;
s = f->sym;
fielddup(newname(s), hash, nhash);
r = l->right;
// No pushtype allowed here. Tried and rejected.
typecheck(&r, Erv);
l->right = assignconv(r, f->type, "field value");
}
}
n->op = OSTRUCTLIT;
break;
}
if(nerr != nerrors)
goto error;
n->orig = norig;
if(isptr[n->type->etype]) {
n = nod(OPTRLIT, n, N);
n->typecheck = 1;
n->type = n->left->type;
n->left->type = t;
n->left->typecheck = 1;
}
n->orig = norig;
*np = n;
lineno = lno;
return;
error:
n->type = T;
*np = n;
lineno = lno;
}
/*
* lvalue etc
*/
int
islvalue(Node *n)
{
switch(n->op) {
case OINDEX:
if(isfixedarray(n->left->type))
return islvalue(n->left);
if(n->left->type != T && n->left->type->etype == TSTRING)
return 0;
// fall through
case OIND:
case ODOTPTR:
case OCLOSUREVAR:
case OPARAM:
return 1;
case ODOT:
return islvalue(n->left);
case ONAME:
if(n->class == PFUNC)
return 0;
return 1;
}
return 0;
}
static void
checklvalue(Node *n, char *verb)
{
if(!islvalue(n))
yyerror("cannot %s %N", verb, n);
}
static void
checkassign(Node *n)
{
if(islvalue(n))
return;
if(n->op == OINDEXMAP) {
n->etype = 1;
return;
}
// have already complained about n being undefined
if(n->op == ONONAME)
return;
yyerror("cannot assign to %N", n);
}
static void
checkassignlist(NodeList *l)
{
for(; l; l=l->next)
checkassign(l->n);
}
// Check whether l and r are the same side effect-free expression,
// so that it is safe to reuse one instead of computing both.
int
samesafeexpr(Node *l, Node *r)
{
if(l->op != r->op || !eqtype(l->type, r->type))
return 0;
switch(l->op) {
case ONAME:
case OCLOSUREVAR:
return l == r;
case ODOT:
case ODOTPTR:
return l->right != nil && r->right != nil && l->right->sym == r->right->sym && samesafeexpr(l->left, r->left);
case OIND:
return samesafeexpr(l->left, r->left);
case OINDEX:
return samesafeexpr(l->left, r->left) && samesafeexpr(l->right, r->right);
}
return 0;
}
/*
* type check assignment.
* if this assignment is the definition of a var on the left side,
* fill in the var's type.
*/
static void
typecheckas(Node *n)
{
// delicate little dance.
// the definition of n may refer to this assignment
// as its definition, in which case it will call typecheckas.
// in that case, do not call typecheck back, or it will cycle.
// if the variable has a type (ntype) then typechecking
// will not look at defn, so it is okay (and desirable,
// so that the conversion below happens).
n->left = resolve(n->left);
if(n->left->defn != n || n->left->ntype)
typecheck(&n->left, Erv | Easgn);
checkassign(n->left);
typecheck(&n->right, Erv);
if(n->right && n->right->type != T) {
if(n->left->type != T)
n->right = assignconv(n->right, n->left->type, "assignment");
}
if(n->left->defn == n && n->left->ntype == N) {
defaultlit(&n->right, T);
n->left->type = n->right->type;
}
// second half of dance.
// now that right is done, typecheck the left
// just to get it over with. see dance above.
n->typecheck = 1;
if(n->left->typecheck == 0)
typecheck(&n->left, Erv | Easgn);
// Recognize slices being updated in place, for better code generation later.
// Don't rewrite if using race detector, to avoid needing to teach race detector
// about this optimization.
if(n->left && n->left->op != OINDEXMAP && n->right && !flag_race) {
switch(n->right->op) {
case OSLICE:
case OSLICE3:
case OSLICESTR:
// For x = x[0:y], x can be updated in place, without touching pointer.
// TODO(rsc): Reenable once it is actually updated in place without touching the pointer.
if(0 && samesafeexpr(n->left, n->right->left) && (n->right->right->left == N || iszero(n->right->right->left)))
n->right->reslice = 1;
break;
case OAPPEND:
// For x = append(x, ...), x can be updated in place when there is capacity,
// without touching the pointer; otherwise the emitted code to growslice
// can take care of updating the pointer, and only in that case.
// TODO(rsc): Reenable once the emitted code does update the pointer.
if(0 && n->right->list != nil && samesafeexpr(n->left, n->right->list->n))
n->right->reslice = 1;
break;
}
}
}
static void
checkassignto(Type *src, Node *dst)
{
char *why;
if(assignop(src, dst->type, &why) == 0) {
yyerror("cannot assign %T to %lN in multiple assignment%s", src, dst, why);
return;
}
}
static void
typecheckas2(Node *n)
{
int cl, cr;
NodeList *ll, *lr;
Node *l, *r;
Iter s;
Type *t;
for(ll=n->list; ll; ll=ll->next) {
// delicate little dance.
ll->n = resolve(ll->n);
if(ll->n->defn != n || ll->n->ntype)
typecheck(&ll->n, Erv | Easgn);
}
cl = count(n->list);
cr = count(n->rlist);
checkassignlist(n->list);
if(cl > 1 && cr == 1)
typecheck(&n->rlist->n, Erv | Efnstruct);
else
typechecklist(n->rlist, Erv);
if(cl == cr) {
// 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 = 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;
}
}
goto out;
}
l = n->list->n;
r = n->rlist->n;
// x,y,z = f()
if(cr == 1) {
if(r->type == T)
goto out;
switch(r->op) {
case OCALLMETH:
case OCALLINTER:
case OCALLFUNC:
if(r->type->etype != TSTRUCT || r->type->funarg == 0)
break;
cr = structcount(r->type);
if(cr != cl)
goto mismatch;
n->op = OAS2FUNC;
t = structfirst(&s, &r->type);
for(ll=n->list; ll; ll=ll->next) {
if(t->type != T && ll->n->type != T)
checkassignto(t->type, ll->n);
if(ll->n->defn == n && ll->n->ntype == N)
ll->n->type = t->type;
t = structnext(&s);
}
goto out;
}
}
// x, ok = y
if(cl == 2 && cr == 1) {
if(r->type == T)
goto out;
switch(r->op) {
case OINDEXMAP:
n->op = OAS2MAPR;
goto common;
case ORECV:
n->op = OAS2RECV;
goto common;
case ODOTTYPE:
n->op = OAS2DOTTYPE;
r->op = ODOTTYPE2;
common:
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 && l->type->etype != TBOOL)
checkassignto(types[TBOOL], l);
if(l->defn == n && l->ntype == N)
l->type = types[TBOOL];
goto out;
}
}
mismatch:
yyerror("assignment count mismatch: %d = %d", cl, cr);
out:
// second half of dance
n->typecheck = 1;
for(ll=n->list; ll; ll=ll->next)
if(ll->n->typecheck == 0)
typecheck(&ll->n, Erv | Easgn);
}
/*
* type check function definition
*/
static void
typecheckfunc(Node *n)
{
Type *t, *rcvr;
typecheck(&n->nname, Erv | Easgn);
if((t = n->nname->type) == T)
return;
n->type = t;
t->nname = n->nname;
rcvr = getthisx(t)->type;
if(rcvr != nil && n->shortname != N && !isblank(n->shortname))
addmethod(n->shortname->sym, t, 1, n->nname->nointerface);
}
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 []rune
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;
}
static int ntypecheckdeftype;
static NodeList *methodqueue;
static void
domethod(Node *n)
{
Node *nt;
Type *t;
nt = n->type->nname;
typecheck(&nt, Etype);
if(nt->type == T) {
// type check failed; leave empty func
n->type->etype = TFUNC;
n->type->nod = N;
return;
}
// If we have
// type I interface {
// M(_ int)
// }
// then even though I.M looks like it doesn't care about the
// value of its argument, a specific implementation of I may
// care. The _ would suppress the assignment to that argument
// while generating a call, so remove it.
for(t=getinargx(nt->type)->type; t; t=t->down) {
if(t->sym != nil && strcmp(t->sym->name, "_") == 0)
t->sym = nil;
}
*n->type = *nt->type;
n->type->nod = N;
checkwidth(n->type);
}
static NodeList *mapqueue;
void
copytype(Node *n, Type *t)
{
int maplineno, embedlineno, lno;
NodeList *l;
if(t->etype == TFORW) {
// This type isn't computed yet; when it is, update n.
t->copyto = list(t->copyto, n);
return;
}
maplineno = n->type->maplineno;
embedlineno = n->type->embedlineno;
l = n->type->copyto;
*n->type = *t;
t = n->type;
t->sym = n->sym;
t->local = n->local;
t->vargen = n->vargen;
t->siggen = 0;
t->method = nil;
t->xmethod = nil;
t->nod = N;
t->printed = 0;
t->deferwidth = 0;
t->copyto = nil;
// Update nodes waiting on this type.
for(; l; l=l->next)
copytype(l->n, t);
// Double-check use of type as embedded type.
lno = lineno;
if(embedlineno) {
lineno = embedlineno;
if(isptr[t->etype])
yyerror("embedded type cannot be a pointer");
}
lineno = lno;
// Queue check for map until all the types are done settling.
if(maplineno) {
t->maplineno = maplineno;
mapqueue = list(mapqueue, n);
}
}
static void
typecheckdeftype(Node *n)
{
int lno;
Type *t;
NodeList *l;
ntypecheckdeftype++;
lno = lineno;
setlineno(n);
n->type->sym = n->sym;
n->typecheck = 1;
typecheck(&n->ntype, Etype);
if((t = n->ntype->type) == T) {
n->diag = 1;
n->type = T;
goto ret;
}
if(n->type == T) {
n->diag = 1;
goto ret;
}
// copy new type and clear fields
// that don't come along.
// anything zeroed here must be zeroed in
// typedcl2 too.
copytype(n, t);
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.
if(ntypecheckdeftype == 1) {
while((l = methodqueue) != nil) {
methodqueue = nil;
for(; l; l=l->next)
domethod(l->n);
}
for(l=mapqueue; l; l=l->next) {
lineno = l->n->type->maplineno;
maptype(l->n->type, types[TBOOL]);
}
lineno = lno;
}
ntypecheckdeftype--;
}
void
queuemethod(Node *n)
{
if(ntypecheckdeftype == 0) {
domethod(n);
return;
}
methodqueue = list(methodqueue, n);
}
Node*
typecheckdef(Node *n)
{
int lno, nerrors0;
Node *e;
Type *t;
NodeList *l;
lno = lineno;
setlineno(n);
if(n->op == ONONAME) {
if(!n->diag) {
n->diag = 1;
if(n->lineno != 0)
lineno = n->lineno;
yyerror("undefined: %S", n->sym);
}
return n;
}
if(n->walkdef == 1)
return n;
l = mal(sizeof *l);
l->n = n;
l->next = typecheckdefstack;
typecheckdefstack = l;
if(n->walkdef == 2) {
flusherrors();
print("typecheckdef loop:");
for(l=typecheckdefstack; l; l=l->next)
print(" %S", l->n->sym);
print("\n");
fatal("typecheckdef loop");
}
n->walkdef = 2;
if(n->type != T || n->sym == S) // builtin or no name
goto ret;
switch(n->op) {
default:
fatal("typecheckdef %O", n->op);
case OGOTO:
case OLABEL:
// not really syms
break;
case OLITERAL:
if(n->ntype != N) {
typecheck(&n->ntype, Etype);
n->type = n->ntype->type;
n->ntype = N;
if(n->type == T) {
n->diag = 1;
goto ret;
}
}
e = n->defn;
n->defn = N;
if(e == N) {
lineno = n->lineno;
dump("typecheckdef nil defn", n);
yyerror("xxx");
}
typecheck(&e, Erv | Eiota);
if(isconst(e, CTNIL)) {
yyerror("const initializer cannot be nil");
goto ret;
}
if(e->type != T && e->op != OLITERAL || !isgoconst(e)) {
if(!e->diag) {
yyerror("const initializer %N is not a constant", e);
e->diag = 1;
}
goto ret;
}
t = n->type;
if(t != T) {
if(!okforconst[t->etype]) {
yyerror("invalid constant type %T", t);
goto ret;
}
if(!isideal(e->type) && !eqtype(t, e->type)) {
yyerror("cannot use %lN as type %T in const initializer", e, t);
goto ret;
}
convlit(&e, t);
}
n->val = e->val;
n->type = e->type;
break;
case ONAME:
if(n->ntype != N) {
typecheck(&n->ntype, Etype);
n->type = n->ntype->type;
if(n->type == T) {
n->diag = 1;
goto ret;
}
}
if(n->type != T)
break;
if(n->defn == N) {
if(n->etype != 0) // like OPRINTN
break;
if(nsavederrors+nerrors > 0) {
// Can have undefined variables in x := foo
// that make x have an n->ndefn == nil.
// If there are other errors anyway, don't
// bother adding to the noise.
break;
}
fatal("var without type, init: %S", n->sym);
}
if(n->defn->op == ONAME) {
typecheck(&n->defn, Erv);
n->type = n->defn->type;
break;
}
typecheck(&n->defn, Etop); // fills in n->type
break;
case OTYPE:
if(curfn)
defercheckwidth();
n->walkdef = 1;
n->type = typ(TFORW);
n->type->sym = n->sym;
nerrors0 = nerrors;
typecheckdeftype(n);
if(n->type->etype == TFORW && nerrors > nerrors0) {
// Something went wrong during type-checking,
// but it was reported. Silence future errors.
n->type->broke = 1;
}
if(curfn)
resumecheckwidth();
break;
case OPACK:
// nothing to see here
break;
}
ret:
if(n->op != OLITERAL && n->type != T && isideal(n->type))
fatal("got %T for %N", n->type, n);
if(typecheckdefstack->n != n)
fatal("typecheckdefstack mismatch");
l = typecheckdefstack;
typecheckdefstack = l->next;
lineno = lno;
n->walkdef = 1;
return n;
}
static int
checkmake(Type *t, char *arg, Node *n)
{
if(n->op == OLITERAL) {
switch(n->val.ctype) {
case CTINT:
case CTRUNE:
case CTFLT:
case CTCPLX:
n->val = toint(n->val);
if(mpcmpfixc(n->val.u.xval, 0) < 0) {
yyerror("negative %s argument in make(%T)", arg, t);
return -1;
}
if(mpcmpfixfix(n->val.u.xval, maxintval[TINT]) > 0) {
yyerror("%s argument too large in make(%T)", arg, t);
return -1;
}
// Delay defaultlit until after we've checked range, to avoid
// a redundant "constant NNN overflows int" error.
defaultlit(&n, types[TINT]);
return 0;
default:
break;
}
}
if(!isint[n->type->etype] && n->type->etype != TIDEAL) {
yyerror("non-integer %s argument in make(%T) - %T", arg, t, n->type);
return -1;
}
// Defaultlit still necessary for non-constant: n might be 1<<k.
defaultlit(&n, types[TINT]);
return 0;
}
static void markbreaklist(NodeList*, Node*);
static void
markbreak(Node *n, Node *implicit)
{
Label *lab;
if(n == N)
return;
switch(n->op) {
case OBREAK:
if(n->left == N) {
if(implicit)
implicit->hasbreak = 1;
} else {
lab = n->left->sym->label;
if(lab != L)
lab->def->hasbreak = 1;
}
break;
case OFOR:
case OSWITCH:
case OTYPESW:
case OSELECT:
case ORANGE:
implicit = n;
// fall through
default:
markbreak(n->left, implicit);
markbreak(n->right, implicit);
markbreak(n->ntest, implicit);
markbreak(n->nincr, implicit);
markbreaklist(n->ninit, implicit);
markbreaklist(n->nbody, implicit);
markbreaklist(n->nelse, implicit);
markbreaklist(n->list, implicit);
markbreaklist(n->rlist, implicit);
break;
}
}
static void
markbreaklist(NodeList *l, Node *implicit)
{
Node *n;
Label *lab;
for(; l; l=l->next) {
n = l->n;
if(n->op == OLABEL && l->next && n->defn == l->next->n) {
switch(n->defn->op) {
case OFOR:
case OSWITCH:
case OTYPESW:
case OSELECT:
case ORANGE:
lab = mal(sizeof *lab);
lab->def = n->defn;
n->left->sym->label = lab;
markbreak(n->defn, n->defn);
n->left->sym->label = L;
l = l->next;
continue;
}
}
markbreak(n, implicit);
}
}
static int
isterminating(NodeList *l, int top)
{
int def;
Node *n;
if(l == nil)
return 0;
if(top) {
while(l->next && l->n->op != OLABEL)
l = l->next;
markbreaklist(l, nil);
}
while(l->next)
l = l->next;
n = l->n;
if(n == N)
return 0;
switch(n->op) {
// NOTE: OLABEL is treated as a separate statement,
// not a separate prefix, so skipping to the last statement
// in the block handles the labeled statement case by
// skipping over the label. No case OLABEL here.
case OBLOCK:
return isterminating(n->list, 0);
case OGOTO:
case ORETURN:
case ORETJMP:
case OPANIC:
case OXFALL:
return 1;
case OFOR:
if(n->ntest != N)
return 0;
if(n->hasbreak)
return 0;
return 1;
case OIF:
return isterminating(n->nbody, 0) && isterminating(n->nelse, 0);
case OSWITCH:
case OTYPESW:
case OSELECT:
if(n->hasbreak)
return 0;
def = 0;
for(l=n->list; l; l=l->next) {
if(!isterminating(l->n->nbody, 0))
return 0;
if(l->n->list == nil) // default
def = 1;
}
if(n->op != OSELECT && !def)
return 0;
return 1;
}
return 0;
}
void
checkreturn(Node *fn)
{
if(fn->type->outtuple && fn->nbody != nil)
if(!isterminating(fn->nbody, 1))
yyerrorl(fn->endlineno, "missing return at end of function");
}