// 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. #include "go.h" #include "md5.h" #include "y.tab.h" #include "opnames.h" typedef struct Error Error; struct Error { int lineno; int seq; char *msg; }; static Error *err; static int nerr; static int merr; void errorexit(void) { flusherrors(); if(outfile) remove(outfile); exit(1); } extern int yychar; int parserline(void) { if(yychar != 0 && yychar != -2) // parser has one symbol lookahead return prevlineno; return lineno; } static void adderr(int line, char *fmt, va_list arg) { Fmt f; Error *p; fmtstrinit(&f); fmtprint(&f, "%L: ", line); fmtvprint(&f, fmt, arg); fmtprint(&f, "\n"); if(nerr >= merr) { if(merr == 0) merr = 16; else merr *= 2; p = realloc(err, merr*sizeof err[0]); if(p == nil) { merr = nerr; flusherrors(); print("out of memory\n"); errorexit(); } err = p; } err[nerr].seq = nerr; err[nerr].lineno = line; err[nerr].msg = fmtstrflush(&f); nerr++; } static int errcmp(const void *va, const void *vb) { Error *a, *b; a = (Error*)va; b = (Error*)vb; if(a->lineno != b->lineno) return a->lineno - b->lineno; if(a->seq != b->seq) return a->seq - b->seq; return 0; } void flusherrors(void) { int i; if(nerr == 0) return; qsort(err, nerr, sizeof err[0], errcmp); for(i=0; i= 10 && !debug['e']) fatal("too many errors"); } void yyerror(char *fmt, ...) { va_list arg; if(strcmp(fmt, "syntax error") == 0) { yyerrorl(lexlineno, "syntax error near %s", lexbuf); nsyntaxerrors++; return; } va_start(arg, fmt); adderr(parserline(), fmt, arg); va_end(arg); hcrash(); nerrors++; if(nerrors >= 10 && !debug['e']) fatal("too many errors"); } void warn(char *fmt, ...) { va_list arg; va_start(arg, fmt); adderr(parserline(), fmt, arg); va_end(arg); hcrash(); } void fatal(char *fmt, ...) { va_list arg; flusherrors(); print("%L: fatal error: ", lineno); va_start(arg, fmt); vfprint(1, fmt, arg); va_end(arg); print("\n"); hcrash(); errorexit(); } void linehist(char *file, int32 off, int relative) { Hist *h; char *cp; if(debug['i']) { if(file != nil) { if(off < 0) print("pragma %s at line %L\n", file, lineno); else print("import %s at line %L\n", file, lineno); } else print("end of import at line %L\n", lineno); } if(off < 0 && file[0] != '/' && !relative) { cp = mal(strlen(file) + strlen(pathname) + 2); sprint(cp, "%s/%s", pathname, file); file = cp; } h = mal(sizeof(Hist)); h->name = file; h->line = lexlineno; h->offset = off; h->link = H; if(ehist == H) { hist = h; ehist = h; return; } ehist->link = h; ehist = h; } int32 setlineno(Node *n) { int32 lno; lno = lineno; if(n != N) switch(n->op) { case ONAME: case OTYPE: case OPACK: case OLITERAL: case ONONAME: break; default: lineno = n->lineno; if(lineno == 0) { if(debug['K']) warn("setlineno: line 0"); lineno = lno; } } return lno; } uint32 stringhash(char *p) { int32 h; int c; h = 0; for(;;) { c = *p++; if(c == 0) break; h = h*PRIME1 + c; } if(h < 0) { h = -h; if(h < 0) h = 0; } return h; } Sym* lookup(char *p) { Sym *s; uint32 h; int c; h = stringhash(p) % NHASH; c = p[0]; for(s = hash[h]; s != S; s = s->link) { if(s->name[0] != c) continue; if(strcmp(s->name, p) == 0) if(s->package && strcmp(s->package, package) == 0) return s; } s = mal(sizeof(*s)); s->name = mal(strlen(p)+1); s->package = package; s->lexical = LNAME; strcpy(s->name, p); s->link = hash[h]; hash[h] = s; return s; } Sym* pkglookup(char *name, char *pkg) { Sym *s; uint32 h; int c; h = stringhash(name) % NHASH; c = name[0]; for(s = hash[h]; s != S; s = s->link) { if(s->name[0] != c) continue; if(strcmp(s->name, name) == 0) if(s->package && strcmp(s->package, pkg) == 0) return s; } s = mal(sizeof(*s)); s->name = mal(strlen(name)+1); strcpy(s->name, name); // botch - should probably try to reuse the pkg string s->package = mal(strlen(pkg)+1); strcpy(s->package, pkg); s->link = hash[h]; hash[h] = s; s->lexical = LNAME; return s; } Sym* restrictlookup(char *name, char *pkg) { if(!exportname(name) && strcmp(pkg, package) != 0) yyerror("cannot refer to %s.%s", pkg, name); return pkglookup(name, pkg); } // find all the exported symbols in package opkg // and make them available in the current package void importdot(Sym *opkg, Node *pack) { Sym *s, *s1; uint32 h; int c, n; if(strcmp(opkg->name, package) == 0) return; n = 0; c = opkg->name[0]; for(h=0; hlink) { if(s->package[0] != c) continue; if(!exportname(s->name) || utfrune(s->name, 0xb7)) // 0xb7 = center dot continue; if(strcmp(s->package, opkg->name) != 0) continue; s1 = lookup(s->name); if(s1->def != N) { redeclare(s1, "during import"); continue; } s1->def = s->def; s1->def->pack = pack; n++; } } if(n == 0) { // can't possibly be used - there were no symbols yyerrorl(pack->lineno, "imported and not used: %s", pack->sym->name); } } void gethunk(void) { char *h; int32 nh; nh = NHUNK; if(thunk >= 10L*NHUNK) nh = 10L*NHUNK; h = (char*)malloc(nh); if(h == (char*)-1) { flusherrors(); yyerror("out of memory"); errorexit(); } hunk = h; nhunk = nh; thunk += nh; } void* mal(int32 n) { void *p; while((uintptr)hunk & MAXALIGN) { hunk++; nhunk--; } while(nhunk < n) gethunk(); p = hunk; nhunk -= n; hunk += n; memset(p, 0, n); return p; } void* remal(void *p, int32 on, int32 n) { void *q; q = (uchar*)p + on; if(q != hunk || nhunk < n) { while(nhunk < on+n) gethunk(); memmove(hunk, p, on); p = hunk; hunk += on; nhunk -= on; } hunk += n; nhunk -= n; return p; } Node* nod(int op, Node *nleft, Node *nright) { Node *n; n = mal(sizeof(*n)); n->op = op; n->left = nleft; n->right = nright; n->lineno = parserline(); n->xoffset = BADWIDTH; return n; } int algtype(Type *t) { int a; if(issimple[t->etype] || isptr[t->etype] || t->etype == TCHAN || t->etype == TFUNC || t->etype == TMAP) a = AMEM; // just bytes (int, ptr, etc) else if(t->etype == TSTRING) a = ASTRING; // string else if(isnilinter(t)) a = ANILINTER; // nil interface else if(t->etype == TINTER) a = AINTER; // interface else a = ANOEQ; // just bytes, but no hash/eq return a; } Type* maptype(Type *key, Type *val) { Type *t; if(key != nil && key->etype != TANY && algtype(key) == ANOEQ) { if(key->etype == TFORW) { // map[key] used during definition of key. // postpone check until key is fully defined. // if there are multiple uses of map[key] // before key is fully defined, the error // will only be printed for the first one. // good enough. if(key->maplineno == 0) key->maplineno = lineno; } else yyerror("invalid map key type %T", key); } t = typ(TMAP); t->down = key; t->type = val; return t; } int iskeytype(Type *t) { return algtype(t) != ANOEQ; } Type* typ(int et) { Type *t; t = mal(sizeof(*t)); t->etype = et; t->width = BADWIDTH; t->lineno = lineno; return t; } Type* sortinter(Type *t) { return t; } Node* nodintconst(int64 v) { Node *c; c = nod(OLITERAL, N, N); c->addable = 1; c->val.u.xval = mal(sizeof(*c->val.u.xval)); mpmovecfix(c->val.u.xval, v); c->val.ctype = CTINT; c->type = types[TIDEAL]; ullmancalc(c); return c; } void nodconst(Node *n, Type *t, int64 v) { memset(n, 0, sizeof(*n)); n->op = OLITERAL; n->addable = 1; ullmancalc(n); n->val.u.xval = mal(sizeof(*n->val.u.xval)); mpmovecfix(n->val.u.xval, v); n->val.ctype = CTINT; n->type = t; if(isfloat[t->etype]) fatal("nodconst: bad type %T", t); } Node* nodnil(void) { Node *c; c = nodintconst(0); c->val.ctype = CTNIL; c->type = types[TNIL]; return c; } Node* nodbool(int b) { Node *c; c = nodintconst(0); c->val.ctype = CTBOOL; c->val.u.bval = b; c->type = idealbool; return c; } Type* aindex(Node *b, Type *t) { NodeList *init; Type *r; int bound; bound = -1; // open bound init = nil; typecheck(&b, Erv); if(b != nil) { switch(consttype(b)) { default: yyerror("array bound must be an integer expression"); break; case CTINT: bound = mpgetfix(b->val.u.xval); if(bound < 0) yyerror("array bound must be non negative"); break; } } // fixed array r = typ(TARRAY); r->type = t; r->bound = bound; return r; } void indent(int dep) { int i; for(i=0; inext) dodump(l->n, dep); } void dodump(Node *n, int dep) { if(n == N) return; indent(dep); if(dep > 10) { print("...\n"); return; } if(n->ninit != nil) { print("%O-init\n", n->op); dodumplist(n->ninit, dep+1); indent(dep); } switch(n->op) { default: print("%N\n", n); dodump(n->left, dep+1); dodump(n->right, dep+1); break; case OTYPE: print("%O %S type=%T\n", n->op, n->sym, n->type); if(n->type == T && n->ntype) { indent(dep); print("%O-ntype\n", n->op); dodump(n->ntype, dep+1); } break; case OIF: print("%O%J\n", n->op, n); dodump(n->ntest, dep+1); if(n->nbody != nil) { indent(dep); print("%O-then\n", n->op); dodumplist(n->nbody, dep+1); } if(n->nelse != nil) { indent(dep); print("%O-else\n", n->op); dodumplist(n->nelse, dep+1); } break; case OSELECT: print("%O%J\n", n->op, n); dodumplist(n->nbody, dep+1); break; case OSWITCH: case OFOR: print("%O%J\n", n->op, n); dodump(n->ntest, dep+1); if(n->nbody != nil) { indent(dep); print("%O-body\n", n->op); dodumplist(n->nbody, dep+1); } if(n->nincr != N) { indent(dep); print("%O-incr\n", n->op); dodump(n->nincr, dep+1); } break; case OCASE: // the right side points to label of the body if(n->right != N && n->right->op == OGOTO && n->right->left->op == ONAME) print("%O%J GOTO %N\n", n->op, n, n->right->left); else print("%O%J\n", n->op, n); dodump(n->left, dep+1); break; case OXCASE: print("%N\n", n); dodump(n->left, dep+1); dodump(n->right, dep+1); indent(dep); print("%O-nbody\n", n->op); dodumplist(n->nbody, dep+1); break; } if(0 && n->ntype != nil) { indent(dep); print("%O-ntype\n", n->op); dodump(n->ntype, dep+1); } if(n->list != nil) { indent(dep); print("%O-list\n", n->op); dodumplist(n->list, dep+1); } if(n->rlist != nil) { indent(dep); print("%O-rlist\n", n->op); dodumplist(n->rlist, dep+1); } if(n->op != OIF && n->nbody != nil) { indent(dep); print("%O-nbody\n", n->op); dodumplist(n->nbody, dep+1); } } void dumplist(char *s, NodeList *l) { print("%s\n", s); dodumplist(l, 1); } void dump(char *s, Node *n) { print("%s [%p]\n", s, n); dodump(n, 1); } static char* goopnames[] = { [OADDR] = "&", [OADD] = "+", [OANDAND] = "&&", [OANDNOT] = "&^", [OAND] = "&", [OAS] = "=", [OAS2] = "=", [OBREAK] = "break", [OCAP] = "cap", [OCASE] = "case", [OCLOSED] = "closed", [OCLOSE] = "close", [OCOM] = "^", [OCONTINUE] = "continue", [OCOPY] = "copy", [ODEC] = "--", [ODEFER] = "defer", [ODIV] = "/", [OEQ] = "==", [OFALL] = "fallthrough", [OFOR] = "for", [OGE] = ">=", [OGOTO] = "goto", [OGT] = ">", [OIF] = "if", [OINC] = "++", [OIND] = "*", [OLEN] = "len", [OLE] = "<=", [OLSH] = "<<", [OLT] = "<", [OMAKE] = "make", [OMINUS] = "-", [OMOD] = "%", [OMUL] = "*", [ONEW] = "new", [ONE] = "!=", [ONOT] = "!", [OOROR] = "||", [OOR] = "|", [OPANICN] = "panicln", [OPANIC] = "panic", [OPLUS] = "+", [OPRINTN] = "println", [OPRINT] = "print", [ORANGE] = "range", [ORECV] = "<-", [ORETURN] = "return", [ORSH] = ">>", [OSELECT] = "select", [OSEND] = "<-", [OSUB] = "-", [OSWITCH] = "switch", [OXOR] = "^", }; int Oconv(Fmt *fp) { char buf[500]; int o; o = va_arg(fp->args, int); if((fp->flags & FmtSharp) && o >= 0 && o < nelem(goopnames) && goopnames[o] != nil) return fmtstrcpy(fp, goopnames[o]); if(o < 0 || o >= nelem(opnames) || opnames[o] == nil) { snprint(buf, sizeof(buf), "O-%d", o); return fmtstrcpy(fp, buf); } return fmtstrcpy(fp, opnames[o]); } int Lconv(Fmt *fp) { char str[STRINGSZ], s[STRINGSZ]; struct { Hist* incl; /* start of this include file */ int32 idel; /* delta line number to apply to include */ Hist* line; /* start of this #line directive */ int32 ldel; /* delta line number to apply to #line */ } a[HISTSZ]; int32 lno, d; int i, n; Hist *h; lno = va_arg(fp->args, int32); n = 0; for(h=hist; h!=H; h=h->link) { if(h->offset < 0) continue; if(lno < h->line) break; if(h->name) { if(n < HISTSZ) { /* beginning of file */ a[n].incl = h; a[n].idel = h->line; a[n].line = 0; } n++; continue; } n--; if(n > 0 && n < HISTSZ) { d = h->line - a[n].incl->line; a[n-1].ldel += d; a[n-1].idel += d; } } if(n > HISTSZ) n = HISTSZ; str[0] = 0; for(i=n-1; i>=0; i--) { if(i != n-1) { if(fp->flags & ~(FmtWidth|FmtPrec)) break; strcat(str, " "); } if(a[i].line) snprint(s, STRINGSZ, "%s:%ld[%s:%ld]", a[i].line->name, lno-a[i].ldel+1, a[i].incl->name, lno-a[i].idel+1); else snprint(s, STRINGSZ, "%s:%ld", a[i].incl->name, lno-a[i].idel+1); if(strlen(s)+strlen(str) >= STRINGSZ-10) break; strcat(str, s); lno = a[i].incl->line - 1; /* now print out start of this file */ } if(n == 0) strcat(str, ""); return fmtstrcpy(fp, str); } /* s%,%,\n%g s%\n+%\n%g s%^[ ]*T%%g s%,.*%%g s%.+% [T&] = "&",%g s%^ ........*\]%&~%g s%~ %%g */ static char* etnames[] = { [TINT8] = "INT8", [TUINT8] = "UINT8", [TINT16] = "INT16", [TUINT16] = "UINT16", [TINT32] = "INT32", [TUINT32] = "UINT32", [TINT64] = "INT64", [TUINT64] = "UINT64", [TINT] = "INT", [TUINT] = "UINT", [TUINTPTR] = "UINTPTR", [TFLOAT32] = "FLOAT32", [TFLOAT64] = "FLOAT64", [TFLOAT] = "FLOAT", [TBOOL] = "BOOL", [TPTR32] = "PTR32", [TPTR64] = "PTR64", [TDDD] = "DDD", [TFUNC] = "FUNC", [TARRAY] = "ARRAY", [TSTRUCT] = "STRUCT", [TCHAN] = "CHAN", [TMAP] = "MAP", [TINTER] = "INTER", [TFORW] = "FORW", [TFIELD] = "FIELD", [TANY] = "ANY", [TSTRING] = "STRING", }; int Econv(Fmt *fp) { char buf[500]; int et; et = va_arg(fp->args, int); if(et < 0 || et >= nelem(etnames) || etnames[et] == nil) { snprint(buf, sizeof(buf), "E-%d", et); return fmtstrcpy(fp, buf); } return fmtstrcpy(fp, etnames[et]); } int Jconv(Fmt *fp) { Node *n; n = va_arg(fp->args, Node*); if(n->ullman != 0) fmtprint(fp, " u(%d)", n->ullman); if(n->addable != 0) fmtprint(fp, " a(%d)", n->addable); if(n->vargen != 0) fmtprint(fp, " g(%ld)", n->vargen); if(n->lineno != 0) fmtprint(fp, " l(%ld)", n->lineno); if(n->xoffset != 0) fmtprint(fp, " x(%lld)", n->xoffset); if(n->class != 0) fmtprint(fp, " class(%d)", n->class); if(n->colas != 0) fmtprint(fp, " colas(%d)", n->colas); if(n->funcdepth != 0) fmtprint(fp, " f(%d)", n->funcdepth); if(n->typecheck != 0) fmtprint(fp, " tc(%d)", n->typecheck); if(n->dodata != 0) fmtprint(fp, " dd(%d)", n->dodata); return 0; } int Gconv(Fmt *fp) { char buf[100]; Type *t; t = va_arg(fp->args, Type*); if(t->etype == TFUNC) { if(t->vargen != 0) { snprint(buf, sizeof(buf), "-%d%d%d g(%ld)", t->thistuple, t->outtuple, t->intuple, t->vargen); goto out; } snprint(buf, sizeof(buf), "-%d%d%d", t->thistuple, t->outtuple, t->intuple); goto out; } if(t->vargen != 0) { snprint(buf, sizeof(buf), " g(%ld)", t->vargen); goto out; } strcpy(buf, ""); out: return fmtstrcpy(fp, buf); } int Sconv(Fmt *fp) { Sym *s; char *pkg, *nam; s = va_arg(fp->args, Sym*); if(s == S) { fmtstrcpy(fp, ""); return 0; } pkg = ""; nam = pkg; if(s->package != nil) pkg = s->package; if(s->name != nil) nam = s->name; if(!(fp->flags & FmtShort)) if(strcmp(pkg, package) != 0 || (fp->flags & FmtLong)) { fmtprint(fp, "%s.%s", pkg, nam); return 0; } fmtstrcpy(fp, nam); return 0; } static char* basicnames[] = { [TINT] = "int", [TUINT] = "uint", [TINT8] = "int8", [TUINT8] = "uint8", [TINT16] = "int16", [TUINT16] = "uint16", [TINT32] = "int32", [TUINT32] = "uint32", [TINT64] = "int64", [TUINT64] = "uint64", [TUINTPTR] = "uintptr", [TFLOAT] = "float", [TFLOAT32] = "float32", [TFLOAT64] = "float64", [TBOOL] = "bool", [TANY] = "any", [TDDD] = "...", [TSTRING] = "string", [TNIL] = "nil", [TIDEAL] = "ideal", [TBLANK] = "blank", }; int Tpretty(Fmt *fp, Type *t) { Type *t1; Sym *s; if(t->etype != TFIELD && t->sym != S && !(fp->flags&FmtLong)) { s = t->sym; if(t == types[t->etype]) return fmtprint(fp, "%s", s->name); if(exporting) { if(fp->flags & FmtShort) fmtprint(fp, "%hS", s); else fmtprint(fp, "%lS", s); if(strcmp(s->package, package) != 0) return 0; if(s->flags & SymImported) return 0; if(t->vargen) fmtprint(fp, "ยท%d", t->vargen); return 0; } return fmtprint(fp, "%S", s); } if(t->etype < nelem(basicnames) && basicnames[t->etype] != nil) { if(isideal(t) && t->etype != TIDEAL && t->etype != TNIL) fmtprint(fp, "ideal "); return fmtprint(fp, "%s", basicnames[t->etype]); } switch(t->etype) { case TPTR32: case TPTR64: if(fp->flags&FmtShort) // pass flag thru for methodsym return fmtprint(fp, "*%hT", t->type); return fmtprint(fp, "*%T", t->type); case TCHAN: switch(t->chan) { case Crecv: return fmtprint(fp, "<-chan %T", t->type); case Csend: if(t->type != T && t->type->etype == TCHAN) return fmtprint(fp, "chan<- (%T)", t->type); return fmtprint(fp, "chan<- %T", t->type); } return fmtprint(fp, "chan %T", t->type); case TMAP: return fmtprint(fp, "map[%T] %T", t->down, t->type); case TFUNC: // t->type is method struct // t->type->down is result struct // t->type->down->down is arg struct if(t->thistuple && !(fp->flags&FmtSharp) && !(fp->flags&FmtShort)) { fmtprint(fp, "method("); for(t1=getthisx(t)->type; t1; t1=t1->down) { fmtprint(fp, "%T", t1); if(t1->down) fmtprint(fp, ", "); } fmtprint(fp, ")"); } if(!(fp->flags&FmtByte)) fmtprint(fp, "func"); fmtprint(fp, "("); for(t1=getinargx(t)->type; t1; t1=t1->down) { if(noargnames && t1->etype == TFIELD) fmtprint(fp, "%T", t1->type); else fmtprint(fp, "%T", t1); if(t1->down) fmtprint(fp, ", "); } fmtprint(fp, ")"); switch(t->outtuple) { case 0: break; case 1: t1 = getoutargx(t)->type; if(t1 == T) { // failure to typecheck earlier; don't know the type fmtprint(fp, " ?unknown-type?"); break; } if(t1->etype != TFIELD && t1->etype != TFUNC) { fmtprint(fp, " %T", t1); break; } default: t1 = getoutargx(t)->type; fmtprint(fp, " ("); for(; t1; t1=t1->down) { if(noargnames && t1->etype == TFIELD) fmtprint(fp, "%T", t1->type); else fmtprint(fp, "%T", t1); if(t1->down) fmtprint(fp, ", "); } fmtprint(fp, ")"); break; } return 0; case TARRAY: if(t->bound >= 0) return fmtprint(fp, "[%d]%T", (int)t->bound, t->type); if(t->bound == -100) return fmtprint(fp, "[...]%T", t->type); return fmtprint(fp, "[]%T", t->type); case TINTER: fmtprint(fp, "interface {"); for(t1=t->type; t1!=T; t1=t1->down) { fmtprint(fp, " %hS %hhT", t1->sym, t1->type); if(t1->down) fmtprint(fp, ";"); } return fmtprint(fp, " }"); case TSTRUCT: if(t->funarg) { fmtprint(fp, "("); for(t1=t->type; t1!=T; t1=t1->down) { fmtprint(fp, "%T", t1); if(t1->down) fmtprint(fp, ", "); } return fmtprint(fp, ")"); } fmtprint(fp, "struct {"); for(t1=t->type; t1!=T; t1=t1->down) { fmtprint(fp, " %T", t1); if(t1->down) fmtprint(fp, ";"); } return fmtprint(fp, " }"); case TFIELD: if(t->sym == S || t->embedded) { if(exporting) fmtprint(fp, "? "); fmtprint(fp, "%T", t->type); } else fmtprint(fp, "%hS %T", t->sym, t->type); if(t->note) fmtprint(fp, " \"%Z\"", t->note); return 0; case TFORW: if(exporting) yyerror("undefined type %S", t->sym); if(t->sym) return fmtprint(fp, "undefined %S", t->sym); return fmtprint(fp, "undefined"); } // Don't know how to handle - fall back to detailed prints. return -1; } int Tconv(Fmt *fp) { Type *t, *t1; int r, et, sharp, minus; sharp = (fp->flags & FmtSharp); minus = (fp->flags & FmtLeft); fp->flags &= ~(FmtSharp|FmtLeft); t = va_arg(fp->args, Type*); if(t == T) return fmtstrcpy(fp, ""); t->trecur++; if(t->trecur > 5) { fmtprint(fp, "..."); goto out; } if(!debug['t']) { if(sharp) exporting++; if(minus) noargnames++; r = Tpretty(fp, t); if(sharp) exporting--; if(minus) noargnames--; if(r >= 0) { t->trecur--; return 0; } } et = t->etype; fmtprint(fp, "%E ", et); if(t->sym != S) fmtprint(fp, "<%S>", t->sym); switch(et) { default: if(t->type != T) fmtprint(fp, " %T", t->type); break; case TFIELD: fmtprint(fp, "%T", t->type); break; case TFUNC: if(fp->flags & FmtLong) fmtprint(fp, "%d%d%d(%lT,%lT)%lT", t->thistuple, t->intuple, t->outtuple, t->type, t->type->down->down, t->type->down); else fmtprint(fp, "%d%d%d(%T,%T)%T", t->thistuple, t->intuple, t->outtuple, t->type, t->type->down->down, t->type->down); break; case TINTER: fmtprint(fp, "{"); if(fp->flags & FmtLong) for(t1=t->type; t1!=T; t1=t1->down) fmtprint(fp, "%lT;", t1); fmtprint(fp, "}"); break; case TSTRUCT: fmtprint(fp, "{"); if(fp->flags & FmtLong) for(t1=t->type; t1!=T; t1=t1->down) fmtprint(fp, "%lT;", t1); fmtprint(fp, "}"); break; case TMAP: fmtprint(fp, "[%T]%T", t->down, t->type); break; case TARRAY: if(t->bound >= 0) fmtprint(fp, "[%ld]%T", t->bound, t->type); else fmtprint(fp, "[]%T", t->type); break; case TPTR32: case TPTR64: fmtprint(fp, "%T", t->type); break; } out: t->trecur--; return 0; } int Nconv(Fmt *fp) { char buf1[500]; Node *n; n = va_arg(fp->args, Node*); if(n == N) { fmtprint(fp, ""); goto out; } if(fp->flags & FmtSign) { if(n->type == T) fmtprint(fp, "%#N", n); else if(n->type->etype == TNIL) fmtprint(fp, "nil"); else fmtprint(fp, "%#N (type %T)", n, n->type); goto out; } if(fp->flags & FmtSharp) { exprfmt(fp, n, 0); goto out; } switch(n->op) { default: fmtprint(fp, "%O%J", n->op, n); break; case ONAME: case ONONAME: if(n->sym == S) { fmtprint(fp, "%O%J", n->op, n); break; } fmtprint(fp, "%O-%S G%ld%J", n->op, n->sym, n->vargen, n); goto ptyp; case OREGISTER: fmtprint(fp, "%O-%R%J", n->op, n->val.u.reg, n); break; case OLITERAL: switch(n->val.ctype) { default: snprint(buf1, sizeof(buf1), "LITERAL-ctype=%d", n->val.ctype); break; case CTINT: snprint(buf1, sizeof(buf1), "I%B", n->val.u.xval); break; case CTFLT: snprint(buf1, sizeof(buf1), "F%g", mpgetflt(n->val.u.fval)); break; case CTSTR: snprint(buf1, sizeof(buf1), "S\"%Z\"", n->val.u.sval); break; case CTBOOL: snprint(buf1, sizeof(buf1), "B%d", n->val.u.bval); break; case CTNIL: snprint(buf1, sizeof(buf1), "N"); break; } fmtprint(fp, "%O-%s%J", n->op, buf1, n); break; case OASOP: fmtprint(fp, "%O-%O%J", n->op, n->etype, n); break; case OTYPE: fmtprint(fp, "%O %T", n->op, n->type); break; } if(n->sym != S) fmtprint(fp, " %S G%ld", n->sym, n->vargen); ptyp: if(n->type != T) fmtprint(fp, " %T", n->type); out: return 0; } Node* treecopy(Node *n) { Node *m; if(n == N) return N; switch(n->op) { default: m = nod(OXXX, N, N); *m = *n; m->left = treecopy(n->left); m->right = treecopy(n->right); m->list = listtreecopy(n->list); if(m->defn) abort(); break; case ONONAME: if(n->iota) { m = nod(OIOTA, n, nodintconst(iota)); break; } // fall through case OLITERAL: case ONAME: case OTYPE: m = n; break; } return m; } int Zconv(Fmt *fp) { Rune r; Strlit *sp; char *s, *se; sp = va_arg(fp->args, Strlit*); if(sp == nil) return fmtstrcpy(fp, ""); s = sp->s; se = s + sp->len; while(s < se) { s += chartorune(&r, s); switch(r) { default: if(r < ' ') { fmtprint(fp, "\\x%02x", r); break; } fmtrune(fp, r); break; case '\t': fmtstrcpy(fp, "\\t"); break; case '\n': fmtstrcpy(fp, "\\n"); break; case '\"': case '\\': fmtrune(fp, '\\'); fmtrune(fp, r); break; } } return 0; } int isnil(Node *n) { if(n == N) return 0; if(n->op != OLITERAL) return 0; if(n->val.ctype != CTNIL) return 0; return 1; } int isptrto(Type *t, int et) { if(t == T) return 0; if(!isptr[t->etype]) return 0; t = t->type; if(t == T) return 0; if(t->etype != et) return 0; return 1; } int istype(Type *t, int et) { return t != T && t->etype == et; } int isfixedarray(Type *t) { return t != T && t->etype == TARRAY && t->bound >= 0; } int isslice(Type *t) { return t != T && t->etype == TARRAY && t->bound < 0; } int isblank(Node *n) { char *p; if(n == N || n->sym == S) return 0; p = n->sym->name; if(p == nil) return 0; return p[0] == '_' && p[1] == '\0'; } int isselect(Node *n) { Sym *s; if(n == N) return 0; n = n->left; s = pkglookup("selectsend", "runtime"); if(s == n->sym) return 1; s = pkglookup("selectrecv", "runtime"); if(s == n->sym) return 1; s = pkglookup("selectdefault", "runtime"); if(s == n->sym) return 1; return 0; } int isinter(Type *t) { if(t != T) { if(t->etype == TINTER) return 1; if(t->etype == TDDD) return 1; } return 0; } int isnilinter(Type *t) { if(!isinter(t)) return 0; if(t->type != T) return 0; return 1; } int isddd(Type *t) { if(t != T && t->etype == TDDD) return 1; return 0; } int isideal(Type *t) { if(t == T) return 0; return t == idealstring || t == idealbool || t->etype == TNIL || t->etype == TIDEAL; } /* * given receiver of type t (t == r or t == *r) * return type to hang methods off (r). */ Type* methtype(Type *t) { int ptr; if(t == T) return T; // strip away pointer if it's there ptr = 0; if(isptr[t->etype]) { if(t->sym != S) return T; ptr = 1; t = t->type; if(t == T) return T; } // need a type name if(t->sym == S) return T; // check types if(!issimple[t->etype]) switch(t->etype) { default: return T; case TSTRUCT: case TARRAY: case TMAP: case TCHAN: case TSTRING: case TFUNC: break; } return t; } int iscomposite(Type *t) { if(t == T) return 0; switch(t->etype) { case TARRAY: case TSTRUCT: case TMAP: return 1; } return 0; } int eqtype1(Type *t1, Type *t2, int d, int names) { if(d >= 20) return 1; if(t1 == t2) return 1; if(t1 == T || t2 == T) return 0; if(t1->etype != t2->etype) return 0; if(names && t1->etype != TFIELD && t1->sym && t2->sym && t1 != t2) return 0; switch(t1->etype) { case TINTER: case TSTRUCT: t1 = t1->type; t2 = t2->type; for(;;) { if(!eqtype1(t1, t2, d+1, names)) return 0; if(t1 == T) return 1; if(t1->embedded != t2->embedded) return 0; if(t1->nname != N && t1->nname->sym != S) { if(t2->nname == N || t2->nname->sym == S) return 0; if(strcmp(t1->nname->sym->name, t2->nname->sym->name) != 0) return 0; } t1 = t1->down; t2 = t2->down; } return 1; case TFUNC: // Loop over structs: receiver, in, out. t1 = t1->type; t2 = t2->type; for(;;) { Type *ta, *tb; if(t1 == t2) break; if(t1 == T || t2 == T) return 0; if(t1->etype != TSTRUCT || t2->etype != TSTRUCT) return 0; // Loop over fields in structs, checking type only. ta = t1->type; tb = t2->type; while(ta != tb) { if(ta == T || tb == T) return 0; if(ta->etype != TFIELD || tb->etype != TFIELD) return 0; if(!eqtype1(ta->type, tb->type, d+1, names)) return 0; ta = ta->down; tb = tb->down; } t1 = t1->down; t2 = t2->down; } return 1; case TARRAY: if(t1->bound != t2->bound) return 0; break; case TCHAN: if(t1->chan != t2->chan) return 0; break; case TMAP: if(!eqtype1(t1->down, t2->down, d+1, names)) return 0; break; } return eqtype1(t1->type, t2->type, d+1, names); } int eqtype(Type *t1, Type *t2) { return eqtype1(t1, t2, 0, 1); } /* * can we convert from type src to dst with * a trivial conversion (no bits changing)? */ int cvttype(Type *dst, Type *src) { return eqtype1(dst, src, 0, 0); } int eqtypenoname(Type *t1, Type *t2) { if(t1 == T || t2 == T || t1->etype != TSTRUCT || t2->etype != TSTRUCT) return eqtype(t1, t2); t1 = t1->type; t2 = t2->type; for(;;) { if(!eqtype(t1, t2)) return 0; if(t1 == T) return 1; t1 = t1->down; t2 = t2->down; } } static int subtype(Type **stp, Type *t, int d) { Type *st; loop: st = *stp; if(st == T) return 0; d++; if(d >= 10) return 0; switch(st->etype) { default: return 0; case TPTR32: case TPTR64: case TCHAN: case TARRAY: stp = &st->type; goto loop; case TANY: if(!st->copyany) return 0; *stp = t; break; case TMAP: if(subtype(&st->down, t, d)) break; stp = &st->type; goto loop; case TFUNC: for(;;) { if(subtype(&st->type, t, d)) break; if(subtype(&st->type->down->down, t, d)) break; if(subtype(&st->type->down, t, d)) break; return 0; } break; case TSTRUCT: for(st=st->type; st!=T; st=st->down) if(subtype(&st->type, t, d)) return 1; return 0; } return 1; } /* * Is this a 64-bit type? */ int is64(Type *t) { if(t == T) return 0; switch(simtype[t->etype]) { case TINT64: case TUINT64: case TPTR64: return 1; } return 0; } /* * Is a conversion between t1 and t2 a no-op? */ int noconv(Type *t1, Type *t2) { int e1, e2; e1 = simtype[t1->etype]; e2 = simtype[t2->etype]; switch(e1) { case TINT8: case TUINT8: return e2 == TINT8 || e2 == TUINT8; case TINT16: case TUINT16: return e2 == TINT16 || e2 == TUINT16; case TINT32: case TUINT32: case TPTR32: return e2 == TINT32 || e2 == TUINT32 || e2 == TPTR32; case TINT64: case TUINT64: case TPTR64: return e2 == TINT64 || e2 == TUINT64 || e2 == TPTR64; case TFLOAT32: return e2 == TFLOAT32; case TFLOAT64: return e2 == TFLOAT64; } return 0; } void argtype(Node *on, Type *t) { dowidth(t); if(!subtype(&on->type, t, 0)) fatal("argtype: failed %N %T\n", on, t); } Type* shallow(Type *t) { Type *nt; if(t == T) return T; nt = typ(0); *nt = *t; return nt; } Type* deep(Type *t) { Type *nt, *xt; if(t == T) return T; switch(t->etype) { default: nt = t; // share from here down break; case TANY: nt = shallow(t); nt->copyany = 1; break; case TPTR32: case TPTR64: case TCHAN: case TARRAY: nt = shallow(t); nt->type = deep(t->type); break; case TMAP: nt = shallow(t); nt->down = deep(t->down); nt->type = deep(t->type); break; case TFUNC: nt = shallow(t); nt->type = deep(t->type); nt->type->down = deep(t->type->down); nt->type->down->down = deep(t->type->down->down); break; case TSTRUCT: nt = shallow(t); nt->type = shallow(t->type); xt = nt->type; for(t=t->type; t!=T; t=t->down) { xt->type = deep(t->type); xt->down = shallow(t->down); xt = xt->down; } break; } return nt; } Node* syslook(char *name, int copy) { Sym *s; Node *n; s = pkglookup(name, "runtime"); if(s == S || s->def == N) fatal("looksys: cant find runtime.%s", name); if(!copy) return s->def; n = nod(0, N, N); *n = *s->def; n->type = deep(s->def->type); return n; } /* * are the arg names of two * functions the same. we know * that eqtype has been called * and has returned true. */ int eqargs(Type *t1, Type *t2) { if(t1 == t2) return 1; if(t1 == T || t2 == T) return 0; if(t1->etype != t2->etype) return 0; if(t1->etype != TFUNC) fatal("eqargs: oops %E", t1->etype); t1 = t1->type; t2 = t2->type; for(;;) { if(t1 == t2) break; if(!eqtype(t1, t2)) return 0; t1 = t1->down; t2 = t2->down; } return 1; } /* * compute a hash value for type t. * if t is a method type, ignore the receiver * so that the hash can be used in interface checks. * %#-T (which calls Tpretty, above) already contains * all the necessary logic to generate a representation * of the type that completely describes it. * using smprint here avoids duplicating that code. * using md5 here is overkill, but i got tired of * accidental collisions making the runtime think * two types are equal when they really aren't. */ uint32 typehash(Type *t) { char *p; MD5 d; if(t->thistuple) { // hide method receiver from Tpretty t->thistuple = 0; p = smprint("%#-T", t); t->thistuple = 1; }else p = smprint("%#-T", t); md5reset(&d); md5write(&d, (uchar*)p, strlen(p)); free(p); return md5sum(&d); } Type* ptrto(Type *t) { Type *t1; if(tptr == 0) fatal("ptrto: nil"); t1 = typ(tptr); t1->type = t; t1->width = types[tptr]->width; return t1; } void frame(int context) { char *p; NodeList *l; Node *n; int flag; p = "stack"; l = nil; if(curfn) l = curfn->dcl; if(context) { p = "external"; l = externdcl; } flag = 1; for(; l; l=l->next) { n = l->n; switch(n->op) { case ONAME: if(flag) print("--- %s frame ---\n", p); print("%O %S G%ld T\n", n->op, n->sym, n->vargen, n->type); flag = 0; break; case OTYPE: if(flag) print("--- %s frame ---\n", p); print("%O %T\n", n->op, n->type); flag = 0; break; } } } /* * calculate sethi/ullman number * roughly how many registers needed to * compile a node. used to compile the * hardest side first to minimize registers. */ void ullmancalc(Node *n) { int ul, ur; if(n == N) return; switch(n->op) { case OREGISTER: case OLITERAL: case ONAME: ul = 1; if(n->class == PPARAMREF || (n->class & PHEAP)) ul++; goto out; case OCALL: case OCALLFUNC: case OCALLMETH: case OCALLINTER: ul = UINF; goto out; } ul = 1; if(n->left != N) ul = n->left->ullman; ur = 1; if(n->right != N) ur = n->right->ullman; if(ul == ur) ul += 1; if(ur > ul) ul = ur; out: n->ullman = ul; } void badtype(int o, Type *tl, Type *tr) { yyerror("illegal types for operand: %O", o); if(tl != T) print(" %T\n", tl); if(tr != T) print(" %T\n", tr); // common mistake: *struct and *interface. if(tl && tr && isptr[tl->etype] && isptr[tr->etype]) { if(tl->type->etype == TSTRUCT && tr->type->etype == TINTER) print(" (*struct vs *interface)\n"); else if(tl->type->etype == TINTER && tr->type->etype == TSTRUCT) print(" (*interface vs *struct)\n"); } } /* * iterator to walk a structure declaration */ Type* structfirst(Iter *s, Type **nn) { Type *n, *t; n = *nn; if(n == T) goto bad; switch(n->etype) { default: goto bad; case TSTRUCT: case TINTER: case TFUNC: break; } t = n->type; if(t == T) goto rnil; if(t->etype != TFIELD) fatal("structfirst: not field %T", t); s->t = t; return t; bad: fatal("structfirst: not struct %T", n); rnil: return T; } Type* structnext(Iter *s) { Type *n, *t; n = s->t; t = n->down; if(t == T) goto rnil; if(t->etype != TFIELD) goto bad; s->t = t; return t; bad: fatal("structnext: not struct %T", n); rnil: return T; } /* * iterator to this and inargs in a function */ Type* funcfirst(Iter *s, Type *t) { Type *fp; if(t == T) goto bad; if(t->etype != TFUNC) goto bad; s->tfunc = t; s->done = 0; fp = structfirst(s, getthis(t)); if(fp == T) { s->done = 1; fp = structfirst(s, getinarg(t)); } return fp; bad: fatal("funcfirst: not func %T", t); return T; } Type* funcnext(Iter *s) { Type *fp; fp = structnext(s); if(fp == T && !s->done) { s->done = 1; fp = structfirst(s, getinarg(s->tfunc)); } return fp; } Type** getthis(Type *t) { if(t->etype != TFUNC) fatal("getthis: not a func %T", t); return &t->type; } Type** getoutarg(Type *t) { if(t->etype != TFUNC) fatal("getoutarg: not a func %T", t); return &t->type->down; } Type** getinarg(Type *t) { if(t->etype != TFUNC) fatal("getinarg: not a func %T", t); return &t->type->down->down; } Type* getthisx(Type *t) { return *getthis(t); } Type* getoutargx(Type *t) { return *getoutarg(t); } Type* getinargx(Type *t) { return *getinarg(t); } /* * return !(op) * eg == <=> != */ int brcom(int a) { switch(a) { case OEQ: return ONE; case ONE: return OEQ; case OLT: return OGE; case OGT: return OLE; case OLE: return OGT; case OGE: return OLT; } fatal("brcom: no com for %A\n", a); return a; } /* * return reverse(op) * eg a op b <=> b r(op) a */ int brrev(int a) { switch(a) { case OEQ: return OEQ; case ONE: return ONE; case OLT: return OGT; case OGT: return OLT; case OLE: return OGE; case OGE: return OLE; } fatal("brcom: no rev for %A\n", a); return a; } Node* staticname(Type *t) { Node *n; snprint(namebuf, sizeof(namebuf), "statictmp_%.4d", statuniqgen); statuniqgen++; n = newname(lookup(namebuf)); addvar(n, t, PEXTERN); return n; } /* * return side effect-free, assignable n, appending side effects to init. */ Node* saferef(Node *n, NodeList **init) { Node *l; Node *r; Node *a; switch(n->op) { case ONAME: return n; case ODOT: l = saferef(n->left, init); if(l == n->left) return n; r = nod(OXXX, N, N); *r = *n; r->left = l; typecheck(&r, Erv); walkexpr(&r, init); return r; case OINDEX: case ODOTPTR: case OIND: l = nod(OXXX, N, N); tempname(l, ptrto(n->type)); a = nod(OAS, l, nod(OADDR, n, N)); typecheck(&a, Etop); walkexpr(&a, init); *init = list(*init, a); r = nod(OIND, l, N); typecheck(&r, Erv); walkexpr(&r, init); return r; } fatal("saferef %N", n); return N; } /* * return side effect-free n, appending side effects to init. */ Node* safeval(Node *n, NodeList **init) { Node *l; Node *a; // is this a local variable or a dot of a local variable? for(l=n; l->op == ODOT; l=l->left) if(l->left->type != T && isptr[l->left->type->etype]) goto copy; if(l->op == ONAME && (l->class == PAUTO || l->class == PPARAM)) return n; copy: l = nod(OXXX, N, N); tempname(l, n->type); a = nod(OAS, l, n); typecheck(&a, Etop); walkexpr(&a, init); *init = list(*init, a); return l; } void setmaxarg(Type *t) { int32 w; dowidth(t); w = t->argwid; if(t->argwid >= MAXWIDTH) fatal("bad argwid %T", t); if(w > maxarg) maxarg = w; } /* * code to resolve elided DOTs * in embedded types */ // search depth 0 -- // return count of fields+methods // found with a given name int lookdot0(Sym *s, Type *t, Type **save) { Type *f, *u; int c; u = t; if(isptr[u->etype]) u = u->type; c = 0; if(u->etype == TSTRUCT || u->etype == TINTER) { for(f=u->type; f!=T; f=f->down) if(f->sym == s) { if(save) *save = f; c++; } } u = methtype(t); if(u != T) { for(f=u->method; f!=T; f=f->down) if(f->sym == s && f->embedded == 0) { if(save) *save = f; c++; } } return c; } // search depth d -- // return count of fields+methods // found at search depth. // answer is in dotlist array and // count of number of ways is returned. int adddot1(Sym *s, Type *t, int d, Type **save) { Type *f, *u; int c, a; if(t->trecur) return 0; t->trecur = 1; if(d == 0) { c = lookdot0(s, t, save); goto out; } c = 0; u = t; if(isptr[u->etype]) u = u->type; if(u->etype != TSTRUCT && u->etype != TINTER) goto out; d--; for(f=u->type; f!=T; f=f->down) { if(!f->embedded) continue; if(f->sym == S) continue; a = adddot1(s, f->type, d, save); if(a != 0 && c == 0) dotlist[d].field = f; c += a; } out: t->trecur = 0; return c; } // in T.field // find missing fields that // will give shortest unique addressing. // modify the tree with missing type names. Node* adddot(Node *n) { Type *t; Sym *s; int c, d; typecheck(&n->left, Erv); t = n->left->type; if(t == T) goto ret; if(n->right->op != ONAME) goto ret; s = n->right->sym; if(s == S) goto ret; for(d=0; d 0) goto out; } goto ret; out: if(c > 1) yyerror("ambiguous DOT reference %T.%S", t, s); // rebuild elided dots for(c=d-1; c>=0; c--) n->left = nod(ODOT, n->left, newname(dotlist[c].field->sym)); ret: return n; } /* * code to help generate trampoline * functions for methods on embedded * subtypes. * these are approx the same as * the corresponding adddot routines * except that they expect to be called * with unique tasks and they return * the actual methods. */ typedef struct Symlink Symlink; struct Symlink { Type* field; uchar good; uchar followptr; Symlink* link; }; static Symlink* slist; static void expand0(Type *t, int followptr) { Type *f, *u; Symlink *sl; u = t; if(isptr[u->etype]) { followptr = 1; u = u->type; } if(u->etype == TINTER) { for(f=u->type; f!=T; f=f->down) { if(!exportname(f->sym->name) && strcmp(f->sym->package, package) != 0) continue; if(f->sym->flags & SymUniq) continue; f->sym->flags |= SymUniq; sl = mal(sizeof(*sl)); sl->field = f; sl->link = slist; sl->followptr = followptr; slist = sl; } return; } u = methtype(t); if(u != T) { for(f=u->method; f!=T; f=f->down) { if(!exportname(f->sym->name) && strcmp(f->sym->package, package) != 0) continue; if(f->sym->flags & SymUniq) continue; f->sym->flags |= SymUniq; sl = mal(sizeof(*sl)); sl->field = f; sl->link = slist; sl->followptr = followptr; slist = sl; } } } static void expand1(Type *t, int d, int followptr) { Type *f, *u; if(t->trecur) return; if(d == 0) return; t->trecur = 1; if(d != nelem(dotlist)-1) expand0(t, followptr); u = t; if(isptr[u->etype]) { followptr = 1; u = u->type; } if(u->etype != TSTRUCT && u->etype != TINTER) goto out; for(f=u->type; f!=T; f=f->down) { if(!f->embedded) continue; if(f->sym == S) continue; expand1(f->type, d-1, followptr); } out: t->trecur = 0; } void expandmeth(Sym *s, Type *t) { Symlink *sl; Type *f; int c, d; if(s == S) return; if(t == T || t->xmethod != nil) return; // generate all reachable methods slist = nil; expand1(t, nelem(dotlist)-1, 0); // check each method to be uniquely reachable for(sl=slist; sl!=nil; sl=sl->link) { sl->field->sym->flags &= ~SymUniq; for(d=0; dfield->sym, t, d, &f); if(c == 0) continue; if(c == 1) { sl->good = 1; sl->field = f; } break; } } t->xmethod = t->method; for(sl=slist; sl!=nil; sl=sl->link) { if(sl->good) { // add it to the base type method list f = typ(TFIELD); *f = *sl->field; f->embedded = 1; // needs a trampoline if(sl->followptr) f->embedded = 2; f->down = t->xmethod; t->xmethod = f; } } } /* * Given funarg struct list, return list of ODCLFIELD Node fn args. */ NodeList* structargs(Type **tl, int mustname) { Iter savet; Node *a, *n; NodeList *args; Type *t; char buf[100]; int gen; args = nil; gen = 0; for(t = structfirst(&savet, tl); t != T; t = structnext(&savet)) { n = N; if(t->sym) n = newname(t->sym); else if(mustname) { // have to give it a name so we can refer to it in trampoline snprint(buf, sizeof buf, ".anon%d", gen++); n = newname(lookup(buf)); } a = nod(ODCLFIELD, n, typenod(t->type)); args = list(args, a); } return args; } /* * Generate a wrapper function to convert from * a receiver of type T to a receiver of type U. * That is, * * func (t T) M() { * ... * } * * already exists; this function generates * * func (u U) M() { * u.M() * } * * where the types T and U are such that u.M() is valid * and calls the T.M method. * The resulting function is for use in method tables. * * rcvr - U * method - M func (t T)(), a TFIELD type struct * newnam - the eventual mangled name of this function */ void genwrapper(Type *rcvr, Type *method, Sym *newnam) { Node *this, *fn, *call, *n, *t; NodeList *l, *args, *in, *out; if(debug['r']) print("genwrapper rcvrtype=%T method=%T newnam=%S\n", rcvr, method, newnam); dclcontext = PEXTERN; markdcl(); this = nod(ODCLFIELD, newname(lookup(".this")), typenod(rcvr)); this->left->ntype = this->right; in = structargs(getinarg(method->type), 1); out = structargs(getoutarg(method->type), 0); fn = nod(ODCLFUNC, N, N); fn->nname = newname(newnam); t = nod(OTFUNC, this, N); t->list = in; t->rlist = out; fn->nname->ntype = t; funchdr(fn); // arg list args = nil; for(l=in; l; l=l->next) args = list(args, l->n->left); // generate call call = nod(OCALL, adddot(nod(OXDOT, this->left, newname(method->sym))), N); call->list = args; fn->nbody = list1(call); if(method->type->outtuple > 0) { n = nod(ORETURN, N, N); n->list = fn->nbody; fn->nbody = list1(n); } if(debug['r']) dumplist("genwrapper body", fn->nbody); funcbody(fn); typecheck(&fn, Etop); funccompile(fn); } /* * delayed interface type check. * remember that there is an interface conversion * on the given line. once the file is completely read * and all methods are known, we can check that * the conversions are valid. */ typedef struct Icheck Icheck; struct Icheck { Icheck *next; Type *dst; Type *src; int lineno; int explicit; }; Icheck *icheck; Icheck *ichecktail; void ifacecheck(Type *dst, Type *src, int lineno, int explicit) { Icheck *p; p = mal(sizeof *p); if(ichecktail) ichecktail->next = p; else icheck = p; p->dst = dst; p->src = src; p->lineno = lineno; p->explicit = explicit; ichecktail = p; } Type* ifacelookdot(Sym *s, Type *t, int *followptr) { int i, c, d; Type *m; *followptr = 0; if(t == T) return T; for(d=0; d 1) { yyerror("%T.%S is ambiguous", t, s); return T; } if(c == 1) { for(i=0; itype->etype]) { *followptr = 1; break; } } return m; } } return T; } // check whether non-interface type t // satisifes inteface type iface. int ifaceokT2I(Type *t0, Type *iface, Type **m, Type **samename) { Type *t, *im, *tm, *rcvr; int imhash, followptr; t = methtype(t0); // if this is too slow, // could sort these first // and then do one loop. // could also do full type compare // instead of using hash, but have to // avoid checking receivers, and // typehash already does that for us. // also, it's what the runtime will do, // so we can both be wrong together. for(im=iface->type; im; im=im->down) { imhash = typehash(im->type); tm = ifacelookdot(im->sym, t, &followptr); if(tm == T || typehash(tm->type) != imhash) { *m = im; *samename = tm; return 0; } // if pointer receiver in method, // the method does not exist for value types. rcvr = getthisx(tm->type)->type->type; if(isptr[rcvr->etype] && !isptr[t0->etype] && !followptr && !isifacemethod(tm)) { if(debug['r']) yyerror("interface pointer mismatch"); *m = im; *samename = nil; return 0; } } return 1; } // check whether interface type i1 satisifes interface type i2. int ifaceokI2I(Type *i1, Type *i2, Type **m) { Type *m1, *m2; // if this is too slow, // could sort these first // and then do one loop. for(m2=i2->type; m2; m2=m2->down) { for(m1=i1->type; m1; m1=m1->down) if(m1->sym == m2->sym && typehash(m1) == typehash(m2)) goto found; *m = m2; return 0; found:; } return 1; } void runifacechecks(void) { Icheck *p; int lno, wrong, needexplicit; Type *m, *t, *iface, *samename; lno = lineno; for(p=icheck; p; p=p->next) { lineno = p->lineno; wrong = 0; needexplicit = 0; m = nil; samename = nil; if(isinter(p->dst) && isinter(p->src)) { iface = p->dst; t = p->src; needexplicit = !ifaceokI2I(t, iface, &m); } else if(isinter(p->dst)) { t = p->src; iface = p->dst; wrong = !ifaceokT2I(t, iface, &m, &samename); } else { t = p->dst; iface = p->src; wrong = !ifaceokT2I(t, iface, &m, &samename); needexplicit = 1; } if(wrong) { if(samename) yyerror("%T is not %T\n\tmissing %S%hhT\n\tdo have %S%hhT", t, iface, m->sym, m->type, samename->sym, samename->type); else yyerror("%T is not %T\n\tmissing %S%hhT", t, iface, m->sym, m->type); } else if(!p->explicit && needexplicit) { if(m) { if(samename) yyerror("need type assertion to use %T as %T\n\tmissing %S %hhT\n\tdo have %S%hhT", p->src, p->dst, m->sym, m->type, samename->sym, samename->type); else yyerror("need type assertion to use %T as %T\n\tmissing %S%hhT", p->src, p->dst, m->sym, m->type); } else yyerror("need type assertion to use %T as %T", p->src, p->dst); } } lineno = lno; } /* * even simpler simtype; get rid of ptr, bool. * assuming that the front end has rejected * all the invalid conversions (like ptr -> bool) */ int simsimtype(Type *t) { int et; if(t == 0) return 0; et = simtype[t->etype]; switch(et) { case TPTR32: et = TUINT32; break; case TPTR64: et = TUINT64; break; case TBOOL: et = TUINT8; break; } return et; } NodeList* concat(NodeList *a, NodeList *b) { if(a == nil) return b; if(b == nil) return a; a->end->next = b; a->end = b->end; b->end = nil; return a; } NodeList* list1(Node *n) { NodeList *l; if(n == nil) return nil; if(n->op == OBLOCK && n->ninit == nil) return n->list; l = mal(sizeof *l); l->n = n; l->end = l; return l; } NodeList* list(NodeList *l, Node *n) { return concat(l, list1(n)); } NodeList* listtreecopy(NodeList *l) { NodeList *out; out = nil; for(; l; l=l->next) out = list(out, treecopy(l->n)); return out; } Node* liststmt(NodeList *l) { Node *n; n = nod(OBLOCK, N, N); n->list = l; if(l) n->lineno = l->n->lineno; return n; } /* * return nelem of list */ int count(NodeList *l) { int n; n = 0; for(; l; l=l->next) n++; return n; } /* * return nelem of list */ int structcount(Type *t) { int v; Iter s; v = 0; for(t = structfirst(&s, &t); t != T; t = structnext(&s)) v++; return v; } /* * return power of 2 of the constant * operand. -1 if it is not a power of 2. * 1000+ if it is a -(power of 2) */ int powtwo(Node *n) { uvlong v, b; int i; if(n == N || n->op != OLITERAL || n->type == T) goto no; if(!isint[n->type->etype]) goto no; v = mpgetfix(n->val.u.xval); b = 1ULL; for(i=0; i<64; i++) { if(b == v) return i; b = b<<1; } if(!issigned[n->type->etype]) goto no; v = -v; b = 1ULL; for(i=0; i<64; i++) { if(b == v) return i+1000; b = b<<1; } no: return -1; } /* * return the unsigned type for * a signed integer type. * returns T if input is not a * signed integer type. */ Type* tounsigned(Type *t) { // this is types[et+1], but not sure // that this relation is immutable switch(t->etype) { default: print("tounsigned: unknown type %T\n", t); t = T; break; case TINT: t = types[TUINT]; break; case TINT8: t = types[TUINT8]; break; case TINT16: t = types[TUINT16]; break; case TINT32: t = types[TUINT32]; break; case TINT64: t = types[TUINT64]; break; } return t; } /* * magic number for signed division * see hacker's delight chapter 10 */ void smagic(Magic *m) { int p; uint64 ad, anc, delta, q1, r1, q2, r2, t; uint64 mask, two31; m->bad = 0; switch(m->w) { default: m->bad = 1; return; case 8: mask = 0xffLL; break; case 16: mask = 0xffffLL; break; case 32: mask = 0xffffffffLL; break; case 64: mask = 0xffffffffffffffffLL; break; } two31 = mask ^ (mask>>1); p = m->w-1; ad = m->sd; if(m->sd < 0) ad = -m->sd; // bad denominators if(ad == 0 || ad == 1 || ad == two31) { m->bad = 1; return; } t = two31; ad &= mask; anc = t - 1 - t%ad; anc &= mask; q1 = two31/anc; r1 = two31 - q1*anc; q1 &= mask; r1 &= mask; q2 = two31/ad; r2 = two31 - q2*ad; q2 &= mask; r2 &= mask; for(;;) { p++; q1 <<= 1; r1 <<= 1; q1 &= mask; r1 &= mask; if(r1 >= anc) { q1++; r1 -= anc; q1 &= mask; r1 &= mask; } q2 <<= 1; r2 <<= 1; q2 &= mask; r2 &= mask; if(r2 >= ad) { q2++; r2 -= ad; q2 &= mask; r2 &= mask; } delta = ad - r2; delta &= mask; if(q1 < delta || (q1 == delta && r1 == 0)) { continue; } break; } m->sm = q2+1; if(m->sm & two31) m->sm |= ~mask; m->s = p-m->w; } /* * magic number for unsigned division * see hacker's delight chapter 10 */ void umagic(Magic *m) { int p; uint64 nc, delta, q1, r1, q2, r2; uint64 mask, two31; m->bad = 0; m->ua = 0; switch(m->w) { default: m->bad = 1; return; case 8: mask = 0xffLL; break; case 16: mask = 0xffffLL; break; case 32: mask = 0xffffffffLL; break; case 64: mask = 0xffffffffffffffffLL; break; } two31 = mask ^ (mask>>1); m->ud &= mask; if(m->ud == 0 || m->ud == two31) { m->bad = 1; return; } nc = mask - (-m->ud&mask)%m->ud; p = m->w-1; q1 = two31/nc; r1 = two31 - q1*nc; q1 &= mask; r1 &= mask; q2 = (two31-1) / m->ud; r2 = (two31-1) - q2*m->ud; q2 &= mask; r2 &= mask; for(;;) { p++; if(r1 >= nc-r1) { q1 <<= 1; q1++; r1 <<= 1; r1 -= nc; } else { q1 <<= 1; r1 <<= 1; } q1 &= mask; r1 &= mask; if(r2+1 >= m->ud-r2) { if(q2 >= two31-1) { m->ua = 1; } q2 <<= 1; q2++; r2 <<= 1; r2++; r2 -= m->ud; } else { if(q2 >= two31) { m->ua = 1; } q2 <<= 1; r2 <<= 1; r2++; } q2 &= mask; r2 &= mask; delta = m->ud - 1 - r2; delta &= mask; if(p < m->w+m->w) if(q1 < delta || (q1 == delta && r1 == 0)) { continue; } break; } m->um = q2+1; m->s = p-m->w; } Sym* ngotype(Node *n) { if(n->sym != S && strncmp(n->sym->name, "autotmp_", 8) != 0) if(n->type->etype != TFUNC || n->type->thistuple == 0) if(n->type->etype != TSTRUCT || n->type->funarg == 0) return typename(n->type)->left->sym; return S; }