go/src/cmd/gc/subr.c
Russ Cox db5f9da425 gc: tweak and enable escape analysis
-s now means *disable* escape analysis.

Fix escape leaks for struct/slice/map literals.
Add ... tracking.
Rewrite new(T) and slice literal into stack allocation when safe.

Add annotations to reflect.
Reflect is too chummy with the compiler,
so changes like these affect it more than they should.

R=lvd, dave, gustavo
CC=golang-dev
https://golang.org/cl/4954043
2011-08-28 12:05:00 -04:00

3919 lines
67 KiB
C

// 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 <u.h>
#include <libc.h>
#include "go.h"
#include "md5.h"
#include "y.tab.h"
#include "opnames.h"
#include "yerr.h"
static void dodump(Node*, int);
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;
erroring++;
fmtstrinit(&f);
fmtprint(&f, "%L: ", line);
fmtvprint(&f, fmt, arg);
fmtprint(&f, "\n");
erroring--;
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 strcmp(a->msg, b->msg);
}
void
flusherrors(void)
{
int i;
if(nerr == 0)
return;
qsort(err, nerr, sizeof err[0], errcmp);
for(i=0; i<nerr; i++)
if(i==0 || strcmp(err[i].msg, err[i-1].msg) != 0)
print("%s", err[i].msg);
nerr = 0;
}
static void
hcrash(void)
{
if(debug['h']) {
flusherrors();
if(outfile)
unlink(outfile);
*(volatile int*)0 = 0;
}
}
void
yyerrorl(int line, char *fmt, ...)
{
va_list arg;
va_start(arg, fmt);
adderr(line, fmt, arg);
va_end(arg);
hcrash();
nerrors++;
if(nerrors >= 10 && !debug['e']) {
flusherrors();
print("%L: too many errors\n", line);
errorexit();
}
}
extern int yystate, yychar;
void
yyerror(char *fmt, ...)
{
int i;
static int lastsyntax;
va_list arg;
char buf[512], *p;
if(strncmp(fmt, "syntax error", 12) == 0) {
nsyntaxerrors++;
if(debug['x'])
print("yyerror: yystate=%d yychar=%d\n", yystate, yychar);
// only one syntax error per line
if(lastsyntax == lexlineno)
return;
lastsyntax = lexlineno;
if(strstr(fmt, "{ or {")) {
// The grammar has { and LBRACE but both show up as {.
// Rewrite syntax error referring to "{ or {" to say just "{".
strecpy(buf, buf+sizeof buf, fmt);
p = strstr(buf, "{ or {");
if(p)
memmove(p+1, p+6, strlen(p+6)+1);
fmt = buf;
}
// look for parse state-specific errors in list (see go.errors).
for(i=0; i<nelem(yymsg); i++) {
if(yymsg[i].yystate == yystate && yymsg[i].yychar == yychar) {
yyerrorl(lexlineno, "syntax error: %s", yymsg[i].msg);
return;
}
}
// plain "syntax error" gets "near foo" added
if(strcmp(fmt, "syntax error") == 0) {
yyerrorl(lexlineno, "syntax error near %s", lexbuf);
return;
}
// if bison says "syntax error, more info"; print "syntax error: more info".
if(fmt[12] == ',') {
yyerrorl(lexlineno, "syntax error:%s", fmt+13);
return;
}
yyerrorl(lexlineno, "%s", fmt);
return;
}
va_start(arg, fmt);
adderr(parserline(), fmt, arg);
va_end(arg);
hcrash();
nerrors++;
if(nerrors >= 10 && !debug['e']) {
flusherrors();
print("%L: too many errors\n", parserline());
errorexit();
}
}
void
warn(char *fmt, ...)
{
va_list arg;
va_start(arg, fmt);
adderr(parserline(), fmt, arg);
va_end(arg);
hcrash();
}
void
warnl(int line, char *fmt, ...)
{
va_list arg;
va_start(arg, fmt);
adderr(line, fmt, arg);
va_end(arg);
}
void
fatal(char *fmt, ...)
{
va_list arg;
flusherrors();
print("%L: internal compiler error: ", lineno);
va_start(arg, fmt);
vfprint(1, fmt, arg);
va_end(arg);
print("\n");
// If this is a released compiler version, ask for a bug report.
if(strncmp(getgoversion(), "release", 7) == 0) {
print("\n");
print("Please file a bug report including a short program that triggers the error.\n");
print("http://code.google.com/p/go/issues/entry?template=compilerbug\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", file);
else
if(off > 0)
print("line %s", file);
else
print("import %s", file);
} else
print("end of import");
print(" at line %L\n", lexlineno);
}
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:
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 *name)
{
return pkglookup(name, localpkg);
}
Sym*
pkglookup(char *name, Pkg *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 || s->pkg != pkg)
continue;
if(strcmp(s->name, name) == 0)
return s;
}
s = mal(sizeof(*s));
s->name = mal(strlen(name)+1);
strcpy(s->name, name);
s->pkg = pkg;
s->link = hash[h];
hash[h] = s;
s->lexical = LNAME;
return s;
}
Sym*
restrictlookup(char *name, Pkg *pkg)
{
if(!exportname(name) && pkg != localpkg)
yyerror("cannot refer to unexported name %s.%s", pkg->name, name);
return pkglookup(name, pkg);
}
// find all the exported symbols in package opkg
// and make them available in the current package
void
importdot(Pkg *opkg, Node *pack)
{
Sym *s, *s1;
uint32 h;
int n;
n = 0;
for(h=0; h<NHASH; h++) {
for(s = hash[h]; s != S; s = s->link) {
if(s->pkg != opkg)
continue;
if(s->def == N)
continue;
if(!exportname(s->name) || utfrune(s->name, 0xb7)) // 0xb7 = center dot
continue;
s1 = lookup(s->name);
if(s1->def != N) {
redeclare(s1, "during import");
continue;
}
s1->def = s->def;
s1->block = s->block;
s1->def->pack = pack;
n++;
}
}
if(n == 0) {
// can't possibly be used - there were no symbols
yyerrorl(pack->lineno, "imported and not used: %Z", opkg->path);
}
}
static void
gethunk(void)
{
char *h;
int32 nh;
nh = NHUNK;
if(thunk >= 10L*NHUNK)
nh = 10L*NHUNK;
h = (char*)malloc(nh);
if(h == nil) {
flusherrors();
yyerror("out of memory");
errorexit();
}
hunk = h;
nhunk = nh;
thunk += nh;
}
void*
mal(int32 n)
{
void *p;
if(n >= NHUNK) {
p = malloc(n);
if(p == nil) {
flusherrors();
yyerror("out of memory");
errorexit();
}
memset(p, 0, n);
return p;
}
while((uintptr)hunk & MAXALIGN) {
hunk++;
nhunk--;
}
if(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) {
if(on+n >= NHUNK) {
q = mal(on+n);
memmove(q, p, on);
return q;
}
if(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;
n->orig = n;
n->curfn = curfn;
return n;
}
int
algtype(Type *t)
{
int a;
if(issimple[t->etype] || isptr[t->etype] ||
t->etype == TCHAN || t->etype == TFUNC || t->etype == TMAP) {
if(t->width == 1)
a = AMEM8;
else if(t->width == 2)
a = AMEM16;
else if(t->width == 4)
a = AMEM32;
else if(t->width == 8)
a = AMEM64;
else if(t->width == 16)
a = AMEM128;
else
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 if(isslice(t))
a = ASLICE; // slice
else {
if(t->width == 1)
a = ANOEQ8;
else if(t->width == 2)
a = ANOEQ16;
else if(t->width == 4)
a = ANOEQ32;
else if(t->width == 8)
a = ANOEQ64;
else if(t->width == 16)
a = ANOEQ128;
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;
}
Type*
typ(int et)
{
Type *t;
t = mal(sizeof(*t));
t->etype = et;
t->width = BADWIDTH;
t->lineno = lineno;
t->orig = t;
return t;
}
static int
methcmp(const void *va, const void *vb)
{
Type *a, *b;
int i;
a = *(Type**)va;
b = *(Type**)vb;
i = strcmp(a->sym->name, b->sym->name);
if(i != 0)
return i;
if(!exportname(a->sym->name)) {
i = strcmp(a->sym->pkg->path->s, b->sym->pkg->path->s);
if(i != 0)
return i;
}
return 0;
}
Type*
sortinter(Type *t)
{
Type *f;
int i;
Type **a;
if(t->type == nil || t->type->down == nil)
return t;
i=0;
for(f=t->type; f; f=f->down)
i++;
a = mal(i*sizeof f);
i = 0;
for(f=t->type; f; f=f->down)
a[i++] = f;
qsort(a, i, sizeof a[0], methcmp);
while(i-- > 0) {
a[i]->down = f;
f = a[i];
}
t->type = f;
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;
}
Node*
nodfltconst(Mpflt* v)
{
Node *c;
c = nod(OLITERAL, N, N);
c->addable = 1;
c->val.u.fval = mal(sizeof(*c->val.u.fval));
mpmovefltflt(c->val.u.fval, v);
c->val.ctype = CTFLT;
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)
{
Type *r;
int bound;
bound = -1; // open bound
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;
}
static void
indent(int dep)
{
int i;
for(i=0; i<dep; i++)
print(". ");
}
static void
dodumplist(NodeList *l, int dep)
{
for(; l; l=l->next)
dodump(l->n, dep);
}
static 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] = "&",
[OAPPEND] = "append",
[OAS] = "=",
[OAS2] = "=",
[OBREAK] = "break",
[OCALL] = "function call",
[OCAP] = "cap",
[OCASE] = "case",
[OCLOSE] = "close",
[OCOMPLEX] = "complex",
[OCOM] = "^",
[OCONTINUE] = "continue",
[OCOPY] = "copy",
[ODEC] = "--",
[ODEFER] = "defer",
[ODIV] = "/",
[OEQ] = "==",
[OFALL] = "fallthrough",
[OFOR] = "for",
[OGE] = ">=",
[OGOTO] = "goto",
[OGT] = ">",
[OIF] = "if",
[OIMAG] = "imag",
[OINC] = "++",
[OIND] = "*",
[OLEN] = "len",
[OLE] = "<=",
[OLSH] = "<<",
[OLT] = "<",
[OMAKE] = "make",
[OMINUS] = "-",
[OMOD] = "%",
[OMUL] = "*",
[ONEW] = "new",
[ONE] = "!=",
[ONOT] = "!",
[OOROR] = "||",
[OOR] = "|",
[OPANIC] = "panic",
[OPLUS] = "+",
[OPRINTN] = "println",
[OPRINT] = "print",
[ORANGE] = "range",
[OREAL] = "real",
[ORECV] = "<-",
[ORETURN] = "return",
[ORSH] = ">>",
[OSELECT] = "select",
[OSEND] = "<-",
[OSUB] = "-",
[OSWITCH] = "switch",
[OXOR] = "^",
};
int
Oconv(Fmt *fp)
{
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)
return fmtprint(fp, "O-%d", o);
return fmtstrcpy(fp, opnames[o]);
}
int
Lconv(Fmt *fp)
{
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(h->offset > 0) {
// #line directive
if(n > 0 && n < HISTSZ) {
a[n-1].line = h;
a[n-1].ldel = h->line - h->offset + 1;
}
} else {
// beginning of file
if(n < HISTSZ) {
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;
for(i=n-1; i>=0; i--) {
if(i != n-1) {
if(fp->flags & ~(FmtWidth|FmtPrec))
break;
fmtprint(fp, " ");
}
if(debug['L'])
fmtprint(fp, "%s/", pathname);
if(a[i].line)
fmtprint(fp, "%s:%d[%s:%d]",
a[i].line->name, lno-a[i].ldel+1,
a[i].incl->name, lno-a[i].idel+1);
else
fmtprint(fp, "%s:%d",
a[i].incl->name, lno-a[i].idel+1);
lno = a[i].incl->line - 1; // now print out start of this file
}
if(n == 0)
fmtprint(fp, "<epoch>");
return 0;
}
/*
s%,%,\n%g
s%\n+%\n%g
s%^[ ]*T%%g
s%,.*%%g
s%.+% [T&] = "&",%g
s%^ ........*\]%&~%g
s%~ %%g
*/
static char*
etnames[] =
{
[TINT] = "INT",
[TUINT] = "UINT",
[TINT8] = "INT8",
[TUINT8] = "UINT8",
[TINT16] = "INT16",
[TUINT16] = "UINT16",
[TINT32] = "INT32",
[TUINT32] = "UINT32",
[TINT64] = "INT64",
[TUINT64] = "UINT64",
[TUINTPTR] = "UINTPTR",
[TFLOAT32] = "FLOAT32",
[TFLOAT64] = "FLOAT64",
[TCOMPLEX64] = "COMPLEX64",
[TCOMPLEX128] = "COMPLEX128",
[TBOOL] = "BOOL",
[TPTR32] = "PTR32",
[TPTR64] = "PTR64",
[TFUNC] = "FUNC",
[TARRAY] = "ARRAY",
[TSTRUCT] = "STRUCT",
[TCHAN] = "CHAN",
[TMAP] = "MAP",
[TINTER] = "INTER",
[TFORW] = "FORW",
[TFIELD] = "FIELD",
[TSTRING] = "STRING",
[TANY] = "ANY",
};
int
Econv(Fmt *fp)
{
int et;
et = va_arg(fp->args, int);
if(et < 0 || et >= nelem(etnames) || etnames[et] == nil)
return fmtprint(fp, "E-%d", et);
return fmtstrcpy(fp, etnames[et]);
}
static const char* classnames[] = {
"Pxxx",
"PEXTERN",
"PAUTO",
"PPARAM",
"PPARAMOUT",
"PPARAMREF",
"PFUNC",
};
int
Jconv(Fmt *fp)
{
Node *n;
char *s;
int c;
n = va_arg(fp->args, Node*);
c = fp->flags&FmtShort;
if(!c && n->ullman != 0)
fmtprint(fp, " u(%d)", n->ullman);
if(!c && n->addable != 0)
fmtprint(fp, " a(%d)", n->addable);
if(!c && n->vargen != 0)
fmtprint(fp, " g(%d)", n->vargen);
if(n->lineno != 0)
fmtprint(fp, " l(%d)", n->lineno);
if(!c && n->xoffset != BADWIDTH)
fmtprint(fp, " x(%lld%+d)", n->xoffset, n->stkdelta);
if(n->class != 0) {
s = "";
if(n->class & PHEAP) s = ",heap";
if((n->class & ~PHEAP) < nelem(classnames))
fmtprint(fp, " class(%s%s)", classnames[n->class&~PHEAP], s);
else
fmtprint(fp, " class(%d?%s)", n->class&~PHEAP, s);
}
if(n->colas != 0)
fmtprint(fp, " colas(%d)", n->colas);
if(n->funcdepth != 0)
fmtprint(fp, " f(%d)", n->funcdepth);
switch(n->esc) {
case EscUnknown:
break;
case EscHeap:
fmtprint(fp, " esc(h)");
break;
case EscScope:
fmtprint(fp, " esc(s)");
break;
case EscNone:
fmtprint(fp, " esc(no)");
break;
case EscNever:
if(!c)
fmtprint(fp, " esc(N)");
break;
default:
fmtprint(fp, " esc(%d)", n->esc);
break;
}
if(n->escloopdepth)
fmtprint(fp, " ld(%d)", n->escloopdepth);
if(!c && n->typecheck != 0)
fmtprint(fp, " tc(%d)", n->typecheck);
if(!c && n->dodata != 0)
fmtprint(fp, " dd(%d)", n->dodata);
if(n->isddd != 0)
fmtprint(fp, " isddd(%d)", n->isddd);
if(n->implicit != 0)
fmtprint(fp, " implicit(%d)", n->implicit);
if(!c && n->pun != 0)
fmtprint(fp, " pun(%d)", n->pun);
if(!c && n->used != 0)
fmtprint(fp, " used(%d)", n->used);
return 0;
}
int
Sconv(Fmt *fp)
{
Sym *s;
s = va_arg(fp->args, Sym*);
if(s == S) {
fmtstrcpy(fp, "<S>");
return 0;
}
if(fp->flags & FmtShort)
goto shrt;
if(exporting || (fp->flags & FmtSharp)) {
if(packagequotes)
fmtprint(fp, "\"%Z\"", s->pkg->path);
else
fmtprint(fp, "%s", s->pkg->prefix);
fmtprint(fp, ".%s", s->name);
return 0;
}
if(s->pkg && s->pkg != localpkg || longsymnames || (fp->flags & FmtLong)) {
// This one is for the user. If the package name
// was used by multiple packages, give the full
// import path to disambiguate.
if(erroring && pkglookup(s->pkg->name, nil)->npkg > 1) {
fmtprint(fp, "\"%Z\".%s", s->pkg->path, s->name);
return 0;
}
fmtprint(fp, "%s.%s", s->pkg->name, s->name);
return 0;
}
shrt:
fmtstrcpy(fp, s->name);
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",
[TFLOAT32] = "float32",
[TFLOAT64] = "float64",
[TCOMPLEX64] = "complex64",
[TCOMPLEX128] = "complex128",
[TBOOL] = "bool",
[TANY] = "any",
[TSTRING] = "string",
[TNIL] = "nil",
[TIDEAL] = "ideal",
[TBLANK] = "blank",
};
int
Tpretty(Fmt *fp, Type *t)
{
Type *t1;
Sym *s;
if(0 && debug['r']) {
debug['r'] = 0;
fmtprint(fp, "%T (orig=%T)", t, t->orig);
debug['r'] = 1;
return 0;
}
if(t->etype != TFIELD
&& t->sym != S
&& !(fp->flags&FmtLong)) {
s = t->sym;
if(t == types[t->etype] && t->etype != TUNSAFEPTR)
return fmtprint(fp, "%s", s->name);
if(exporting) {
if(fp->flags & FmtShort)
fmtprint(fp, "%hS", s);
else
fmtprint(fp, "%S", s);
if(s->pkg != localpkg)
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:
return fmtprint(fp, "chan<- %T", t->type);
}
if(t->type != T && t->type->etype == TCHAN && t->type->sym == S && t->type->chan == Crecv)
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) {
if(t1->isddd)
fmtprint(fp, "...%T", t1->type->type);
else
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 = t1->type;
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, " ");
if(exportname(t1->sym->name))
fmtprint(fp, "%hS", t1->sym);
else
fmtprint(fp, "%S", t1->sym);
fmtprint(fp, "%hhT", 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, "? ");
} else
fmtprint(fp, "%hS ", t->sym);
if(t->isddd)
fmtprint(fp, "...%T", t->type->type);
else
fmtprint(fp, "%T", t->type);
if(t->note) {
fmtprint(fp, " ");
if(exporting)
fmtprint(fp, ":");
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");
case TUNSAFEPTR:
if(exporting)
return fmtprint(fp, "\"unsafe\".Pointer");
return fmtprint(fp, "unsafe.Pointer");
}
// 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>");
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;
}
}
if(sharp || exporting)
fatal("missing %E case during export", t->etype);
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, "[%d]%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, "<N>");
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) {
if(n->orig != N)
n = n->orig;
exprfmt(fp, n, 0);
goto out;
}
switch(n->op) {
default:
if(fp->flags & FmtShort)
fmtprint(fp, "%O%hJ", n->op, n);
else
fmtprint(fp, "%O%J", n->op, n);
break;
case ONAME:
case ONONAME:
if(n->sym == S) {
if(fp->flags & FmtShort)
fmtprint(fp, "%O%hJ", n->op, n);
else
fmtprint(fp, "%O%J", n->op, n);
break;
}
if(fp->flags & FmtShort)
fmtprint(fp, "%O-%S%hJ", n->op, n->sym, n);
else
fmtprint(fp, "%O-%S%J", n->op, n->sym, 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 CTCPLX:
snprint(buf1, sizeof(buf1), "(F%g+F%gi)",
mpgetflt(&n->val.u.cval->real),
mpgetflt(&n->val.u.cval->imag));
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%d", 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->sym == lookup("iota")) {
// Not sure yet whether this is the real iota,
// but make a copy of the Node* just in case,
// so that all the copies of this const definition
// don't have the same iota value.
m = nod(OXXX, N, N);
*m = *n;
m->iota = iota;
break;
}
// fall through
case ONAME:
case OLITERAL:
case OTYPE:
m = n;
break;
}
return m;
}
int
Zconv(Fmt *fp)
{
Rune r;
Strlit *sp;
char *s, *se;
int n;
sp = va_arg(fp->args, Strlit*);
if(sp == nil)
return fmtstrcpy(fp, "<nil>");
s = sp->s;
se = s + sp->len;
while(s < se) {
n = chartorune(&r, s);
s += n;
switch(r) {
case Runeerror:
if(n == 1) {
fmtprint(fp, "\\x%02x", (uchar)*(s-1));
break;
}
// fall through
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
isinter(Type *t)
{
return t != T && t->etype == TINTER;
}
int
isnilinter(Type *t)
{
if(!isinter(t))
return 0;
if(t->type != T)
return 0;
return 1;
}
int
isideal(Type *t)
{
if(t == T)
return 0;
if(t == idealstring || t == idealbool)
return 1;
switch(t->etype) {
case TNIL:
case TIDEAL:
return 1;
}
return 0;
}
/*
* given receiver of type t (t == r or t == *r)
* return type to hang methods off (r).
*/
Type*
methtype(Type *t)
{
if(t == T)
return T;
// strip away pointer if it's there
if(isptr[t->etype]) {
if(t->sym != S)
return T;
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
cplxsubtype(int et)
{
switch(et) {
case TCOMPLEX64:
return TFLOAT32;
case TCOMPLEX128:
return TFLOAT64;
}
fatal("cplxsubtype: %E\n", et);
return 0;
}
static int
eqnote(Strlit *a, Strlit *b)
{
if(a == b)
return 1;
if(a == nil || b == nil)
return 0;
if(a->len != b->len)
return 0;
return memcmp(a->s, b->s, a->len) == 0;
}
// Return 1 if t1 and t2 are identical, following the spec rules.
//
// Any cyclic type must go through a named type, and if one is
// named, it is only identical to the other if they are the same
// pointer (t1 == t2), so there's no chance of chasing cycles
// ad infinitum, so no need for a depth counter.
int
eqtype(Type *t1, Type *t2)
{
if(t1 == t2)
return 1;
if(t1 == T || t2 == T || t1->etype != t2->etype || t1->sym || t2->sym)
return 0;
switch(t1->etype) {
case TINTER:
case TSTRUCT:
for(t1=t1->type, t2=t2->type; t1 && t2; t1=t1->down, t2=t2->down) {
if(t1->etype != TFIELD || t2->etype != TFIELD)
fatal("struct/interface missing field: %T %T", t1, t2);
if(t1->sym != t2->sym || t1->embedded != t2->embedded || !eqtype(t1->type, t2->type) || !eqnote(t1->note, t2->note))
return 0;
}
return t1 == T && t2 == T;
case TFUNC:
// Loop over structs: receiver, in, out.
for(t1=t1->type, t2=t2->type; t1 && t2; t1=t1->down, t2=t2->down) {
Type *ta, *tb;
if(t1->etype != TSTRUCT || t2->etype != TSTRUCT)
fatal("func missing struct: %T %T", t1, t2);
// Loop over fields in structs, ignoring argument names.
for(ta=t1->type, tb=t2->type; ta && tb; ta=ta->down, tb=tb->down) {
if(ta->etype != TFIELD || tb->etype != TFIELD)
fatal("func struct missing field: %T %T", ta, tb);
if(ta->isddd != tb->isddd || !eqtype(ta->type, tb->type))
return 0;
}
if(ta != T || tb != T)
return 0;
}
return t1 == T && t2 == T;
case TARRAY:
if(t1->bound != t2->bound)
return 0;
break;
case TCHAN:
if(t1->chan != t2->chan)
return 0;
break;
}
return eqtype(t1->down, t2->down) && eqtype(t1->type, t2->type);
}
// Are t1 and t2 equal struct types when field names are ignored?
// For deciding whether the result struct from g can be copied
// directly when compiling f(g()).
int
eqtypenoname(Type *t1, Type *t2)
{
if(t1 == T || t2 == T || t1->etype != TSTRUCT || t2->etype != TSTRUCT)
return 0;
t1 = t1->type;
t2 = t2->type;
for(;;) {
if(!eqtype(t1, t2))
return 0;
if(t1 == T)
return 1;
t1 = t1->down;
t2 = t2->down;
}
}
// Is type src assignment compatible to type dst?
// If so, return op code to use in conversion.
// If not, return 0.
//
// It is the caller's responsibility to call exportassignok
// to check for assignments to other packages' unexported fields,
int
assignop(Type *src, Type *dst, char **why)
{
Type *missing, *have;
int ptr;
if(why != nil)
*why = "";
if(safemode && src != T && src->etype == TUNSAFEPTR) {
yyerror("cannot use unsafe.Pointer");
errorexit();
}
if(src == dst)
return OCONVNOP;
if(src == T || dst == T || src->etype == TFORW || dst->etype == TFORW || src->orig == T || dst->orig == T)
return 0;
// 1. src type is identical to dst.
if(eqtype(src, dst))
return OCONVNOP;
// 2. src and dst have identical underlying types
// and either src or dst is not a named type or
// both are interface types.
if(eqtype(src->orig, dst->orig) && (src->sym == S || dst->sym == S || src->etype == TINTER))
return OCONVNOP;
// 3. dst is an interface type and src implements dst.
if(dst->etype == TINTER && src->etype != TNIL) {
if(implements(src, dst, &missing, &have, &ptr))
return OCONVIFACE;
if(why != nil) {
if(isptrto(src, TINTER))
*why = smprint(":\n\t%T is pointer to interface, not interface", src);
else if(have && have->sym == missing->sym)
*why = smprint(":\n\t%T does not implement %T (wrong type for %S method)\n"
"\t\thave %S%hhT\n\t\twant %S%hhT", src, dst, missing->sym,
have->sym, have->type, missing->sym, missing->type);
else if(ptr)
*why = smprint(":\n\t%T does not implement %T (%S method requires pointer receiver)",
src, dst, missing->sym);
else if(have)
*why = smprint(":\n\t%T does not implement %T (missing %S method)\n"
"\t\thave %S%hhT\n\t\twant %S%hhT", src, dst, missing->sym,
have->sym, have->type, missing->sym, missing->type);
else
*why = smprint(":\n\t%T does not implement %T (missing %S method)",
src, dst, missing->sym);
}
return 0;
}
if(isptrto(dst, TINTER)) {
if(why != nil)
*why = smprint(":\n\t%T is pointer to interface, not interface", dst);
return 0;
}
if(src->etype == TINTER && dst->etype != TBLANK) {
if(why != nil)
*why = ": need type assertion";
return 0;
}
// 4. src is a bidirectional channel value, dst is a channel type,
// src and dst have identical element types, and
// either src or dst is not a named type.
if(src->etype == TCHAN && src->chan == Cboth && dst->etype == TCHAN)
if(eqtype(src->type, dst->type) && (src->sym == S || dst->sym == S))
return OCONVNOP;
// 5. src is the predeclared identifier nil and dst is a nillable type.
if(src->etype == TNIL) {
switch(dst->etype) {
case TARRAY:
if(dst->bound != -100) // not slice
break;
case TPTR32:
case TPTR64:
case TFUNC:
case TMAP:
case TCHAN:
case TINTER:
return OCONVNOP;
}
}
// 6. rule about untyped constants - already converted by defaultlit.
// 7. Any typed value can be assigned to the blank identifier.
if(dst->etype == TBLANK)
return OCONVNOP;
return 0;
}
// Can we convert a value of type src to a value of type dst?
// If so, return op code to use in conversion (maybe OCONVNOP).
// If not, return 0.
int
convertop(Type *src, Type *dst, char **why)
{
int op;
if(why != nil)
*why = "";
if(src == dst)
return OCONVNOP;
if(src == T || dst == T)
return 0;
// 1. src can be assigned to dst.
if((op = assignop(src, dst, why)) != 0)
return op;
// The rules for interfaces are no different in conversions
// than assignments. If interfaces are involved, stop now
// with the good message from assignop.
// Otherwise clear the error.
if(src->etype == TINTER || dst->etype == TINTER)
return 0;
if(why != nil)
*why = "";
// 2. src and dst have identical underlying types.
if(eqtype(src->orig, dst->orig))
return OCONVNOP;
// 3. src and dst are unnamed pointer types
// and their base types have identical underlying types.
if(isptr[src->etype] && isptr[dst->etype] && src->sym == S && dst->sym == S)
if(eqtype(src->type->orig, dst->type->orig))
return OCONVNOP;
// 4. src and dst are both integer or floating point types.
if((isint[src->etype] || isfloat[src->etype]) && (isint[dst->etype] || isfloat[dst->etype])) {
if(simtype[src->etype] == simtype[dst->etype])
return OCONVNOP;
return OCONV;
}
// 5. src and dst are both complex types.
if(iscomplex[src->etype] && iscomplex[dst->etype]) {
if(simtype[src->etype] == simtype[dst->etype])
return OCONVNOP;
return OCONV;
}
// 6. src is an integer or has type []byte or []int
// and dst is a string type.
if(isint[src->etype] && dst->etype == TSTRING)
return ORUNESTR;
if(isslice(src) && src->sym == nil && src->type == types[src->type->etype] && dst->etype == TSTRING) {
switch(src->type->etype) {
case TUINT8:
return OARRAYBYTESTR;
case TINT:
return OARRAYRUNESTR;
}
}
// 7. src is a string and dst is []byte or []int.
// String to slice.
if(src->etype == TSTRING && isslice(dst) && dst->sym == nil && dst->type == types[dst->type->etype]) {
switch(dst->type->etype) {
case TUINT8:
return OSTRARRAYBYTE;
case TINT:
return OSTRARRAYRUNE;
}
}
// 8. src is a pointer or uintptr and dst is unsafe.Pointer.
if((isptr[src->etype] || src->etype == TUINTPTR) && dst->etype == TUNSAFEPTR)
return OCONVNOP;
// 9. src is unsafe.Pointer and dst is a pointer or uintptr.
if(src->etype == TUNSAFEPTR && (isptr[dst->etype] || dst->etype == TUINTPTR))
return OCONVNOP;
return 0;
}
// Convert node n for assignment to type t.
Node*
assignconv(Node *n, Type *t, char *context)
{
int op;
Node *r, *old;
char *why;
if(n == N || n->type == T)
return n;
old = n;
old->diag++; // silence errors about n; we'll issue one below
defaultlit(&n, t);
old->diag--;
if(t->etype == TBLANK)
return n;
exportassignok(n->type, context);
if(eqtype(n->type, t))
return n;
op = assignop(n->type, t, &why);
if(op == 0) {
yyerror("cannot use %+N as type %T in %s%s", n, t, context, why);
op = OCONV;
}
r = nod(op, n, N);
r->type = t;
r->typecheck = 1;
r->implicit = 1;
return r;
}
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;
if(t->orig == t)
nt->orig = nt;
return nt;
}
static 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, runtimepkg);
if(s == S || s->def == N)
fatal("syslook: can't 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;
}
/*
* 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;
longsymnames = 1;
if(t->thistuple) {
// hide method receiver from Tpretty
t->thistuple = 0;
p = smprint("%-T", t);
t->thistuple = 1;
}else
p = smprint("%-T", t);
longsymnames = 0;
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 = widthptr;
t1->align = widthptr;
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%d %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)
{
Fmt fmt;
char *s;
fmtstrinit(&fmt);
if(tl != T)
fmtprint(&fmt, "\n %T", tl);
if(tr != T)
fmtprint(&fmt, "\n %T", tr);
// common mistake: *struct and *interface.
if(tl && tr && isptr[tl->etype] && isptr[tr->etype]) {
if(tl->type->etype == TSTRUCT && tr->type->etype == TINTER)
fmtprint(&fmt, "\n (*struct vs *interface)");
else if(tl->type->etype == TINTER && tr->type->etype == TSTRUCT)
fmtprint(&fmt, "\n (*interface vs *struct)");
}
s = fmtstrflush(&fmt);
yyerror("illegal types for operand: %O%s", o, s);
}
/*
* 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;
}
/*
* return side effect-free n, appending side effects to init.
* result is assignable if n is.
*/
Node*
safeexpr(Node *n, NodeList **init)
{
Node *l;
Node *r;
Node *a;
if(n == N)
return N;
switch(n->op) {
case ONAME:
case OLITERAL:
return n;
case ODOT:
l = safeexpr(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 ODOTPTR:
case OIND:
l = safeexpr(n->left, init);
if(l == n->left)
return n;
a = nod(OXXX, N, N);
*a = *n;
a->left = l;
walkexpr(&a, init);
return a;
case OINDEX:
case OINDEXMAP:
l = safeexpr(n->left, init);
r = safeexpr(n->right, init);
if(l == n->left && r == n->right)
return n;
a = nod(OXXX, N, N);
*a = *n;
a->left = l;
a->right = r;
walkexpr(&a, init);
return a;
}
// make a copy; must not be used as an lvalue
if(islvalue(n))
fatal("missing lvalue case in safeexpr: %N", n);
return cheapexpr(n, init);
}
static Node*
copyexpr(Node *n, Type *t, NodeList **init)
{
Node *a, *l;
l = nod(OXXX, N, N);
tempname(l, t);
a = nod(OAS, l, n);
typecheck(&a, Etop);
walkexpr(&a, init);
*init = list(*init, a);
return l;
}
/*
* return side-effect free and cheap n, appending side effects to init.
* result may not be assignable.
*/
Node*
cheapexpr(Node *n, NodeList **init)
{
switch(n->op) {
case ONAME:
case OLITERAL:
return n;
}
return copyexpr(n, n->type, init);
}
/*
* return n in a local variable of type t if it is not already.
*/
Node*
localexpr(Node *n, Type *t, NodeList **init)
{
if(n->op == ONAME &&
(n->class == PAUTO || n->class == PPARAM || n->class == PPARAMOUT) &&
convertop(n->type, t, nil) == OCONVNOP)
return n;
return copyexpr(n, t, init);
}
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;
}
/* unicode-aware case-insensitive strcmp */
static int
cistrcmp(char *p, char *q)
{
Rune rp, rq;
while(*p || *q) {
if(*p == 0)
return +1;
if(*q == 0)
return -1;
p += chartorune(&rp, p);
q += chartorune(&rq, q);
rp = tolowerrune(rp);
rq = tolowerrune(rq);
if(rp < rq)
return -1;
if(rp > rq)
return +1;
}
return 0;
}
/*
* code to resolve elided DOTs
* in embedded types
*/
// search depth 0 --
// return count of fields+methods
// found with a given name
static int
lookdot0(Sym *s, Type *t, Type **save, int ignorecase)
{
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 || (ignorecase && cistrcmp(f->sym->name, s->name) == 0)) {
if(save)
*save = f;
c++;
}
}
u = methtype(t);
if(u != T) {
for(f=u->method; f!=T; f=f->down)
if(f->embedded == 0 && (f->sym == s || (ignorecase && cistrcmp(f->sym->name, s->name) == 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, int ignorecase)
{
Type *f, *u;
int c, a;
if(t->trecur)
return 0;
t->trecur = 1;
if(d == 0) {
c = lookdot0(s, t, save, ignorecase);
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, ignorecase);
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, Etype|Erv);
t = n->left->type;
if(t == T)
goto ret;
if(n->left->op == OTYPE)
goto ret;
if(n->right->op != ONAME)
goto ret;
s = n->right->sym;
if(s == S)
goto ret;
for(d=0; d<nelem(dotlist); d++) {
c = adddot1(s, t, d, nil, 0);
if(c > 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) && f->sym->pkg != localpkg)
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) && f->sym->pkg != localpkg)
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;
// mark top-level method symbols
// so that expand1 doesn't consider them.
for(f=t->method; f != nil; f=f->down)
f->sym->flags |= SymUniq;
// 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; d<nelem(dotlist); d++) {
c = adddot1(sl->field->sym, t, d, &f, 0);
if(c == 0)
continue;
if(c == 1) {
sl->good = 1;
sl->field = f;
}
break;
}
}
for(f=t->method; f != nil; f=f->down)
f->sym->flags &= ~SymUniq;
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.
*/
static 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));
a->isddd = t->isddd;
if(n != N)
n->isddd = t->isddd;
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, int iface)
{
Node *this, *fn, *call, *n, *t, *pad;
NodeList *l, *args, *in, *out;
Type *tpad;
int isddd;
Val v;
if(0 && debug['r'])
print("genwrapper rcvrtype=%T method=%T newnam=%S\n",
rcvr, method, newnam);
lineno = 1; // less confusing than end of input
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, N, N);
l = list1(this);
if(iface && rcvr->width < types[tptr]->width) {
// Building method for interface table and receiver
// is smaller than the single pointer-sized word
// that the interface call will pass in.
// Add a dummy padding argument after the
// receiver to make up the difference.
tpad = typ(TARRAY);
tpad->type = types[TUINT8];
tpad->bound = types[tptr]->width - rcvr->width;
pad = nod(ODCLFIELD, newname(lookup(".pad")), typenod(tpad));
l = list(l, pad);
}
t->list = concat(l, in);
t->rlist = out;
fn->nname->ntype = t;
funchdr(fn);
// arg list
args = nil;
isddd = 0;
for(l=in; l; l=l->next) {
args = list(args, l->n->left);
isddd = l->n->left->isddd;
}
// generate nil pointer check for better error
if(isptr[rcvr->etype] && rcvr->type == getthisx(method->type)->type->type) {
// generating wrapper from *T to T.
n = nod(OIF, N, N);
n->ntest = nod(OEQ, this->left, nodnil());
// these strings are already in the reflect tables,
// so no space cost to use them here.
l = nil;
v.ctype = CTSTR;
v.u.sval = strlit(rcvr->type->sym->pkg->name); // package name
l = list(l, nodlit(v));
v.u.sval = strlit(rcvr->type->sym->name); // type name
l = list(l, nodlit(v));
v.u.sval = strlit(method->sym->name);
l = list(l, nodlit(v)); // method name
call = nod(OCALL, syslook("panicwrap", 0), N);
call->list = l;
n->nbody = list1(call);
fn->nbody = list(fn->nbody, n);
}
// generate call
call = nod(OCALL, adddot(nod(OXDOT, this->left, newname(method->sym))), N);
call->list = args;
call->isddd = isddd;
if(method->type->outtuple > 0) {
n = nod(ORETURN, N, N);
n->list = list1(call);
call = n;
}
fn->nbody = list(fn->nbody, call);
if(0 && debug['r'])
dumplist("genwrapper body", fn->nbody);
funcbody(fn);
curfn = fn;
typecheck(&fn, Etop);
typechecklist(fn->nbody, Etop);
curfn = nil;
funccompile(fn, 0);
}
static Type*
ifacelookdot(Sym *s, Type *t, int *followptr, int ignorecase)
{
int i, c, d;
Type *m;
*followptr = 0;
if(t == T)
return T;
for(d=0; d<nelem(dotlist); d++) {
c = adddot1(s, t, d, &m, ignorecase);
if(c > 1) {
yyerror("%T.%S is ambiguous", t, s);
return T;
}
if(c == 1) {
for(i=0; i<d; i++) {
if(isptr[dotlist[i].field->type->etype]) {
*followptr = 1;
break;
}
}
if(m->type->etype != TFUNC || m->type->thistuple == 0) {
yyerror("%T.%S is a field, not a method", t, s);
return T;
}
return m;
}
}
return T;
}
int
implements(Type *t, Type *iface, Type **m, Type **samename, int *ptr)
{
Type *t0, *im, *tm, *rcvr, *imtype;
int followptr;
t0 = t;
if(t == T)
return 0;
// if this is too slow,
// could sort these first
// and then do one loop.
if(t->etype == TINTER) {
for(im=iface->type; im; im=im->down) {
for(tm=t->type; tm; tm=tm->down) {
if(tm->sym == im->sym) {
if(eqtype(tm->type, im->type))
goto found;
*m = im;
*samename = tm;
*ptr = 0;
return 0;
}
}
*m = im;
*samename = nil;
*ptr = 0;
return 0;
found:;
}
return 1;
}
t = methtype(t);
if(t != T)
expandmeth(t->sym, t);
for(im=iface->type; im; im=im->down) {
imtype = methodfunc(im->type, 0);
tm = ifacelookdot(im->sym, t, &followptr, 0);
if(tm == T || !eqtype(methodfunc(tm->type, 0), imtype)) {
if(tm == T)
tm = ifacelookdot(im->sym, t, &followptr, 1);
*m = im;
*samename = tm;
*ptr = 0;
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->type)) {
if(0 && debug['r'])
yyerror("interface pointer mismatch");
*m = im;
*samename = nil;
*ptr = 1;
return 0;
}
}
return 1;
}
/*
* 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));
}
void
listsort(NodeList** l, int(*f)(Node*, Node*))
{
NodeList *l1, *l2, *le;
if(*l == nil || (*l)->next == nil)
return;
l1 = *l;
l2 = *l;
for(;;) {
l2 = l2->next;
if(l2 == nil)
break;
l2 = l2->next;
if(l2 == nil)
break;
l1 = l1->next;
}
l2 = l1->next;
l1->next = nil;
l2->end = (*l)->end;
(*l)->end = l1;
l1 = *l;
listsort(&l1, f);
listsort(&l2, f);
if((*f)(l1->n, l2->n) < 0) {
*l = l1;
} else {
*l = l2;
l2 = l1;
l1 = *l;
}
// now l1 == *l; and l1 < l2
while ((l1 != nil) && (l2 != nil)) {
while ((l1->next != nil) && (*f)(l1->next->n, l2->n) < 0)
l1 = l1->next;
// l1 is last one from l1 that is < l2
le = l1->next; // le is the rest of l1, first one that is >= l2
if(le != nil)
le->end = (*l)->end;
(*l)->end = l1; // cut *l at l1
*l = concat(*l, l2); // glue l2 to *l's tail
l1 = l2; // l1 is the first element of *l that is < the new l2
l2 = le; // ... because l2 now is the old tail of l1
}
*l = concat(*l, l2); // any remainder
}
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 && n->realtype != T)
if(strncmp(n->sym->name, "autotmp_", 8) != 0)
if(strncmp(n->sym->name, "statictmp_", 8) != 0)
return typename(n->realtype)->left->sym;
return S;
}
/*
* Convert raw string to the prefix that will be used in the symbol table.
* Invalid bytes turn into %xx. Right now the only bytes that need
* escaping are %, ., and ", but we escape all control characters too.
*/
static char*
pathtoprefix(char *s)
{
static char hex[] = "0123456789abcdef";
char *p, *r, *w;
int n;
// check for chars that need escaping
n = 0;
for(r=s; *r; r++)
if(*r <= ' ' || *r == '.' || *r == '%' || *r == '"')
n++;
// quick exit
if(n == 0)
return s;
// escape
p = mal((r-s)+1+2*n);
for(r=s, w=p; *r; r++) {
if(*r <= ' ' || *r == '.' || *r == '%' || *r == '"') {
*w++ = '%';
*w++ = hex[(*r>>4)&0xF];
*w++ = hex[*r&0xF];
} else
*w++ = *r;
}
*w = '\0';
return p;
}
Pkg*
mkpkg(Strlit *path)
{
Pkg *p;
int h;
if(strlen(path->s) != path->len) {
yyerror("import path contains NUL byte");
errorexit();
}
h = stringhash(path->s) & (nelem(phash)-1);
for(p=phash[h]; p; p=p->link)
if(p->path->len == path->len && memcmp(path->s, p->path->s, path->len) == 0)
return p;
p = mal(sizeof *p);
p->path = path;
p->prefix = pathtoprefix(path->s);
p->link = phash[h];
phash[h] = p;
return p;
}
Strlit*
strlit(char *s)
{
Strlit *t;
t = mal(sizeof *t + strlen(s));
strcpy(t->s, s);
t->len = strlen(s);
return t;
}