root/src/cmd/gc/typecheck.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. resolve
  2. typechecklist
  3. typekind
  4. sprint_depchain
  5. typecheck
  6. callrecv
  7. callrecvlist
  8. indexlit
  9. typecheck1
  10. checksliceindex
  11. checksliceconst
  12. checkdefergo
  13. implicitstar
  14. onearg
  15. twoarg
  16. lookdot1
  17. looktypedot
  18. derefall
  19. lookdot
  20. nokeys
  21. hasddd
  22. downcount
  23. typecheckaste
  24. fielddup
  25. keydup
  26. indexdup
  27. prime
  28. inithash
  29. iscomptype
  30. pushtype
  31. typecheckcomplit
  32. islvalue
  33. checklvalue
  34. checkassign
  35. checkassignlist
  36. typecheckas
  37. checkassignto
  38. typecheckas2
  39. typecheckfunc
  40. stringtoarraylit
  41. domethod
  42. copytype
  43. typecheckdeftype
  44. queuemethod
  45. typecheckdef
  46. checkmake
  47. markbreak
  48. markbreaklist
  49. isterminating
  50. checkreturn

// 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");
}

/* [<][>][^][v][top][bottom][index][help] */