mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
-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
3919 lines
67 KiB
C
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;
|
|
}
|