| // 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*, 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]; | |
| 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 OTPAREN: | |
| ok |= Etype; | |
| l = typecheck(&n->left, Etype); | |
| if(l->type == T) | |
| goto error; | |
| n->op = OTYPE; | |
| n->type = l->type; | |
| n->left = N; | |
| 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: | |
| 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 OADDPTR: | |
| ok |= Erv; | |
| l = typecheck(&n->left, Erv); | |
| r = typecheck(&n->right, Erv); | |
| if(l->type == T || r->type == T) | |
| goto error; | |
| if(l->type->etype != tptr) | |
| fatal("bad OADDPTR left type %E for %N", l->type->etype, n->left); | |
| if(r->type->etype != TUINTPTR) | |
| fatal("bad OADDPTR right type %E for %N", r->type->etype, n->right); | |
| n->type = types[tptr]; | |
| goto ret; | |
| 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); | |
| 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; | |
| } | |
| // 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; | |
| // top&Eindir means this is &x in *&x. (or the arg to built-in print) | |
| // n->etype means code generator flagged it as non-escaping. | |
| if(debug['N'] && !(top & Eindir) && !n->etype) | |
| addrescapes(n->left); | |
| 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)) { | |
| if(mpgetfix(n->right->val.u.xval) < 0) | |
| yyerror("invalid %s index %N (index must be non-negative)", why, n->right); | |
| else if(isfixedarray(t) && t->bound > 0 && mpgetfix(n->right->val.u.xval) >= t->bound) | |
| yyerror("invalid array index %N (out of bounds for %d-element array)", n->right, t->bound); | |
| else if(isconst(n->left, CTSTR) && mpgetfix(n->right->val.u.xval) >= 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(lo, tp) < 0) | |
| goto error; | |
| if((hi = n->right->right) != N && checksliceindex(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(lo, tp) < 0) | |
| goto error; | |
| if((mid = n->right->right->left) != N && checksliceindex(mid, tp) < 0) | |
| goto error; | |
| if((hi = n->right->right->right) != N && checksliceindex(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); | |
| 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) { | |
| 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 *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(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"); | |
| if(debug['N']) | |
| addrescapes(n->left); | |
| n->left = nod(OADDR, n->left, N); | |
| n->left->implicit = 1; | |
| typecheck(&n->left, Etype|Erv); | |
| } else if(tt->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), rcvr)) { | |
| yyerror("calling method %N with receiver %lN requires explicit dereference", n->right, n->left); | |
| while(tt->etype == tptr) { | |
| 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("%lL: 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: | |
| 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); | |
| } | |
| /* | |
| * 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); | |
| } | |
| 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; | |
| // m[i] = x, ok | |
| if(cl == 1 && cr == 2 && l->op == OINDEXMAP) { | |
| if(l->type == T) | |
| goto out; | |
| yyerror("assignment count mismatch: %d = %d (use delete)", cl, cr); | |
| goto out; | |
| } | |
| // 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) | |
| 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"); | |
| } |