mirror of
https://github.com/golang/go
synced 2024-11-20 10:14:43 -07:00
b04c890a89
Fixes #3465. R=golang-dev, rsc, remyoudompheng, iant CC=golang-dev https://golang.org/cl/6448097
3661 lines
66 KiB
C
3661 lines
66 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 "yerr.h"
|
|
|
|
typedef struct Error Error;
|
|
struct Error
|
|
{
|
|
int lineno;
|
|
int seq;
|
|
char *msg;
|
|
};
|
|
static Error *err;
|
|
static int nerr;
|
|
static int merr;
|
|
|
|
void
|
|
errorexit(void)
|
|
{
|
|
flusherrors();
|
|
if(outfile)
|
|
remove(outfile);
|
|
exits("error");
|
|
}
|
|
|
|
extern int yychar;
|
|
int
|
|
parserline(void)
|
|
{
|
|
if(yychar != 0 && yychar != -2) // parser has one symbol lookahead
|
|
return prevlineno;
|
|
return lineno;
|
|
}
|
|
|
|
static void
|
|
adderr(int line, char *fmt, va_list arg)
|
|
{
|
|
Fmt f;
|
|
Error *p;
|
|
|
|
fmtstrinit(&f);
|
|
fmtprint(&f, "%L: ", line);
|
|
fmtvprint(&f, fmt, arg);
|
|
fmtprint(&f, "\n");
|
|
|
|
if(nerr >= merr) {
|
|
if(merr == 0)
|
|
merr = 16;
|
|
else
|
|
merr *= 2;
|
|
p = realloc(err, merr*sizeof err[0]);
|
|
if(p == nil) {
|
|
merr = nerr;
|
|
flusherrors();
|
|
print("out of memory\n");
|
|
errorexit();
|
|
}
|
|
err = p;
|
|
}
|
|
err[nerr].seq = nerr;
|
|
err[nerr].lineno = line;
|
|
err[nerr].msg = fmtstrflush(&f);
|
|
nerr++;
|
|
}
|
|
|
|
static int
|
|
errcmp(const void *va, const void *vb)
|
|
{
|
|
Error *a, *b;
|
|
|
|
a = (Error*)va;
|
|
b = (Error*)vb;
|
|
if(a->lineno != b->lineno)
|
|
return a->lineno - b->lineno;
|
|
if(a->seq != b->seq)
|
|
return a->seq - b->seq;
|
|
return 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)
|
|
remove(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(nsavederrors+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);
|
|
|
|
// An unexpected EOF caused a syntax error. Use the previous
|
|
// line number since getc generated a fake newline character.
|
|
if(curio.eofnl)
|
|
lexlineno = prevlineno;
|
|
|
|
// 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(nsavederrors+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
|
|
algtype1(Type *t, Type **bad)
|
|
{
|
|
int a, ret;
|
|
Type *t1;
|
|
|
|
if(bad)
|
|
*bad = T;
|
|
|
|
switch(t->etype) {
|
|
case TINT8:
|
|
case TUINT8:
|
|
case TINT16:
|
|
case TUINT16:
|
|
case TINT32:
|
|
case TUINT32:
|
|
case TINT64:
|
|
case TUINT64:
|
|
case TINT:
|
|
case TUINT:
|
|
case TUINTPTR:
|
|
case TBOOL:
|
|
case TPTR32:
|
|
case TPTR64:
|
|
case TCHAN:
|
|
case TUNSAFEPTR:
|
|
return AMEM;
|
|
|
|
case TFUNC:
|
|
case TMAP:
|
|
if(bad)
|
|
*bad = t;
|
|
return ANOEQ;
|
|
|
|
case TFLOAT32:
|
|
return AFLOAT32;
|
|
|
|
case TFLOAT64:
|
|
return AFLOAT64;
|
|
|
|
case TCOMPLEX64:
|
|
return ACPLX64;
|
|
|
|
case TCOMPLEX128:
|
|
return ACPLX128;
|
|
|
|
case TSTRING:
|
|
return ASTRING;
|
|
|
|
case TINTER:
|
|
if(isnilinter(t))
|
|
return ANILINTER;
|
|
return AINTER;
|
|
|
|
case TARRAY:
|
|
if(isslice(t)) {
|
|
if(bad)
|
|
*bad = t;
|
|
return ANOEQ;
|
|
}
|
|
if(t->bound == 0)
|
|
return AMEM;
|
|
a = algtype1(t->type, bad);
|
|
if(a == ANOEQ || a == AMEM) {
|
|
if(a == ANOEQ && bad)
|
|
*bad = t;
|
|
return a;
|
|
}
|
|
return -1; // needs special compare
|
|
|
|
case TSTRUCT:
|
|
if(t->type != T && t->type->down == T) {
|
|
// One-field struct is same as that one field alone.
|
|
return algtype1(t->type->type, bad);
|
|
}
|
|
ret = AMEM;
|
|
for(t1=t->type; t1!=T; t1=t1->down) {
|
|
if(isblanksym(t1->sym))
|
|
continue;
|
|
a = algtype1(t1->type, bad);
|
|
if(a == ANOEQ)
|
|
return ANOEQ; // not comparable
|
|
if(a != AMEM)
|
|
ret = -1; // needs special compare
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
fatal("algtype1: unexpected type %T", t);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
algtype(Type *t)
|
|
{
|
|
int a;
|
|
|
|
a = algtype1(t, nil);
|
|
if(a == AMEM || a == ANOEQ) {
|
|
if(isslice(t))
|
|
return ASLICE;
|
|
switch(t->width) {
|
|
case 0:
|
|
return a + AMEM0 - AMEM;
|
|
case 1:
|
|
return a + AMEM8 - AMEM;
|
|
case 2:
|
|
return a + AMEM16 - AMEM;
|
|
case 4:
|
|
return a + AMEM32 - AMEM;
|
|
case 8:
|
|
return a + AMEM64 - AMEM;
|
|
case 16:
|
|
return a + AMEM128 - AMEM;
|
|
}
|
|
}
|
|
return a;
|
|
}
|
|
|
|
Type*
|
|
maptype(Type *key, Type *val)
|
|
{
|
|
Type *t;
|
|
|
|
if(key != nil) {
|
|
switch(key->etype) {
|
|
default:
|
|
if(algtype1(key, nil) == ANOEQ)
|
|
yyerror("invalid map key type %T", key);
|
|
break;
|
|
case TANY:
|
|
// will be resolved later.
|
|
break;
|
|
case 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;
|
|
break;
|
|
}
|
|
}
|
|
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:
|
|
case CTRUNE:
|
|
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;
|
|
}
|
|
|
|
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
|
|
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)
|
|
{
|
|
if(n == N)
|
|
return 0;
|
|
return isblanksym(n->sym);
|
|
}
|
|
|
|
int
|
|
isblanksym(Sym *s)
|
|
{
|
|
char *p;
|
|
|
|
if(s == S)
|
|
return 0;
|
|
p = s->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, int mustname)
|
|
{
|
|
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 && (mustname || t->etype != TSTRUCT))
|
|
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;
|
|
}
|
|
|
|
typedef struct TypePairList TypePairList;
|
|
struct TypePairList
|
|
{
|
|
Type *t1;
|
|
Type *t2;
|
|
TypePairList *next;
|
|
};
|
|
|
|
static int
|
|
onlist(TypePairList *l, Type *t1, Type *t2)
|
|
{
|
|
for(; l; l=l->next)
|
|
if((l->t1 == t1 && l->t2 == t2) || (l->t1 == t2 && l->t2 == t1))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
static int eqtype1(Type*, Type*, TypePairList*);
|
|
|
|
// 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)
|
|
{
|
|
return eqtype1(t1, t2, nil);
|
|
}
|
|
|
|
static int
|
|
eqtype1(Type *t1, Type *t2, TypePairList *assumed_equal)
|
|
{
|
|
TypePairList l;
|
|
|
|
if(t1 == t2)
|
|
return 1;
|
|
if(t1 == T || t2 == T || t1->etype != t2->etype)
|
|
return 0;
|
|
if(t1->sym || t2->sym) {
|
|
// Special case: we keep byte and uint8 separate
|
|
// for error messages. Treat them as equal.
|
|
switch(t1->etype) {
|
|
case TUINT8:
|
|
if((t1 == types[TUINT8] || t1 == bytetype) && (t2 == types[TUINT8] || t2 == bytetype))
|
|
return 1;
|
|
break;
|
|
case TINT:
|
|
case TINT32:
|
|
if((t1 == types[runetype->etype] || t1 == runetype) && (t2 == types[runetype->etype] || t2 == runetype))
|
|
return 1;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if(onlist(assumed_equal, t1, t2))
|
|
return 1;
|
|
l.next = assumed_equal;
|
|
l.t1 = t1;
|
|
l.t2 = t2;
|
|
|
|
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 || !eqtype1(t1->type, t2->type, &l) || !eqnote(t1->note, t2->note))
|
|
goto no;
|
|
}
|
|
if(t1 == T && t2 == T)
|
|
goto yes;
|
|
goto no;
|
|
|
|
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 || !eqtype1(ta->type, tb->type, &l))
|
|
goto no;
|
|
}
|
|
if(ta != T || tb != T)
|
|
goto no;
|
|
}
|
|
if(t1 == T && t2 == T)
|
|
goto yes;
|
|
goto no;
|
|
|
|
case TARRAY:
|
|
if(t1->bound != t2->bound)
|
|
goto no;
|
|
break;
|
|
|
|
case TCHAN:
|
|
if(t1->chan != t2->chan)
|
|
goto no;
|
|
break;
|
|
}
|
|
|
|
if(eqtype1(t1->down, t2->down, &l) && eqtype1(t1->type, t2->type, &l))
|
|
goto yes;
|
|
goto no;
|
|
|
|
yes:
|
|
return 1;
|
|
|
|
no:
|
|
return 0;
|
|
}
|
|
|
|
// 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.
|
|
int
|
|
assignop(Type *src, Type *dst, char **why)
|
|
{
|
|
Type *missing, *have;
|
|
int ptr;
|
|
|
|
if(why != nil)
|
|
*why = "";
|
|
|
|
// TODO(rsc,lvd): This behaves poorly in the presence of inlining.
|
|
// https://code.google.com/p/go/issues/detail?id=2795
|
|
if(safemode && importpkg == nil && 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;
|
|
|
|
// we'll have complained about this method anyway, supress spurious messages.
|
|
if(have && have->sym == missing->sym && (have->type->broke || missing->type->broke))
|
|
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 && implements(dst, src, &missing, &have, &ptr))
|
|
*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 []rune
|
|
// and dst is a string type.
|
|
if(isint[src->etype] && dst->etype == TSTRING)
|
|
return ORUNESTR;
|
|
|
|
if(isslice(src) && dst->etype == TSTRING) {
|
|
if(src->type->etype == bytetype->etype)
|
|
return OARRAYBYTESTR;
|
|
if(src->type->etype == runetype->etype)
|
|
return OARRAYRUNESTR;
|
|
}
|
|
|
|
// 7. src is a string and dst is []byte or []rune.
|
|
// String to slice.
|
|
if(src->etype == TSTRING && isslice(dst)) {
|
|
if(dst->type->etype == bytetype->etype)
|
|
return OSTRARRAYBYTE;
|
|
if(dst->type->etype == runetype->etype)
|
|
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;
|
|
|
|
// Convert ideal bool from comparison to plain bool
|
|
// if the next step is non-bool (like interface{}).
|
|
if(n->type == idealbool && t->etype != TBOOL) {
|
|
if(n->op == ONAME || n->op == OLITERAL) {
|
|
r = nod(OCONVNOP, n, N);
|
|
r->type = types[TBOOL];
|
|
r->typecheck = 1;
|
|
r->implicit = 1;
|
|
n = r;
|
|
}
|
|
}
|
|
|
|
if(eqtype(n->type, t))
|
|
return n;
|
|
|
|
op = assignop(n->type, t, &why);
|
|
if(op == 0) {
|
|
yyerror("cannot use %lN 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;
|
|
r->orig = n;
|
|
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 already contains
|
|
* all the necessary logic to generate a representation
|
|
* of the type that completely describes it.
|
|
* using smprint here avoids duplicating that code.
|
|
* using md5 here is overkill, but i got tired of
|
|
* accidental collisions making the runtime think
|
|
* two types are equal when they really aren't.
|
|
*/
|
|
uint32
|
|
typehash(Type *t)
|
|
{
|
|
char *p;
|
|
MD5 d;
|
|
|
|
if(t->thistuple) {
|
|
// hide method receiver from Tpretty
|
|
t->thistuple = 0;
|
|
p = smprint("%-uT", t);
|
|
t->thistuple = 1;
|
|
} else
|
|
p = smprint("%-uT", t);
|
|
//print("typehash: %s\n", p);
|
|
md5reset(&d);
|
|
md5write(&d, (uchar*)p, strlen(p));
|
|
free(p);
|
|
return md5sum(&d);
|
|
}
|
|
|
|
Type*
|
|
ptrto(Type *t)
|
|
{
|
|
Type *t1;
|
|
|
|
if(tptr == 0)
|
|
fatal("ptrto: no tptr");
|
|
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;
|
|
|
|
if(n->ninit != nil) {
|
|
ul = UINF;
|
|
goto out;
|
|
}
|
|
|
|
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;
|
|
|
|
if(n->ninit) {
|
|
walkstmtlist(n->ninit);
|
|
*init = concat(*init, n->ninit);
|
|
n->ninit = nil;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
Node*
|
|
copyexpr(Node *n, Type *t, NodeList **init)
|
|
{
|
|
Node *a, *l;
|
|
|
|
l = temp(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
|
|
ucistrcmp(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 && ucistrcmp(f->sym->name, s->name) == 0)) {
|
|
if(save)
|
|
*save = f;
|
|
c++;
|
|
}
|
|
}
|
|
u = methtype(t, 0);
|
|
if(u != T) {
|
|
for(f=u->method; f!=T; f=f->down)
|
|
if(f->embedded == 0 && (f->sym == s || (ignorecase && ucistrcmp(f->sym->name, s->name) == 0))) {
|
|
if(save)
|
|
*save = f;
|
|
c++;
|
|
}
|
|
}
|
|
return c;
|
|
}
|
|
|
|
// search depth d for field/method s --
|
|
// 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 selector %N", n);
|
|
n->left = N;
|
|
return n;
|
|
}
|
|
|
|
// 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(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, 0);
|
|
if(u != T) {
|
|
for(f=u->method; f!=T; f=f->down) {
|
|
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(Type *t)
|
|
{
|
|
Symlink *sl;
|
|
Type *f;
|
|
int c, d;
|
|
|
|
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) {
|
|
// addot1 may have dug out arbitrary fields, we only want methods.
|
|
if(f->type->etype == TFUNC && f->type->thistuple > 0) {
|
|
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(mustname && (t->sym == nil || strcmp(t->sym->name, "_") == 0)) {
|
|
// invent a name so that we can refer to it in the trampoline
|
|
snprint(buf, sizeof buf, ".anon%d", gen++);
|
|
n = newname(lookup(buf));
|
|
} else if(t->sym)
|
|
n = newname(t->sym);
|
|
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(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);
|
|
|
|
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 = nod(ODCLFUNC, N, N);
|
|
fn->nname = newname(newnam);
|
|
fn->nname->defn = fn;
|
|
fn->nname->ntype = t;
|
|
declare(fn->nname, PFUNC);
|
|
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 Node*
|
|
hashmem(Type *t)
|
|
{
|
|
Node *tfn, *n;
|
|
Sym *sym;
|
|
|
|
sym = pkglookup("memhash", runtimepkg);
|
|
|
|
n = newname(sym);
|
|
n->class = PFUNC;
|
|
tfn = nod(OTFUNC, N, N);
|
|
tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(ptrto(types[TUINTPTR]))));
|
|
tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR])));
|
|
tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(ptrto(t))));
|
|
typecheck(&tfn, Etype);
|
|
n->type = tfn->type;
|
|
return n;
|
|
}
|
|
|
|
static Node*
|
|
hashfor(Type *t)
|
|
{
|
|
int a;
|
|
Sym *sym;
|
|
Node *tfn, *n;
|
|
|
|
a = algtype1(t, nil);
|
|
switch(a) {
|
|
case AMEM:
|
|
return hashmem(t);
|
|
case AINTER:
|
|
sym = pkglookup("interhash", runtimepkg);
|
|
break;
|
|
case ANILINTER:
|
|
sym = pkglookup("nilinterhash", runtimepkg);
|
|
break;
|
|
case ASTRING:
|
|
sym = pkglookup("strhash", runtimepkg);
|
|
break;
|
|
case AFLOAT32:
|
|
sym = pkglookup("f32hash", runtimepkg);
|
|
break;
|
|
case AFLOAT64:
|
|
sym = pkglookup("f64hash", runtimepkg);
|
|
break;
|
|
case ACPLX64:
|
|
sym = pkglookup("c64hash", runtimepkg);
|
|
break;
|
|
case ACPLX128:
|
|
sym = pkglookup("c128hash", runtimepkg);
|
|
break;
|
|
default:
|
|
sym = typesymprefix(".hash", t);
|
|
break;
|
|
}
|
|
|
|
n = newname(sym);
|
|
n->class = PFUNC;
|
|
tfn = nod(OTFUNC, N, N);
|
|
tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(ptrto(types[TUINTPTR]))));
|
|
tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR])));
|
|
tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(ptrto(t))));
|
|
typecheck(&tfn, Etype);
|
|
n->type = tfn->type;
|
|
return n;
|
|
}
|
|
|
|
/*
|
|
* Generate a helper function to compute the hash of a value of type t.
|
|
*/
|
|
void
|
|
genhash(Sym *sym, Type *t)
|
|
{
|
|
Node *n, *fn, *np, *nh, *ni, *call, *nx, *na, *tfn;
|
|
Node *hashel;
|
|
Type *first, *t1;
|
|
int old_safemode;
|
|
int64 size, mul;
|
|
|
|
if(debug['r'])
|
|
print("genhash %S %T\n", sym, t);
|
|
|
|
lineno = 1; // less confusing than end of input
|
|
dclcontext = PEXTERN;
|
|
markdcl();
|
|
|
|
// func sym(h *uintptr, s uintptr, p *T)
|
|
fn = nod(ODCLFUNC, N, N);
|
|
fn->nname = newname(sym);
|
|
fn->nname->class = PFUNC;
|
|
tfn = nod(OTFUNC, N, N);
|
|
fn->nname->ntype = tfn;
|
|
|
|
n = nod(ODCLFIELD, newname(lookup("h")), typenod(ptrto(types[TUINTPTR])));
|
|
tfn->list = list(tfn->list, n);
|
|
nh = n->left;
|
|
n = nod(ODCLFIELD, newname(lookup("s")), typenod(types[TUINTPTR]));
|
|
tfn->list = list(tfn->list, n);
|
|
n = nod(ODCLFIELD, newname(lookup("p")), typenod(ptrto(t)));
|
|
tfn->list = list(tfn->list, n);
|
|
np = n->left;
|
|
|
|
funchdr(fn);
|
|
typecheck(&fn->nname->ntype, Etype);
|
|
|
|
// genhash is only called for types that have equality but
|
|
// cannot be handled by the standard algorithms,
|
|
// so t must be either an array or a struct.
|
|
switch(t->etype) {
|
|
default:
|
|
fatal("genhash %T", t);
|
|
case TARRAY:
|
|
if(isslice(t))
|
|
fatal("genhash %T", t);
|
|
// An array of pure memory would be handled by the
|
|
// standard algorithm, so the element type must not be
|
|
// pure memory.
|
|
hashel = hashfor(t->type);
|
|
n = nod(ORANGE, N, nod(OIND, np, N));
|
|
ni = newname(lookup("i"));
|
|
ni->type = types[TINT];
|
|
n->list = list1(ni);
|
|
n->colas = 1;
|
|
colasdefn(n->list, n);
|
|
ni = n->list->n;
|
|
|
|
// *h = *h<<3 | *h>>61
|
|
n->nbody = list(n->nbody,
|
|
nod(OAS,
|
|
nod(OIND, nh, N),
|
|
nod(OOR,
|
|
nod(OLSH, nod(OIND, nh, N), nodintconst(3)),
|
|
nod(ORSH, nod(OIND, nh, N), nodintconst(widthptr*8-3)))));
|
|
|
|
// *h *= mul
|
|
// Same multipliers as in runtime.memhash.
|
|
if(widthptr == 4)
|
|
mul = 3267000013LL;
|
|
else
|
|
mul = 23344194077549503LL;
|
|
n->nbody = list(n->nbody,
|
|
nod(OAS,
|
|
nod(OIND, nh, N),
|
|
nod(OMUL, nod(OIND, nh, N), nodintconst(mul))));
|
|
|
|
// hashel(h, sizeof(p[i]), &p[i])
|
|
call = nod(OCALL, hashel, N);
|
|
call->list = list(call->list, nh);
|
|
call->list = list(call->list, nodintconst(t->type->width));
|
|
nx = nod(OINDEX, np, ni);
|
|
nx->bounded = 1;
|
|
na = nod(OADDR, nx, N);
|
|
na->etype = 1; // no escape to heap
|
|
call->list = list(call->list, na);
|
|
n->nbody = list(n->nbody, call);
|
|
|
|
fn->nbody = list(fn->nbody, n);
|
|
break;
|
|
|
|
case TSTRUCT:
|
|
// Walk the struct using memhash for runs of AMEM
|
|
// and calling specific hash functions for the others.
|
|
first = T;
|
|
for(t1=t->type;; t1=t1->down) {
|
|
if(t1 != T && (isblanksym(t1->sym) || algtype1(t1->type, nil) == AMEM)) {
|
|
if(first == T && !isblanksym(t1->sym))
|
|
first = t1;
|
|
continue;
|
|
}
|
|
// Run memhash for fields up to this one.
|
|
while(first != T && isblanksym(first->sym))
|
|
first = first->down;
|
|
if(first != T) {
|
|
if(first->down == t1)
|
|
size = first->type->width;
|
|
else if(t1 == T)
|
|
size = t->width - first->width; // first->width is offset
|
|
else
|
|
size = t1->width - first->width; // both are offsets
|
|
hashel = hashmem(first->type);
|
|
// hashel(h, size, &p.first)
|
|
call = nod(OCALL, hashel, N);
|
|
call->list = list(call->list, nh);
|
|
call->list = list(call->list, nodintconst(size));
|
|
nx = nod(OXDOT, np, newname(first->sym)); // TODO: fields from other packages?
|
|
na = nod(OADDR, nx, N);
|
|
na->etype = 1; // no escape to heap
|
|
call->list = list(call->list, na);
|
|
fn->nbody = list(fn->nbody, call);
|
|
|
|
first = T;
|
|
}
|
|
if(t1 == T)
|
|
break;
|
|
|
|
// Run hash for this field.
|
|
hashel = hashfor(t1->type);
|
|
// hashel(h, size, &p.t1)
|
|
call = nod(OCALL, hashel, N);
|
|
call->list = list(call->list, nh);
|
|
call->list = list(call->list, nodintconst(t1->type->width));
|
|
nx = nod(OXDOT, np, newname(t1->sym)); // TODO: fields from other packages?
|
|
na = nod(OADDR, nx, N);
|
|
na->etype = 1; // no escape to heap
|
|
call->list = list(call->list, na);
|
|
fn->nbody = list(fn->nbody, call);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if(debug['r'])
|
|
dumplist("genhash body", fn->nbody);
|
|
|
|
funcbody(fn);
|
|
curfn = fn;
|
|
fn->dupok = 1;
|
|
typecheck(&fn, Etop);
|
|
typechecklist(fn->nbody, Etop);
|
|
curfn = nil;
|
|
|
|
// Disable safemode while compiling this code: the code we
|
|
// generate internally can refer to unsafe.Pointer.
|
|
// In this case it can happen if we need to generate an ==
|
|
// for a struct containing a reflect.Value, which itself has
|
|
// an unexported field of type unsafe.Pointer.
|
|
old_safemode = safemode;
|
|
safemode = 0;
|
|
funccompile(fn, 0);
|
|
safemode = old_safemode;
|
|
}
|
|
|
|
// Return node for
|
|
// if p.field != q.field { *eq = false; return }
|
|
static Node*
|
|
eqfield(Node *p, Node *q, Node *field, Node *eq)
|
|
{
|
|
Node *nif, *nx, *ny;
|
|
|
|
nx = nod(OXDOT, p, field);
|
|
ny = nod(OXDOT, q, field);
|
|
nif = nod(OIF, N, N);
|
|
nif->ntest = nod(ONE, nx, ny);
|
|
nif->nbody = list(nif->nbody, nod(OAS, nod(OIND, eq, N), nodbool(0)));
|
|
nif->nbody = list(nif->nbody, nod(ORETURN, N, N));
|
|
return nif;
|
|
}
|
|
|
|
static Node*
|
|
eqmemfunc(vlong size, Type *type)
|
|
{
|
|
char buf[30];
|
|
Node *fn;
|
|
|
|
switch(size) {
|
|
default:
|
|
fn = syslook("memequal", 1);
|
|
break;
|
|
case 1:
|
|
case 2:
|
|
case 4:
|
|
case 8:
|
|
case 16:
|
|
snprint(buf, sizeof buf, "memequal%d", (int)size*8);
|
|
fn = syslook(buf, 1);
|
|
break;
|
|
}
|
|
argtype(fn, type);
|
|
argtype(fn, type);
|
|
return fn;
|
|
}
|
|
|
|
// Return node for
|
|
// if memequal(size, &p.field, &q.field, eq); !*eq { return }
|
|
static Node*
|
|
eqmem(Node *p, Node *q, Node *field, vlong size, Node *eq)
|
|
{
|
|
Node *nif, *nx, *ny, *call;
|
|
|
|
nx = nod(OADDR, nod(OXDOT, p, field), N);
|
|
nx->etype = 1; // does not escape
|
|
ny = nod(OADDR, nod(OXDOT, q, field), N);
|
|
ny->etype = 1; // does not escape
|
|
typecheck(&nx, Erv);
|
|
typecheck(&ny, Erv);
|
|
|
|
call = nod(OCALL, eqmemfunc(size, nx->type->type), N);
|
|
call->list = list(call->list, eq);
|
|
call->list = list(call->list, nodintconst(size));
|
|
call->list = list(call->list, nx);
|
|
call->list = list(call->list, ny);
|
|
|
|
nif = nod(OIF, N, N);
|
|
nif->ninit = list(nif->ninit, call);
|
|
nif->ntest = nod(ONOT, nod(OIND, eq, N), N);
|
|
nif->nbody = list(nif->nbody, nod(ORETURN, N, N));
|
|
return nif;
|
|
}
|
|
|
|
/*
|
|
* Generate a helper function to check equality of two values of type t.
|
|
*/
|
|
void
|
|
geneq(Sym *sym, Type *t)
|
|
{
|
|
Node *n, *fn, *np, *neq, *nq, *tfn, *nif, *ni, *nx, *ny, *nrange;
|
|
Type *t1, *first;
|
|
int old_safemode;
|
|
int64 size;
|
|
|
|
if(debug['r'])
|
|
print("geneq %S %T\n", sym, t);
|
|
|
|
lineno = 1; // less confusing than end of input
|
|
dclcontext = PEXTERN;
|
|
markdcl();
|
|
|
|
// func sym(eq *bool, s uintptr, p, q *T)
|
|
fn = nod(ODCLFUNC, N, N);
|
|
fn->nname = newname(sym);
|
|
fn->nname->class = PFUNC;
|
|
tfn = nod(OTFUNC, N, N);
|
|
fn->nname->ntype = tfn;
|
|
|
|
n = nod(ODCLFIELD, newname(lookup("eq")), typenod(ptrto(types[TBOOL])));
|
|
tfn->list = list(tfn->list, n);
|
|
neq = n->left;
|
|
n = nod(ODCLFIELD, newname(lookup("s")), typenod(types[TUINTPTR]));
|
|
tfn->list = list(tfn->list, n);
|
|
n = nod(ODCLFIELD, newname(lookup("p")), typenod(ptrto(t)));
|
|
tfn->list = list(tfn->list, n);
|
|
np = n->left;
|
|
n = nod(ODCLFIELD, newname(lookup("q")), typenod(ptrto(t)));
|
|
tfn->list = list(tfn->list, n);
|
|
nq = n->left;
|
|
|
|
funchdr(fn);
|
|
|
|
// geneq is only called for types that have equality but
|
|
// cannot be handled by the standard algorithms,
|
|
// so t must be either an array or a struct.
|
|
switch(t->etype) {
|
|
default:
|
|
fatal("geneq %T", t);
|
|
case TARRAY:
|
|
if(isslice(t))
|
|
fatal("geneq %T", t);
|
|
// An array of pure memory would be handled by the
|
|
// standard memequal, so the element type must not be
|
|
// pure memory. Even if we unrolled the range loop,
|
|
// each iteration would be a function call, so don't bother
|
|
// unrolling.
|
|
nrange = nod(ORANGE, N, nod(OIND, np, N));
|
|
ni = newname(lookup("i"));
|
|
ni->type = types[TINT];
|
|
nrange->list = list1(ni);
|
|
nrange->colas = 1;
|
|
colasdefn(nrange->list, nrange);
|
|
ni = nrange->list->n;
|
|
|
|
// if p[i] != q[i] { *eq = false; return }
|
|
nx = nod(OINDEX, np, ni);
|
|
nx->bounded = 1;
|
|
ny = nod(OINDEX, nq, ni);
|
|
ny->bounded = 1;
|
|
|
|
nif = nod(OIF, N, N);
|
|
nif->ntest = nod(ONE, nx, ny);
|
|
nif->nbody = list(nif->nbody, nod(OAS, nod(OIND, neq, N), nodbool(0)));
|
|
nif->nbody = list(nif->nbody, nod(ORETURN, N, N));
|
|
nrange->nbody = list(nrange->nbody, nif);
|
|
fn->nbody = list(fn->nbody, nrange);
|
|
|
|
// *eq = true;
|
|
fn->nbody = list(fn->nbody, nod(OAS, nod(OIND, neq, N), nodbool(1)));
|
|
break;
|
|
|
|
case TSTRUCT:
|
|
// Walk the struct using memequal for runs of AMEM
|
|
// and calling specific equality tests for the others.
|
|
first = T;
|
|
for(t1=t->type;; t1=t1->down) {
|
|
if(t1 != T && (isblanksym(t1->sym) || algtype1(t1->type, nil) == AMEM)) {
|
|
if(first == T && !isblanksym(t1->sym))
|
|
first = t1;
|
|
continue;
|
|
}
|
|
// Run memequal for fields up to this one.
|
|
// TODO(rsc): All the calls to newname are wrong for
|
|
// cross-package unexported fields.
|
|
while(first != T && isblanksym(first->sym))
|
|
first = first->down;
|
|
if(first != T) {
|
|
if(first->down == t1) {
|
|
fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym), neq));
|
|
} else if(first->down->down == t1) {
|
|
fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym), neq));
|
|
first = first->down;
|
|
if(!isblanksym(first->sym))
|
|
fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym), neq));
|
|
} else {
|
|
// More than two fields: use memequal.
|
|
if(t1 == T)
|
|
size = t->width - first->width; // first->width is offset
|
|
else
|
|
size = t1->width - first->width; // both are offsets
|
|
fn->nbody = list(fn->nbody, eqmem(np, nq, newname(first->sym), size, neq));
|
|
}
|
|
first = T;
|
|
}
|
|
if(t1 == T)
|
|
break;
|
|
|
|
// Check this field, which is not just memory.
|
|
fn->nbody = list(fn->nbody, eqfield(np, nq, newname(t1->sym), neq));
|
|
}
|
|
|
|
// *eq = true;
|
|
fn->nbody = list(fn->nbody, nod(OAS, nod(OIND, neq, N), nodbool(1)));
|
|
break;
|
|
}
|
|
|
|
if(debug['r'])
|
|
dumplist("geneq body", fn->nbody);
|
|
|
|
funcbody(fn);
|
|
curfn = fn;
|
|
fn->dupok = 1;
|
|
typecheck(&fn, Etop);
|
|
typechecklist(fn->nbody, Etop);
|
|
curfn = nil;
|
|
|
|
// Disable safemode while compiling this code: the code we
|
|
// generate internally can refer to unsafe.Pointer.
|
|
// In this case it can happen if we need to generate an ==
|
|
// for a struct containing a reflect.Value, which itself has
|
|
// an unexported field of type unsafe.Pointer.
|
|
old_safemode = safemode;
|
|
safemode = 0;
|
|
funccompile(fn, 0);
|
|
safemode = old_safemode;
|
|
}
|
|
|
|
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, 0);
|
|
if(t != T)
|
|
expandmeth(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) {
|
|
// Flatten list and steal storage.
|
|
// Poison pointer to catch errant uses.
|
|
l = n->list;
|
|
n->list = (NodeList*)1;
|
|
return l;
|
|
}
|
|
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. All control characters, space, '%' and '"', as well as
|
|
* non-7-bit clean bytes turn into %xx. The period needs escaping
|
|
* only in the last segment of the path, and it makes for happier
|
|
* users if we escape that as little as possible.
|
|
*
|
|
* If you edit this, edit ../ld/lib.c:/^pathtoprefix copy too.
|
|
*/
|
|
static char*
|
|
pathtoprefix(char *s)
|
|
{
|
|
static char hex[] = "0123456789abcdef";
|
|
char *p, *r, *w, *l;
|
|
int n;
|
|
|
|
// find first character past the last slash, if any.
|
|
l = s;
|
|
for(r=s; *r; r++)
|
|
if(*r == '/')
|
|
l = r+1;
|
|
|
|
// check for chars that need escaping
|
|
n = 0;
|
|
for(r=s; *r; r++)
|
|
if(*r <= ' ' || (*r == '.' && r >= l) || *r == '%' || *r == '"' || *r >= 0x7f)
|
|
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 >= l) || *r == '%' || *r == '"' || *r >= 0x7f) {
|
|
*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(isbadimport(path))
|
|
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;
|
|
}
|
|
|
|
void
|
|
addinit(Node **np, NodeList *init)
|
|
{
|
|
Node *n;
|
|
|
|
if(init == nil)
|
|
return;
|
|
|
|
n = *np;
|
|
switch(n->op) {
|
|
case ONAME:
|
|
case OLITERAL:
|
|
// There may be multiple refs to this node;
|
|
// introduce OCONVNOP to hold init list.
|
|
n = nod(OCONVNOP, n, N);
|
|
n->type = n->left->type;
|
|
n->typecheck = 1;
|
|
*np = n;
|
|
break;
|
|
}
|
|
n->ninit = concat(init, n->ninit);
|
|
n->ullman = UINF;
|
|
}
|
|
|
|
int
|
|
isbadimport(Strlit *path)
|
|
{
|
|
char *s;
|
|
Rune r;
|
|
|
|
if(strlen(path->s) != path->len) {
|
|
yyerror("import path contains NUL");
|
|
return 1;
|
|
}
|
|
|
|
s = path->s;
|
|
while(*s) {
|
|
s += chartorune(&r, s);
|
|
if(r == Runeerror) {
|
|
yyerror("import path contains invalid UTF-8 sequence: \"%Z\"", path);
|
|
return 1;
|
|
}
|
|
if(r < 0x20 || r == 0x7f) {
|
|
yyerror("import path contains control character: \"%Z\"", path);
|
|
return 1;
|
|
}
|
|
if(r == '\\') {
|
|
yyerror("import path contains backslash; use slash: \"%Z\"", path);
|
|
return 1;
|
|
}
|
|
if(isspacerune(r)) {
|
|
yyerror("import path contains space character: \"%Z\"", path);
|
|
return 1;
|
|
}
|
|
if(utfrune("!\"#$%&'()*,:;<=>?[]^`{|}", r)) {
|
|
yyerror("import path contains invalid character '%C': \"%Z\"", r, path);
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|